mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 23:08:31 +01:00
Merge branch 'master' of https://github.com/bitcoin-s/bitcoin-s-core into refactor_number_system
This commit is contained in:
commit
0dd210f769
5 changed files with 138 additions and 88 deletions
|
@ -21,7 +21,7 @@ trait Base58 extends BitcoinSLogger {
|
|||
* Verifies a given base58 string against its checksum (last 4 decoded bytes)
|
||||
*
|
||||
* @param input base58 string
|
||||
* @return decoded bytes excluding the checksum
|
||||
* @return
|
||||
*/
|
||||
def decodeCheck(input: String) : Try[Seq[Byte]] = {
|
||||
val decoded : Seq[Byte] = decode(input)
|
||||
|
@ -36,12 +36,11 @@ trait Base58 extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes in sequence of bytes and returns base58 bitcoin string
|
||||
*
|
||||
* @param bytes sequence of bytes to be encoded into base58
|
||||
* @return base58 String
|
||||
* @return
|
||||
*/
|
||||
//TODO: Create Base58 Type
|
||||
def encode(bytes : Seq[Byte]) : String = {
|
||||
|
@ -75,51 +74,41 @@ trait Base58 extends BitcoinSLogger {
|
|||
* Encodes a Base58 address from a hash
|
||||
*
|
||||
* @param hash The result of Sha256(RipeMD-160(public key))
|
||||
* @param addressType string. Either "pubkey" or "script"
|
||||
* @param isTestNet boolean
|
||||
* @param addressType "pubkey" or "script"
|
||||
* @param isTestNet Boolean
|
||||
* @return
|
||||
*/
|
||||
def encodePubKeyHashToBase58Address(hash: Sha256Hash160Digest,
|
||||
addressType : String,
|
||||
isTestNet : Boolean) : String = {
|
||||
/* val versionByte : Byte = {
|
||||
require(addressType != "pubkey" || addressType != "script",
|
||||
throw new IllegalArgumentException("Address must be of type 'pubkey' or 'script'."))
|
||||
if (!isTestNet) {
|
||||
if (addressType == "pubkey") MainNet.p2pkhNetworkByte
|
||||
else MainNet.p2shNetworkByte
|
||||
}
|
||||
else if (isTestNet) {
|
||||
if (addressType == "pubkey") TestNet3.p2pkhNetworkByte
|
||||
else TestNet3.p2shNetworkByte
|
||||
}
|
||||
else throw new IllegalArgumentException("Something broke -- Check your parameters. There should be a " +
|
||||
"Sha256RipeMD160 hash, addressType('pubkey' or 'script'), and testnet boolean (true or false).")
|
||||
}*/
|
||||
|
||||
def parseVersionByte(addressType : String, isTestnet : Boolean) : Byte = isTestnet match {
|
||||
case true => if (addressType == "pubkey") TestNet3.p2pkhNetworkByte else TestNet3.p2shNetworkByte
|
||||
case false => if (addressType == "pubkey") MainNet.p2pkhNetworkByte else MainNet.p2shNetworkByte
|
||||
}
|
||||
val versionByte : Byte = parseVersionByte(addressType, isTestNet)
|
||||
//TODO: add logic to determine pubkey/script from first byte
|
||||
def encodePubKeyHashToBase58Address(hash: Sha256Hash160Digest, addressType : String, isTestNet : Boolean) : Address = {
|
||||
val versionByte : Byte = findVersionByte(addressType, isTestNet)
|
||||
val bytes : Seq[Byte] = Seq(versionByte) ++ hash.bytes
|
||||
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
|
||||
encode(bytes ++ checksum)
|
||||
Address(encode(bytes ++ checksum))
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the version byte of an address given the address type and network
|
||||
*
|
||||
* @param addressType "pubkey" or "script"
|
||||
* @param isTestnet Boolean
|
||||
* @return
|
||||
*/
|
||||
private def findVersionByte(addressType : String, isTestnet : Boolean) : Byte = isTestnet match {
|
||||
case true => if (addressType == "pubkey") TestNet3.p2pkhNetworkByte else TestNet3.p2shNetworkByte
|
||||
case false => if (addressType == "pubkey") MainNet.p2pkhNetworkByte else MainNet.p2shNetworkByte
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a private key into Wallet Import Format (WIF)
|
||||
* https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
*
|
||||
* @param privateKey
|
||||
* @param isCompressed
|
||||
* @param isTestNet
|
||||
* @param privateKey Private Key in Hex format
|
||||
* @param isCompressed Boolean
|
||||
* @param isTestNet Boolean
|
||||
* @return
|
||||
*/
|
||||
//TODO: Create WIF PrivateKey Type
|
||||
def encodePrivateKeyToWIF(privateKey : ECPrivateKey,
|
||||
isCompressed : Boolean,
|
||||
isTestNet : Boolean) : String = {
|
||||
def encodePrivateKeyToWIF(privateKey : ECPrivateKey, isCompressed : Boolean, isTestNet : Boolean) : String = {
|
||||
val versionByte : Byte = isTestNet match {
|
||||
case true => TestNet3.privateKey
|
||||
case false => MainNet.privateKey
|
||||
|
@ -137,8 +126,8 @@ trait Base58 extends BitcoinSLogger {
|
|||
* Takes in base58 string and returns sequence of bytes
|
||||
* https://github.com/ACINQ/bitcoin-lib/blob/master/src/main/scala/fr/acinq/bitcoin/Base58.scala
|
||||
*
|
||||
* @param input base58 string to be decoded into a sequence of bytes
|
||||
* @return decoded sequence of bytes
|
||||
* @param input Base58 string to be decoded into a sequence of bytes
|
||||
* @return
|
||||
*/
|
||||
def decode(input: String) : Seq[Byte] = {
|
||||
val zeroes = input.takeWhile(_ == '1').map(_ => 0:Byte).toArray
|
||||
|
@ -148,51 +137,79 @@ trait Base58 extends BitcoinSLogger {
|
|||
if (trim.isEmpty) zeroes else zeroes ++ decoded.toByteArray.dropWhile(_ == 0)
|
||||
}
|
||||
|
||||
def isValid(base58 : String) : Boolean = {
|
||||
if (base58.isEmpty) false
|
||||
else {
|
||||
val decoded = decode(base58)
|
||||
val firstByte = decoded.head
|
||||
val validAddressPreFixBytes: Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(PubKeyAddress) ++ MainNetChainParams.base58Prefixes(ScriptAddress) ++
|
||||
TestNetChainParams.base58Prefixes(PubKeyAddress) ++ TestNetChainParams.base58Prefixes(ScriptAddress)
|
||||
val validSecretKeyPreFixBytes : Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(SecretKey) ++ TestNetChainParams.base58Prefixes(SecretKey)
|
||||
val compressedPubKey = List('K', 'L', 'c').contains(base58.head)
|
||||
def checkCompressedPubKeyValidity : Boolean = {
|
||||
val compressedByte = decoded(decoded.length - 5)
|
||||
compressedByte == 0x01.toByte
|
||||
}
|
||||
try {
|
||||
if (base58.contains(List('0', 'O', 'l', 'I'))) false
|
||||
else if (compressedPubKey) checkCompressedPubKeyValidity
|
||||
else if (validAddressPreFixBytes.contains(firstByte)) base58.length >= 26 && base58.length <= 35
|
||||
else if (validSecretKeyPreFixBytes.contains(firstByte)) {
|
||||
val byteSize = ECPrivateKey.fromBase58ToPrivateKey(base58).bytes.size
|
||||
byteSize == 32
|
||||
}
|
||||
else false
|
||||
}
|
||||
catch {
|
||||
case error : IllegalArgumentException => false
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines if a string is a valid Base58-encoded string.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
def isValid(base58 : String) : Boolean = validityChecks(base58) match {
|
||||
case Success(bool) => bool
|
||||
case Failure(exception) => false
|
||||
}
|
||||
|
||||
/* def isValid(base58 : String) : Boolean = {
|
||||
val firstByte : Seq[Byte]= if (base58.isEmpty) List() else Seq(decode(base58).head)
|
||||
val length = base58.length
|
||||
val validFirstByteInHex = List("00", "05", "80", "6f", "c4", "ef")
|
||||
val invalidChars = List('0','O','l','I')
|
||||
val firstByteInHex = BitcoinSUtil.encodeHex(firstByte)
|
||||
if (!validFirstByteInHex.contains(firstByteInHex)) false
|
||||
else if (length < 25 || length > 36) false
|
||||
else if (base58.contains(invalidChars)) false
|
||||
else true
|
||||
}*/
|
||||
/**
|
||||
* Checks a private key that begins with a symbol corresponding that private key to a compressed public key ('K', 'L', 'c').
|
||||
* In a Base58-encoded private key corresponding to a compressed public key, the 5th-to-last byte should be 0x01.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
private def checkCompressedPubKeyValidity(base58 : String) : Boolean = {
|
||||
val decoded = Base58.decode(base58)
|
||||
val compressedByte = decoded(decoded.length - 5)
|
||||
compressedByte == 0x01.toByte
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the string begins with an Address prefix byte/character.
|
||||
* ('1', '3', 'm', 'n', '2')
|
||||
*
|
||||
* @param byte first byte in the sequence
|
||||
* @return
|
||||
*/
|
||||
private def isValidAddressPreFixByte(byte : Byte) : Boolean = {
|
||||
val validAddressPreFixBytes: Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(PubKeyAddress) ++ MainNetChainParams.base58Prefixes(ScriptAddress) ++
|
||||
TestNetChainParams.base58Prefixes(PubKeyAddress) ++ TestNetChainParams.base58Prefixes(ScriptAddress)
|
||||
validAddressPreFixBytes.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the string begins with a private key prefix byte/character.
|
||||
* ('5', '9', 'c')
|
||||
*
|
||||
* @param byte first byte in the sequence
|
||||
* @return
|
||||
*/
|
||||
private def isValidSecretKeyPreFixByte(byte : Byte) : Boolean = {
|
||||
val validSecretKeyPreFixBytes : Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(SecretKey) ++ TestNetChainParams.base58Prefixes(SecretKey)
|
||||
validSecretKeyPreFixBytes.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of a Base58 encoded string. A Base58 encoded string must not contain ('0', 'O', 'l', 'I').
|
||||
* If the string is an address: it must have a valid address prefix byte and must be between 26-35 characters in length.
|
||||
* If the string is a private key: it must have a valid private key prefix byte and must have a byte size of 32.
|
||||
* If the string is a private key corresponding to a compressed public key, the 5th-to-last byte must be 0x01.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
private def validityChecks(base58: String) : Try[Boolean] = Try {
|
||||
val decoded = decode(base58)
|
||||
val firstByte = decoded.head
|
||||
val compressedPubKey = List('K', 'L', 'c').contains(base58.head)
|
||||
if (base58.contains(List('0', 'O', 'l', 'I'))) false
|
||||
else if (compressedPubKey) checkCompressedPubKeyValidity(base58)
|
||||
else if (isValidAddressPreFixByte(firstByte)) base58.length >= 26 && base58.length <= 35
|
||||
else if (isValidSecretKeyPreFixByte(firstByte)) {
|
||||
val byteSize = ECPrivateKey.fromWIFToPrivateKey(base58).bytes.size
|
||||
byteSize == 32
|
||||
}
|
||||
else false
|
||||
}
|
||||
}
|
||||
|
||||
object Base58 extends Base58
|
||||
|
||||
|
||||
|
||||
object Base58 extends Base58
|
|
@ -40,10 +40,8 @@ class ECPrivateKeyTest extends FlatSpec with MustMatchers {
|
|||
val privateKeyBase58 = CryptoTestUtil.privateKeyBase58
|
||||
val bitcoinjDumpedPrivateKey = new org.bitcoinj.core.DumpedPrivateKey(BitcoinJTestUtil.params,privateKeyBase58)
|
||||
val bitcoinjPrivateKey = bitcoinjDumpedPrivateKey.getKey
|
||||
val privateKey = ECPrivateKey.fromBase58ToPrivateKey(privateKeyBase58)
|
||||
|
||||
val privateKey = ECPrivateKey.fromWIFToPrivateKey(privateKeyBase58)
|
||||
privateKey.hex must be (bitcoinjPrivateKey.getPrivateKeyAsHex)
|
||||
|
||||
}
|
||||
|
||||
it must "create a private key from a sequence of bytes that has the same byte representation of bitcoinj ECKeys" in {
|
||||
|
@ -64,4 +62,15 @@ class ECPrivateKeyTest extends FlatSpec with MustMatchers {
|
|||
key.hex must be (privateKeyHex)
|
||||
}
|
||||
|
||||
it must "determine if a private key corresponds to a compressed public key or not" in {
|
||||
val compressedKey = "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi"
|
||||
val uncompressedKey = "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj"
|
||||
ECPrivateKey.isCompressed(compressedKey) must be (true)
|
||||
ECPrivateKey.isCompressed(uncompressedKey) must be (false)
|
||||
}
|
||||
|
||||
it must "create a fresh private key" in {
|
||||
ECPrivateKey.apply().isInstanceOf[ECPrivateKey] must be (true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -140,4 +140,28 @@ class Base58Test extends FlatSpec with MustMatchers with BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
it must "check validity of base58 string with illegal characters and fail" in {
|
||||
Base58.isValid("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xol") must be (false)
|
||||
}
|
||||
|
||||
it must "decodeCheck a string with a length less than 4 and fail" in {
|
||||
Base58.decodeCheck("asf").isFailure must be (true)
|
||||
}
|
||||
it must "decodeCheck a valid string and succeed" in {
|
||||
Base58.decodeCheck("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou").isSuccess must be (true)
|
||||
}
|
||||
|
||||
it must "encode a pubKeyHash to an address" in {
|
||||
val hash = Sha256Hash160Digest("74f209f6ea907e2ea48f74fae05782ae8a665257")
|
||||
val address = Address("3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou")
|
||||
Base58.encodePubKeyHashToBase58Address(hash, "script", false) must be (address)
|
||||
}
|
||||
|
||||
it must "encode a private key to WIF, then decode it from WIF to hex private key" in {
|
||||
val WIF = "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr"
|
||||
val privateKey = ECPrivateKey.fromWIFToPrivateKey(WIF)
|
||||
Base58.encodePrivateKeyToWIF(privateKey, false, false) must be (WIF)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ trait CryptoTestUtil {
|
|||
def privateKeyHex = BitcoinSUtil.encodeHex(privateKeyBytes)
|
||||
def bitcoinjDumpedPrivateKey = new DumpedPrivateKey(BitcoinJTestUtil.params,privateKeyBase58)
|
||||
def bitcoinjPrivateKey = bitcoinjDumpedPrivateKey.getKey
|
||||
def privateKey = ECPrivateKey.fromBase58ToPrivateKey(privateKeyBase58)
|
||||
def privateKey = ECPrivateKey.fromWIFToPrivateKey(privateKeyBase58)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -102,9 +102,9 @@ trait TransactionTestUtil extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
def signedMultiSignatureTransaction : (Transaction, Int, ScriptPubKey, Seq[ECPublicKey]) = {
|
||||
val key1 = ECPrivateKey.fromBase58ToPrivateKey("cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
|
||||
val key2 = ECPrivateKey.fromBase58ToPrivateKey("cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo")
|
||||
def key3 = ECPrivateKey.fromBase58ToPrivateKey("cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg")
|
||||
val key1 = ECPrivateKey.fromWIFToPrivateKey("cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
|
||||
val key2 = ECPrivateKey.fromWIFToPrivateKey("cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo")
|
||||
def key3 = ECPrivateKey.fromWIFToPrivateKey("cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg")
|
||||
(signedMultiSignatureTx,0,multiSignatureScriptPubKey, Seq(key1.publicKey,key2.publicKey,key3.publicKey))
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue