mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 05:13:29 +01:00
Adding support for bitcoin & asset addresses
This commit is contained in:
parent
b784c8d643
commit
21b1873dfe
96
src/main/scala/org/scalacoin/protocol/Address.scala
Normal file
96
src/main/scala/org/scalacoin/protocol/Address.scala
Normal file
@ -0,0 +1,96 @@
|
||||
package org.scalacoin.protocol
|
||||
|
||||
import org.bitcoinj.core.{VersionedChecksummedBytes, Base58, Utils}
|
||||
|
||||
case class AddressInfo(bitcoinAddress: BitcoinAddress, n_tx: Long, total_received: Long, total_sent: Long,
|
||||
final_balance: Long)
|
||||
|
||||
sealed abstract class Address(val address : String)
|
||||
sealed case class BitcoinAddress(bitcoinAddress: String) extends Address(bitcoinAddress) {
|
||||
require(BitcoinAddress.validate(bitcoinAddress), "Bitcoin address was invalid " + bitcoinAddress)
|
||||
}
|
||||
|
||||
sealed case class AssetAddress(assetAddress : String) extends Address(assetAddress) {
|
||||
require(AssetAddress.validate(assetAddress), "The provided asset was was invalid: " + assetAddress)
|
||||
}
|
||||
|
||||
object BitcoinAddress {
|
||||
def validate(bitcoinAddress: String): Boolean = {
|
||||
val illegalChars = List('O', 'I', 'l', '0')
|
||||
bitcoinAddress.length >= 26 && bitcoinAddress.length <= 35 &&
|
||||
(p2pkh(bitcoinAddress) || p2shAddress(bitcoinAddress)) &&
|
||||
bitcoinAddress.filter(c => illegalChars.contains(c)).size == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a bitcoin address to an asset address
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
def convertToAssetAddress(address : BitcoinAddress) : AssetAddress = {
|
||||
val underlying : String = address.bitcoinAddress
|
||||
val base58decodeChecked : Array[Byte] = Base58.decodeChecked(underlying)
|
||||
require (
|
||||
base58decodeChecked.size == 21
|
||||
)
|
||||
AssetAddress(new VersionedChecksummedBytes(0x13, base58decodeChecked){}.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a address is a valid p2sh address
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
def p2shAddress(address : String) : Boolean = {
|
||||
address.charAt(0) == '3' || address.charAt(0) == '2'
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a address is a valid p2sh address
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
def p2shAddress(address : BitcoinAddress) : Boolean = p2shAddress(address.bitcoinAddress)
|
||||
|
||||
/**
|
||||
* Checks if an address is a valid p2pkh address
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
def p2pkh(address : String) : Boolean = {
|
||||
val firstChar = address.charAt(0)
|
||||
firstChar == '1' || firstChar == 'm' || firstChar == 'n'
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an address is a valid p2pkh address
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
def p2pkh(address : BitcoinAddress) : Boolean = p2pkh(address.bitcoinAddress)
|
||||
}
|
||||
|
||||
object AssetAddress {
|
||||
def validate(assetAddress : String) = {
|
||||
//asset addresses must have the one byte namespace equivalent to 19
|
||||
//which ends up being 'a' in the ascii character set
|
||||
val base58decodechecked : Array[Byte] = Base58.decodeChecked(assetAddress)
|
||||
require(base58decodechecked != null)
|
||||
base58decodechecked.size == 22 && base58decodechecked(0) == 0x13
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an asset address into a bitcoin address
|
||||
* @param assetAddress
|
||||
* @return
|
||||
*/
|
||||
def convertToBitcoinAddress(assetAddress : AssetAddress) = {
|
||||
val underlying : String = assetAddress.assetAddress
|
||||
val base58decodeChecked : Array[Byte] = Base58.decodeChecked(underlying)
|
||||
|
||||
require(base58decodeChecked.size == 22)
|
||||
|
||||
val slice = base58decodeChecked.slice(2, base58decodeChecked.length)
|
||||
BitcoinAddress(new VersionedChecksummedBytes(base58decodeChecked(1), slice){}.toString())
|
||||
}
|
||||
}
|
27
src/test/scala/org/scalacoin/protocol/AddressTest.scala
Normal file
27
src/test/scala/org/scalacoin/protocol/AddressTest.scala
Normal file
@ -0,0 +1,27 @@
|
||||
package org.scalacoin.protocol
|
||||
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
* Created by chris on 3/23/15.
|
||||
*/
|
||||
class AddressTest extends FlatSpec with MustMatchers {
|
||||
val assetAddress = TestUtil.assetAddress
|
||||
"Addresses" must "be able to convert back and forth between a Bitcoin Address & an asset address" in {
|
||||
val convertedOnce = BitcoinAddress.convertToAssetAddress(TestUtil.bitcoinAddress)
|
||||
val actual : BitcoinAddress = AssetAddress.convertToBitcoinAddress(convertedOnce)
|
||||
actual must be (TestUtil.bitcoinAddress)
|
||||
val bitcoinAddress = AssetAddress.convertToBitcoinAddress(assetAddress)
|
||||
val actualAssetAddress = BitcoinAddress.convertToAssetAddress(bitcoinAddress)
|
||||
actualAssetAddress must be (assetAddress)
|
||||
}
|
||||
|
||||
it must "allow type encapsulation for addresses" in {
|
||||
|
||||
val bitcoinAddress : Address = TestUtil.bitcoinAddress
|
||||
val assetAddress : Address = TestUtil.assetAddress
|
||||
assetAddress must be (TestUtil.assetAddress)
|
||||
bitcoinAddress must be (TestUtil.bitcoinAddress)
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.scalacoin.protocol
|
||||
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
class BitcoinAddressTest extends FlatSpec with MustMatchers {
|
||||
|
||||
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" must "be a valid bitcoin address" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"
|
||||
BitcoinAddress(address).bitcoinAddress must be(address)
|
||||
|
||||
}
|
||||
|
||||
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" must "be a valid p2sh address and not a valid p2pkh" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"
|
||||
BitcoinAddress.p2shAddress(address) must be (true)
|
||||
BitcoinAddress.p2pkh(address) must be (false)
|
||||
}
|
||||
|
||||
"17WN1kFw8D6w1eHzqvkh49xwjE3iPN925b" must "be a valid p2pkh" in {
|
||||
val address = "17WN1kFw8D6w1eHzqvkh49xwjE3iPN925b"
|
||||
BitcoinAddress.p2pkh(address) must be (true)
|
||||
BitcoinAddress.p2shAddress(address) must be (false)
|
||||
}
|
||||
|
||||
"The empty string" must "not be a valid bitcoin address" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
BitcoinAddress("")
|
||||
}
|
||||
}
|
||||
"A string that is 25 characters long" must "not be a valid bitcoin address" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiW"
|
||||
intercept[IllegalArgumentException] {
|
||||
BitcoinAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
"A string that is 36 characters long" must "not be a valid bitcoin address" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyyy"
|
||||
intercept[IllegalArgumentException] {
|
||||
BitcoinAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
"3J98t1WpEZ73CNmQviecrnyiWr (26 characters) " must "be a valid bitcoin address" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiWr"
|
||||
BitcoinAddress(address).bitcoinAddress must be(address)
|
||||
}
|
||||
|
||||
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy (35 characters)" must "be a valid bitcoin address" in {
|
||||
val address = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy"
|
||||
BitcoinAddress(address).bitcoinAddress must be(address)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
"akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA" must "be a valid asset address" in {
|
||||
val assetAddress = AssetAddress("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA")
|
||||
assetAddress.assetAddress must be ("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA")
|
||||
}
|
||||
|
||||
"An asset address with the first character replaced" must "not be a valid asset address" in {
|
||||
//3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy
|
||||
intercept[org.bitcoinj.core.AddressFormatException] {
|
||||
val assetAddress = AssetAddress("aJ98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy")
|
||||
}
|
||||
}
|
||||
}
|
15
src/test/scala/org/scalacoin/util/TestUtil.scala
Normal file
15
src/test/scala/org/scalacoin/util/TestUtil.scala
Normal file
@ -0,0 +1,15 @@
|
||||
package org.scalacoin.util
|
||||
|
||||
import org.scalacoin.protocol.{AssetAddress, BitcoinAddress}
|
||||
|
||||
/**
|
||||
* Created by chris on 12/2/15.
|
||||
*/
|
||||
object TestUtil {
|
||||
|
||||
val testBitcoinAddress = BitcoinAddress("n3p1ct69ao3qxWvEvzLhLtWG2zJGTjN3EV")
|
||||
val testP2SHAddress = BitcoinAddress("2MzYbQdkSVp5wVyMRp6A5PHPuQNHpiaTbCj")
|
||||
val bitcoinAddress = BitcoinAddress("1C4kYhyLftmkn48YarSoLupxHfYFo8kp64")
|
||||
val multiSigAddress = BitcoinAddress("342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey")
|
||||
val assetAddress = AssetAddress("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA")
|
||||
}
|
Loading…
Reference in New Issue
Block a user