mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 22:36:34 +01:00
scodec
Migration to scodec Src is compiling tests compiling All unit tests passing now fix compiler issue in ScriptParser bump timeout to run on travis ci Fixing two unit tests that had types failing Turn down excessive logging Uncomment rpc tests fixing nits
This commit is contained in:
parent
c83959a2a6
commit
5b5852bf72
115 changed files with 1333 additions and 1504 deletions
|
@ -49,6 +49,8 @@ lazy val rpc = project
|
|||
.dependsOn(
|
||||
core,
|
||||
coreGen % "test->test"
|
||||
).settings(
|
||||
testOptions in Test += Tests.Argument("-oF")
|
||||
)
|
||||
|
||||
publishArtifact in root := false
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.crypto
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 3/22/16.
|
||||
|
@ -8,7 +9,7 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class ECDigitalSignatureTest extends FlatSpec with MustMatchers {
|
||||
|
||||
"ECDigitalSignature" must "say that empty signature is a valid DER encoded signature" in {
|
||||
val emptySiganture = ECDigitalSignature(Seq())
|
||||
val emptySiganture = ECDigitalSignature(ByteVector.empty)
|
||||
emptySiganture.isDEREncoded must be(true)
|
||||
|
||||
}
|
||||
|
@ -44,7 +45,7 @@ class ECDigitalSignatureTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "create an empty digital signature when given 0 in hex or byte format" in {
|
||||
val hex = ECDigitalSignature("00")
|
||||
val byte = ECDigitalSignature(Seq(0.toByte))
|
||||
val byte = ECDigitalSignature(scodec.bits.ByteVector.low(1))
|
||||
val emptySignature = ECDigitalSignature("")
|
||||
byte must be(emptySignature)
|
||||
hex must be(emptySignature)
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.math.BigInteger
|
|||
import org.bitcoinj.core.Sha256Hash
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 2/29/16.
|
||||
|
@ -16,17 +17,17 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|||
val privateKeyHex = "180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
|
||||
val key: ECPrivateKey = ECPrivateKey(privateKeyHex)
|
||||
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val hash = DoubleSha256Digest(ByteVector(Sha256Hash.ZERO_HASH.getBytes))
|
||||
val signature: ECDigitalSignature = key.sign(hash)
|
||||
|
||||
val isValid: Boolean = key.publicKey.verify(Sha256Hash.ZERO_HASH.getBytes.toSeq, signature)
|
||||
val isValid: Boolean = key.publicKey.verify(ByteVector(Sha256Hash.ZERO_HASH.getBytes), signature)
|
||||
isValid must be(true)
|
||||
}
|
||||
|
||||
it must "fail to verify a piece of data if the wrong public key is given" in {
|
||||
val privateKeyHex = "180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
|
||||
val key: ECPrivateKey = ECPrivateKey(privateKeyHex)
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes.toSeq)
|
||||
val hash = DoubleSha256Digest(ByteVector(Sha256Hash.ZERO_HASH.getBytes))
|
||||
val signature: ECDigitalSignature = key.sign(hash)
|
||||
|
||||
val wrongPublicKey = ECPublicKey.freshPublicKey
|
||||
|
@ -37,15 +38,15 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|||
it must "verify a piece of data signed with a bitcoinj private key" in {
|
||||
val bitcoinjPrivKey = new org.bitcoinj.core.ECKey
|
||||
val bitcoinjSignature = bitcoinjPrivKey.sign(Sha256Hash.ZERO_HASH)
|
||||
val bitcoinsSignature = ECDigitalSignature(bitcoinjSignature.encodeToDER())
|
||||
val bitcoinsPublicKey = ECPublicKey(bitcoinjPrivKey.getPubKey)
|
||||
bitcoinsPublicKey.verify(Sha256Hash.ZERO_HASH.getBytes, bitcoinsSignature) must be(true)
|
||||
val bitcoinsSignature = ECDigitalSignature(ByteVector(bitcoinjSignature.encodeToDER()))
|
||||
val bitcoinsPublicKey = ECPublicKey(ByteVector(bitcoinjPrivKey.getPubKey))
|
||||
bitcoinsPublicKey.verify(ByteVector(Sha256Hash.ZERO_HASH.getBytes), bitcoinsSignature) must be(true)
|
||||
|
||||
}
|
||||
|
||||
it must "verify a piece of data was signed with a bitcoins private key inside of bitcoinj" in {
|
||||
val bitcoinsPrivKey = ECPrivateKey.freshPrivateKey
|
||||
val hash = DoubleSha256Digest(Sha256Hash.ZERO_HASH.getBytes)
|
||||
val hash = DoubleSha256Digest(ByteVector(Sha256Hash.ZERO_HASH.getBytes))
|
||||
val bitcoinsSignature = bitcoinsPrivKey.sign(hash)
|
||||
val bitcoinjPublicKey = org.bitcoinj.core.ECKey.fromPublicOnly(bitcoinsPrivKey.publicKey.bytes.toArray)
|
||||
bitcoinjPublicKey.verify(
|
||||
|
|
|
@ -211,8 +211,8 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Sc
|
|||
inputIndex = inputIndex,
|
||||
output = TransactionOutput(CurrencyUnits.zero, redeemScript),
|
||||
flags = Policy.standardScriptVerifyFlags)
|
||||
val sign: Seq[Byte] => Future[ECDigitalSignature] = {
|
||||
bytes: Seq[Byte] => Future(privateKey.sign(bytes))
|
||||
val sign: scodec.bits.ByteVector => Future[ECDigitalSignature] = {
|
||||
bytes: scodec.bits.ByteVector => Future(privateKey.sign(bytes))
|
||||
}
|
||||
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, sign, HashType.sigHashAll)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bitcoins.core.script.crypto._
|
|||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.util._
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 2/19/16.
|
||||
|
@ -17,278 +18,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
|||
private def logger = BitcoinSLogger.logger
|
||||
val scriptPubKey = BitcoinjConversions.toScriptPubKey(BitcoinJTestUtil.multiSigScript)
|
||||
|
||||
"TransactionSignatureSerializer" must "serialize a transaction for SIGHASH_ALL correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val sigBytes: Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashAll)
|
||||
val bitcoinjSerialization = BitcoinSUtil.encodeHex(
|
||||
BitcoinJSignatureSerialization.serializeForSignature(BitcoinJTestUtil.multiSigTransaction, 0,
|
||||
BitcoinJTestUtil.multiSigScript.getProgram(), HashType.sigHashAllByte))
|
||||
|
||||
BitcoinSUtil.encodeHex(sigBytes) must be(bitcoinjSerialization)
|
||||
}
|
||||
|
||||
it must "hash a tranasction with SIGHASH_ALL correfctly" in {
|
||||
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val bitcoinsTxSigHash = TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashAll)
|
||||
val bitcoinjTxSigHash = BitcoinSUtil.encodeHex(
|
||||
BitcoinJSignatureSerialization.hashForSignature(BitcoinJTestUtil.multiSigTransaction, 0,
|
||||
BitcoinJTestUtil.multiSigScript.getProgram(), HashType.sigHashAllByte))
|
||||
bitcoinsTxSigHash.hex must be(bitcoinjTxSigHash)
|
||||
}
|
||||
|
||||
it must "serialize a transaction for a SIGHASH_SINGLE transaction correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashSingle)
|
||||
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashSingleByte))
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "hash a transaction for a SIGHASH_SINGLE signature correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashSingle)
|
||||
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashSingleByte))
|
||||
hashedTxForSig.hex must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "serialize a transaction for SIGHASH_NONE signature correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashNone)
|
||||
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashNoneByte))
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "hash a transaction for a SIGHASH_NONE signature correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashNone)
|
||||
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashNoneByte))
|
||||
hashedTxForSig.hex must be(bitcoinjSigSerialization)
|
||||
|
||||
}
|
||||
|
||||
it must "serialize a transaction that has the SIGHASH_ANYONECANPAY flag set" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(
|
||||
txSigComponent,
|
||||
HashType.sigHashAnyoneCanPay)
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashAnyoneCanPayByte))
|
||||
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
|
||||
}
|
||||
|
||||
it must "hash a transaction for SIGHASH_ANYONECANPAY correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashAnyoneCanPay)
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashAnyoneCanPayByte))
|
||||
|
||||
hashedTxForSig.hex must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "serialize a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_ALL" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashAnyoneCanPay)
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashAnyoneCanPayByte))
|
||||
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "serialize a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_SINGLE" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(
|
||||
txSigComponent,
|
||||
HashType.sigHashSingleAnyoneCanPay)
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram,
|
||||
HashType.sigHashSingleAnyoneCanPayByte))
|
||||
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "serialize a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_NONE" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val serializedTxForSig: Seq[Byte] =
|
||||
TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashNoneAnyoneCanPay)
|
||||
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashNoneAnyoneCanPayByte))
|
||||
|
||||
BitcoinSUtil.encodeHex(serializedTxForSig) must be(bitcoinjSigSerialization)
|
||||
}
|
||||
|
||||
it must "hash a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_ALL" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashAllAnyoneCanPay)
|
||||
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashAllAnyoneCanPayByte))
|
||||
|
||||
hashedTxForSig.hex must be(bitcoinjTxHashForSig)
|
||||
}
|
||||
|
||||
it must "hash a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_SINGLE" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashSingleAnyoneCanPay)
|
||||
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashSingleAnyoneCanPayByte))
|
||||
|
||||
hashedTxForSig.hex must be(bitcoinjTxHashForSig)
|
||||
}
|
||||
|
||||
it must "hash a transaction that uses both SIGHASH_ANYONECANPAY & SIGHASH_NONE" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be(BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
UInt32.zero,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashNoneAnyoneCanPay)
|
||||
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
|
||||
BitcoinJTestUtil.multiSigTransaction, 0, BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashNoneAnyoneCanPayByte))
|
||||
|
||||
hashedTxForSig.hex must be(bitcoinjTxHashForSig)
|
||||
}
|
||||
|
||||
it must "hash a transaction that has script operations after OP_CHECKSIGVERIFY" in {
|
||||
//this example is from tx_valid.json
|
||||
val rawTx = "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000"
|
||||
val inputIndex = UInt32.zero
|
||||
val spendingTx = Transaction(rawTx)
|
||||
val scriptPubKeyFromString = ScriptParser.fromString("DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 " +
|
||||
"EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed02" +
|
||||
"2026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01")
|
||||
|
||||
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
|
||||
|
||||
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
|
||||
val bitcoinjHashForSig: Seq[Byte] = BitcoinJSignatureSerialization.hashForSignature(
|
||||
bitcoinjTx, inputIndex.toInt, scriptPubKey.asmBytes.toArray, HashType.sigHashAllByte)
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
inputIndex,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashAll)
|
||||
hashedTxForSig.hex must be(BitcoinSUtil.encodeHex(bitcoinjHashForSig))
|
||||
|
||||
}
|
||||
|
||||
it must "correctly serialize an input that is being checked where another input in the same tx is using SIGHASH_ANYONECANPAY" in {
|
||||
"TransactionSignatureSerializer" must "correctly serialize an input that is being checked where another input in the same tx is using SIGHASH_ANYONECANPAY" in {
|
||||
//this is from a test case inside of tx_valid.json
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L91
|
||||
val rawTx = "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000"
|
||||
|
@ -310,30 +40,6 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
|||
|
||||
}
|
||||
|
||||
it must "correctly serialize an input that is using the SIGHASH_ANYONECANPAY flag in conjunction with another input that uses SIGHASH_ALL" in {
|
||||
//this is from a test case inside of tx_valid.json
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L91
|
||||
val rawTx = "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000"
|
||||
val inputIndex = UInt32.one
|
||||
val spendingTx = Transaction(rawTx)
|
||||
val scriptPubKeyFromString = ScriptParser.fromString("0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG")
|
||||
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
|
||||
|
||||
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
|
||||
val bitcoinjHashForSig: Seq[Byte] = BitcoinJSignatureSerialization.hashForSignature(
|
||||
bitcoinjTx, inputIndex.toInt, scriptPubKey.bytes.toArray, HashType.sigHashAllAnyoneCanPayByte)
|
||||
val txSigComponent = BaseTxSigComponent(
|
||||
spendingTx,
|
||||
inputIndex,
|
||||
TransactionOutput(CurrencyUnits.zero, scriptPubKey),
|
||||
Policy.standardFlags)
|
||||
val hashedTxForSig =
|
||||
TransactionSignatureSerializer.hashForSignature(txSigComponent, HashType.sigHashAllAnyoneCanPay)
|
||||
//hash is from bitcoin core
|
||||
|
||||
hashedTxForSig.hex must be("57f5a54d548db73fa8ef7a43d011120f9935fe792f0a0630d28ee70b4c72a7e8")
|
||||
}
|
||||
|
||||
it must "correctly serialize a tx for signing with multiple inputs using SIGHASH_SINGLE" in {
|
||||
//this is from a test case inside of tx_valid.json
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L96
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.number
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 6/15/16.
|
||||
|
@ -8,43 +9,43 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class Int32Test extends FlatSpec with MustMatchers {
|
||||
|
||||
"Int32" must "create the number zero" in {
|
||||
val int32 = Int32(Seq(0.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector.low(1))
|
||||
int32.toInt must be(0)
|
||||
}
|
||||
|
||||
it must "represent the number -1" in {
|
||||
val int32 = Int32(Seq(0xff.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0xff.toByte))
|
||||
int32.toInt must be(-1)
|
||||
}
|
||||
|
||||
it must "represent the number -1 with 4 bytes" in {
|
||||
val int32 = Int32(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int32.toInt must be(-1)
|
||||
}
|
||||
|
||||
it must "create the max number for a single byte" in {
|
||||
val int32 = Int32(Seq(0x7f.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0x7f.toByte))
|
||||
int32.toInt must be(127)
|
||||
}
|
||||
|
||||
it must "create the min number for a single byte" in {
|
||||
val int32 = Int32(Seq(0x80.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0x80.toByte))
|
||||
int32.toInt must be(-128)
|
||||
}
|
||||
|
||||
it must "create the max number for an Int32" in {
|
||||
val int32 = Int32(Seq(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int32.toInt must be(2147483647)
|
||||
}
|
||||
|
||||
it must "create the minimum number for an Int32" in {
|
||||
val int32 = Int32(Seq(0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val int32 = Int32(scodec.bits.ByteVector(0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
int32.toInt must be(-2147483648)
|
||||
}
|
||||
|
||||
it must "throw an exception if we try and create an Int32 with more than 4 bytes" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
Int32(Seq(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
Int32(ByteVector(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.number
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 6/15/16.
|
||||
|
@ -8,57 +9,57 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class Int64Test extends FlatSpec with MustMatchers {
|
||||
|
||||
"Int64" must "represent the nubmer zero" in {
|
||||
val int64 = Int64(Seq(0.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector.low(1))
|
||||
int64.toLong must be(0)
|
||||
}
|
||||
it must "represent the number 1" in {
|
||||
val int64 = Int64(Seq(1.toByte))
|
||||
val int64 = Int64(ByteVector(1.toByte))
|
||||
int64.toLong must be(1)
|
||||
}
|
||||
it must "represent the number -1 with 1 byte" in {
|
||||
val int64 = Int64(Seq(0xff.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0xff.toByte))
|
||||
int64.toLong must be(-1)
|
||||
}
|
||||
|
||||
it must "represent the number -1 with 8 bytes" in {
|
||||
val int64 = Int64(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
val int64 = Int64(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int64.toLong must be(-1)
|
||||
}
|
||||
it must "represent the Int32 max value" in {
|
||||
val int64 = Int64(Seq(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int64.toLong must be(2147483647)
|
||||
}
|
||||
|
||||
it must "represent the Int32 min value" in {
|
||||
val int64 = Int64(Seq(0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
int64.toLong must be(-2147483648L)
|
||||
}
|
||||
|
||||
it must "represent the Int32 max value + 1" in {
|
||||
val int64 = Int64(Seq(0x0.toByte, 0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0x0.toByte, 0x80.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
int64.toLong must be(2147483648L)
|
||||
}
|
||||
|
||||
it must "represent the Int32 min value - 1" in {
|
||||
val int64 = Int64(Seq(0xff.toByte, 0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0xff.toByte, 0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int64.toLong must be(-2147483649L)
|
||||
}
|
||||
|
||||
it must "represent the minimum value for int64" in {
|
||||
val int64 = Int64(Seq(0x80.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val int64 = Int64(scodec.bits.ByteVector(0x80.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
int64.toLong must be(-9223372036854775808L)
|
||||
}
|
||||
|
||||
it must "represent the maximum value for a int64" in {
|
||||
val int64 = Int64(Seq(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
val int64 = Int64(scodec.bits.ByteVector(0x7f.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
int64.toLong must be(9223372036854775807L)
|
||||
}
|
||||
|
||||
it must "throw an exception when trying to create a Int64 out of 9 bytes or more" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
Int64(Seq(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
Int64(ByteVector(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.number
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 6/14/16.
|
||||
|
@ -8,43 +9,43 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class UInt32Test extends FlatSpec with MustMatchers {
|
||||
|
||||
"UInt32" must "create the number zero as an unsigned 32 bit integer" in {
|
||||
val zero = UInt32(Seq(0x0.toByte))
|
||||
val zero = UInt32(scodec.bits.ByteVector(0x0.toByte))
|
||||
zero.toLong must be(0)
|
||||
}
|
||||
|
||||
it must "create the max number for an unsigned byte" in {
|
||||
val maxByteValue = UInt32(Seq(0xff.toByte))
|
||||
val maxByteValue = UInt32(scodec.bits.ByteVector(0xff.toByte))
|
||||
maxByteValue.toLong must be(255)
|
||||
}
|
||||
|
||||
it must "create the number 256" in {
|
||||
val uInt32 = UInt32(Seq(0x01.toByte, 0x00.toByte))
|
||||
val uInt32 = UInt32(scodec.bits.ByteVector(0x01.toByte, 0x00.toByte))
|
||||
uInt32.toLong must be(256)
|
||||
}
|
||||
|
||||
it must "create the number 65535" in {
|
||||
val uInt32 = UInt32(Seq(0xff.toByte, 0xff.toByte))
|
||||
val uInt32 = UInt32(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte))
|
||||
uInt32.toLong must be(65535)
|
||||
}
|
||||
|
||||
it must "create the number 65536" in {
|
||||
val uInt32 = UInt32(Seq(0x01.toByte, 0x0.toByte, 0x0.toByte))
|
||||
val uInt32 = UInt32(scodec.bits.ByteVector(0x01.toByte, 0x0.toByte, 0x0.toByte))
|
||||
uInt32.toLong must be(65536)
|
||||
}
|
||||
|
||||
it must "create the number 16777215" in {
|
||||
val uInt32 = UInt32(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val uInt32 = UInt32(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
uInt32.toLong must be(16777215)
|
||||
}
|
||||
|
||||
it must "create the number 16777216" in {
|
||||
val uInt32 = UInt32(Seq(1.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val uInt32 = UInt32(ByteVector(1.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
uInt32.toLong must be(16777216)
|
||||
}
|
||||
|
||||
it must "create the number 4294967295" in {
|
||||
//this is UInt32_t's max value
|
||||
val uInt32 = UInt32(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val uInt32 = UInt32(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
uInt32.toLong must be(4294967295L)
|
||||
uInt32.hex must be("ffffffff")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.number
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 6/15/16.
|
||||
|
@ -8,7 +9,7 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class UInt64Test extends FlatSpec with MustMatchers {
|
||||
|
||||
"UInt64" must "hold the number 0" in {
|
||||
val uInt64 = UInt64(Seq(0.toByte))
|
||||
val uInt64 = UInt64(scodec.bits.ByteVector.low(1))
|
||||
uInt64.hex must be("0000000000000000")
|
||||
uInt64.toBigInt must be(0)
|
||||
}
|
||||
|
@ -21,17 +22,17 @@ class UInt64Test extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "hold the max for a uint32_t" in {
|
||||
//this is UInt32_t's max value
|
||||
val uInt64 = UInt64(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
val uInt64 = UInt64(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
uInt64.toBigInt must be(4294967295L)
|
||||
}
|
||||
|
||||
it must "hold the max for uint32_t + 1" in {
|
||||
val uInt64 = UInt64(Seq(1.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
val uInt64 = UInt64(ByteVector(1.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
uInt64.toBigInt must be(4294967296L)
|
||||
}
|
||||
|
||||
it must "hold the max number for uint64_t" in {
|
||||
val uInt64 = UInt64(Seq(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
val uInt64 = UInt64(scodec.bits.ByteVector(0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte,
|
||||
0xff.toByte, 0xff.toByte, 0xff.toByte, 0xff.toByte))
|
||||
uInt64.toBigInt must be(BigInt("18446744073709551615"))
|
||||
uInt64.hex must be("ffffffffffffffff")
|
||||
|
@ -39,7 +40,7 @@ class UInt64Test extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "throw an exception if we try and create a number larger than 8 bytes" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
UInt64(Seq(1.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
UInt64(ByteVector(1.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.number.UInt64
|
|||
import org.bitcoins.core.protocol.script.ScriptSignature
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, TestUtil }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 7/26/15.
|
||||
|
@ -30,21 +31,21 @@ class CompactSizeUIntTest extends FlatSpec with MustMatchers {
|
|||
CompactSizeUInt.calculateCompactSizeUInt("00") must be(CompactSizeUInt(UInt64.one, 1))
|
||||
|
||||
//for a string that is 256 bytes long
|
||||
val byteSeq256Size = for (_ <- 0 until 256) yield 0.toByte
|
||||
val byteSeq256Size = ByteVector(Array.fill(256)(0.toByte))
|
||||
CompactSizeUInt.calculateCompactSizeUInt(byteSeq256Size) must be(CompactSizeUInt(UInt64(256), 3))
|
||||
}
|
||||
|
||||
it must "calculate the correct compact size uint for a number 515 bytes long" in {
|
||||
//from the bitcoin developer reference
|
||||
//https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
|
||||
val byteSeq515Size = for (_ <- 0 until 515) yield 0.toByte
|
||||
val byteSeq515Size = ByteVector(Array.fill(515)(0.toByte))
|
||||
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(byteSeq515Size)
|
||||
compactSizeUInt must be(CompactSizeUInt(UInt64(515), 3))
|
||||
compactSizeUInt.hex must be("fd0302")
|
||||
}
|
||||
|
||||
it must "calculate correct compact size uint for a number 500,000 bytes long" in {
|
||||
val byteSeq500000Size = for (_ <- 0 until 500000) yield 0.toByte
|
||||
val byteSeq500000Size = ByteVector(Array.fill(500000)(0.toByte))
|
||||
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(byteSeq500000Size)
|
||||
compactSizeUInt must be(CompactSizeUInt(UInt64(500000), 5))
|
||||
compactSizeUInt.hex must be("fe20a10700")
|
||||
|
@ -101,7 +102,7 @@ class CompactSizeUIntTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "intercept a failed requirement when the byte array size is zero" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val emptyBytes: Seq[Byte] = Seq()
|
||||
val emptyBytes: scodec.bits.ByteVector = ByteVector.empty
|
||||
CompactSizeUInt.parseCompactSizeUInt(emptyBytes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
package org.bitcoins.core.protocol.blockchain
|
||||
|
||||
import java.io.File
|
||||
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.serializers.script.RawScriptSignatureParser
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.io.Source
|
||||
|
||||
/**
|
||||
* Created by chris on 7/15/16.
|
||||
*/
|
||||
class BlockTest extends FlatSpec with MustMatchers {
|
||||
private val logger = LoggerFactory.getLogger(this.getClass.getSimpleName)
|
||||
def timeBlockParsing[R](block: => R): Long = {
|
||||
val t0 = System.currentTimeMillis()
|
||||
val result = block // call-by-name
|
||||
val t1 = System.currentTimeMillis()
|
||||
val time = t1 - t0
|
||||
logger.info("Elapsed time: " + (time) + "ms")
|
||||
time
|
||||
}
|
||||
|
||||
"Block" must "deserialize and serialize a block with two txs" in {
|
||||
val hex = "b0aa0dced271237b5badb4365c33bc42b8fdb05c3defa03e75dcae425f42ddd210d69b8695e0f3cdf1eb9f56d0daa3c1e384204faa20792217d3103217773f4177b6b0f067079ac1ee733798f3bef9a0" +
|
||||
|
@ -17,4 +31,19 @@ class BlockTest extends FlatSpec with MustMatchers {
|
|||
val block = Block(hex)
|
||||
block.hex must be(hex)
|
||||
}
|
||||
|
||||
it must "parse a large block 00000000000000000008513c860373da0484f065983aeb063ebf81c172e81d48" in {
|
||||
|
||||
val fileName = "/00000000000000000008513c860373da0484f065983aeb063ebf81c172e81d48.txt"
|
||||
val lines = Source.fromURL(getClass.getResource(fileName)).mkString
|
||||
val time = timeBlockParsing(Block.fromHex(lines))
|
||||
assert(time <= 15000)
|
||||
}
|
||||
|
||||
it must "parse a large block 000000000000000000050f70113ab1932c195442cb49bcc4ee4d7f426c8a3295" in {
|
||||
val fileName = "/000000000000000000050f70113ab1932c195442cb49bcc4ee4d7f426c8a3295.txt"
|
||||
val lines = Source.fromURL(getClass.getResource(fileName)).mkString
|
||||
val time = timeBlockParsing(Block.fromHex(lines))
|
||||
assert(time <= 15000)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ class MerkleBlockTests extends FlatSpec with MustMatchers {
|
|||
partialMerkleTree must be(merkleBlock.partialMerkleTree)
|
||||
merkleBlock.partialMerkleTree.extractMatches must be(txIds)
|
||||
partialMerkleTree.extractMatches must be(txIds)
|
||||
partialMerkleTree.bits must be(List(true, true, true, true, true, true, true, false))
|
||||
partialMerkleTree.bits.toIndexedSeq must be(List(true, true, true, true, true, true, true, false))
|
||||
}
|
||||
|
||||
it must "create a merkle block from a sequence of txids and a block" in {
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.bitcoins.core.crypto.DoubleSha256Digest
|
|||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, Leaf, Node }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.BitVector
|
||||
|
||||
/**
|
||||
* Created by chris on 8/9/16.
|
||||
|
@ -54,8 +55,8 @@ class PartialMerkleTreeTests extends FlatSpec with MustMatchers {
|
|||
val filter = BloomFilter(10, 0.000001, UInt32.zero, BloomUpdateAll).insert(hash1).insert(hash2)
|
||||
val (merkleBlock, _) = MerkleBlock(block, filter)
|
||||
val partialMerkleTree = merkleBlock.partialMerkleTree
|
||||
partialMerkleTree.bits.slice(0, 8) must be(Seq(true, true, false, true, false, true, false, true))
|
||||
partialMerkleTree.bits.slice(8, partialMerkleTree.bits.size) must be(Seq(true, true, true, true, false, false, false, false))
|
||||
partialMerkleTree.bits.slice(0, 8).toIndexedSeq must be(Seq(true, true, false, true, false, true, false, true))
|
||||
partialMerkleTree.bits.slice(8, partialMerkleTree.bits.size).toIndexedSeq must be(Seq(true, true, true, true, false, false, false, false))
|
||||
partialMerkleTree.hashes must be(Seq(
|
||||
DoubleSha256Digest("5ef2374cf1cd1c0b8c1b795950c077fc571cca44866c375a1ed17ace665cfaaa"),
|
||||
DoubleSha256Digest("d403489f6b1a2f7de72c6e4573e1cc7ac15745518e42a0bf884f58dc48f45533"),
|
||||
|
@ -134,7 +135,7 @@ class PartialMerkleTreeTests extends FlatSpec with MustMatchers {
|
|||
it must "build a partial merkle tree with no matches and 1 transaction in the original block" in {
|
||||
val txMatches = Seq((false, DoubleSha256Digest("01272b2b1c8c33a1b4e9ab111db41c9ac275e686fbd9c5d482e586d03e9e0552")))
|
||||
val partialMerkleTree = PartialMerkleTree(txMatches)
|
||||
partialMerkleTree.bits must be(Seq(false, false, false, false, false, false, false, false))
|
||||
partialMerkleTree.bits.toIndexedSeq must be(Seq(false, false, false, false, false, false, false, false))
|
||||
partialMerkleTree.tree must be(Leaf(DoubleSha256Digest("01272b2b1c8c33a1b4e9ab111db41c9ac275e686fbd9c5d482e586d03e9e0552")))
|
||||
partialMerkleTree.transactionCount must be(UInt32(1))
|
||||
partialMerkleTree.extractMatches.isEmpty must be(true)
|
||||
|
@ -145,7 +146,7 @@ class PartialMerkleTreeTests extends FlatSpec with MustMatchers {
|
|||
(false, DoubleSha256Digest("01272b2b1c8c33a1b4e9ab111db41c9ac275e686fbd9c5d482e586d03e9e0552")),
|
||||
(true, DoubleSha256Digest("076d0317ee70ee36cf396a9871ab3bf6f8e6d538d7f8a9062437dcb71c75fcf9")))
|
||||
val partialMerkleTree = PartialMerkleTree(txMatches)
|
||||
partialMerkleTree.bits must be(Seq(true, false, true, false, false, false, false, false))
|
||||
partialMerkleTree.bits.toIndexedSeq must be(Seq(true, false, true, false, false, false, false, false))
|
||||
partialMerkleTree.tree must be(Node(
|
||||
DoubleSha256Digest("b130d701e65ac8c65f30dc4b20aabf349036b7c87f11f012f4f3f53f666791e6"),
|
||||
Leaf(DoubleSha256Digest("01272b2b1c8c33a1b4e9ab111db41c9ac275e686fbd9c5d482e586d03e9e0552")),
|
||||
|
@ -157,7 +158,7 @@ class PartialMerkleTreeTests extends FlatSpec with MustMatchers {
|
|||
val matches = List((true, DoubleSha256Digest("caa02f1194fb44dea407a7cf713ddcf30e69f49c297f9275f9236fec42d945b2")))
|
||||
val partialMerkleTree = PartialMerkleTree(matches)
|
||||
partialMerkleTree.tree must be(Leaf(DoubleSha256Digest("caa02f1194fb44dea407a7cf713ddcf30e69f49c297f9275f9236fec42d945b2")))
|
||||
partialMerkleTree.bits must be(Seq(true, false, false, false, false, false, false, false))
|
||||
partialMerkleTree.bits.toIndexedSeq must be(Seq(true, false, false, false, false, false, false, false))
|
||||
}
|
||||
|
||||
it must "correctly compute a merkle tree that has an odd amount of txids on the merkle tree" in {
|
||||
|
@ -183,9 +184,9 @@ class PartialMerkleTreeTests extends FlatSpec with MustMatchers {
|
|||
DoubleSha256Digest("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"),
|
||||
DoubleSha256Digest("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"))
|
||||
val numTransactions = UInt32(20)
|
||||
val bits = List(true, true, true, true, true, true, true, false, true, true, false, true,
|
||||
val bits = BitVector.bits(List(true, true, true, true, true, true, true, false, true, true, false, true,
|
||||
true, true, false, true, true, true, true, false, true, true, true, true, true, true, true,
|
||||
false, true, true, true, true, false, true, true, true, true, false, false, false)
|
||||
false, true, true, true, true, false, true, true, true, true, false, false, false))
|
||||
val tree = PartialMerkleTree(numTransactions, hashes, bits)
|
||||
tree.extractMatches.contains(DoubleSha256Digest("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456")) must be(true)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.core.crypto.ECDigitalSignature
|
||||
import org.bitcoins.core.script.crypto.{ SIGHASH_ALL, HashType }
|
||||
import org.bitcoins.core.script.crypto.{ HashType, SIGHASH_ALL }
|
||||
import org.bitcoins.core.util.TestUtil
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 4/1/16.
|
||||
|
@ -15,7 +16,7 @@ class P2PKHScriptSignatureTest extends FlatSpec with MustMatchers {
|
|||
case s: P2PKHScriptSignature => s
|
||||
case _ => throw new RuntimeException("Must be p2pkh scriptSig")
|
||||
}
|
||||
HashType.fromBytes(Seq(p2pkhScriptSig.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
HashType.fromBytes(ByteVector.fromByte(p2pkhScriptSig.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
}
|
||||
|
||||
it must "be able to identify the signature in a p2pkh scriptSig" in {
|
||||
|
|
|
@ -13,7 +13,7 @@ class ScriptSignatureFactoryTest extends FlatSpec with MustMatchers {
|
|||
"ScriptSignatureFactory" must "give the exact same result whether parsing bytes or parsing hex" in {
|
||||
val signatureHex = "30450221008949f0cb400094ad2b5eb399d59d01c14d73d8fe6e96df1a7150deb388ab8935022079656090d7" +
|
||||
"f6bac4c9a94e0aad311a4268e082a725f8aeae0573fb12ff866a5f01"
|
||||
val signatureBytes: Seq[Byte] = BitcoinSUtil.decodeHex(signatureHex)
|
||||
val signatureBytes: scodec.bits.ByteVector = BitcoinSUtil.decodeHex(signatureHex)
|
||||
|
||||
val scriptSigFromHex = ScriptSignature(signatureHex)
|
||||
val scriptSigFromBytes = ScriptSignature(signatureBytes)
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.bitcoins.core.script.crypto.{ HashType, SIGHASH_ALL, SIGHASH_SINGLE }
|
|||
import org.bitcoins.core.serializers.script.RawScriptSignatureParser
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, TestUtil }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
import spray.json._
|
||||
|
||||
import scala.io.Source
|
||||
|
@ -26,7 +27,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
|
|||
}
|
||||
|
||||
it must "derive the signature hash type from the signature" in {
|
||||
HashType(Seq(TestUtil.scriptSig.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
HashType(ByteVector.fromByte(TestUtil.scriptSig.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
}
|
||||
|
||||
it must "find the digital signature for a p2sh script signature" in {
|
||||
|
@ -52,7 +53,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
|
|||
ECDigitalSignature("30440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed01")))
|
||||
}
|
||||
it must "find the hash type for a p2sh script signature" in {
|
||||
HashType(Seq(TestUtil.p2shInputScript2Of2.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
HashType(ByteVector.fromByte(TestUtil.p2shInputScript2Of2.signatures.head.bytes.last)) must be(HashType.sigHashAll)
|
||||
}
|
||||
|
||||
it must "find the digital signature and hash type for a SIGHASH_SINGLE" in {
|
||||
|
@ -69,7 +70,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "have an empty script signature" in {
|
||||
EmptyScriptSignature.hex must be("00")
|
||||
EmptyScriptSignature.bytes must be(Seq(0.toByte))
|
||||
EmptyScriptSignature.bytes must be(scodec.bits.ByteVector.low(1))
|
||||
EmptyScriptSignature.asm must be(Nil)
|
||||
EmptyScriptSignature.signatures must be(Nil)
|
||||
}
|
||||
|
|
|
@ -120,13 +120,6 @@ class TransactionTest extends FlatSpec with MustMatchers {
|
|||
tx = testCase.spendingTx
|
||||
(input, inputIndex) = findInput(tx, outPoint).getOrElse((EmptyTransactionInput, 0))
|
||||
} yield {
|
||||
logger.debug("Raw test case: " + testCase.raw)
|
||||
logger.debug("Parsed ScriptSig: " + tx.inputs(inputIndex).scriptSignature)
|
||||
logger.debug("Sequence number: " + tx.inputs(inputIndex).sequence)
|
||||
logger.debug("ScriptPubKey: " + scriptPubKey)
|
||||
logger.debug("OutPoint: " + outPoint)
|
||||
logger.debug("Flags after parsing: " + testCase.flags)
|
||||
logger.debug("Satoshis: " + amountOpt)
|
||||
require(
|
||||
outPoint.txId == input.previousOutput.txId,
|
||||
"OutPoint txId not the same as input prevout txid\noutPoint.txId: " + outPoint.txId + "\n" +
|
||||
|
@ -203,12 +196,6 @@ class TransactionTest extends FlatSpec with MustMatchers {
|
|||
tx = testCase.spendingTx
|
||||
(input, inputIndex) = findInput(tx, outPoint).getOrElse((EmptyTransactionInput, 0))
|
||||
} yield {
|
||||
logger.debug("Raw test case: " + testCase.raw)
|
||||
logger.debug("ScriptPubKey: " + scriptPubKey)
|
||||
logger.debug("OutPoint: " + outPoint)
|
||||
logger.debug("Flags after parsing: " + testCase.flags)
|
||||
logger.debug("spending tx: " + testCase.spendingTx)
|
||||
logger.debug("" + testCase.scriptPubKeys)
|
||||
val isValidTx = ScriptInterpreter.checkTransaction(tx)
|
||||
if (isValidTx) {
|
||||
val txSigComponent = amountOpt match {
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.bitcoins.core.script.flag.{ ScriptFlag, ScriptVerifyMinimalData }
|
|||
import org.bitcoins.core.script.result.{ ScriptErrorBadOpCode, ScriptErrorMinimalData }
|
||||
import org.bitcoins.core.util.{ ScriptProgramTestUtil, TestUtil }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 1/24/16.
|
||||
|
@ -15,7 +16,7 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers {
|
|||
val CI = ConstantInterpreter
|
||||
"ConstantInterpreter" must "interpret OP_PUSHDATA1 correctly" in {
|
||||
val byteConstantSize = 76
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val byteConstant = ByteVector(Array.fill(byteConstantSize)(0.toByte))
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val stack = List()
|
||||
val script = List(OP_PUSHDATA1, ScriptNumber(byteConstantSize), scriptConstant, OP_7, OP_EQUAL)
|
||||
|
@ -27,7 +28,7 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "interpret OP_PUSHDATA2 correctly" in {
|
||||
val byteConstantSize = 256
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val byteConstant = ByteVector(Array.fill(byteConstantSize)(0.toByte))
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val stack = List()
|
||||
val script = List(OP_PUSHDATA2, ScriptNumber(256), scriptConstant, OP_8, OP_EQUAL)
|
||||
|
@ -39,7 +40,7 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "interpret OP_PUSHDATA4 correctly" in {
|
||||
val byteConstantSize = 65536
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val byteConstant = ByteVector(Array.fill(byteConstantSize)(0.toByte))
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val stack = List()
|
||||
val script = List(OP_PUSHDATA4, ScriptNumber(byteConstantSize), scriptConstant, OP_9, OP_EQUAL)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.script.constant
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
|
@ -124,7 +125,7 @@ class ConstantsTest extends FlatSpec with MustMatchers {
|
|||
}
|
||||
|
||||
it must "create the number zero from an empty sequence" in {
|
||||
val number: Try[ScriptNumber] = ScriptNumber(Seq(), true)
|
||||
val number: Try[ScriptNumber] = ScriptNumber(ByteVector.empty, true)
|
||||
number.isSuccess must be(true)
|
||||
number.get must be(ScriptNumber.zero)
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class HashTypeTest extends FlatSpec with MustMatchers {
|
|||
}
|
||||
|
||||
it must "default to SIGHASH_ALL if the given string/byte is not known" in {
|
||||
HashType(Seq(0x124.toByte)) must be(SIGHASH_ALL(Int32(36)))
|
||||
HashType(scodec.bits.ByteVector(0x124.toByte)) must be(SIGHASH_ALL(Int32(36)))
|
||||
}
|
||||
|
||||
it must "find hashType for number 1190874345" in {
|
||||
|
|
|
@ -36,17 +36,9 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers {
|
|||
(creditingTx, outputIndex) = TransactionTestUtil.buildCreditingTransaction(testCase.scriptPubKey, testCase.witness.map(_._2))
|
||||
(tx, inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx, testCase.scriptSig, outputIndex, testCase.witness)
|
||||
} yield {
|
||||
logger.info("Raw test case: " + testCase.raw)
|
||||
logger.info("Parsed ScriptSig: " + testCase.scriptSig)
|
||||
logger.info("Parsed ScriptPubKey: " + testCase.scriptPubKey)
|
||||
logger.info("Parsed tx: " + tx.hex)
|
||||
logger.info("Flags: " + testCase.flags)
|
||||
logger.info("Comments: " + testCase.comments)
|
||||
val scriptPubKey = ScriptPubKey.fromAsm(testCase.scriptPubKey.asm)
|
||||
val flags = ScriptFlagFactory.fromList(testCase.flags)
|
||||
val witness = testCase.witness
|
||||
logger.info("Flags after parsing: " + flags)
|
||||
logger.info("Witness after parsing: " + witness)
|
||||
val txSigComponent = witness match {
|
||||
case Some((w, amount)) => scriptPubKey match {
|
||||
case p2sh: P2SHScriptPubKey =>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package org.bitcoins.core.serializers
|
||||
|
||||
import org.bitcoins.core.number.UInt64
|
||||
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
|
||||
import org.bitcoins.core.protocol.transaction.{ EmptyTransactionOutput, TransactionInput, TransactionOutput }
|
||||
import org.bitcoins.core.serializers.transaction.{ RawTransactionInputParser, RawTransactionOutputParser }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
class RawSerializerHelperTest extends FlatSpec with MustMatchers {
|
||||
|
||||
"RawBitcoinSerializerHelper" must "serialize an empty vector" in {
|
||||
val bytes = ByteVector(0.toByte)
|
||||
val construct: ByteVector => TransactionInput = RawTransactionInputParser.read(_)
|
||||
val (inputs, _) = RawSerializerHelper.parseCmpctSizeUIntSeq(bytes, construct)
|
||||
|
||||
val serialize = RawTransactionInputParser.write(_)
|
||||
val write = RawSerializerHelper.writeCmpctSizeUInt(inputs, serialize)
|
||||
write must be(bytes)
|
||||
}
|
||||
|
||||
it must "serialize one element in a vector correctly" in {
|
||||
val bytes = CompactSizeUInt(UInt64.one).bytes ++ EmptyTransactionOutput.bytes
|
||||
val constructor: ByteVector => TransactionOutput = RawTransactionOutputParser.read(_)
|
||||
|
||||
val (outputs, _) = RawSerializerHelper.parseCmpctSizeUIntSeq(bytes, constructor)
|
||||
|
||||
val serialize = RawTransactionOutputParser.write(_)
|
||||
val write = RawSerializerHelper.writeCmpctSizeUInt(outputs, serialize)
|
||||
write must be(bytes)
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
package org.bitcoins.core.serializers
|
||||
|
||||
import org.bitcoins.core.gen.TransactionGenerators
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.protocol.transaction.{ Transaction, TransactionInput, TransactionOutput }
|
||||
import org.scalacheck.{ Prop, Properties }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
class RawSerializerHelperSpec extends Properties("RawSerializerHelperSpec") {
|
||||
|
||||
property("serialization symmetry of txs") = {
|
||||
Prop.forAll(TransactionGenerators.smallTransactions) { txs: Seq[Transaction] =>
|
||||
val serialized = RawSerializerHelper.writeCmpctSizeUInt[Transaction](txs, { tx: Transaction => tx.bytes })
|
||||
val (deserialized, remaining) = RawSerializerHelper.parseCmpctSizeUIntSeq(serialized, Transaction(_: Seq[Byte]))
|
||||
deserialized == txs && remaining == Nil
|
||||
Prop.forAll(TransactionGenerators.smallOutputs) { txs: Seq[TransactionOutput] =>
|
||||
val serialized = RawSerializerHelper.writeCmpctSizeUInt(txs, { tx: TransactionOutput => tx.bytes })
|
||||
val (deserialized, remaining) = RawSerializerHelper.parseCmpctSizeUIntSeq(serialized, TransactionOutput(_: scodec.bits.ByteVector))
|
||||
deserialized == txs && remaining == ByteVector.empty
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class RawBlockHeaderSerializerTest extends FlatSpec with MustMatchers {
|
|||
val nBits = "FFFF001D".toLowerCase
|
||||
val nonce = "1DAC2B7C".toLowerCase
|
||||
val hash = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
val hex = version + prevBlockHash + merkleRoot + timeStamp + nBits + nonce
|
||||
"BlockHeader" must "parse genesis block header" in {
|
||||
val blockHeader = RawBlockHeaderSerializer.read(hex)
|
||||
|
|
|
@ -31,7 +31,7 @@ class RawBlockSerializerTest extends FlatSpec with MustMatchers {
|
|||
val txSeq = List(tx1)
|
||||
val uInt = CompactSizeUInt(UInt64.one, 1)
|
||||
val hex = header + uInt.hex + rawTx1
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawBlockSerializer" must "parse genesis block" in {
|
||||
val block = RawBlockSerializer.read(hex)
|
||||
block.txCount.num must be(UInt64(txSeq.size))
|
||||
|
@ -115,4 +115,4 @@ class RawBlockSerializerTest extends FlatSpec with MustMatchers {
|
|||
block.txCount.num must be(UInt64(txSeq.size))
|
||||
block.hex must be(hex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.bitcoins.core.number.{ Int32, UInt32 }
|
|||
import org.bitcoins.core.protocol.blockchain.{ BlockHeader, MerkleBlock, PartialMerkleTree }
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, Leaf, Node }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.BitVector
|
||||
|
||||
/**
|
||||
* Created by chris on 8/22/16.
|
||||
|
@ -22,7 +23,7 @@ class RawMerkleBlockSerializerTest extends FlatSpec with MustMatchers {
|
|||
Leaf(DoubleSha256Digest(
|
||||
"442abdc8e74ad35ebd9571f88fda91ff511dcda8d241a5aed52cea1e00d69e03")),
|
||||
UInt32(1),
|
||||
List(false, false, false, false, false, false, false, false),
|
||||
BitVector.bits(Vector(false, false, false, false, false, false, false, false)),
|
||||
List(DoubleSha256Digest("442abdc8e74ad35ebd9571f88fda91ff511dcda8d241a5aed52cea1e00d69e03")))), List())
|
||||
|
||||
val hex = "bcfaed026cf34aac6e3de2bf4b429d114ed4572a7ce4b1c44f2091ae6825ee9774dbae2f4487def8ba376b38c1e4e5910d3c9efd27e740cb9be8d452598cbf2e243fad8a6c2858af42dfee60e037a5640100000001442abdc8e74ad35ebd9571f88fda91ff511dcda8d241a5aed52cea1e00d69e030100"
|
||||
|
@ -51,7 +52,10 @@ class RawMerkleBlockSerializerTest extends FlatSpec with MustMatchers {
|
|||
Leaf(DoubleSha256Digest("77352045b2995c9e0dfff9089e5563cd13914eb4b0723cdd54675c5c3f1c4f6a")),
|
||||
Leaf(DoubleSha256Digest("7ae10c30932c07e4ed25abab233565f9ab279eabbcd60e1bc028c6cdc400361b")))),
|
||||
Leaf(DoubleSha256Digest("8ca2e6b66c55fbb63cb7c9b5ccd19be508034eedcd8511d216b9fe93aafc2ceb"))),
|
||||
UInt32(6), List(true, true, true, false, true, true, false, true, false, false, false, false, false, false, false, false),
|
||||
UInt32(6),
|
||||
BitVector.bits(List(true, true, true, false, true, true,
|
||||
false, true, false, false, false,
|
||||
false, false, false, false, false)),
|
||||
List(
|
||||
DoubleSha256Digest("e4aeaf729035a7fb939e12c4f6a2072a9b2e7da784207ce7852d398593210a45"),
|
||||
DoubleSha256Digest("010506d2103d0feb477224926eedaf3d7478fe3d93b54bd24e5eb2c0adc309b3"),
|
||||
|
@ -85,7 +89,13 @@ class RawMerkleBlockSerializerTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "serialize and deserialize a merkle block with two bytes worth of bit flags" in {
|
||||
//https://github.com/bitcoinj/bitcoinj/blob/840df06b79beac1b984e6e247e90fcdedc4ad6e0/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java#L129
|
||||
val hex = "0100000006e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba0300000000006abbb3eb3d733a9fe18967fd7d4c117e4ccbbac5bec4d910d900b3ae0793e77f54241b4d4c86041b4089cc9b0c000000084c30b63cfcdc2d35e3329421b9805ef0c6565d35381ca857762ea0b3a5a128bbca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefbbb15ac1d57d0182aaee61c74743a9c4f785895e563909bafec45c9a2b0ff3181d77706be8b1dcc91112eada86d424e2d0a8907c3488b6e44fda5a74a25cbc7d6bb4fa04245f4ac8a1a571d5537eac24adca1454d65eda446055479af6c6d4dd3c9ab658448c10b6921b7a4ce3021eb22ed6bb6a7fde1e5bcc4b1db6615c6abc5ca042127bfaf9f44ebce29cb29c6df9d05b47f35b2edff4f0064b578ab741fa78276222651209fe1a2c4c0fa1c58510aec8b090dd1eb1f82f9d261b8273b525b02ff1a"
|
||||
val hex = "0100000006e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba0300000000006abbb3eb3d733a9fe18967fd7d4c117" +
|
||||
"e4ccbbac5bec4d910d900b3ae0793e77f54241b4d4c86041b4089cc9b0c000000084c30b63cfcdc2d35e3329421b9805ef0c6565d35381c" +
|
||||
"a857762ea0b3a5a128bbca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefbbb15ac1d57d0182aaee61c74743" +
|
||||
"a9c4f785895e563909bafec45c9a2b0ff3181d77706be8b1dcc91112eada86d424e2d0a8907c3488b6e44fda5a74a25cbc7d6bb4fa04245" +
|
||||
"f4ac8a1a571d5537eac24adca1454d65eda446055479af6c6d4dd3c9ab658448c10b6921b7a4ce3021eb22ed6bb6a7fde1e5bcc4b1db661" +
|
||||
"5c6abc5ca042127bfaf9f44ebce29cb29c6df9d05b47f35b2edff4f0064b578ab741fa78276222651209fe1a2c4c0fa1c58510aec8b090d" +
|
||||
"d1eb1f82f9d261b8273b525b02ff1a"
|
||||
val merkleBlock = MerkleBlock(hex)
|
||||
merkleBlock.hex must be(hex)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
* Created by chris on 1/12/16.
|
||||
*/
|
||||
class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers {
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawScriptPubKeyParser" must "read then write the scriptPubKey and get the original scriptPubKey" in {
|
||||
val scriptPubKey: ScriptPubKey = RawScriptPubKeyParser.read(TestUtil.rawScriptPubKey)
|
||||
encode(RawScriptPubKeyParser.write(scriptPubKey)) must be(TestUtil.rawScriptPubKey)
|
||||
|
|
|
@ -14,7 +14,7 @@ class RawScriptSignatureParserTest extends FlatSpec with MustMatchers {
|
|||
//from bitcoin developer examples
|
||||
//https://bitcoin.org/en/developer-reference#raw-transaction-format
|
||||
val rawScriptSig = "4a494830450221008949f0cb400094ad2b5eb399d59d01c14d73d8fe6e96df1a7150deb388ab8935022079656090d7f6bac4c9a94e0aad311a4268e082a725f8aeae0573fb12ff866a5f01"
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawScriptSignatureParser" must "write a raw script sig" in {
|
||||
val scriptSig = RawScriptSignatureParser.read(rawScriptSig)
|
||||
encode(RawScriptSignatureParser.write(scriptSig)) must be(rawScriptSig)
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bitcoins.core.script.reserved.OP_NOP
|
|||
import org.bitcoins.core.script.stack.OP_PICK
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, TestUtil }
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 1/7/16.
|
||||
|
@ -16,7 +17,7 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
class ScriptParserTest extends FlatSpec with MustMatchers {
|
||||
|
||||
"ScriptParser" must "parse 0x00 to a OP_0" in {
|
||||
ScriptParser.fromBytes(List(0.toByte)) must be(List(OP_0))
|
||||
ScriptParser.fromBytes(ByteVector(List(0.toByte))) must be(List(OP_0))
|
||||
}
|
||||
|
||||
it must "parse the number 0 as an OP_0" in {
|
||||
|
@ -47,7 +48,7 @@ class ScriptParserTest extends FlatSpec with MustMatchers {
|
|||
}
|
||||
|
||||
it must "parse a p2pkh output script from a byte array to script tokens" in {
|
||||
val bytes: Seq[Byte] = BitcoinSUtil.decodeHex(TestUtil.p2pkhOutputScript).tail
|
||||
val bytes: scodec.bits.ByteVector = BitcoinSUtil.decodeHex(TestUtil.p2pkhOutputScript).tail
|
||||
ScriptParser.fromBytes(bytes) must be(TestUtil.p2pkhOutputScriptAsm)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.scalatest.{ FlatSpec, MustMatchers }
|
|||
* Created by chris on 1/14/16.
|
||||
*/
|
||||
class RawBaseTransactionParserTest extends FlatSpec with MustMatchers {
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawBaseTransactionParser" must "parse a raw transaction" in {
|
||||
val tx: Transaction = RawBaseTransactionParser.read(TestUtil.rawTransaction)
|
||||
tx.version must be(Int32.one)
|
||||
|
|
|
@ -12,7 +12,7 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers {
|
|||
private val logger = BitcoinSLogger.logger
|
||||
//txid cad1082e674a7bd3bc9ab1bc7804ba8a57523607c876b8eb2cbe645f2b1803d6
|
||||
val rawTxInput = "85d6b0da2edf96b282030d3f4f79d14cc8c882cfef1b3064170c850660317de100000000" + "6f0047304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f00125512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae" + "ffffffff"
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawTransactionInputParser" must "parse a raw serialized transaction input" in {
|
||||
val txInput = RawTransactionInputParser.read(rawTxInput)
|
||||
txInput.previousOutput.vout must be(UInt32.zero)
|
||||
|
@ -55,7 +55,6 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers {
|
|||
txInput.previousOutput.txId.hex must be(BitcoinSUtil.flipEndianness("e99eb3e6551844d0db252ef242c043796b3b0ccfb126c0ae09f9dd0230e2f10d"))
|
||||
txInput.previousOutput.vout must be(UInt32.zero)
|
||||
txInput.scriptSignature.hex must be("fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
|
||||
txInput.scriptSignature.compactSizeUInt.num must be(UInt64(txInput.scriptSignature.asm.flatMap(_.bytes).size))
|
||||
txInput.sequence must be(UInt32(4294967295L))
|
||||
encode(RawTransactionInputParser.write(txInput)) must be(rawTxInput)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class RawTransactionOutPointParserTest extends FlatSpec with MustMatchers {
|
|||
//txid cad1082e674a7bd3bc9ab1bc7804ba8a57523607c876b8eb2cbe645f2b1803d6
|
||||
val rawOutPoint = "85d6b0da2edf96b282030d3f4f79d14cc8c882cfef1b3064170c850660317de100000000"
|
||||
val rawOutPointLargeVout = "df80e3e6eba7dcd4650281d3c13f140dafbb823a7227a78eb6ee9f6cedd0400134000000"
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawTransactionOutPointParser" must "read a raw outpoint into a native scala TransactionOutPoint" in {
|
||||
val outPoint = RawTransactionOutPointParser.read(rawOutPoint)
|
||||
outPoint.txId.hex must be(BitcoinSUtil.flipEndianness("e17d316006850c1764301befcf82c8c84cd1794f3f0d0382b296df2edab0d685"))
|
||||
|
|
|
@ -18,7 +18,7 @@ class RawTransactionOutputParserTest extends FlatSpec with MustMatchers {
|
|||
|
||||
//txid cad1082e674a7bd3bc9ab1bc7804ba8a57523607c876b8eb2cbe645f2b1803d6
|
||||
val rawTxOutput = "204e00000000000017a914eda8ae08b5c9f973f49543e90a7c292367b3337c87"
|
||||
val encode = BitcoinSUtil.encodeHex(_: Seq[Byte])
|
||||
val encode = BitcoinSUtil.encodeHex(_: scodec.bits.ByteVector)
|
||||
"RawTransactionOutputTest" must "read a serialized tx output" in {
|
||||
|
||||
val txOutput: TransactionOutput = RawTransactionOutputParser.read(rawTxOutput)
|
||||
|
|
|
@ -34,7 +34,7 @@ class Base58Test extends FlatSpec with MustMatchers {
|
|||
it must "decode and return same result as bitcoinj" in {
|
||||
val address = "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"
|
||||
val bitcoinj = org.bitcoinj.core.Base58.decode(address)
|
||||
Base58.decode(address) must be(bitcoinj)
|
||||
Base58.decode(address).toArray must be(bitcoinj)
|
||||
}
|
||||
|
||||
it must "encode tests in base58_encode_decode.json" in {
|
||||
|
|
|
@ -19,12 +19,6 @@ class BitcoinSUtilSpec extends Properties("BitcoinSUtilSpec") {
|
|||
|
||||
property("Convert a byte to a bit vector, convert it back to the original byte") =
|
||||
Prop.forAll { byte: Byte =>
|
||||
BitcoinSUtil.bitVectorToByte(BitcoinSUtil.byteToBitVector(byte)) == byte
|
||||
}
|
||||
|
||||
property("Convert a sequence of bit vectors to a sequence of bytes") =
|
||||
Prop.forAll(NumberGenerator.bitVectors) { bitVectors: Seq[Seq[Boolean]] =>
|
||||
BitcoinSUtil.bytesToBitVectors(BitcoinSUtil.bitVectorsToBytes(bitVectors)) == bitVectors
|
||||
|
||||
BitcoinSUtil.bitVectorToBytes(BitcoinSUtil.byteToBitVector(byte)).toByte() == byte
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.util
|
||||
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.BitVector
|
||||
|
||||
/**
|
||||
* Created by chris on 4/1/16.
|
||||
|
@ -23,33 +24,33 @@ class BitcoinSUtilTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "convert a byte to a bit vector" in {
|
||||
val byte = 0.toByte
|
||||
BitcoinSUtil.byteToBitVector(byte) must be(Seq(false, false, false, false, false, false, false, false))
|
||||
BitcoinSUtil.byteToBitVector(byte).toIndexedSeq must be(Seq(false, false, false, false, false, false, false, false))
|
||||
|
||||
val byte1 = 1.toByte
|
||||
BitcoinSUtil.byteToBitVector(byte1) must be(Seq(false, false, false, false, false, false, false, true))
|
||||
BitcoinSUtil.byteToBitVector(byte1).toIndexedSeq must be(Seq(false, false, false, false, false, false, false, true))
|
||||
|
||||
val byte2 = 2.toByte
|
||||
BitcoinSUtil.byteToBitVector(byte2) must be(Seq(false, false, false, false, false, false, true, false))
|
||||
BitcoinSUtil.byteToBitVector(byte2).toIndexedSeq must be(Seq(false, false, false, false, false, false, true, false))
|
||||
|
||||
val byte3 = 3.toByte
|
||||
BitcoinSUtil.byteToBitVector(byte3) must be(Seq(false, false, false, false, false, false, true, true))
|
||||
BitcoinSUtil.byteToBitVector(byte3).toIndexedSeq must be(Seq(false, false, false, false, false, false, true, true))
|
||||
|
||||
val maxByte = 0xff.toByte
|
||||
BitcoinSUtil.byteToBitVector(maxByte) must be(Seq(true, true, true, true, true, true, true, true))
|
||||
BitcoinSUtil.byteToBitVector(maxByte).toIndexedSeq must be(Seq(true, true, true, true, true, true, true, true))
|
||||
}
|
||||
|
||||
it must "convert a bit vector to a byte" in {
|
||||
val bitVector0 = Seq(false, false, false, false, false, false, false, false)
|
||||
BitcoinSUtil.bitVectorToByte(bitVector0) must be(0.toByte)
|
||||
val bitVector0 = BitVector.bits(Seq(false, false, false, false, false, false, false, false))
|
||||
BitcoinSUtil.bitVectorToBytes(bitVector0).toByte() must be(0.toByte)
|
||||
|
||||
val bitVector1 = Seq(false, false, false, false, false, false, false, true)
|
||||
BitcoinSUtil.bitVectorToByte(bitVector1) must be(1.toByte)
|
||||
val bitVector1 = BitVector.bits(Seq(false, false, false, false, false, false, false, true))
|
||||
BitcoinSUtil.bitVectorToBytes(bitVector1).toByte() must be(1.toByte)
|
||||
|
||||
val bitVector2 = Seq(false, false, false, false, false, false, true, false)
|
||||
BitcoinSUtil.bitVectorToByte(bitVector2) must be(2.toByte)
|
||||
val bitVector2 = BitVector.bits(Seq(false, false, false, false, false, false, true, false))
|
||||
BitcoinSUtil.bitVectorToBytes(bitVector2).toByte() must be(2.toByte)
|
||||
|
||||
val bitVectorMax = Seq(true, true, true, true, true, true, true, true)
|
||||
BitcoinSUtil.bitVectorToByte(bitVectorMax) must be(0xff.toByte)
|
||||
val bitVectorMax = BitVector.bits(Seq(true, true, true, true, true, true, true, true))
|
||||
BitcoinSUtil.bitVectorToBytes(bitVectorMax).toByte() must be(0xff.toByte)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
package org.bitcoins.core.util
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import org.bitcoins.core.crypto.{ ECPrivateKey, ECPublicKey }
|
||||
import org.bitcoins.core.protocol.script.{ SigVersionBase, SigVersionWitnessV0 }
|
||||
import org.bitcoins.core.script.bitwise.{ OP_EQUALVERIFY, OP_OR }
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.crypto._
|
||||
import org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType
|
||||
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
|
||||
import org.bitcoins.core.script.reserved.{ OP_NOP, OP_RESERVED }
|
||||
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
|
||||
import org.bitcoins.core.script.stack.OP_DUP
|
||||
import org.scalatest.{ FlatSpec, MustMatchers }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 3/2/16.
|
||||
|
@ -26,10 +24,6 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
|
|||
BitcoinScriptUtil.asmToHex(asm) must be(asm.flatMap(_.hex).mkString)
|
||||
}
|
||||
|
||||
it must "give us the correct byte representation of an asm script" in {
|
||||
BitcoinScriptUtil.asmToBytes(asm) must be(asm.flatMap(_.bytes))
|
||||
}
|
||||
|
||||
it must "filter out all of the push operations in a scriptSig" in {
|
||||
BitcoinScriptUtil.filterPushOps(Seq(OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4) ++
|
||||
BytesToPushOntoStack.operations).isEmpty must be(true)
|
||||
|
@ -118,43 +112,43 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "determine that a OP_PUSHDATA1 operation is the minimal push op for a 76 byte script constant" in {
|
||||
val byteConstantSize = 76
|
||||
val byteConstant = for { x <- 0 to byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA1, scriptConstant) must be(true)
|
||||
}
|
||||
|
||||
it must "determine that a OP_PUSHDATA1 operation is NOT the minimal push op for a 75 byte script constant" in {
|
||||
val byteConstantSize = 75
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA1, scriptConstant) must be(false)
|
||||
}
|
||||
|
||||
it must "determine that a OP_PUSHDATA2 operation is NOT the minimal push op for a 255 byte script constant" in {
|
||||
val byteConstantSize = 255
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA2, scriptConstant) must be(false)
|
||||
}
|
||||
|
||||
it must "determine that a OP_PUSHDATA2 operation is the minimal push op for a 256 byte script constant" in {
|
||||
val byteConstantSize = 256
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA2, scriptConstant) must be(true)
|
||||
}
|
||||
|
||||
it must "determine that a OP_PUSHDATA4 operation is NOT the minimal push op for a 65535 byte script constant" in {
|
||||
val byteConstantSize = 65535
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA4, scriptConstant) must be(false)
|
||||
}
|
||||
|
||||
it must "determine that a OP_PUSHDATA4 operation is the minimal push op for a 65536 byte script constant" in {
|
||||
val byteConstantSize = 65536
|
||||
val byteConstant = for { x <- 0 until byteConstantSize } yield 0x0.toByte
|
||||
val scriptConstant = ScriptConstant(byteConstant)
|
||||
val byteConstant = Array.fill(byteConstantSize)(0.toByte)
|
||||
val scriptConstant = ScriptConstant(ByteVector(byteConstant))
|
||||
BitcoinScriptUtil.isMinimalPush(OP_PUSHDATA4, scriptConstant) must be(true)
|
||||
}
|
||||
|
||||
|
@ -222,7 +216,7 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "cast a script token to a boolean value" in {
|
||||
BitcoinScriptUtil.castToBool(ScriptConstant("")) must be(false)
|
||||
BitcoinScriptUtil.castToBool(ScriptConstant(Seq(0x80.toByte))) must be(false)
|
||||
BitcoinScriptUtil.castToBool(ScriptConstant(scodec.bits.ByteVector(0x80.toByte))) must be(false)
|
||||
BitcoinScriptUtil.castToBool(ScriptConstant("000000")) must be(false)
|
||||
BitcoinScriptUtil.castToBool(ScriptConstant("00000080")) must be(false)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
|
|||
import org.bitcoins.core.script.constant.{ ScriptConstant, ScriptToken }
|
||||
import org.bitcoins.core.serializers.bloom.RawBloomFilterSerializer
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
|
||||
import scodec.bits.{ BitVector, ByteVector }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.hashing.MurmurHash3
|
||||
|
@ -24,7 +25,7 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
def filterSize: CompactSizeUInt
|
||||
|
||||
/** The bits that are set inside of the bloom filter */
|
||||
def data: Seq[Byte]
|
||||
def data: scodec.bits.ByteVector
|
||||
|
||||
/** The number of hash functions used in the bloom filter */
|
||||
def hashFuncs: UInt32
|
||||
|
@ -40,11 +41,11 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
def flags: BloomFlag
|
||||
|
||||
/** Inserts a sequence of bytes into the [[BloomFilter]] */
|
||||
def insert(bytes: Seq[Byte]): BloomFilter = {
|
||||
def insert(bytes: scodec.bits.ByteVector): BloomFilter = {
|
||||
//these are the bit indexes that need to be set inside of data
|
||||
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i, bytes))
|
||||
@tailrec
|
||||
def loop(remainingBitIndexes: Seq[Int], accum: Seq[Byte]): Seq[Byte] = {
|
||||
def loop(remainingBitIndexes: Seq[Int], accum: scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
if (remainingBitIndexes.isEmpty) accum
|
||||
else {
|
||||
val currentIndex = remainingBitIndexes.head
|
||||
|
@ -56,7 +57,7 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
val byte = accum(byteIndex)
|
||||
val setBitByte: Byte = (byte | bitIndex).toByte
|
||||
//replace old byte with new byte with bit set
|
||||
val newAccum: Seq[Byte] = accum.updated(byteIndex, setBitByte)
|
||||
val newAccum: scodec.bits.ByteVector = accum.update(byteIndex, setBitByte)
|
||||
loop(remainingBitIndexes.tail, newAccum)
|
||||
}
|
||||
}
|
||||
|
@ -77,12 +78,13 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
def insert(outPoint: TransactionOutPoint): BloomFilter = insert(outPoint.bytes)
|
||||
|
||||
/** Checks if [[data]] contains the given sequence of bytes */
|
||||
def contains(bytes: Seq[Byte]): Boolean = {
|
||||
def contains(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i, bytes))
|
||||
@tailrec
|
||||
def loop(remainingBitIndexes: Seq[Int], accum: Seq[Boolean]): Boolean = {
|
||||
if (remainingBitIndexes.isEmpty) !accum.exists(_ == false)
|
||||
else {
|
||||
def loop(remainingBitIndexes: Seq[Int], accum: scodec.bits.BitVector): Boolean = {
|
||||
if (remainingBitIndexes.isEmpty) {
|
||||
!accum.toIndexedSeq.exists(_ == false)
|
||||
} else {
|
||||
val currentIndex = remainingBitIndexes.head
|
||||
val byteIndex = currentIndex >>> 3
|
||||
val bitIndex = (1 << (7 & currentIndex)).toByte
|
||||
|
@ -91,7 +93,7 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
loop(remainingBitIndexes.tail, isBitSet +: accum)
|
||||
}
|
||||
}
|
||||
loop(bitIndexes, Nil)
|
||||
loop(bitIndexes, BitVector.empty)
|
||||
}
|
||||
|
||||
/** Checks if [[data]] contains a [[DoubleSha256Digest]] */
|
||||
|
@ -211,7 +213,7 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
* @param bytes the bytes of the data that needs to be inserted into the [[BloomFilter]]
|
||||
* @return the index of the bit inside of [[data]] that needs to be set to 1
|
||||
*/
|
||||
private def murmurHash(hashNum: Int, bytes: Seq[Byte]): Int = {
|
||||
private def murmurHash(hashNum: Int, bytes: scodec.bits.ByteVector): Int = {
|
||||
//TODO: The call of .toInt is probably the source of a bug here, need to come back and look at this
|
||||
//since this isn't consensus critical though I'm leaving this for now
|
||||
val seed = (hashNum * murmurConstant.toLong + tweak.toLong).toInt
|
||||
|
@ -228,9 +230,9 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
private def murmurConstant = UInt32("fba4c795")
|
||||
|
||||
/** Adds a sequence of byte vectors to our bloom filter then returns that new filter*/
|
||||
def insertByteVectors(bytes: Seq[Seq[Byte]]): BloomFilter = {
|
||||
def insertByteVectors(bytes: Seq[scodec.bits.ByteVector]): BloomFilter = {
|
||||
@tailrec
|
||||
def loop(remainingByteVectors: Seq[Seq[Byte]], accumBloomFilter: BloomFilter): BloomFilter = {
|
||||
def loop(remainingByteVectors: Seq[scodec.bits.ByteVector], accumBloomFilter: BloomFilter): BloomFilter = {
|
||||
if (remainingByteVectors.isEmpty) accumBloomFilter
|
||||
else loop(remainingByteVectors.tail, accumBloomFilter.insert(remainingByteVectors.head))
|
||||
}
|
||||
|
@ -242,7 +244,7 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
|
||||
object BloomFilter extends Factory[BloomFilter] {
|
||||
|
||||
private case class BloomFilterImpl(filterSize: CompactSizeUInt, data: Seq[Byte], hashFuncs: UInt32,
|
||||
private case class BloomFilterImpl(filterSize: CompactSizeUInt, data: scodec.bits.ByteVector, hashFuncs: UInt32,
|
||||
tweak: UInt32, flags: BloomFlag) extends BloomFilter
|
||||
/** Max bloom filter size as per [[https://bitcoin.org/en/developer-reference#filterload]] */
|
||||
val maxSize = UInt32(36000)
|
||||
|
@ -269,14 +271,14 @@ object BloomFilter extends Factory[BloomFilter] {
|
|||
//BIP37 places a limit on the amount of hashFuncs we can use, which is 50
|
||||
val actualHashFuncs: Int = max(1, min(optimalHashFuncs, maxHashFuncs.toInt)).toInt
|
||||
|
||||
val emptyByteArray = Seq.fill(actualFilterSize)(0.toByte)
|
||||
val emptyByteArray = ByteVector(Array.fill(actualFilterSize)(0.toByte))
|
||||
BloomFilter(CompactSizeUInt(UInt64(actualFilterSize)), emptyByteArray, UInt32(actualHashFuncs), tweak, flags)
|
||||
}
|
||||
|
||||
def apply(filterSize: CompactSizeUInt, data: Seq[Byte], hashFuncs: UInt32, tweak: UInt32, flags: BloomFlag): BloomFilter = {
|
||||
def apply(filterSize: CompactSizeUInt, data: scodec.bits.ByteVector, hashFuncs: UInt32, tweak: UInt32, flags: BloomFlag): BloomFilter = {
|
||||
BloomFilterImpl(filterSize, data, hashFuncs, tweak, flags)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): BloomFilter = RawBloomFilterSerializer.read(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): BloomFilter = RawBloomFilterSerializer.read(bytes)
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,6 @@ object BloomFlag extends Factory[BloomFlag] {
|
|||
else throw new IllegalArgumentException("The given byte was not defined for BloomFlag, got: " + byte)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): BloomFlag = BloomFlag(bytes.head)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): BloomFlag = BloomFlag(bytes.head)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ sealed abstract class NetworkParameters {
|
|||
/** The parameters of the blockchain we are connecting to */
|
||||
def chainParams: ChainParams
|
||||
|
||||
def p2pkhNetworkByte: Seq[Byte] = chainParams.base58Prefix(Base58Type.PubKeyAddress)
|
||||
def p2shNetworkByte: Seq[Byte] = chainParams.base58Prefix(Base58Type.ScriptAddress)
|
||||
def privateKey: Seq[Byte] = chainParams.base58Prefix(Base58Type.SecretKey)
|
||||
def p2pkhNetworkByte: scodec.bits.ByteVector = chainParams.base58Prefix(Base58Type.PubKeyAddress)
|
||||
def p2shNetworkByte: scodec.bits.ByteVector = chainParams.base58Prefix(Base58Type.ScriptAddress)
|
||||
def privateKey: scodec.bits.ByteVector = chainParams.base58Prefix(Base58Type.SecretKey)
|
||||
|
||||
def port: Int
|
||||
def rpcPort: Int
|
||||
|
@ -25,7 +25,7 @@ sealed abstract class NetworkParameters {
|
|||
* a large 32-bit integer with any alignment.
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L108
|
||||
*/
|
||||
def magicBytes: Seq[Byte]
|
||||
def magicBytes: scodec.bits.ByteVector
|
||||
|
||||
/** In bitcoin, the network recaculates the difficulty for the network every 2016 blocks */
|
||||
def difficultyChangeThreshold: Int
|
||||
|
@ -46,7 +46,7 @@ sealed abstract class MainNet extends BitcoinNetwork {
|
|||
override def dnsSeeds = Seq("seed.bitcoin.sipa.be", "dnsseed.bluematt.me", "dnsseed.bitcoin.dashjr.org",
|
||||
"seed.bitcoinstats.com", "bitseed.xf2.org", "seed.bitcoin.jonasschnelli.ch")
|
||||
|
||||
override def magicBytes = Seq(0xf9.toByte, 0xbe.toByte, 0xb4.toByte, 0xd9.toByte)
|
||||
override def magicBytes = scodec.bits.ByteVector(0xf9, 0xbe, 0xb4, 0xd9)
|
||||
|
||||
override def difficultyChangeThreshold: Int = 2016
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ sealed abstract class TestNet3 extends BitcoinNetwork {
|
|||
override def dnsSeeds = Seq(
|
||||
"testnet-seed.bitcoin.petertodd.org",
|
||||
"testnet-seed.bluematt.me", "testnet-seed.bitcoin.schildbach.de")
|
||||
override def magicBytes = Seq(0x0b.toByte, 0x11.toByte, 0x09.toByte, 0x07.toByte)
|
||||
override def magicBytes = scodec.bits.ByteVector(0x0b, 0x11, 0x09, 0x07)
|
||||
|
||||
override def difficultyChangeThreshold: Int = 2016
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ sealed abstract class RegTest extends BitcoinNetwork {
|
|||
override def port = 18444
|
||||
override def rpcPort = TestNet3.rpcPort
|
||||
override def dnsSeeds = Nil
|
||||
override def magicBytes = Seq(0xfa.toByte, 0xbf.toByte, 0xb5.toByte, 0xda.toByte)
|
||||
override def magicBytes = scodec.bits.ByteVector(0xfa, 0xbf, 0xb5, 0xda)
|
||||
override def difficultyChangeThreshold: Int = 2016
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ object Networks {
|
|||
val p2pkhNetworkBytes = knownNetworks.map(_.p2pkhNetworkByte)
|
||||
val p2shNetworkBytes = knownNetworks.map(_.p2shNetworkByte)
|
||||
|
||||
def bytesToNetwork: Map[Seq[Byte], NetworkParameters] = Map(
|
||||
def bytesToNetwork: Map[scodec.bits.ByteVector, NetworkParameters] = Map(
|
||||
MainNet.p2shNetworkByte -> MainNet,
|
||||
MainNet.p2pkhNetworkByte -> MainNet,
|
||||
MainNet.privateKey -> MainNet,
|
||||
|
|
|
@ -5,10 +5,10 @@ import org.bitcoins.core.util.Factory
|
|||
|
||||
sealed abstract class ChainCode extends NetworkElement
|
||||
object ChainCode extends Factory[ChainCode] {
|
||||
private case class ChainCodeImpl(bytes: Seq[Byte]) extends ChainCode {
|
||||
private case class ChainCodeImpl(bytes: scodec.bits.ByteVector) extends ChainCode {
|
||||
require(bytes.size == 32, "ChainCode must be 32 bytes in size, got: " + bytes.size)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): ChainCode = ChainCodeImpl(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): ChainCode = ChainCodeImpl(bytes)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ sealed abstract class DERSignatureUtil {
|
|||
* This will fail if this signature contains the hash type appended to the end of it
|
||||
* @return boolean representing if the signature is a valid
|
||||
*/
|
||||
def isDEREncoded(bytes: Seq[Byte]): Boolean = {
|
||||
def isDEREncoded(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
//signature is trivially valid if the signature is empty
|
||||
if (bytes.nonEmpty && bytes.size < 9) false
|
||||
else if (bytes.nonEmpty) {
|
||||
|
@ -65,7 +65,7 @@ sealed abstract class DERSignatureUtil {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def decodeSignature(bytes: Seq[Byte]): (BigInt, BigInt) = {
|
||||
def decodeSignature(bytes: scodec.bits.ByteVector): (BigInt, BigInt) = {
|
||||
logger.debug("Signature to decode: " + BitcoinSUtil.encodeHex(bytes))
|
||||
val asn1InputStream = new ASN1InputStream(bytes.toArray)
|
||||
//TODO: this is nasty, is there any way to get rid of all this casting???
|
||||
|
@ -124,7 +124,7 @@ sealed abstract class DERSignatureUtil {
|
|||
* @param bytes the bytes to check if they are strictly der encoded
|
||||
* @return boolean indicating whether the bytes were der encoded or not
|
||||
*/
|
||||
def isValidSignatureEncoding(bytes: Seq[Byte]): Boolean = {
|
||||
def isValidSignatureEncoding(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
|
||||
// * total-length: 1-byte length descriptor of everything that follows,
|
||||
// excluding the sighash byte.
|
||||
|
@ -219,7 +219,7 @@ sealed abstract class DERSignatureUtil {
|
|||
* @param signature
|
||||
* @return if the S value is the low version
|
||||
*/
|
||||
def isLowS(signature: Seq[Byte]): Boolean = {
|
||||
def isLowS(signature: scodec.bits.ByteVector): Boolean = {
|
||||
val result = Try {
|
||||
val (r, s) = decodeSignature(signature)
|
||||
s.bigInteger.compareTo(CryptoParams.halfCurveOrder) <= 0
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.crypto
|
||||
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
|
||||
import scodec.bits.ByteVector
|
||||
/**
|
||||
* Created by chris on 2/26/16.
|
||||
*/
|
||||
|
@ -9,7 +10,7 @@ sealed abstract class ECDigitalSignature {
|
|||
|
||||
def hex: String = BitcoinSUtil.encodeHex(bytes)
|
||||
|
||||
def bytes: Seq[Byte]
|
||||
def bytes: scodec.bits.ByteVector
|
||||
|
||||
def isEmpty = bytes.isEmpty
|
||||
|
||||
|
@ -44,7 +45,7 @@ sealed abstract class ECDigitalSignature {
|
|||
}
|
||||
|
||||
case object EmptyDigitalSignature extends ECDigitalSignature {
|
||||
override val bytes = Nil
|
||||
override val bytes = ByteVector.empty
|
||||
override def r = java.math.BigInteger.valueOf(0)
|
||||
override def s = r
|
||||
}
|
||||
|
@ -57,13 +58,13 @@ case object EmptyDigitalSignature extends ECDigitalSignature {
|
|||
* https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
|
||||
*/
|
||||
case object DummyECDigitalSignature extends ECDigitalSignature {
|
||||
override val bytes = 0.until(72).map(_ => 0.toByte)
|
||||
override val bytes = ByteVector(Array.fill(72)(0.toByte))
|
||||
}
|
||||
|
||||
object ECDigitalSignature extends Factory[ECDigitalSignature] {
|
||||
private case class ECDigitalSignatureImpl(bytes: Seq[Byte]) extends ECDigitalSignature
|
||||
private case class ECDigitalSignatureImpl(bytes: scodec.bits.ByteVector) extends ECDigitalSignature
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): ECDigitalSignature = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ECDigitalSignature = {
|
||||
//this represents the empty signature
|
||||
if (bytes.size == 1 && bytes.head == 0x0) EmptyDigitalSignature
|
||||
else if (bytes.size == 0) EmptyDigitalSignature
|
||||
|
@ -89,8 +90,13 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
|
|||
def fromRS(r: BigInt, s: BigInt): ECDigitalSignature = {
|
||||
val rsSize = r.toByteArray.size + s.toByteArray.size
|
||||
val totalSize = 4 + rsSize
|
||||
val bytes: Seq[Byte] = Seq(0x30.toByte, totalSize.toByte, 0x2.toByte, r.toByteArray.size.toByte) ++
|
||||
r.toByteArray.toSeq ++ Seq(0x2.toByte, s.toByteArray.size.toByte) ++ s.toByteArray.toSeq
|
||||
val bytes: scodec.bits.ByteVector = {
|
||||
ByteVector(Array(0x30.toByte, totalSize.toByte, 0x2.toByte, r.toByteArray.size.toByte))
|
||||
.++(ByteVector(r.toByteArray))
|
||||
.++(ByteVector(Array(0x2.toByte, s.toByteArray.size.toByte)))
|
||||
.++(ByteVector(s.toByteArray))
|
||||
}
|
||||
|
||||
fromBytes(bytes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.bouncycastle.crypto.digests.SHA256Digest
|
|||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator
|
||||
import org.bouncycastle.crypto.params.{ ECKeyGenerationParameters, ECPrivateKeyParameters, ECPublicKeyParameters }
|
||||
import org.bouncycastle.crypto.signers.{ ECDSASigner, HMacDSAKCalculator }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext.Implicits
|
||||
|
@ -23,7 +24,7 @@ import scala.util.{ Failure, Success, Try }
|
|||
*/
|
||||
sealed abstract class BaseECKey extends NetworkElement with Sign {
|
||||
|
||||
override def signFunction: Seq[Byte] => Future[ECDigitalSignature] = { bytes =>
|
||||
override def signFunction: scodec.bits.ByteVector => Future[ECDigitalSignature] = { bytes =>
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
Future(sign(bytes))
|
||||
}
|
||||
|
@ -33,13 +34,13 @@ sealed abstract class BaseECKey extends NetworkElement with Sign {
|
|||
* @param signingKey the key to sign the bytes with
|
||||
* @return the digital signature
|
||||
*/
|
||||
private def sign(dataToSign: Seq[Byte], signingKey: BaseECKey): ECDigitalSignature = {
|
||||
private def sign(dataToSign: scodec.bits.ByteVector, signingKey: BaseECKey): ECDigitalSignature = {
|
||||
require(dataToSign.length == 32 && signingKey.bytes.length <= 32)
|
||||
val signature = NativeSecp256k1.sign(dataToSign.toArray, signingKey.bytes.toArray)
|
||||
ECDigitalSignature(signature)
|
||||
ECDigitalSignature(scodec.bits.ByteVector(signature))
|
||||
}
|
||||
|
||||
override def sign(dataToSign: Seq[Byte]): ECDigitalSignature = sign(dataToSign, this)
|
||||
override def sign(dataToSign: scodec.bits.ByteVector): ECDigitalSignature = sign(dataToSign, this)
|
||||
|
||||
def sign(hash: HashDigest, signingKey: BaseECKey): ECDigitalSignature = sign(hash.bytes, signingKey)
|
||||
|
||||
|
@ -48,7 +49,7 @@ sealed abstract class BaseECKey extends NetworkElement with Sign {
|
|||
def signFuture(hash: HashDigest)(implicit ec: ExecutionContext): Future[ECDigitalSignature] = Future(sign(hash))
|
||||
|
||||
@deprecated("Deprecated in favor of signing algorithm inside of secp256k1", "2/20/2017")
|
||||
private def oldSign(dataToSign: Seq[Byte], signingKey: BaseECKey): ECDigitalSignature = {
|
||||
private def oldSign(dataToSign: scodec.bits.ByteVector, signingKey: BaseECKey): ECDigitalSignature = {
|
||||
val signer: ECDSASigner = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()))
|
||||
val privKey: ECPrivateKeyParameters = new ECPrivateKeyParameters(
|
||||
new BigInteger(1, signingKey.bytes.toArray), CryptoParams.curve)
|
||||
|
@ -76,9 +77,11 @@ sealed abstract class ECPrivateKey extends BaseECKey {
|
|||
|
||||
/** Derives the public for a the private key */
|
||||
def publicKey: ECPublicKey = {
|
||||
val pubKeyBytes: Seq[Byte] = NativeSecp256k1.computePubkey(bytes.toArray, isCompressed)
|
||||
require(NativeSecp256k1.isValidPubKey(pubKeyBytes.toArray), "secp256k1 failed to generate a valid public key, got: " + BitcoinSUtil.encodeHex(pubKeyBytes))
|
||||
ECPublicKey(pubKeyBytes)
|
||||
val pubKeyBytes: Array[Byte] = NativeSecp256k1.computePubkey(bytes.toArray, isCompressed)
|
||||
require(
|
||||
NativeSecp256k1.isValidPubKey(pubKeyBytes),
|
||||
s"secp256k1 failed to generate a valid public key, got: ${BitcoinSUtil.encodeHex(ByteVector(pubKeyBytes))}")
|
||||
ECPublicKey(ByteVector(pubKeyBytes))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +92,7 @@ sealed abstract class ECPrivateKey extends BaseECKey {
|
|||
def toWIF(network: NetworkParameters): String = {
|
||||
val networkByte = network.privateKey
|
||||
//append 1 byte to the end of the priv key byte representation if we need a compressed pub key
|
||||
val fullBytes = if (isCompressed) networkByte ++ (bytes ++ Seq(1.toByte)) else networkByte ++ bytes
|
||||
val fullBytes = if (isCompressed) networkByte ++ (bytes ++ scodec.bits.ByteVector(1)) else networkByte ++ bytes
|
||||
val hash = CryptoUtil.doubleSHA256(fullBytes)
|
||||
val checksum = hash.bytes.take(4)
|
||||
val encodedPrivKey = fullBytes ++ checksum
|
||||
|
@ -101,25 +104,23 @@ sealed abstract class ECPrivateKey extends BaseECKey {
|
|||
|
||||
object ECPrivateKey extends Factory[ECPrivateKey] {
|
||||
|
||||
private case class ECPrivateKeyImpl(override val bytes: Seq[Byte], isCompressed: Boolean, ec: ExecutionContext) extends ECPrivateKey {
|
||||
private case class ECPrivateKeyImpl(override val bytes: scodec.bits.ByteVector, isCompressed: Boolean, ec: ExecutionContext) extends ECPrivateKey {
|
||||
require(NativeSecp256k1.secKeyVerify(bytes.toArray), "Invalid key according to secp256k1, hex: " + BitcoinSUtil.encodeHex(bytes))
|
||||
}
|
||||
|
||||
def apply(bytes: Seq[Byte], isCompressed: Boolean)(implicit ec: ExecutionContext): ECPrivateKey = {
|
||||
def apply(bytes: scodec.bits.ByteVector, isCompressed: Boolean)(implicit ec: ExecutionContext): ECPrivateKey = {
|
||||
ECPrivateKeyImpl(bytes, isCompressed, ec)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): ECPrivateKey = fromBytes(bytes, true)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ECPrivateKey = fromBytes(bytes, true)
|
||||
|
||||
@tailrec
|
||||
def fromBytes(bytes: Seq[Byte], isCompressed: Boolean): ECPrivateKey = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector, isCompressed: Boolean): ECPrivateKey = {
|
||||
|
||||
if (bytes.size == 32) ECPrivateKeyImpl(bytes, isCompressed, Implicits.global)
|
||||
else if (bytes.size < 32) {
|
||||
//means we need to pad the private key with 0 bytes so we have 32 bytes
|
||||
val paddingNeeded = 32 - bytes.size
|
||||
val padding = for { _ <- 0 until paddingNeeded } yield 0.toByte
|
||||
ECPrivateKey.fromBytes(padding ++ bytes, isCompressed)
|
||||
ECPrivateKey.fromBytes(bytes.padLeft(32), isCompressed)
|
||||
} //this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
|
||||
else if (bytes.size == 33) ECPrivateKey.fromBytes(bytes.slice(1, 33), isCompressed)
|
||||
else throw new IllegalArgumentException("Private keys cannot be greater than 33 bytes in size, got: " +
|
||||
|
@ -144,7 +145,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
|
|||
val keypair: AsymmetricCipherKeyPair = generator.generateKeyPair
|
||||
val privParams: ECPrivateKeyParameters = keypair.getPrivate.asInstanceOf[ECPrivateKeyParameters]
|
||||
val priv: BigInteger = privParams.getD
|
||||
val bytes = priv.toByteArray
|
||||
val bytes = ByteVector(priv.toByteArray)
|
||||
ECPrivateKey.fromBytes(bytes, isCompressed)
|
||||
}
|
||||
/**
|
||||
|
@ -169,8 +170,8 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
|
|||
* @param bytes private key in bytes
|
||||
* @return
|
||||
*/
|
||||
def isCompressed(bytes: Seq[Byte]): Boolean = {
|
||||
val validCompressedBytes: Seq[Seq[Byte]] = Networks.secretKeyBytes
|
||||
def isCompressed(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
val validCompressedBytes: Seq[scodec.bits.ByteVector] = Networks.secretKeyBytes
|
||||
val validCompressedBytesInHex: Seq[String] = validCompressedBytes.map(b => BitcoinSUtil.encodeHex(b))
|
||||
val firstByteHex = BitcoinSUtil.encodeHex(bytes.head)
|
||||
if (validCompressedBytesInHex.contains(firstByteHex)) bytes(bytes.length - 5) == 0x01.toByte
|
||||
|
@ -189,7 +190,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
|
|||
* @param WIF Wallet Import Format. Encoded in Base58
|
||||
* @return
|
||||
*/
|
||||
private def trimFunction(WIF: String): Seq[Byte] = {
|
||||
private def trimFunction(WIF: String): scodec.bits.ByteVector = {
|
||||
val bytesChecked = Base58.decodeCheck(WIF)
|
||||
|
||||
//see https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
|
@ -232,7 +233,7 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean = verify(hash.bytes, signature)
|
||||
|
||||
/** Verifies if a given piece of data is signed by the [[ECPrivateKey]]'s corresponding [[ECPublicKey]]. */
|
||||
def verify(data: Seq[Byte], signature: ECDigitalSignature): Boolean = {
|
||||
def verify(data: scodec.bits.ByteVector, signature: ECDigitalSignature): Boolean = {
|
||||
val result = NativeSecp256k1.verify(data.toArray, signature.bytes.toArray, bytes.toArray)
|
||||
if (!result) {
|
||||
//if signature verification fails with libsecp256k1 we need to use our old
|
||||
|
@ -250,7 +251,7 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
override def toString = "ECPublicKey(" + hex + ")"
|
||||
|
||||
@deprecated("Deprecated in favor of using verify functionality inside of secp256k1", "2/20/2017")
|
||||
private def oldVerify(data: Seq[Byte], signature: ECDigitalSignature): Boolean = {
|
||||
private def oldVerify(data: scodec.bits.ByteVector, signature: ECDigitalSignature): Boolean = {
|
||||
/** The elliptic curve used by bitcoin. */
|
||||
def curve = CryptoParams.curve
|
||||
/** This represents this public key in the bouncy castle library */
|
||||
|
@ -280,13 +281,16 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
|
||||
/** Returns the decompressed version of this [[ECPublicKey]] */
|
||||
def decompressed: ECPublicKey = {
|
||||
if (isCompressed) ECPublicKey.fromBytes(NativeSecp256k1.decompress(bytes.toArray)) else this
|
||||
if (isCompressed) {
|
||||
val decompressed = NativeSecp256k1.decompress(bytes.toArray)
|
||||
ECPublicKey.fromBytes(ByteVector(decompressed))
|
||||
} else this
|
||||
}
|
||||
}
|
||||
|
||||
object ECPublicKey extends Factory[ECPublicKey] {
|
||||
|
||||
private case class ECPublicKeyImpl(override val bytes: Seq[Byte], ec: ExecutionContext) extends ECPublicKey {
|
||||
private case class ECPublicKeyImpl(override val bytes: scodec.bits.ByteVector, ec: ExecutionContext) extends ECPublicKey {
|
||||
//unfortunately we cannot place ANY invariants here
|
||||
//because of old transactions on the blockchain that have weirdly formatted public keys. Look at example in script_tests.json
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_tests.json#L457
|
||||
|
@ -295,7 +299,7 @@ object ECPublicKey extends Factory[ECPublicKey] {
|
|||
//Eventually we would like this to be CPubKey::IsFullyValid() but since we are remaining backwards compatible
|
||||
//we cannot do this. If there ever is a hard fork this would be a good thing to add.
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): ECPublicKey = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ECPublicKey = {
|
||||
ECPublicKeyImpl(bytes, Implicits.global)
|
||||
}
|
||||
|
||||
|
@ -309,11 +313,11 @@ object ECPublicKey extends Factory[ECPublicKey] {
|
|||
* Mimics this function in bitcoin core
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.cpp#L207-L212]]
|
||||
*/
|
||||
def isFullyValid(bytes: Seq[Byte]): Boolean = Try(NativeSecp256k1.isValidPubKey(bytes.toArray)).isSuccess && isValid(bytes)
|
||||
def isFullyValid(bytes: scodec.bits.ByteVector): Boolean = Try(NativeSecp256k1.isValidPubKey(bytes.toArray)).isSuccess && isValid(bytes)
|
||||
|
||||
/**
|
||||
* Mimics the CPubKey::IsValid function in Bitcoin core, this is a consensus rule
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.h#L158]]
|
||||
*/
|
||||
def isValid(bytes: Seq[Byte]): Boolean = bytes.nonEmpty
|
||||
def isValid(bytes: scodec.bits.ByteVector): Boolean = bytes.nonEmpty
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.bitcoin.NativeSecp256k1
|
|||
import org.bitcoins.core.number.{ UInt32, UInt8 }
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.util._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
||||
|
@ -20,7 +21,7 @@ sealed abstract class ExtKey extends NetworkElement {
|
|||
/** 0 for master nodes, 1 for level-1 derived keys, .... */
|
||||
def depth: UInt8
|
||||
/** The fingerprint of the parent key */
|
||||
def fingerprint: Seq[Byte]
|
||||
def fingerprint: scodec.bits.ByteVector
|
||||
/**
|
||||
* Child number. This is ser32(i) for i in xi = xpar/i, with xi the key being serialized.
|
||||
* (0x00000000 if master key)
|
||||
|
@ -48,10 +49,10 @@ sealed abstract class ExtKey extends NetworkElement {
|
|||
Try(UInt32(idx)).flatMap(deriveChildPubKey(_))
|
||||
}
|
||||
|
||||
override def bytes: Seq[Byte] = key match {
|
||||
override def bytes: scodec.bits.ByteVector = key match {
|
||||
case priv: ECPrivateKey =>
|
||||
version.bytes ++ depth.bytes ++ fingerprint ++
|
||||
childNum.bytes ++ chainCode.bytes ++ Seq(0.toByte) ++ priv.bytes
|
||||
childNum.bytes ++ chainCode.bytes ++ scodec.bits.ByteVector.low(1) ++ priv.bytes
|
||||
case pub: ECPublicKey =>
|
||||
version.bytes ++ depth.bytes ++ fingerprint ++
|
||||
childNum.bytes ++ chainCode.bytes ++ pub.bytes
|
||||
|
@ -70,7 +71,7 @@ object ExtKey extends Factory[ExtKey] {
|
|||
|
||||
/** Takes in a base58 string and tries to convert it to an extended key */
|
||||
def fromString(base58: String): Try[ExtKey] = {
|
||||
val decoded: Try[Seq[Byte]] = Base58.decodeCheck(base58)
|
||||
val decoded: Try[scodec.bits.ByteVector] = Base58.decodeCheck(base58)
|
||||
val extKey = decoded.flatMap { bytes =>
|
||||
require(bytes.size == 78, "Not 78 bytes")
|
||||
val version: Try[ExtKeyVersion] = ExtKeyVersion(bytes.take(4)) match {
|
||||
|
@ -95,7 +96,7 @@ object ExtKey extends Factory[ExtKey] {
|
|||
extKey
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): ExtKey = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ExtKey = {
|
||||
val privTry = Try(ExtPrivateKey(bytes))
|
||||
if (privTry.isSuccess) privTry.get
|
||||
else {
|
||||
|
@ -108,7 +109,7 @@ sealed abstract class ExtPrivateKey extends ExtKey {
|
|||
override def key: ECPrivateKey
|
||||
|
||||
def deriveChildPrivKey(idx: UInt32): ExtPrivateKey = {
|
||||
val data: Seq[Byte] = if (idx >= ExtKey.hardenedIdx) {
|
||||
val data: scodec.bits.ByteVector = if (idx >= ExtKey.hardenedIdx) {
|
||||
//derive hardened key
|
||||
0.toByte +: ((key.bytes) ++ idx.bytes)
|
||||
} else {
|
||||
|
@ -119,7 +120,8 @@ sealed abstract class ExtPrivateKey extends ExtKey {
|
|||
val (il, ir) = hmac.splitAt(32)
|
||||
//should be ECGroup addition
|
||||
//parse256(IL) + kpar (mod n)
|
||||
val childKey = ECPrivateKey(NativeSecp256k1.privKeyTweakAdd(il.toArray, key.bytes.toArray))
|
||||
val tweak = NativeSecp256k1.privKeyTweakAdd(il.toArray, key.bytes.toArray)
|
||||
val childKey = ECPrivateKey(ByteVector(tweak))
|
||||
val fp = CryptoUtil.sha256Hash160(key.publicKey.bytes).bytes.take(4)
|
||||
ExtPrivateKey(version, depth + UInt8.one, fp, idx,
|
||||
ChainCode(ir), childKey)
|
||||
|
@ -137,12 +139,12 @@ sealed abstract class ExtPrivateKey extends ExtKey {
|
|||
}
|
||||
object ExtPrivateKey extends Factory[ExtPrivateKey] {
|
||||
private case class ExtPrivateKeyImpl(version: ExtKeyVersion, depth: UInt8,
|
||||
fingerprint: Seq[Byte], childNum: UInt32,
|
||||
fingerprint: scodec.bits.ByteVector, childNum: UInt32,
|
||||
chainCode: ChainCode, key: ECPrivateKey) extends ExtPrivateKey {
|
||||
require(fingerprint.size == 4, "Fingerprint must be 4 bytes in size, got: " + fingerprint)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): ExtPrivateKey = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ExtPrivateKey = {
|
||||
require(bytes.size == 78, "ExtPrivateKey can only be 78 bytes")
|
||||
val base58 = Base58.encode(bytes ++ CryptoUtil.doubleSHA256(bytes).bytes.take(4))
|
||||
ExtKey.fromString(base58) match {
|
||||
|
@ -152,7 +154,7 @@ object ExtPrivateKey extends Factory[ExtPrivateKey] {
|
|||
}
|
||||
}
|
||||
def apply(version: ExtKeyVersion, depth: UInt8,
|
||||
fingerprint: Seq[Byte], child: UInt32,
|
||||
fingerprint: scodec.bits.ByteVector, child: UInt32,
|
||||
chainCode: ChainCode, privateKey: ECPrivateKey): ExtPrivateKey = {
|
||||
ExtPrivateKeyImpl(version, depth, fingerprint, child, chainCode, privateKey)
|
||||
}
|
||||
|
@ -161,12 +163,12 @@ object ExtPrivateKey extends Factory[ExtPrivateKey] {
|
|||
* Generates a master private key
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
|
||||
*/
|
||||
def apply(version: ExtKeyVersion, seedOpt: Option[Seq[Byte]] = None): ExtPrivateKey = {
|
||||
def apply(version: ExtKeyVersion, seedOpt: Option[scodec.bits.ByteVector] = None): ExtPrivateKey = {
|
||||
val seed = seedOpt match {
|
||||
case Some(bytes) => bytes
|
||||
case None => ECPrivateKey().bytes
|
||||
}
|
||||
val i = CryptoUtil.hmac512("Bitcoin seed".map(_.toByte), seed)
|
||||
val i = CryptoUtil.hmac512(scodec.bits.ByteVector.encodeAscii("Bitcoin seed").right.get, seed)
|
||||
val (il, ir) = i.splitAt(32)
|
||||
val masterPrivKey = ECPrivateKey(il)
|
||||
val fp = UInt32.zero.bytes
|
||||
|
@ -190,7 +192,7 @@ sealed abstract class ExtPublicKey extends ExtKey {
|
|||
key.bytes.toArray,
|
||||
hmac.toArray,
|
||||
priv.isCompressed)
|
||||
val childPubKey = ECPublicKey(tweaked)
|
||||
val childPubKey = ECPublicKey(ByteVector(tweaked))
|
||||
val bi = BigInt(new BigInteger(1, priv.bytes.toArray))
|
||||
//we do not handle this case since it is impossible
|
||||
//In case parse256(IL) ≥ n or Ki is the point at infinity, the resulting key is invalid,
|
||||
|
@ -205,15 +207,15 @@ sealed abstract class ExtPublicKey extends ExtKey {
|
|||
|
||||
object ExtPublicKey extends Factory[ExtPublicKey] {
|
||||
private case class ExtPublicKeyImpl(version: ExtKeyVersion, depth: UInt8,
|
||||
fingerprint: Seq[Byte], childNum: UInt32,
|
||||
fingerprint: scodec.bits.ByteVector, childNum: UInt32,
|
||||
chainCode: ChainCode, key: ECPublicKey) extends ExtPublicKey
|
||||
|
||||
def apply(version: ExtKeyVersion, depth: UInt8,
|
||||
fingerprint: Seq[Byte], child: UInt32, chainCode: ChainCode, publicKey: ECPublicKey): ExtPublicKey = {
|
||||
fingerprint: scodec.bits.ByteVector, child: UInt32, chainCode: ChainCode, publicKey: ECPublicKey): ExtPublicKey = {
|
||||
ExtPublicKeyImpl(version, depth, fingerprint, child, chainCode, publicKey)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): ExtPublicKey = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): ExtPublicKey = {
|
||||
require(bytes.size == 78, "ExtPublicKey can only be 78 bytes")
|
||||
val base58 = Base58.encode(bytes ++ CryptoUtil.doubleSHA256(bytes).bytes.take(4))
|
||||
ExtKey.fromString(base58) match {
|
||||
|
|
|
@ -4,28 +4,28 @@ import org.bitcoins.core.protocol.NetworkElement
|
|||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
|
||||
sealed abstract class ExtKeyVersion extends NetworkElement {
|
||||
def bytes: Seq[Byte]
|
||||
def bytes: scodec.bits.ByteVector
|
||||
override def hex = BitcoinSUtil.encodeHex(bytes)
|
||||
}
|
||||
|
||||
case object MainNetPub extends ExtKeyVersion {
|
||||
override def bytes = Seq(0x04, 0x88, 0xb2, 0x1E).map(_.toByte)
|
||||
override def bytes = scodec.bits.ByteVector(0x04, 0x88, 0xb2, 0x1E)
|
||||
}
|
||||
|
||||
case object MainNetPriv extends ExtKeyVersion {
|
||||
override def bytes = Seq(0x04, 0x88, 0xAD, 0xE4).map(_.toByte)
|
||||
override def bytes = scodec.bits.ByteVector(0x04, 0x88, 0xAD, 0xE4)
|
||||
}
|
||||
|
||||
case object TestNet3Pub extends ExtKeyVersion {
|
||||
override def bytes = Seq(0x04, 0x35, 0x87, 0xCF).map(_.toByte)
|
||||
override def bytes = scodec.bits.ByteVector(0x04, 0x35, 0x87, 0xCF)
|
||||
}
|
||||
|
||||
case object TestNet3Priv extends ExtKeyVersion {
|
||||
override def bytes = Seq(0x04, 0x35, 0x83, 0x94).map(_.toByte)
|
||||
override def bytes = scodec.bits.ByteVector(0x04, 0x35, 0x83, 0x94)
|
||||
}
|
||||
|
||||
object ExtKeyVersion {
|
||||
private val all: Seq[ExtKeyVersion] = Seq(MainNetPriv, MainNetPub, TestNet3Pub, TestNet3Priv)
|
||||
|
||||
def apply(bytes: Seq[Byte]): Option[ExtKeyVersion] = all.find(_.bytes == bytes)
|
||||
def apply(bytes: scodec.bits.ByteVector): Option[ExtKeyVersion] = all.find(_.bytes == bytes)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.bitcoins.core.util.{ BitcoinSUtil, Factory }
|
|||
* Created by chris on 5/24/16.
|
||||
*/
|
||||
sealed abstract class HashDigest extends NetworkElement {
|
||||
/** The message digest represented in bytes */
|
||||
def bytes: scodec.bits.ByteVector
|
||||
/**
|
||||
* Flips the endianness of the byte sequence.
|
||||
* Since bitcoin unfortunately has inconsistent endianness between the protocol
|
||||
|
@ -24,10 +26,10 @@ sealed abstract class Sha1Digest extends HashDigest {
|
|||
}
|
||||
|
||||
object Sha1Digest extends Factory[Sha1Digest] {
|
||||
private case class Sha1DigestImpl(bytes: Seq[Byte]) extends Sha1Digest {
|
||||
private case class Sha1DigestImpl(bytes: scodec.bits.ByteVector) extends Sha1Digest {
|
||||
override def toString = s"Sha1DigestImpl($hex)"
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): Sha1Digest = Sha1DigestImpl(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Sha1Digest = Sha1DigestImpl(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,11 +40,11 @@ sealed abstract class Sha256Digest extends HashDigest {
|
|||
}
|
||||
|
||||
object Sha256Digest extends Factory[Sha256Digest] {
|
||||
private case class Sha256DigestImpl(bytes: Seq[Byte]) extends Sha256Digest {
|
||||
private case class Sha256DigestImpl(bytes: scodec.bits.ByteVector) extends Sha256Digest {
|
||||
require(bytes.length == 32, "Sha256Digest must be 32 bytes in size, got: " + bytes.length)
|
||||
override def toString = s"Sha256DigestImpl($hex)"
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): Sha256Digest = Sha256DigestImpl(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Sha256Digest = Sha256DigestImpl(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,11 +55,11 @@ sealed abstract class DoubleSha256Digest extends HashDigest {
|
|||
}
|
||||
|
||||
object DoubleSha256Digest extends Factory[DoubleSha256Digest] {
|
||||
private case class DoubleSha256DigestImpl(bytes: Seq[Byte]) extends DoubleSha256Digest {
|
||||
private case class DoubleSha256DigestImpl(bytes: scodec.bits.ByteVector) extends DoubleSha256Digest {
|
||||
require(bytes.length == 32, "DoubleSha256Digest must always be 32 bytes, got: " + bytes.length)
|
||||
override def toString = s"DoubleSha256DigestImpl($hex)"
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): DoubleSha256Digest = DoubleSha256DigestImpl(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): DoubleSha256Digest = DoubleSha256DigestImpl(bytes)
|
||||
|
||||
}
|
||||
|
||||
|
@ -69,11 +71,11 @@ sealed abstract class RipeMd160Digest extends HashDigest {
|
|||
}
|
||||
|
||||
object RipeMd160Digest extends Factory[RipeMd160Digest] {
|
||||
private case class RipeMd160DigestImpl(bytes: Seq[Byte]) extends RipeMd160Digest {
|
||||
private case class RipeMd160DigestImpl(bytes: scodec.bits.ByteVector) extends RipeMd160Digest {
|
||||
require(bytes.length == 20, "RIPEMD160Digest must always be 20 bytes, got: " + bytes.length)
|
||||
override def toString = s"RipeMd160DigestImpl($hex)"
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): RipeMd160Digest = RipeMd160DigestImpl(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): RipeMd160Digest = RipeMd160DigestImpl(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,9 +86,9 @@ sealed abstract class Sha256Hash160Digest extends HashDigest {
|
|||
}
|
||||
|
||||
object Sha256Hash160Digest extends Factory[Sha256Hash160Digest] {
|
||||
private case class Sha256Hash160DigestImpl(bytes: Seq[Byte]) extends Sha256Hash160Digest {
|
||||
private case class Sha256Hash160DigestImpl(bytes: scodec.bits.ByteVector) extends Sha256Hash160Digest {
|
||||
require(bytes.length == 20, "Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length)
|
||||
override def toString = s"Sha256Hash160DigestImpl($hex)"
|
||||
}
|
||||
override def fromBytes(bytes: Seq[Byte]): Sha256Hash160Digest = Sha256Hash160DigestImpl(bytes)
|
||||
}
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Sha256Hash160Digest = Sha256Hash160DigestImpl(bytes)
|
||||
}
|
||||
|
|
|
@ -6,23 +6,23 @@ import scala.concurrent.duration.DurationInt
|
|||
/**
|
||||
* This is meant to be an abstraction for a [[org.bitcoins.core.crypto.ECPrivateKey]], sometimes we will not
|
||||
* have direct access to a private key in memory -- for instance if that key is on a hardware device -- so we need to create an
|
||||
* abstraction of the signing process. Fundamentally a private key takes in a Seq[Byte] and returns a [[ECDigitalSignature]]
|
||||
* abstraction of the signing process. Fundamentally a private key takes in a scodec.bits.ByteVector and returns a [[ECDigitalSignature]]
|
||||
* That is what this abstraction is meant to represent. If you have a [[ECPrivateKey]] in your application, you can get it's
|
||||
* [[Sign]] type by doing this:
|
||||
*
|
||||
* val key = ECPrivateKey()
|
||||
* val sign: Seq[Byte] => Future[ECDigitalSignature] = key.signFunction
|
||||
* val sign: scodec.bits.ByteVector => Future[ECDigitalSignature] = key.signFunction
|
||||
*
|
||||
* If you have a hardware wallet, you will need to implement the protocol to send a message to the hardware device. The
|
||||
* type signature of the function you implement must be Seq[Byte] => Future[ECDigitalSignature]
|
||||
* type signature of the function you implement must be scodec.bits.ByteVector => Future[ECDigitalSignature]
|
||||
*
|
||||
*/
|
||||
trait Sign {
|
||||
def signFunction: Seq[Byte] => Future[ECDigitalSignature]
|
||||
def signFunction: scodec.bits.ByteVector => Future[ECDigitalSignature]
|
||||
|
||||
def signFuture(bytes: Seq[Byte]): Future[ECDigitalSignature] = signFunction(bytes)
|
||||
def signFuture(bytes: scodec.bits.ByteVector): Future[ECDigitalSignature] = signFunction(bytes)
|
||||
|
||||
def sign(bytes: Seq[Byte]): ECDigitalSignature = {
|
||||
def sign(bytes: scodec.bits.ByteVector): ECDigitalSignature = {
|
||||
Await.result(signFuture(bytes), 30.seconds)
|
||||
}
|
||||
|
||||
|
@ -30,9 +30,9 @@ trait Sign {
|
|||
}
|
||||
|
||||
object Sign {
|
||||
private case class SignImpl(signFunction: Seq[Byte] => Future[ECDigitalSignature], publicKey: ECPublicKey) extends Sign
|
||||
private case class SignImpl(signFunction: scodec.bits.ByteVector => Future[ECDigitalSignature], publicKey: ECPublicKey) extends Sign
|
||||
|
||||
def apply(signFunction: Seq[Byte] => Future[ECDigitalSignature], pubKey: ECPublicKey): Sign = {
|
||||
def apply(signFunction: scodec.bits.ByteVector => Future[ECDigitalSignature], pubKey: ECPublicKey): Sign = {
|
||||
SignImpl(signFunction, pubKey)
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,6 @@ object Sign {
|
|||
* a specific private key on another server
|
||||
*/
|
||||
def dummySign(publicKey: ECPublicKey): Sign = {
|
||||
SignImpl({ _: Seq[Byte] => Future.successful(EmptyDigitalSignature) }, publicKey)
|
||||
SignImpl({ _: scodec.bits.ByteVector => Future.successful(EmptyDigitalSignature) }, publicKey)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.bitcoins.core.script.crypto._
|
|||
import org.bitcoins.core.script.flag.{ ScriptFlag, ScriptFlagUtil }
|
||||
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -52,7 +53,7 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
} else {
|
||||
val sigsRemovedScript = BitcoinScriptUtil.calculateScriptForChecking(txSignatureComponent, signature, script)
|
||||
val hashTypeByte = if (signature.bytes.nonEmpty) signature.bytes.last else 0x00.toByte
|
||||
val hashType = HashType(Seq(0.toByte, 0.toByte, 0.toByte, hashTypeByte))
|
||||
val hashType = HashType(ByteVector(0.toByte, 0.toByte, 0.toByte, hashTypeByte))
|
||||
val spk = ScriptPubKey.fromAsm(sigsRemovedScript)
|
||||
val hashForSignature = txSignatureComponent match {
|
||||
case b: BaseTxSigComponent =>
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.crypto
|
|||
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.concurrent.{ ExecutionContext, Future }
|
||||
|
||||
|
@ -19,36 +20,36 @@ sealed abstract class TransactionSignatureCreator {
|
|||
* @return
|
||||
*/
|
||||
def createSig(txSignatureComponent: TxSigComponent, privateKey: ECPrivateKey, hashType: HashType): ECDigitalSignature = {
|
||||
val sign: Seq[Byte] => ECDigitalSignature = privateKey.sign(_: Seq[Byte])
|
||||
val sign: scodec.bits.ByteVector => ECDigitalSignature = privateKey.sign(_: scodec.bits.ByteVector)
|
||||
createSig(txSignatureComponent, sign, hashType)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is intended to be a low level hardware wallet API.
|
||||
* At a fundamental level, a hardware wallet expects a Seq[Byte] as input, and returns an [[ECDigitalSignature]]
|
||||
* if it is able to sign the Seq[Byte]'s correctly.
|
||||
* At a fundamental level, a hardware wallet expects a scodec.bits.ByteVector as input, and returns an [[ECDigitalSignature]]
|
||||
* if it is able to sign the scodec.bits.ByteVector's correctly.
|
||||
* @param component - the information needed to sign the transaction
|
||||
* @param sign - the implementation of the hardware wallet protocol to sign the Seq[Byte] w/ the given public key
|
||||
* @param sign - the implementation of the hardware wallet protocol to sign the scodec.bits.ByteVector w/ the given public key
|
||||
* @param hashType - the hash type to be appended on the digital signature when the hardware wallet is done being signed
|
||||
* @return the digital signature returned by the hardware wallet
|
||||
*/
|
||||
def createSig(component: TxSigComponent, sign: Seq[Byte] => ECDigitalSignature, hashType: HashType): ECDigitalSignature = {
|
||||
def createSig(component: TxSigComponent, sign: scodec.bits.ByteVector => ECDigitalSignature, hashType: HashType): ECDigitalSignature = {
|
||||
val hash = TransactionSignatureSerializer.hashForSignature(component, hashType)
|
||||
val signature = sign(hash.bytes)
|
||||
//append 1 byte hash type onto the end
|
||||
val sig = ECDigitalSignature(signature.bytes ++ Seq(hashType.byte))
|
||||
val sig = ECDigitalSignature(signature.bytes ++ ByteVector.fromByte(hashType.byte))
|
||||
require(sig.isStrictEncoded, "We did not create a signature that is strictly encoded, got: " + sig)
|
||||
require(DERSignatureUtil.isLowS(sig), "Sig does not have a low s value")
|
||||
sig
|
||||
}
|
||||
|
||||
/** This is the same as createSig above, except the 'sign' function returns a Future[ECDigitalSignature] */
|
||||
def createSig(component: TxSigComponent, sign: Seq[Byte] => Future[ECDigitalSignature],
|
||||
def createSig(component: TxSigComponent, sign: scodec.bits.ByteVector => Future[ECDigitalSignature],
|
||||
hashType: HashType)(implicit ec: ExecutionContext): Future[ECDigitalSignature] = {
|
||||
val hash = TransactionSignatureSerializer.hashForSignature(component, hashType)
|
||||
val signature = sign(hash.bytes)
|
||||
//append 1 byte hash type onto the end
|
||||
val sig = signature.map(s => ECDigitalSignature(s.bytes ++ Seq(hashType.byte)))
|
||||
val sig = signature.map(s => ECDigitalSignature(s.bytes ++ ByteVector.fromByte(hashType.byte)))
|
||||
sig.map { s =>
|
||||
require(s.isStrictEncoded, "We did not create a signature that is strictly encoded, got: " + sig)
|
||||
require(DERSignatureUtil.isLowS(s), "Sig does not have a low s value")
|
||||
|
@ -57,4 +58,4 @@ sealed abstract class TransactionSignatureCreator {
|
|||
}
|
||||
}
|
||||
|
||||
object TransactionSignatureCreator extends TransactionSignatureCreator
|
||||
object TransactionSignatureCreator extends TransactionSignatureCreator
|
||||
|
|
|
@ -33,7 +33,7 @@ sealed abstract class TransactionSignatureSerializer {
|
|||
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1113]]
|
||||
*/
|
||||
def serializeForSignature(txSigComponent: TxSigComponent, hashType: HashType): Seq[Byte] = {
|
||||
def serializeForSignature(txSigComponent: TxSigComponent, hashType: HashType): scodec.bits.ByteVector = {
|
||||
val spendingTransaction = txSigComponent.transaction
|
||||
val inputIndex = txSigComponent.inputIndex
|
||||
val output = txSigComponent.output
|
||||
|
@ -141,18 +141,21 @@ sealed abstract class TransactionSignatureSerializer {
|
|||
val inputIndexInt = inputIndex.toInt
|
||||
val emptyHash = CryptoUtil.emptyDoubleSha256Hash
|
||||
|
||||
val outPointHash: Seq[Byte] = if (isNotAnyoneCanPay) {
|
||||
val bytes: Seq[Byte] = spendingTransaction.inputs.flatMap(_.previousOutput.bytes)
|
||||
val outPointHash: scodec.bits.ByteVector = if (isNotAnyoneCanPay) {
|
||||
val prevOuts = spendingTransaction.inputs.map(_.previousOutput)
|
||||
val bytes: scodec.bits.ByteVector = BitcoinSUtil.toByteVector(prevOuts)
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
val sequenceHash: Seq[Byte] = if (isNotAnyoneCanPay && isNotSigHashNone && isNotSigHashSingle) {
|
||||
val bytes = spendingTransaction.inputs.flatMap(_.sequence.bytes)
|
||||
val sequenceHash: scodec.bits.ByteVector = if (isNotAnyoneCanPay && isNotSigHashNone && isNotSigHashSingle) {
|
||||
val sequences = spendingTransaction.inputs.map(_.sequence)
|
||||
val bytes = BitcoinSUtil.toByteVector(sequences)
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
val outputHash: Seq[Byte] = if (isNotSigHashSingle && isNotSigHashNone) {
|
||||
val bytes = spendingTransaction.outputs.flatMap(o => o.bytes)
|
||||
val outputHash: scodec.bits.ByteVector = if (isNotSigHashSingle && isNotSigHashNone) {
|
||||
val outputs = spendingTransaction.outputs
|
||||
val bytes = BitcoinSUtil.toByteVector(outputs)
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else if (HashType.isSigHashSingle(hashType.num) &&
|
||||
inputIndex < UInt32(spendingTransaction.outputs.size)) {
|
||||
|
@ -161,10 +164,10 @@ sealed abstract class TransactionSignatureSerializer {
|
|||
bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
val scriptBytes = script.flatMap(_.bytes)
|
||||
val scriptBytes = BitcoinSUtil.toByteVector(script)
|
||||
|
||||
val i = spendingTransaction.inputs(inputIndexInt)
|
||||
val serializationForSig: Seq[Byte] = spendingTransaction.version.bytes.reverse ++ outPointHash ++ sequenceHash ++
|
||||
val serializationForSig: scodec.bits.ByteVector = spendingTransaction.version.bytes.reverse ++ outPointHash ++ sequenceHash ++
|
||||
i.previousOutput.bytes ++ CompactSizeUInt.calc(scriptBytes).bytes ++
|
||||
scriptBytes ++ amount.bytes ++ i.sequence.bytes.reverse ++
|
||||
outputHash ++ spendingTransaction.lockTime.bytes.reverse ++ hashType.num.bytes.reverse
|
||||
|
@ -227,7 +230,6 @@ sealed abstract class TransactionSignatureSerializer {
|
|||
(output, index) <- spendingTransaction.outputs.zipWithIndex
|
||||
} yield {
|
||||
if (UInt32(index) < inputIndex) {
|
||||
logger.info("Updating tx output to null in bitcoin core")
|
||||
Some(EmptyTransactionOutput)
|
||||
} else if (UInt32(index) == inputIndex) Some(output)
|
||||
else None
|
||||
|
|
|
@ -77,7 +77,7 @@ object Satoshis extends Factory[Satoshis] with BaseNumbers[Satoshis] {
|
|||
val zero = Satoshis(Int64.zero)
|
||||
val one = Satoshis(Int64.one)
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): Satoshis = RawSatoshisSerializer.read(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Satoshis = RawSatoshisSerializer.read(bytes)
|
||||
|
||||
def apply(int64: Int64): Satoshis = SatoshisImpl(int64)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.number
|
|||
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, Factory, NumberUtil }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
||||
|
@ -183,21 +184,25 @@ object UInt8 extends Factory[UInt8] with BaseNumbers[UInt8] {
|
|||
|
||||
def isValid(bigInt: BigInt): Boolean = bigInt >= 0 && bigInt < 256
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): UInt8 = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): UInt8 = {
|
||||
require(bytes.size == 1, "Can only create a uint8 from a byte array of size one, got: " + bytes)
|
||||
val res = NumberUtil.toUnsignedInt(bytes)
|
||||
checkBounds(res)
|
||||
}
|
||||
|
||||
def toUInt8(byte: Byte): UInt8 = {
|
||||
fromBytes(Seq(byte))
|
||||
fromBytes(ByteVector.fromByte(byte))
|
||||
}
|
||||
|
||||
def toByte(uInt8: UInt8): Byte = uInt8.underlying.toByte
|
||||
|
||||
def toBytes(us: Seq[UInt8]): Seq[Byte] = us.map(toByte(_))
|
||||
def toBytes(us: Seq[UInt8]): scodec.bits.ByteVector = {
|
||||
ByteVector(us.map(toByte(_)))
|
||||
}
|
||||
|
||||
def toUInt8s(bytes: Seq[Byte]): Seq[UInt8] = bytes.map(toUInt8(_))
|
||||
def toUInt8s(bytes: scodec.bits.ByteVector): Seq[UInt8] = {
|
||||
bytes.toSeq.map { b: Byte => toUInt8(b) }
|
||||
}
|
||||
|
||||
def checkBounds(res: BigInt): UInt8 = {
|
||||
if (res > max.underlying || res < min.underlying) {
|
||||
|
@ -218,7 +223,7 @@ object UInt32 extends Factory[UInt32] with BaseNumbers[UInt32] {
|
|||
lazy val min = zero
|
||||
lazy val max = UInt32(4294967295L)
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): UInt32 = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): UInt32 = {
|
||||
require(bytes.size <= 4, "UInt32 byte array was too large, got: " + BitcoinSUtil.encodeHex(bytes))
|
||||
val res = NumberUtil.toUnsignedInt(bytes)
|
||||
checkBounds(res)
|
||||
|
@ -248,7 +253,7 @@ object UInt64 extends Factory[UInt64] with BaseNumbers[UInt64] {
|
|||
lazy val min = zero
|
||||
lazy val max = UInt64(BigInt("18446744073709551615"))
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): UInt64 = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): UInt64 = {
|
||||
require(bytes.size <= 8)
|
||||
val res: BigInt = NumberUtil.toUnsignedInt(bytes)
|
||||
if (res > max.underlying || res < min.underlying) {
|
||||
|
@ -272,7 +277,7 @@ object Int32 extends Factory[Int32] with BaseNumbers[Int32] {
|
|||
lazy val min = Int32(-2147483648)
|
||||
lazy val max = Int32(2147483647)
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): Int32 = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Int32 = {
|
||||
require(bytes.size <= 4, "We cannot have an Int32 be larger than 4 bytes")
|
||||
Int32(BigInt(bytes.toArray).toInt)
|
||||
}
|
||||
|
@ -294,7 +299,7 @@ object Int64 extends Factory[Int64] with BaseNumbers[Int64] {
|
|||
lazy val min = Int64(-9223372036854775808L)
|
||||
lazy val max = Int64(9223372036854775807L)
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): Int64 = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): Int64 = {
|
||||
require(bytes.size <= 8, "We cannot have an Int64 be larger than 8 bytes")
|
||||
Int64(BigInt(bytes.toArray).toLong)
|
||||
}
|
||||
|
@ -302,4 +307,4 @@ object Int64 extends Factory[Int64] with BaseNumbers[Int64] {
|
|||
def apply(long: Long): Int64 = Int64(BigInt(long))
|
||||
|
||||
def apply(bigInt: BigInt): Int64 = Int64Impl(bigInt)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.bitcoins.core.protocol.script._
|
|||
import org.bitcoins.core.script.constant.ScriptConstant
|
||||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.util._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
@ -84,7 +85,10 @@ sealed abstract class Bech32Address extends BitcoinAddress {
|
|||
Bech32Address.fromStringToWitSPK(value).get
|
||||
}
|
||||
|
||||
override def hash: Sha256Digest = Sha256Digest(scriptPubKey.witnessProgram.flatMap(_.bytes))
|
||||
override def hash: Sha256Digest = {
|
||||
val byteVector = BitcoinSUtil.toByteVector(scriptPubKey.witnessProgram)
|
||||
Sha256Digest(byteVector)
|
||||
}
|
||||
|
||||
override def toString = "Bech32Address(" + value + ")"
|
||||
|
||||
|
@ -134,12 +138,12 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
|||
}
|
||||
|
||||
def hrpExpand(hrp: HumanReadablePart): Seq[UInt8] = {
|
||||
val x: Seq[Byte] = hrp.bytes.map { b: Byte =>
|
||||
val x: scodec.bits.ByteVector = hrp.bytes.map { b: Byte =>
|
||||
(b >> 5).toByte
|
||||
}
|
||||
val withZero: Seq[Byte] = x ++ Seq(0.toByte)
|
||||
val withZero: scodec.bits.ByteVector = x ++ scodec.bits.ByteVector.low(1)
|
||||
|
||||
val y: Seq[Byte] = hrp.bytes.map { char =>
|
||||
val y: scodec.bits.ByteVector = hrp.bytes.map { char =>
|
||||
(char & 0x1f).toByte
|
||||
}
|
||||
val result = UInt8.toUInt8s(withZero ++ y)
|
||||
|
@ -167,7 +171,7 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
|||
chk
|
||||
}
|
||||
|
||||
def verifyChecksum(hrp: HumanReadablePart, data: Seq[Byte]): Boolean = {
|
||||
def verifyChecksum(hrp: HumanReadablePart, data: scodec.bits.ByteVector): Boolean = {
|
||||
val u8s = UInt8.toUInt8s(data)
|
||||
verifyCheckSum(hrp, u8s)
|
||||
}
|
||||
|
@ -231,15 +235,15 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
|||
} else {
|
||||
val hrpValid = checkHrpValidity(hrp)
|
||||
val dataValid = checkDataValidity(data)
|
||||
val isChecksumValid: Try[Seq[Byte]] = hrpValid.flatMap { h =>
|
||||
val isChecksumValid: Try[scodec.bits.ByteVector] = hrpValid.flatMap { h =>
|
||||
dataValid.flatMap { d =>
|
||||
if (verifyChecksum(h, d)) {
|
||||
if (d.size < 6) Success(Nil)
|
||||
if (d.size < 6) Success(ByteVector.empty)
|
||||
else Success(d.take(d.size - 6))
|
||||
} else Failure(new IllegalArgumentException("Checksum was invalid on the bech32 address"))
|
||||
}
|
||||
}
|
||||
isChecksumValid.flatMap { d: Seq[Byte] =>
|
||||
isChecksumValid.flatMap { d: scodec.bits.ByteVector =>
|
||||
val u8s = UInt8.toUInt8s(d)
|
||||
hrpValid.map(h => Bech32Address(h, u8s))
|
||||
}
|
||||
|
@ -286,9 +290,9 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
|||
* Takes in the data portion of a bech32 address and decodes it to a byte array
|
||||
* It also checks the validity of the data portion according to BIP173
|
||||
*/
|
||||
def checkDataValidity(data: String): Try[Seq[Byte]] = {
|
||||
def checkDataValidity(data: String): Try[scodec.bits.ByteVector] = {
|
||||
@tailrec
|
||||
def loop(remaining: List[Char], accum: Seq[Byte], hasUpper: Boolean, hasLower: Boolean): Try[Seq[Byte]] = remaining match {
|
||||
def loop(remaining: List[Char], accum: scodec.bits.ByteVector, hasUpper: Boolean, hasLower: Boolean): Try[scodec.bits.ByteVector] = remaining match {
|
||||
case Nil => Success(accum.reverse)
|
||||
case h :: t =>
|
||||
if (!charset.contains(h.toLower)) {
|
||||
|
@ -303,7 +307,8 @@ object Bech32Address extends AddressFactory[Bech32Address] {
|
|||
}
|
||||
}
|
||||
}
|
||||
val payload: Try[Seq[Byte]] = loop(data.toCharArray.toList, Nil, false, false)
|
||||
val payload: Try[scodec.bits.ByteVector] = loop(data.toCharArray.toList, ByteVector.empty,
|
||||
false, false)
|
||||
payload
|
||||
}
|
||||
|
||||
|
@ -331,9 +336,9 @@ object P2PKHAddress extends AddressFactory[P2PKHAddress] {
|
|||
}
|
||||
|
||||
override def fromString(address: String): Try[P2PKHAddress] = {
|
||||
val decodeCheckP2PKH: Try[Seq[Byte]] = Base58.decodeCheck(address)
|
||||
val decodeCheckP2PKH: Try[scodec.bits.ByteVector] = Base58.decodeCheck(address)
|
||||
decodeCheckP2PKH.flatMap { bytes =>
|
||||
val networkBytes: Option[(NetworkParameters, Seq[Byte])] = Networks.knownNetworks.map(n => (n, n.p2pkhNetworkByte))
|
||||
val networkBytes: Option[(NetworkParameters, scodec.bits.ByteVector)] = Networks.knownNetworks.map(n => (n, n.p2pkhNetworkByte))
|
||||
.find {
|
||||
case (_, bs) =>
|
||||
bytes.startsWith(bs)
|
||||
|
@ -382,9 +387,9 @@ object P2SHAddress extends AddressFactory[P2SHAddress] {
|
|||
def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2SHAddress = P2SHAddressImpl(hash, network)
|
||||
|
||||
override def fromString(address: String): Try[P2SHAddress] = {
|
||||
val decodeCheckP2SH: Try[Seq[Byte]] = Base58.decodeCheck(address)
|
||||
val decodeCheckP2SH: Try[scodec.bits.ByteVector] = Base58.decodeCheck(address)
|
||||
decodeCheckP2SH.flatMap { bytes =>
|
||||
val networkBytes: Option[(NetworkParameters, Seq[Byte])] = Networks.knownNetworks.map(n => (n, n.p2shNetworkByte))
|
||||
val networkBytes: Option[(NetworkParameters, scodec.bits.ByteVector)] = Networks.knownNetworks.map(n => (n, n.p2shNetworkByte))
|
||||
.find {
|
||||
case (_, bs) =>
|
||||
bytes.startsWith(bs)
|
||||
|
@ -452,14 +457,14 @@ object BitcoinAddress extends AddressFactory[BitcoinAddress] {
|
|||
|
||||
object Address extends AddressFactory[Address] {
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): Try[Address] = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): Try[Address] = {
|
||||
val encoded = Base58.encode(bytes)
|
||||
BitcoinAddress.fromString(encoded)
|
||||
}
|
||||
|
||||
def fromHex(hex: String): Try[Address] = fromBytes(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def apply(bytes: Seq[Byte]): Try[Address] = fromBytes(bytes)
|
||||
def apply(bytes: scodec.bits.ByteVector): Try[Address] = fromBytes(bytes)
|
||||
|
||||
def apply(str: String): Try[Address] = fromString(str)
|
||||
|
||||
|
@ -472,4 +477,4 @@ object Address extends AddressFactory[Address] {
|
|||
def apply(spk: ScriptPubKey, networkParameters: NetworkParameters): Try[Address] = {
|
||||
fromScriptPubKey(spk, networkParameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.number.{ UInt32, UInt64 }
|
|||
import org.bitcoins.core.protocol.script.{ ScriptPubKey, ScriptSignature }
|
||||
import org.bitcoins.core.script.constant.ScriptNumberUtil
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, Factory }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 7/14/15.
|
||||
|
@ -25,7 +26,7 @@ sealed abstract class CompactSizeUInt extends NetworkElement {
|
|||
case _ => "ff" + BitcoinSUtil.flipEndianness(num.hex)
|
||||
}
|
||||
|
||||
def bytes: Seq[Byte] = BitcoinSUtil.decodeHex(hex)
|
||||
def bytes: scodec.bits.ByteVector = BitcoinSUtil.decodeHex(hex)
|
||||
|
||||
def toLong: Long = num.toLong
|
||||
|
||||
|
@ -37,9 +38,9 @@ sealed abstract class CompactSizeUInt extends NetworkElement {
|
|||
}
|
||||
|
||||
object CompactSizeUInt extends Factory[CompactSizeUInt] {
|
||||
private case class CompactSizeUIntImpl(num: UInt64, override val size: Int) extends CompactSizeUInt
|
||||
private case class CompactSizeUIntImpl(num: UInt64, override val size: Long) extends CompactSizeUInt
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): CompactSizeUInt = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): CompactSizeUInt = {
|
||||
parseCompactSizeUInt(bytes)
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
|
|||
* sequence of bytes
|
||||
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers.
|
||||
*/
|
||||
def calculateCompactSizeUInt(bytes: Seq[Byte]): CompactSizeUInt = {
|
||||
def calculateCompactSizeUInt(bytes: scodec.bits.ByteVector): CompactSizeUInt = {
|
||||
//means we can represent the number with a single byte
|
||||
if (bytes.size <= 252) CompactSizeUInt(UInt64(bytes.size), 1)
|
||||
// can be represented with two bytes
|
||||
|
@ -75,7 +76,7 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
|
|||
else CompactSizeUInt(UInt64(bytes.size), 9)
|
||||
}
|
||||
|
||||
def calc(bytes: Seq[Byte]): CompactSizeUInt = calculateCompactSizeUInt(bytes)
|
||||
def calc(bytes: scodec.bits.ByteVector): CompactSizeUInt = calculateCompactSizeUInt(bytes)
|
||||
|
||||
/** Responsible for calculating what the [[CompactSizeUInt]] is for this hex string. */
|
||||
def calculateCompactSizeUInt(hex: String): CompactSizeUInt = calculateCompactSizeUInt(BitcoinSUtil.decodeHex(hex))
|
||||
|
@ -90,20 +91,21 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
|
|||
* Parses a [[CompactSizeUInt]] from a sequence of bytes
|
||||
* [[https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers]]
|
||||
*/
|
||||
def parseCompactSizeUInt(bytes: Seq[Byte]): CompactSizeUInt = {
|
||||
def parseCompactSizeUInt(bytes: scodec.bits.ByteVector): CompactSizeUInt = {
|
||||
require(bytes.nonEmpty, "Cannot parse a VarInt if the byte array is size 0")
|
||||
val firstByte = UInt64(ByteVector(bytes.head))
|
||||
//8 bit number
|
||||
if (UInt64(Seq(bytes.head)).toBigInt < 253)
|
||||
CompactSizeUInt(UInt64(Seq(bytes.head)), 1)
|
||||
if (firstByte.toInt < 253)
|
||||
CompactSizeUInt(firstByte, 1)
|
||||
//16 bit number
|
||||
else if (UInt64(Seq(bytes.head)).toInt == 253) CompactSizeUInt(UInt64(bytes.slice(1, 3).reverse), 3)
|
||||
else if (firstByte.toInt == 253) CompactSizeUInt(UInt64(bytes.slice(1, 3).reverse), 3)
|
||||
//32 bit number
|
||||
else if (UInt64(Seq(bytes.head)).toInt == 254) CompactSizeUInt(UInt64(bytes.slice(1, 5).reverse), 5)
|
||||
else if (firstByte.toInt == 254) CompactSizeUInt(UInt64(bytes.slice(1, 5).reverse), 5)
|
||||
//64 bit number
|
||||
else CompactSizeUInt(UInt64(bytes.slice(1, 9).reverse), 9)
|
||||
}
|
||||
|
||||
def parse(bytes: Seq[Byte]): CompactSizeUInt = parseCompactSizeUInt(bytes)
|
||||
def parse(bytes: scodec.bits.ByteVector): CompactSizeUInt = parseCompactSizeUInt(bytes)
|
||||
|
||||
/**
|
||||
* Returns the size of a VarInt in the number of bytes
|
||||
|
@ -136,8 +138,8 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
|
|||
|
||||
private def parseLong(hex: String): Long = java.lang.Long.parseLong(hex, 16)
|
||||
|
||||
private def parseLong(bytes: List[Byte]): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
|
||||
private def parseLong(bytes: scodec.bits.ByteVector): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
|
||||
|
||||
private def parseLong(byte: Byte): Long = parseLong(List(byte))
|
||||
private def parseLong(byte: Byte): Long = parseLong(ByteVector.fromByte(byte))
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@ import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil }
|
|||
abstract class NetworkElement {
|
||||
|
||||
/** The size of the NetworkElement in bytes. */
|
||||
def size: Int = bytes.size
|
||||
def size: Long = bytes.size
|
||||
|
||||
/** The hexadecimal representation of the NetworkElement */
|
||||
def hex: String = BitcoinSUtil.encodeHex(bytes)
|
||||
|
||||
/** The byte representation of the NetworkElement */
|
||||
def bytes: Seq[Byte]
|
||||
def bytes: scodec.bits.ByteVector
|
||||
|
||||
lazy val logger = BitcoinSLogger.logger
|
||||
}
|
||||
|
|
|
@ -63,6 +63,6 @@ object Block extends Factory[Block] {
|
|||
Block(blockHeader, txCount, transactions)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): Block = RawBlockSerializer.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): Block = RawBlockSerializer.read(bytes)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ sealed trait BlockHeader extends NetworkElement {
|
|||
*/
|
||||
def hashBE: DoubleSha256Digest = hash.flip
|
||||
|
||||
override def bytes: Seq[Byte] = RawBlockHeaderSerializer.write(this)
|
||||
override def bytes: scodec.bits.ByteVector = RawBlockHeaderSerializer.write(this)
|
||||
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,6 @@ object BlockHeader extends Factory[BlockHeader] {
|
|||
BlockHeaderImpl(version, previousBlockHash, merkleRootHash, time, nBits, nonce)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): BlockHeader = RawBlockHeaderSerializer.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): BlockHeader = RawBlockHeaderSerializer.read(bytes)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.bitcoins.core.protocol.transaction._
|
|||
import org.bitcoins.core.script.constant.{ BytesToPushOntoStack, ScriptConstant, ScriptNumber }
|
||||
import org.bitcoins.core.script.crypto.OP_CHECKSIG
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, BitcoinScriptUtil }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 5/22/16.
|
||||
|
@ -37,14 +38,14 @@ sealed abstract class ChainParams {
|
|||
def requireStandardTransaction: Boolean = true
|
||||
|
||||
/** Takes in a [[Base58Type]] and returns its base58 prefix. */
|
||||
def base58Prefix(base58: Base58Type): Seq[Byte] = base58Prefixes(base58)
|
||||
def base58Prefix(base58: Base58Type): scodec.bits.ByteVector = base58Prefixes(base58)
|
||||
|
||||
/**
|
||||
* The mapping from a [[Base58Type]]to a String.
|
||||
* Base58 prefixes for various keys/hashes on the network.
|
||||
* See: [[https://en.bitcoin.it/wiki/List_of_address_prefixes]].
|
||||
*/
|
||||
def base58Prefixes: Map[Base58Type, Seq[Byte]]
|
||||
def base58Prefixes: Map[Base58Type, scodec.bits.ByteVector]
|
||||
|
||||
/**
|
||||
* Creates the Genesis [[Block]] for this blockchain.
|
||||
|
@ -76,7 +77,7 @@ sealed abstract class ChainParams {
|
|||
*/
|
||||
def createGenesisBlock(timestamp: String, scriptPubKey: ScriptPubKey, time: UInt32, nonce: UInt32, nBits: UInt32,
|
||||
version: Int32, amount: CurrencyUnit): Block = {
|
||||
val timestampBytes = timestamp.getBytes(StandardCharsets.UTF_8)
|
||||
val timestampBytes = ByteVector(timestamp.getBytes(StandardCharsets.UTF_8))
|
||||
//see https://bitcoin.stackexchange.com/questions/13122/scriptsig-coinbase-structure-of-the-genesis-block
|
||||
//for a full breakdown of the genesis block & its script signature
|
||||
val const = ScriptConstant(timestampBytes)
|
||||
|
@ -101,13 +102,13 @@ object MainNetChainParams extends BitcoinChainParams {
|
|||
|
||||
override def genesisBlock: Block = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), Int32.one, Satoshis(Int64(5000000000L)))
|
||||
|
||||
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = Map(
|
||||
override def base58Prefixes: Map[Base58Type, scodec.bits.ByteVector] = Map(
|
||||
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("00"),
|
||||
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("05"),
|
||||
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("80"),
|
||||
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
|
||||
Base58Type.ExtPublicKey -> ByteVector(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
|
||||
BitcoinSUtil.hexToByte("b2"), BitcoinSUtil.hexToByte("1e")),
|
||||
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
|
||||
Base58Type.ExtSecretKey -> ByteVector(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
|
||||
BitcoinSUtil.hexToByte("ad"), BitcoinSUtil.hexToByte("e4")))
|
||||
}
|
||||
|
||||
|
@ -117,20 +118,20 @@ object TestNetChainParams extends BitcoinChainParams {
|
|||
|
||||
override def genesisBlock: Block = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), Int32.one, Satoshis(Int64(5000000000L)))
|
||||
|
||||
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = Map(
|
||||
override def base58Prefixes: Map[Base58Type, scodec.bits.ByteVector] = Map(
|
||||
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("6f"),
|
||||
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("c4"),
|
||||
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("ef"),
|
||||
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
|
||||
Base58Type.ExtPublicKey -> ByteVector(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
|
||||
BitcoinSUtil.hexToByte("87"), BitcoinSUtil.hexToByte("cf")),
|
||||
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
|
||||
Base58Type.ExtSecretKey -> ByteVector(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
|
||||
BitcoinSUtil.hexToByte("83"), BitcoinSUtil.hexToByte("94")))
|
||||
}
|
||||
|
||||
object RegTestNetChainParams extends BitcoinChainParams {
|
||||
override def networkId = "regtest"
|
||||
override def genesisBlock: Block = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), Int32.one, Satoshis(Int64(5000000000L)))
|
||||
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes
|
||||
override def base58Prefixes: Map[Base58Type, scodec.bits.ByteVector] = TestNetChainParams.base58Prefixes
|
||||
}
|
||||
|
||||
sealed abstract class Base58Type
|
||||
|
@ -140,4 +141,4 @@ object Base58Type {
|
|||
case object SecretKey extends Base58Type
|
||||
case object ExtPublicKey extends Base58Type
|
||||
case object ExtSecretKey extends Base58Type
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,10 +89,10 @@ object MerkleBlock extends Factory[MerkleBlock] {
|
|||
MerkleBlockImpl(blockHeader, txCount, partialMerkleTree)
|
||||
}
|
||||
|
||||
def apply(blockHeader: BlockHeader, txCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): MerkleBlock = {
|
||||
def apply(blockHeader: BlockHeader, txCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: scodec.bits.BitVector): MerkleBlock = {
|
||||
val partialMerkleTree = PartialMerkleTree(txCount, hashes, bits)
|
||||
MerkleBlock(blockHeader, txCount, partialMerkleTree)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): MerkleBlock = RawMerkleBlockSerializer.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): MerkleBlock = RawMerkleBlockSerializer.read(bytes)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.bitcoins.core.protocol.blockchain
|
|||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.util._
|
||||
import scodec.bits.{ BitVector, ByteVector }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.math._
|
||||
|
@ -46,7 +47,7 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
|
|||
def tree: BinaryTree[DoubleSha256Digest]
|
||||
|
||||
/** A sequence representing if this node is the parent of another node that matched a txid */
|
||||
def bits: Seq[Boolean]
|
||||
def bits: scodec.bits.BitVector
|
||||
|
||||
/** The hashes used to create the binary tree */
|
||||
def hashes: Seq[DoubleSha256Digest]
|
||||
|
@ -56,7 +57,7 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
|
|||
//TODO: This is some really ugly that isn't tail recursive, try to clean this up eventually
|
||||
def loop(
|
||||
subTree: BinaryTree[DoubleSha256Digest],
|
||||
remainingBits: Seq[Boolean], height: Int, pos: Int, accumMatches: Seq[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Boolean]) = {
|
||||
remainingBits: scodec.bits.BitVector, height: Int, pos: Int, accumMatches: Seq[DoubleSha256Digest]): (Seq[DoubleSha256Digest], scodec.bits.BitVector) = {
|
||||
if (height == maxHeight) extractLeafMatch(accumMatches, remainingBits, subTree)
|
||||
else {
|
||||
//means we have a nontxid node
|
||||
|
@ -86,8 +87,8 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
/** Handles a leaf match when we are extracting matches from the partial merkle tree */
|
||||
private def extractLeafMatch(accumMatches: Seq[DoubleSha256Digest], remainingBits: Seq[Boolean],
|
||||
subTree: BinaryTree[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Boolean]) = {
|
||||
private def extractLeafMatch(accumMatches: Seq[DoubleSha256Digest], remainingBits: scodec.bits.BitVector,
|
||||
subTree: BinaryTree[DoubleSha256Digest]): (Seq[DoubleSha256Digest], scodec.bits.BitVector) = {
|
||||
if (remainingBits.head) {
|
||||
//means we have a txid node that matched the filter
|
||||
subTree match {
|
||||
|
@ -108,7 +109,9 @@ object PartialMerkleTree {
|
|||
private val logger = BitcoinSLogger.logger
|
||||
|
||||
private case class PartialMerkleTreeImpl(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32,
|
||||
bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest]) extends PartialMerkleTree
|
||||
bits: scodec.bits.BitVector, hashes: Seq[DoubleSha256Digest]) extends PartialMerkleTree {
|
||||
require(bits.size % 8 == 0, "As per BIP37, bits must be padded to the nearest byte")
|
||||
}
|
||||
|
||||
def apply(txMatches: Seq[(Boolean, DoubleSha256Digest)]): PartialMerkleTree = {
|
||||
val txIds = txMatches.map(_._2)
|
||||
|
@ -123,7 +126,7 @@ object PartialMerkleTree {
|
|||
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
|
||||
* according to the flags inside of bits
|
||||
*/
|
||||
private def build(txMatches: Seq[(Boolean, DoubleSha256Digest)]): (Seq[Boolean], Seq[DoubleSha256Digest]) = {
|
||||
private def build(txMatches: Seq[(Boolean, DoubleSha256Digest)]): (scodec.bits.BitVector, Seq[DoubleSha256Digest]) = {
|
||||
val maxHeight = calcMaxHeight(txMatches.size)
|
||||
|
||||
/**
|
||||
|
@ -135,7 +138,7 @@ object PartialMerkleTree {
|
|||
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
|
||||
* according to the flags inside of bits
|
||||
*/
|
||||
def loop(bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest], height: Int, pos: Int): (Seq[Boolean], Seq[DoubleSha256Digest]) = {
|
||||
def loop(bits: scodec.bits.BitVector, hashes: Seq[DoubleSha256Digest], height: Int, pos: Int): (scodec.bits.BitVector, Seq[DoubleSha256Digest]) = {
|
||||
val parentOfMatch = matchesTx(maxHeight, maxHeight - height, pos, txMatches)
|
||||
val newBits = parentOfMatch +: bits
|
||||
if (height == 0 || !parentOfMatch) {
|
||||
|
@ -153,11 +156,11 @@ object PartialMerkleTree {
|
|||
} else (leftBits, leftHashes)
|
||||
}
|
||||
}
|
||||
val (bits, hashes) = loop(Nil, Nil, maxHeight, 0)
|
||||
val (bits, hashes) = loop(BitVector.empty, Nil, maxHeight, 0)
|
||||
//pad the bit array to the nearest byte as required by BIP37
|
||||
val bitsNeeded = if ((bits.size % 8) == 0) 0 else 8 - (bits.size % 8)
|
||||
val paddingBits = for { _ <- 0 until bitsNeeded } yield false
|
||||
(bits.reverse ++ paddingBits, hashes.reverse)
|
||||
val bitsNeeded = if (bits.size % 8 == 0) 0 else (8 - (bits.size % 8)) + bits.size
|
||||
val paddedBits = if (bitsNeeded == 0) bits else bits.padLeft(bitsNeeded)
|
||||
(paddedBits.reverse, hashes.reverse)
|
||||
}
|
||||
|
||||
/** Checks if a node at given the given height and position matches a transaction in the sequence */
|
||||
|
@ -201,7 +204,7 @@ object PartialMerkleTree {
|
|||
* @param bits the bits used indicate the structure of the partial merkle tree
|
||||
* @return
|
||||
*/
|
||||
def apply(transactionCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): PartialMerkleTree = {
|
||||
def apply(transactionCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: scodec.bits.BitVector): PartialMerkleTree = {
|
||||
val tree = reconstruct(transactionCount.toInt, hashes, bits)
|
||||
PartialMerkleTree(tree, transactionCount, bits, hashes)
|
||||
}
|
||||
|
@ -215,7 +218,7 @@ object PartialMerkleTree {
|
|||
* @param bits the path to the matches in the partial merkle tree
|
||||
* @param hashes the hashes used to reconstruct the binary tree according to [[bits]]
|
||||
*/
|
||||
def apply(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32, bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest]): PartialMerkleTree = {
|
||||
def apply(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32, bits: scodec.bits.BitVector, hashes: Seq[DoubleSha256Digest]): PartialMerkleTree = {
|
||||
PartialMerkleTreeImpl(tree, transactionCount, bits, hashes)
|
||||
}
|
||||
|
||||
|
@ -224,10 +227,10 @@ object PartialMerkleTree {
|
|||
* [[https://bitcoin.org/en/developer-reference#parsing-a-merkleblock-message]]
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L96]]
|
||||
*/
|
||||
private def reconstruct(numTransaction: Int, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): BinaryTree[DoubleSha256Digest] = {
|
||||
private def reconstruct(numTransaction: Int, hashes: Seq[DoubleSha256Digest], bits: scodec.bits.BitVector): BinaryTree[DoubleSha256Digest] = {
|
||||
val maxHeight = calcMaxHeight(numTransaction)
|
||||
//TODO: Optimize to tailrec function
|
||||
def loop(remainingHashes: Seq[DoubleSha256Digest], remainingMatches: Seq[Boolean], height: Int, pos: Int): (BinaryTree[DoubleSha256Digest], Seq[DoubleSha256Digest], Seq[Boolean]) = {
|
||||
def loop(remainingHashes: Seq[DoubleSha256Digest], remainingMatches: BitVector, height: Int, pos: Int): (BinaryTree[DoubleSha256Digest], Seq[DoubleSha256Digest], scodec.bits.BitVector) = {
|
||||
if (height == maxHeight) {
|
||||
//means we have a txid node
|
||||
(
|
||||
|
@ -241,7 +244,7 @@ object PartialMerkleTree {
|
|||
val leftNodePos = pos * 2
|
||||
val rightNodePos = (pos * 2) + 1
|
||||
val (leftNode, leftRemainingHashes, leftRemainingBits) = loop(remainingHashes, remainingMatches.tail, nextHeight, leftNodePos)
|
||||
val (rightNode, rightRemainingHashes, rightRemainingBits) =
|
||||
val (rightNode, rightRemainingHashes, rightRemainingBits) = {
|
||||
if (existsRightSubTree(pos, numTransaction, maxHeight, height)) {
|
||||
val (rightNode, rightRemainingHashes, rightRemainingBits) =
|
||||
loop(leftRemainingHashes, leftRemainingBits, nextHeight, rightNodePos)
|
||||
|
@ -254,13 +257,18 @@ object PartialMerkleTree {
|
|||
}
|
||||
(rightNode, rightRemainingHashes, rightRemainingBits)
|
||||
} else (leftNode, leftRemainingHashes, leftRemainingBits)
|
||||
}
|
||||
val nodeHash = CryptoUtil.doubleSHA256(leftNode.value.get.bytes ++ rightNode.value.get.bytes)
|
||||
val node = Node(nodeHash, leftNode, rightNode)
|
||||
(node, rightRemainingHashes, rightRemainingBits)
|
||||
} else (Leaf(remainingHashes.head), remainingHashes.tail, remainingMatches.tail)
|
||||
} else {
|
||||
(Leaf(remainingHashes.head), remainingHashes.tail, remainingMatches.tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
val (tree, remainingHashes, remainingBits) = loop(hashes, bits, 0, 0)
|
||||
|
||||
//require(remainingBits.isEmpty, s"Remainging bits should be empty, got ${remainingBits}")
|
||||
//we must have used all the hashes provided to us to reconstruct the partial merkle tree as per BIP37
|
||||
require(remainingHashes.size == 0, "We should not have any left over hashes after building our partial merkle tree, got: " + remainingHashes)
|
||||
//we must not have any matches remaining, unless the remaining bits were use to pad our byte vector to 8 bits
|
||||
|
@ -294,7 +302,7 @@ object PartialMerkleTree {
|
|||
* in a byte array when reconstruction a partial merkle tree
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L174-L175]]
|
||||
*/
|
||||
private def usedAllBits(bits: Seq[Boolean], remainingBits: Seq[Boolean]): Boolean = {
|
||||
private def usedAllBits(bits: scodec.bits.BitVector, remainingBits: scodec.bits.BitVector): Boolean = {
|
||||
val bitsUsed = bits.size - remainingBits.size
|
||||
((bitsUsed + 7) / 8) == ((bits.size + 7) / 8)
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import org.bitcoins.core.util.{ BitcoinSUtil, Factory }
|
|||
trait ScriptFactory[T] extends Factory[T] {
|
||||
|
||||
/** Builds a script from the given asm with the given constructor if the invariant holds true, else throws an error */
|
||||
def buildScript(asm: Seq[ScriptToken], constructor: Seq[Byte] => T,
|
||||
def buildScript(asm: Seq[ScriptToken], constructor: scodec.bits.ByteVector => T,
|
||||
invariant: Seq[ScriptToken] => Boolean, errorMsg: String): T = {
|
||||
if (invariant(asm)) {
|
||||
val asmBytes = asm.flatMap(_.bytes)
|
||||
val asmBytes = BitcoinSUtil.toByteVector(asm)
|
||||
val compactSizeUInt = CompactSizeUInt.calc(asmBytes)
|
||||
constructor(compactSizeUInt.bytes ++ asmBytes)
|
||||
} else throw new IllegalArgumentException(errorMsg)
|
||||
|
@ -23,7 +23,7 @@ trait ScriptFactory[T] extends Factory[T] {
|
|||
/** Creates a T from the given [[ScriptToken]]s */
|
||||
def fromAsm(asm: Seq[ScriptToken]): T
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): T = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): T = {
|
||||
val cpmct = CompactSizeUInt.parseCompactSizeUInt(bytes)
|
||||
val (_, noCmpctUInt) = bytes.splitAt(cpmct.bytes.size)
|
||||
val asm = ScriptParser.fromBytes(noCmpctUInt)
|
||||
|
@ -37,7 +37,7 @@ trait ScriptFactory[T] extends Factory[T] {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def fromAsmBytes(bytes: Seq[Byte]): T = {
|
||||
def fromAsmBytes(bytes: scodec.bits.ByteVector): T = {
|
||||
val cmpct = CompactSizeUInt.calc(bytes)
|
||||
val fullBytes = cmpct.bytes ++ bytes
|
||||
fromBytes(fullBytes)
|
||||
|
|
|
@ -38,7 +38,7 @@ sealed trait ScriptPubKey extends NetworkElement {
|
|||
* The byte representation of [[asm]], this does NOT have the bytes
|
||||
* for the [[org.bitcoins.core.protocol.CompactSizeUInt]] in the [[org.bitcoins.core.protocol.script.ScriptPubKey]]
|
||||
*/
|
||||
lazy val asmBytes: Seq[Byte] = asm.flatMap(_.bytes)
|
||||
lazy val asmBytes: scodec.bits.ByteVector = BitcoinSUtil.toByteVector(asm)
|
||||
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ sealed trait P2PKHScriptPubKey extends ScriptPubKey {
|
|||
}
|
||||
|
||||
object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
|
||||
private case class P2PKHScriptPubKeyImpl(bytes: Seq[Byte]) extends P2PKHScriptPubKey {
|
||||
private case class P2PKHScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends P2PKHScriptPubKey {
|
||||
override def toString = "P2PKHScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ sealed trait MultiSignatureScriptPubKey extends ScriptPubKey {
|
|||
|
||||
object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubKey] {
|
||||
|
||||
private case class MultiSignatureScriptPubKeyImpl(bytes: Seq[Byte]) extends MultiSignatureScriptPubKey {
|
||||
private case class MultiSignatureScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends MultiSignatureScriptPubKey {
|
||||
override def toString = "MultiSignatureScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ sealed trait P2SHScriptPubKey extends ScriptPubKey {
|
|||
}
|
||||
|
||||
object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
|
||||
private case class P2SHScriptPubKeyImpl(bytes: Seq[Byte]) extends P2SHScriptPubKey {
|
||||
private case class P2SHScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends P2SHScriptPubKey {
|
||||
override def toString = "P2SHScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ sealed trait P2PKScriptPubKey extends ScriptPubKey {
|
|||
|
||||
object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
|
||||
|
||||
private case class P2PKScriptPubKeyImpl(bytes: Seq[Byte]) extends P2PKScriptPubKey {
|
||||
private case class P2PKScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends P2PKScriptPubKey {
|
||||
override def toString = "P2PKScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
|
|||
sealed trait CLTVScriptPubKey extends LockTimeScriptPubKey
|
||||
|
||||
object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
|
||||
private case class CLTVScriptPubKeyImpl(bytes: Seq[Byte]) extends CLTVScriptPubKey {
|
||||
private case class CLTVScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends CLTVScriptPubKey {
|
||||
override def toString = "CLTVScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -405,7 +405,7 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
|
|||
sealed trait CSVScriptPubKey extends LockTimeScriptPubKey
|
||||
|
||||
object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
|
||||
private case class CSVScriptPubKeyImpl(bytes: Seq[Byte]) extends CSVScriptPubKey {
|
||||
private case class CSVScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends CSVScriptPubKey {
|
||||
override def toString = "CSVScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -456,7 +456,7 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
|
|||
sealed trait NonStandardScriptPubKey extends ScriptPubKey
|
||||
|
||||
object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
|
||||
private case class NonStandardScriptPubKeyImpl(bytes: Seq[Byte]) extends NonStandardScriptPubKey {
|
||||
private case class NonStandardScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends NonStandardScriptPubKey {
|
||||
override def toString = "NonStandardScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
|
|||
|
||||
/** Represents the empty ScriptPubKey */
|
||||
case object EmptyScriptPubKey extends ScriptPubKey {
|
||||
override def bytes = Seq(0.toByte)
|
||||
override def bytes = scodec.bits.ByteVector.low(1)
|
||||
}
|
||||
|
||||
/** Factory companion object used to create ScriptPubKey objects */
|
||||
|
@ -524,7 +524,7 @@ object WitnessScriptPubKey {
|
|||
* [[https://github.com/bitcoin/bitcoin/blob/14d01309bed59afb08651f2b701ff90371b15b20/src/script/script.cpp#L223-L237]]
|
||||
*/
|
||||
def isWitnessScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
|
||||
val bytes = asm.flatMap(_.bytes)
|
||||
val bytes = BitcoinSUtil.toByteVector(asm)
|
||||
val firstOp = asm.headOption
|
||||
if (bytes.size < 4 || bytes.size > 42) false
|
||||
else if (!validWitVersions.contains(firstOp.getOrElse(OP_1NEGATE))) false
|
||||
|
@ -562,15 +562,16 @@ sealed abstract class P2WPKHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
|
|||
}
|
||||
|
||||
object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
|
||||
private case class P2WPKHWitnessSPKV0Impl(bytes: Seq[Byte]) extends P2WPKHWitnessSPKV0
|
||||
private case class P2WPKHWitnessSPKV0Impl(bytes: scodec.bits.ByteVector) extends P2WPKHWitnessSPKV0
|
||||
|
||||
override def fromAsm(asm: Seq[ScriptToken]): P2WPKHWitnessSPKV0 = {
|
||||
buildScript(asm, P2WPKHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WPKHWitnessSPKV0, got $asm")
|
||||
}
|
||||
|
||||
def isValid(asm: Seq[ScriptToken]): Boolean = {
|
||||
val asmBytes = BitcoinSUtil.toByteVector(asm)
|
||||
WitnessScriptPubKeyV0.isValid(asm) &&
|
||||
asm.flatMap(_.bytes).size == 22
|
||||
asmBytes.size == 22
|
||||
}
|
||||
|
||||
/** Creates a P2WPKH witness script pubkey */
|
||||
|
@ -593,15 +594,16 @@ sealed abstract class P2WSHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
|
|||
}
|
||||
|
||||
object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
|
||||
private case class P2WSHWitnessSPKV0Impl(bytes: Seq[Byte]) extends P2WSHWitnessSPKV0
|
||||
private case class P2WSHWitnessSPKV0Impl(bytes: scodec.bits.ByteVector) extends P2WSHWitnessSPKV0
|
||||
|
||||
override def fromAsm(asm: Seq[ScriptToken]): P2WSHWitnessSPKV0 = {
|
||||
buildScript(asm, P2WSHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
|
||||
}
|
||||
|
||||
def isValid(asm: Seq[ScriptToken]): Boolean = {
|
||||
val asmBytes = BitcoinSUtil.toByteVector(asm)
|
||||
WitnessScriptPubKeyV0.isValid(asm) &&
|
||||
asm.flatMap(_.bytes).size == 34
|
||||
asmBytes.size == 34
|
||||
}
|
||||
|
||||
def apply(spk: ScriptPubKey): P2WSHWitnessSPKV0 = {
|
||||
|
@ -618,7 +620,7 @@ sealed trait UnassignedWitnessScriptPubKey extends WitnessScriptPubKey {
|
|||
}
|
||||
|
||||
object UnassignedWitnessScriptPubKey extends ScriptFactory[UnassignedWitnessScriptPubKey] {
|
||||
private case class UnassignedWitnessScriptPubKeyImpl(bytes: Seq[Byte]) extends UnassignedWitnessScriptPubKey {
|
||||
private case class UnassignedWitnessScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends UnassignedWitnessScriptPubKey {
|
||||
override def toString = "UnassignedWitnessScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -642,7 +644,7 @@ sealed trait WitnessCommitment extends ScriptPubKey {
|
|||
}
|
||||
|
||||
object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
|
||||
private case class WitnessCommitmentImpl(bytes: Seq[Byte]) extends WitnessCommitment {
|
||||
private case class WitnessCommitmentImpl(bytes: scodec.bits.ByteVector) extends WitnessCommitment {
|
||||
override def toString = "WitnessCommitmentImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
@ -666,9 +668,10 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
|
|||
if (asm.size < 3) false
|
||||
else {
|
||||
val minCommitmentSize = 38
|
||||
val asmBytes = BitcoinSUtil.toByteVector(asm)
|
||||
val Seq(opReturn, pushOp, constant) = asm.take(3)
|
||||
opReturn == OP_RETURN && pushOp == BytesToPushOntoStack(36) &&
|
||||
constant.hex.take(8) == commitmentHeader && asm.flatMap(_.bytes).size >= minCommitmentSize
|
||||
constant.hex.take(8) == commitmentHeader && asmBytes.size >= minCommitmentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -700,7 +703,7 @@ sealed trait EscrowTimeoutScriptPubKey extends ScriptPubKey {
|
|||
}
|
||||
|
||||
object EscrowTimeoutScriptPubKey extends ScriptFactory[EscrowTimeoutScriptPubKey] {
|
||||
private case class EscrowTimeoutScriptPubKeyImpl(bytes: Seq[Byte]) extends EscrowTimeoutScriptPubKey {
|
||||
private case class EscrowTimeoutScriptPubKeyImpl(bytes: scodec.bits.ByteVector) extends EscrowTimeoutScriptPubKey {
|
||||
override def toString = "EscrowTimeoutScriptPubKeyImpl(" + hex + ")"
|
||||
}
|
||||
|
||||
|
|
|
@ -8,5 +8,5 @@ import org.bitcoins.core.script.constant._
|
|||
|
||||
sealed trait ScriptPubKeyUpdateIndicator
|
||||
case class UpdateScriptPubKeyAsm(asm: Seq[ScriptToken]) extends ScriptPubKeyUpdateIndicator
|
||||
case class UpdateScriptPubKeyBytes(bytes: Seq[Byte]) extends ScriptPubKeyUpdateIndicator
|
||||
case class UpdateScriptPubKeyBytes(bytes: scodec.bits.ByteVector) extends ScriptPubKeyUpdateIndicator
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
|
|||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.serializers.script.{ RawScriptSignatureParser, ScriptParser }
|
||||
import org.bitcoins.core.util._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
||||
|
@ -27,7 +28,7 @@ sealed abstract class ScriptSignature extends NetworkElement {
|
|||
lazy val asm: Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
|
||||
|
||||
/** Byte vector for script program WITHOUT the [[CompactSizeUInt]], this is the raw byte vector that can be run */
|
||||
lazy val asmBytes = asm.flatMap(_.bytes)
|
||||
lazy val asmBytes: ByteVector = BitcoinSUtil.toByteVector(asm)
|
||||
|
||||
/**
|
||||
* The digital signatures contained inside of the script signature
|
||||
|
@ -46,7 +47,7 @@ sealed trait NonStandardScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object NonStandardScriptSignature extends ScriptFactory[NonStandardScriptSignature] {
|
||||
private case class NonStandardScriptSignatureImpl(bytes: Seq[Byte]) extends NonStandardScriptSignature {
|
||||
private case class NonStandardScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends NonStandardScriptSignature {
|
||||
|
||||
}
|
||||
|
||||
|
@ -78,7 +79,7 @@ sealed trait P2PKHScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
|
||||
private case class P2PKHScriptSignatureImpl(bytes: Seq[Byte]) extends P2PKHScriptSignature
|
||||
private case class P2PKHScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends P2PKHScriptSignature
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptSignature = {
|
||||
buildScript(asm, P2PKHScriptSignatureImpl(_), isP2PKHScriptSig(_), "Given asm was not a P2PKHScriptSignature, got: " + asm)
|
||||
|
@ -167,7 +168,7 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
|
||||
private case class P2SHScriptSignatureImpl(bytes: Seq[Byte]) extends P2SHScriptSignature
|
||||
private case class P2SHScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends P2SHScriptSignature
|
||||
|
||||
def apply(scriptSig: ScriptSignature, redeemScript: ScriptPubKey): P2SHScriptSignature = {
|
||||
//we need to calculate the size of the redeemScript and add the corresponding push op
|
||||
|
@ -242,7 +243,7 @@ sealed trait MultiSignatureScriptSignature extends ScriptSignature {
|
|||
|
||||
object MultiSignatureScriptSignature extends ScriptFactory[MultiSignatureScriptSignature] {
|
||||
|
||||
private case class MultiSignatureScriptSignatureImpl(bytes: Seq[Byte]) extends MultiSignatureScriptSignature
|
||||
private case class MultiSignatureScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends MultiSignatureScriptSignature
|
||||
|
||||
def apply(signatures: Seq[ECDigitalSignature]): MultiSignatureScriptSignature = {
|
||||
val sigsPushOpsPairs: Seq[Seq[ScriptToken]] = for {
|
||||
|
@ -298,7 +299,7 @@ sealed trait P2PKScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
|
||||
private case class P2PKScriptSignatureImpl(bytes: Seq[Byte]) extends P2PKScriptSignature
|
||||
private case class P2PKScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends P2PKScriptSignature
|
||||
|
||||
def apply(signature: ECDigitalSignature): P2PKScriptSignature = {
|
||||
val pushOps = BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
|
@ -331,9 +332,9 @@ sealed trait CLTVScriptSignature extends LockTimeScriptSignature {
|
|||
}
|
||||
|
||||
object CLTVScriptSignature extends Factory[CLTVScriptSignature] {
|
||||
private case class CLTVScriptSignatureImpl(bytes: Seq[Byte]) extends CLTVScriptSignature
|
||||
private case class CLTVScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends CLTVScriptSignature
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): CLTVScriptSignature = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): CLTVScriptSignature = {
|
||||
CLTVScriptSignatureImpl(bytes)
|
||||
}
|
||||
|
||||
|
@ -351,9 +352,9 @@ sealed trait CSVScriptSignature extends LockTimeScriptSignature {
|
|||
}
|
||||
|
||||
object CSVScriptSignature extends Factory[CSVScriptSignature] {
|
||||
private case class CSVScriptSignatureImpl(bytes: Seq[Byte]) extends CSVScriptSignature
|
||||
private case class CSVScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends CSVScriptSignature
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): CSVScriptSignature = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): CSVScriptSignature = {
|
||||
CSVScriptSignatureImpl(bytes)
|
||||
}
|
||||
|
||||
|
@ -369,7 +370,7 @@ object CSVScriptSignature extends Factory[CSVScriptSignature] {
|
|||
/** Represents the empty script signature */
|
||||
case object EmptyScriptSignature extends ScriptSignature {
|
||||
def signatures = Nil
|
||||
def bytes = Seq(0.toByte)
|
||||
def bytes = scodec.bits.ByteVector.low(1)
|
||||
}
|
||||
|
||||
object ScriptSignature extends ScriptFactory[ScriptSignature] {
|
||||
|
@ -443,9 +444,9 @@ sealed trait EscrowTimeoutScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object EscrowTimeoutScriptSignature extends Factory[EscrowTimeoutScriptSignature] {
|
||||
private case class EscrowTimeoutScriptSignatureImpl(bytes: Seq[Byte]) extends EscrowTimeoutScriptSignature
|
||||
private case class EscrowTimeoutScriptSignatureImpl(bytes: scodec.bits.ByteVector) extends EscrowTimeoutScriptSignature
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): EscrowTimeoutScriptSignature = {
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): EscrowTimeoutScriptSignature = {
|
||||
//TODO: Come back and look at this, there is no invariant check here to see if the EscrowTiemoutScriptSig is valid
|
||||
EscrowTimeoutScriptSignatureImpl(bytes)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.crypto.{ ECDigitalSignature, ECPublicKey, EmptyDigitalS
|
|||
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
|
||||
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 11/10/16.
|
||||
|
@ -13,7 +14,7 @@ import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil
|
|||
sealed abstract class ScriptWitness extends NetworkElement {
|
||||
|
||||
/** The byte vectors that are placed on to the stack when evaluating a witness program */
|
||||
def stack: Seq[Seq[Byte]]
|
||||
def stack: Seq[scodec.bits.ByteVector]
|
||||
|
||||
override def bytes = RawScriptWitnessParser.write(this)
|
||||
}
|
||||
|
@ -21,7 +22,7 @@ sealed abstract class ScriptWitness extends NetworkElement {
|
|||
case object EmptyScriptWitness extends ScriptWitness {
|
||||
override def stack = Nil
|
||||
|
||||
override def bytes = Seq(0.toByte)
|
||||
override def bytes = scodec.bits.ByteVector.low(1)
|
||||
}
|
||||
|
||||
sealed abstract class ScriptWitnessV0 extends ScriptWitness
|
||||
|
@ -36,17 +37,17 @@ sealed abstract class P2WPKHWitnessV0 extends ScriptWitness {
|
|||
def pubKey: ECPublicKey = ECPublicKey(stack.head)
|
||||
|
||||
def signature: ECDigitalSignature = stack(1) match {
|
||||
case Nil => EmptyDigitalSignature
|
||||
case bytes: Seq[Byte] => ECDigitalSignature(bytes)
|
||||
case ByteVector.empty => EmptyDigitalSignature
|
||||
case bytes: scodec.bits.ByteVector => ECDigitalSignature(bytes)
|
||||
}
|
||||
|
||||
override def toString = "P2WPKHWitnessV0(" + stack.map(BitcoinSUtil.encodeHex(_)).toString + ")"
|
||||
}
|
||||
|
||||
object P2WPKHWitnessV0 {
|
||||
private case class P2WPKHWitnessV0Impl(stack: Seq[Seq[Byte]]) extends P2WPKHWitnessV0
|
||||
private case class P2WPKHWitnessV0Impl(stack: Seq[scodec.bits.ByteVector]) extends P2WPKHWitnessV0
|
||||
|
||||
private def apply(stack: Seq[Seq[Byte]]): P2WPKHWitnessV0 = P2WPKHWitnessV0Impl(stack)
|
||||
private def apply(stack: Seq[scodec.bits.ByteVector]): P2WPKHWitnessV0 = P2WPKHWitnessV0Impl(stack)
|
||||
|
||||
def apply(pubKey: ECPublicKey): P2WPKHWitnessV0 = {
|
||||
P2WPKHWitnessV0(pubKey, EmptyDigitalSignature)
|
||||
|
@ -72,7 +73,7 @@ sealed abstract class P2WSHWitnessV0 extends ScriptWitness {
|
|||
}
|
||||
|
||||
object P2WSHWitnessV0 {
|
||||
private case class P2WSHWitnessV0Impl(stack: Seq[Seq[Byte]]) extends P2WSHWitnessV0
|
||||
private case class P2WSHWitnessV0Impl(stack: Seq[scodec.bits.ByteVector]) extends P2WSHWitnessV0
|
||||
|
||||
def apply(spk: ScriptPubKey): P2WSHWitnessV0 = {
|
||||
P2WSHWitnessV0(spk, EmptyScriptSignature)
|
||||
|
@ -84,26 +85,31 @@ object P2WSHWitnessV0 {
|
|||
val minimalIf = BitcoinScriptUtil.minimalIfOp(scriptSig.asm)
|
||||
val noPushOps = BitcoinScriptUtil.filterPushOps(minimalIf)
|
||||
val minimalDummy = BitcoinScriptUtil.minimalDummy(noPushOps).reverse
|
||||
val stack: Seq[Seq[Byte]] = spk.asmBytes +: minimalDummy.map(_.bytes)
|
||||
val stack: Seq[scodec.bits.ByteVector] = spk.asmBytes +: minimalDummy.map(_.bytes)
|
||||
P2WSHWitnessV0(stack)
|
||||
}
|
||||
|
||||
private def apply(stack: Seq[Seq[Byte]]): P2WSHWitnessV0 = {
|
||||
private def apply(stack: Seq[scodec.bits.ByteVector]): P2WSHWitnessV0 = {
|
||||
P2WSHWitnessV0Impl(stack)
|
||||
}
|
||||
|
||||
def apply(spk: ScriptPubKey, stack: Seq[Seq[Byte]]): P2WSHWitnessV0 = {
|
||||
val fullStack: Seq[Seq[Byte]] = spk.asmBytes +: stack
|
||||
def apply(spk: ScriptPubKey, stack: Seq[scodec.bits.ByteVector]): P2WSHWitnessV0 = {
|
||||
val fullStack: Seq[scodec.bits.ByteVector] = spk.asmBytes +: stack
|
||||
P2WSHWitnessV0(fullStack)
|
||||
}
|
||||
}
|
||||
|
||||
object ScriptWitness {
|
||||
private val logger = BitcoinSLogger.logger
|
||||
def apply(stack: Seq[Seq[Byte]]): ScriptWitness = {
|
||||
def apply(stack: Seq[scodec.bits.ByteVector]): ScriptWitness = {
|
||||
//TODO: eventually only compressed public keys will be allowed in v0 scripts
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type
|
||||
val isPubKey = stack.headOption.isDefined && ECPublicKey.isFullyValid(stack.head) && (stack.head.size == 33 || stack.head.size == 65)
|
||||
val isPubKey = {
|
||||
stack.headOption.isDefined &&
|
||||
ECPublicKey.isFullyValid(stack.head) &&
|
||||
(stack.head.size == 33
|
||||
|| stack.head.size == 65)
|
||||
}
|
||||
if (stack.isEmpty) {
|
||||
EmptyScriptWitness
|
||||
} else if (isPubKey && stack.size == 2) {
|
||||
|
@ -126,4 +132,4 @@ object ScriptWitness {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ case object WitnessVersion0 extends WitnessVersion {
|
|||
|
||||
/** Rebuilds a witness version 0 program, see BIP141 */
|
||||
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] = {
|
||||
val programBytes = witnessProgram.flatMap(_.bytes)
|
||||
val programBytes = BitcoinSUtil.toByteVector(witnessProgram)
|
||||
programBytes.size match {
|
||||
case 20 =>
|
||||
//p2wpkh
|
||||
|
|
|
@ -128,7 +128,7 @@ sealed abstract class WitnessTransaction extends Transaction {
|
|||
|
||||
object Transaction extends Factory[Transaction] {
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): Transaction = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): Transaction = {
|
||||
val wtxTry = Try(RawWitnessTransactionParser.read(bytes))
|
||||
wtxTry match {
|
||||
case Success(wtx) =>
|
||||
|
@ -144,7 +144,7 @@ object BaseTransaction extends Factory[BaseTransaction] {
|
|||
private case class BaseTransactionImpl(version: Int32, inputs: Seq[TransactionInput],
|
||||
outputs: Seq[TransactionOutput], lockTime: UInt32) extends BaseTransaction
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): BaseTransaction = RawBaseTransactionParser.read(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): BaseTransaction = RawBaseTransactionParser.read(bytes)
|
||||
|
||||
def apply(version: Int32, inputs: Seq[TransactionInput],
|
||||
outputs: Seq[TransactionOutput], lockTime: UInt32): BaseTransaction = BaseTransactionImpl(version, inputs, outputs, lockTime)
|
||||
|
@ -159,6 +159,6 @@ object WitnessTransaction extends Factory[WitnessTransaction] {
|
|||
lockTime: UInt32, witness: TransactionWitness): WitnessTransaction =
|
||||
WitnessTransactionImpl(version, inputs, outputs, lockTime, witness)
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): WitnessTransaction = RawWitnessTransactionParser.read(bytes)
|
||||
override def fromBytes(bytes: scodec.bits.ByteVector): WitnessTransaction = RawWitnessTransactionParser.read(bytes)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ object TransactionInput extends Factory[TransactionInput] {
|
|||
scriptSignature: ScriptSignature, sequence: UInt32) extends TransactionInput
|
||||
def empty: TransactionInput = EmptyTransactionInput
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): TransactionInput = RawTransactionInputParser.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): TransactionInput = RawTransactionInputParser.read(bytes)
|
||||
|
||||
def apply(outPoint: TransactionOutPoint, scriptSignature: ScriptSignature,
|
||||
sequenceNumber: UInt32): TransactionInput = outPoint match {
|
||||
|
@ -60,4 +60,4 @@ object CoinbaseInput {
|
|||
def apply(scriptSignature: ScriptSignature, sequence: UInt32): CoinbaseInput = {
|
||||
CoinbaseInputImpl(scriptSignature, sequence)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ object TransactionOutPoint extends Factory[TransactionOutPoint] {
|
|||
|
||||
private case class TransactionOutPointImpl(txId: DoubleSha256Digest, vout: UInt32) extends TransactionOutPoint
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): TransactionOutPoint = RawTransactionOutPointParser.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): TransactionOutPoint = RawTransactionOutPointParser.read(bytes)
|
||||
|
||||
def apply(txId: DoubleSha256Digest, index: UInt32): TransactionOutPoint = {
|
||||
if (txId == EmptyTransactionOutPoint.txId && index == EmptyTransactionOutPoint.vout) {
|
||||
|
|
|
@ -28,9 +28,9 @@ case object EmptyTransactionOutput extends TransactionOutput {
|
|||
object TransactionOutput extends Factory[TransactionOutput] {
|
||||
private case class TransactionOutputImpl(value: CurrencyUnit, scriptPubKey: ScriptPubKey) extends TransactionOutput
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]): TransactionOutput = RawTransactionOutputParser.read(bytes)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): TransactionOutput = RawTransactionOutputParser.read(bytes)
|
||||
|
||||
def apply(currencyUnit: CurrencyUnit, scriptPubKey: ScriptPubKey): TransactionOutput = {
|
||||
TransactionOutputImpl(currencyUnit, scriptPubKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ sealed abstract class TransactionWitness extends NetworkElement {
|
|||
|
||||
/** Used to represent a transaction witness pre segwit, see BIP141 for details */
|
||||
case object EmptyWitness extends TransactionWitness {
|
||||
override def bytes = Seq(0.toByte)
|
||||
override def bytes = scodec.bits.ByteVector.low(1)
|
||||
override def witnesses = Nil
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,9 @@ object TransactionWitness {
|
|||
}
|
||||
TransactionWitness(replaced)
|
||||
}
|
||||
def fromBytes(bytes: Seq[Byte], numInputs: Int): TransactionWitness = RawTransactionWitnessParser.read(bytes, numInputs)
|
||||
def fromBytes(bytes: scodec.bits.ByteVector, numInputs: Int): TransactionWitness = RawTransactionWitnessParser.read(bytes, numInputs)
|
||||
|
||||
def apply(bytes: Seq[Byte], numInputs: Int): TransactionWitness = fromBytes(bytes, numInputs)
|
||||
def apply(bytes: scodec.bits.ByteVector, numInputs: Int): TransactionWitness = fromBytes(bytes, numInputs)
|
||||
|
||||
def apply(hex: String, numInputs: Int): TransactionWitness = fromBytes(BitcoinSUtil.decodeHex(hex), numInputs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ object BytesToPushOntoStack extends ScriptOperationFactory[BytesToPushOntoStack]
|
|||
override def operations: Seq[BytesToPushOntoStack] =
|
||||
(for { i <- 0 to 75 } yield BytesToPushOntoStackImpl(i))
|
||||
|
||||
def fromNumber(num: Int): BytesToPushOntoStack = {
|
||||
def fromNumber(num: Long): BytesToPushOntoStack = {
|
||||
if (num > 75) throw new IllegalArgumentException("We cannot have a BytesToPushOntoStack for greater than 75 bytes")
|
||||
else {
|
||||
val bytesToPushOntoStackOpt = operations.find(_.opCode == num)
|
||||
|
@ -36,5 +36,5 @@ object BytesToPushOntoStack extends ScriptOperationFactory[BytesToPushOntoStack]
|
|||
}
|
||||
}
|
||||
|
||||
def apply(num: Int): BytesToPushOntoStack = fromNumber(num)
|
||||
def apply(num: Long): BytesToPushOntoStack = fromNumber(num)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.script.ScriptProgram
|
|||
import org.bitcoins.core.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -62,7 +63,7 @@ sealed abstract class ConstantInterpreter {
|
|||
logger.debug("new script: " + newScript)
|
||||
logger.debug("Bytes to push onto stack: " + bytesToPushOntoStack)
|
||||
val constant: ScriptToken = if (bytesToPushOntoStack.size == 1) bytesToPushOntoStack.head
|
||||
else ScriptConstant(BitcoinSUtil.flipEndianness(bytesToPushOntoStack.flatMap(_.bytes)))
|
||||
else ScriptConstant(BitcoinSUtil.flipEndianness(BitcoinSUtil.toByteVector(bytesToPushOntoStack)))
|
||||
|
||||
logger.debug("Constant to be pushed onto stack: " + constant)
|
||||
//check to see if we have the exact amount of bytes needed to be pushed onto the stack
|
||||
|
@ -93,10 +94,13 @@ sealed abstract class ConstantInterpreter {
|
|||
//for the case when we have the minimal data flag and the bytes to push onto stack is represented by the
|
||||
//constant telling OP_PUSHDATA how many bytes need to go onto the stack
|
||||
//for instance OP_PUSHDATA1 OP_0
|
||||
val scriptNumOp = program.script(1).bytes match {
|
||||
case h :: t => ScriptNumberOperation.fromNumber(h.toInt)
|
||||
case Nil => None
|
||||
val scriptNumOp = if (program.script(1).bytes.nonEmpty) {
|
||||
val h = program.script(1).bytes.head
|
||||
ScriptNumberOperation.fromNumber(h.toInt)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) && program.script(1).bytes.size == 1 &&
|
||||
scriptNumOp.isDefined) {
|
||||
logger.error("We cannot use an OP_PUSHDATA operation for pushing " +
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package org.bitcoins.core.script.constant
|
||||
|
||||
import org.bitcoins.core.number.Int64
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.script.ScriptOperationFactory
|
||||
import org.bitcoins.core.util.{ BitcoinSUtil, BitcoinScriptUtil, Factory }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
||||
|
@ -14,12 +16,9 @@ import scala.util.{ Failure, Success, Try }
|
|||
* This is the root class of Script. Every element in the Script language is a
|
||||
* ScriptToken - think of this the same way you think about Object in Java.
|
||||
*/
|
||||
sealed trait ScriptToken {
|
||||
/** The hexadecimal representation of this [[ScriptToken]]. */
|
||||
def hex: String
|
||||
|
||||
sealed trait ScriptToken extends NetworkElement {
|
||||
/** The byte representation of this [[ScriptToken]]. */
|
||||
def bytes: Seq[Byte] = BitcoinSUtil.decodeHex(hex)
|
||||
def bytes: scodec.bits.ByteVector
|
||||
|
||||
/** The conversion from the byte representation of a [[ScriptToken]] to a number. */
|
||||
def toLong = ScriptNumberUtil.toLong(hex)
|
||||
|
@ -32,7 +31,7 @@ sealed trait ScriptToken {
|
|||
trait ScriptOperation extends ScriptToken {
|
||||
def opCode: Int
|
||||
|
||||
override def hex: String = BitcoinSUtil.encodeHex(opCode.toByte)
|
||||
def bytes: ByteVector = ByteVector.fromByte(opCode.toByte)
|
||||
}
|
||||
|
||||
/** A constant in the Script language for instance as String or a number. */
|
||||
|
@ -95,7 +94,7 @@ sealed abstract class ScriptNumber extends ScriptConstant {
|
|||
object ScriptNumber extends Factory[ScriptNumber] {
|
||||
|
||||
/** Represents the number zero inside of bitcoin's script language. */
|
||||
lazy val zero: ScriptNumber = ScriptNumberImpl(0, "")
|
||||
lazy val zero: ScriptNumber = ScriptNumberImpl(0, ByteVector.empty)
|
||||
/** Represents the number one inside of bitcoin's script language. */
|
||||
lazy val one: ScriptNumber = ScriptNumberImpl(1)
|
||||
/** Represents the number negative one inside of bitcoin's script language. */
|
||||
|
@ -103,16 +102,16 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
|||
/** Bitcoin has a numbering system which has a negative zero. */
|
||||
lazy val negativeZero: ScriptNumber = fromHex("80")
|
||||
|
||||
def fromBytes(bytes: Seq[Byte]) = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector) = {
|
||||
if (bytes.isEmpty) zero
|
||||
else ScriptNumberImpl(ScriptNumberUtil.toLong(bytes), BitcoinSUtil.encodeHex(bytes))
|
||||
else ScriptNumberImpl(ScriptNumberUtil.toLong(bytes), bytes)
|
||||
}
|
||||
|
||||
def apply(underlying: Long): ScriptNumber = {
|
||||
if (underlying == 0) zero else apply(ScriptNumberUtil.longToHex(underlying))
|
||||
}
|
||||
|
||||
def apply(bytes: Seq[Byte], requireMinimal: Boolean): Try[ScriptNumber] = apply(BitcoinSUtil.encodeHex(bytes), requireMinimal)
|
||||
def apply(bytes: scodec.bits.ByteVector, requireMinimal: Boolean): Try[ScriptNumber] = apply(BitcoinSUtil.encodeHex(bytes), requireMinimal)
|
||||
|
||||
def apply(hex: String, requireMinimal: Boolean): Try[ScriptNumber] = {
|
||||
if (requireMinimal && !BitcoinScriptUtil.isShortestEncoding(hex)) {
|
||||
|
@ -123,25 +122,22 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a [[ScriptNumber]] inside of bitcoin
|
||||
*
|
||||
* @param underlying the number being represented
|
||||
* @param hex the hex representation of the number - this can be different than the obvious value for
|
||||
* the number. For instance we could have padded the number with another word of zeros
|
||||
*/
|
||||
private case class ScriptNumberImpl(underlying: Long, override val hex: String) extends ScriptNumber
|
||||
private case class ScriptNumberImpl(underlying: Long, bytes: ByteVector) extends ScriptNumber
|
||||
|
||||
/**
|
||||
* Companion object for [[ScriptNumberImpl]] that gives us access to more constructor types for the
|
||||
* [[ScriptNumberImpl]] case class.
|
||||
*/
|
||||
private object ScriptNumberImpl {
|
||||
def apply(hex: String): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(hex), hex)
|
||||
def apply(hex: String): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(hex), BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def apply(bytes: Seq[Byte]): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(bytes))
|
||||
def apply(bytes: scodec.bits.ByteVector): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(bytes))
|
||||
|
||||
def apply(underlying: Long): ScriptNumber = ScriptNumberImpl(underlying, ScriptNumberUtil.longToHex(underlying))
|
||||
def apply(underlying: Long): ScriptNumber = {
|
||||
ScriptNumberImpl(
|
||||
underlying,
|
||||
BitcoinSUtil.decodeHex(ScriptNumberUtil.longToHex(underlying)))
|
||||
}
|
||||
|
||||
def apply(int64: Int64): ScriptNumber = ScriptNumberImpl(int64.toLong)
|
||||
}
|
||||
|
@ -342,11 +338,9 @@ object ScriptConstant extends Factory[ScriptConstant] {
|
|||
lazy val negativeOne = ScriptConstant("81")
|
||||
|
||||
/** Creates a [[ScriptConstant]] from a sequence of bytes. */
|
||||
def fromBytes(bytes: Seq[Byte]): ScriptConstant = ScriptConstantImpl(BitcoinSUtil.encodeHex(bytes))
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): ScriptConstant = ScriptConstantImpl(bytes)
|
||||
|
||||
/** Represent a public key or hash of a public key on our stack. */
|
||||
private case class ScriptConstantImpl(hex: String) extends ScriptConstant {
|
||||
def this(bytes: List[Byte]) = this(BitcoinSUtil.encodeHex(bytes))
|
||||
}
|
||||
private case class ScriptConstantImpl(bytes: ByteVector) extends ScriptConstant
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.script.constant
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 6/5/16.
|
||||
|
@ -40,7 +41,7 @@ trait ScriptNumberUtil {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def toInt(bytes: Seq[Byte]): Int = {
|
||||
def toInt(bytes: scodec.bits.ByteVector): Int = {
|
||||
require(bytes.size <= 4, "We cannot have an integer with more than 4 bytes (32 bits)")
|
||||
toLong(bytes).toInt
|
||||
}
|
||||
|
@ -54,18 +55,18 @@ trait ScriptNumberUtil {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def toLong(bytes: Seq[Byte]): Long = {
|
||||
def toLong(bytes: scodec.bits.ByteVector): Long = {
|
||||
val reversedBytes = bytes.reverse
|
||||
if (bytes.size == 1 && bytes.head == -128) {
|
||||
//the case for negative zero
|
||||
0
|
||||
} else if (isPositive(bytes)) {
|
||||
if (firstByteAllZeros(reversedBytes.toList) && reversedBytes.size > 1) {
|
||||
if (firstByteAllZeros(reversedBytes) && reversedBytes.size > 1) {
|
||||
parseLong(reversedBytes.slice(1, reversedBytes.size))
|
||||
} else parseLong(reversedBytes)
|
||||
} else {
|
||||
//remove the sign bit
|
||||
val removedSignBit = changeSignBitToPositive(reversedBytes.toList)
|
||||
val removedSignBit = changeSignBitToPositive(reversedBytes)
|
||||
if (firstByteAllZeros(removedSignBit)) -parseLong(removedSignBit.slice(1, removedSignBit.size))
|
||||
else -parseLong(removedSignBit)
|
||||
}
|
||||
|
@ -77,7 +78,7 @@ trait ScriptNumberUtil {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def isPositive(bytes: Seq[Byte]): Boolean = {
|
||||
def isPositive(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
if (bytes.isEmpty) false
|
||||
else {
|
||||
val result: Int = bytes(bytes.size - 1) & 0x80
|
||||
|
@ -92,19 +93,17 @@ trait ScriptNumberUtil {
|
|||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def changeSignBitToPositive(bytes: Seq[Byte]): Seq[Byte] = {
|
||||
def changeSignBitToPositive(bytes: scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
val newByte: Byte = (bytes.head & 0x7F).toByte
|
||||
(newByte :: bytes.tail.toList)
|
||||
(newByte +: bytes.tail)
|
||||
}
|
||||
|
||||
def firstByteAllZeros(bytes: Seq[Byte]): Boolean = {
|
||||
def firstByteAllZeros(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
val lastByte = bytes.head
|
||||
(lastByte & 0xFF) == 0
|
||||
}
|
||||
|
||||
private def parseLong(bytes: Seq[Byte]): Long = parseLong(bytes.toList)
|
||||
|
||||
private def parseLong(bytes: List[Byte]): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
|
||||
private def parseLong(bytes: scodec.bits.ByteVector): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
|
||||
|
||||
private def parseLong(hex: String): Long = java.lang.Long.parseLong(hex, 16)
|
||||
|
||||
|
@ -116,10 +115,10 @@ trait ScriptNumberUtil {
|
|||
*/
|
||||
def longToHex(long: Long): String = {
|
||||
if (long > -1) {
|
||||
val bytes = toByteSeq(long)
|
||||
val bytes = toByteVec(long)
|
||||
BitcoinSUtil.flipEndianness(BitcoinSUtil.encodeHex(bytes))
|
||||
} else {
|
||||
val bytes = toByteSeq(long.abs)
|
||||
val bytes = toByteVec(long.abs)
|
||||
//add sign bit
|
||||
val negativeNumberBytes = changeSignBitToNegative(bytes)
|
||||
val hex = BitcoinSUtil.encodeHex(negativeNumberBytes.reverse)
|
||||
|
@ -127,7 +126,9 @@ trait ScriptNumberUtil {
|
|||
}
|
||||
}
|
||||
|
||||
def toByteSeq(long: Long): Seq[Byte] = BigInt(long).toByteArray
|
||||
def toByteVec(long: Long): scodec.bits.ByteVector = {
|
||||
ByteVector(BigInt(long).toByteArray)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given hex string is a positive number
|
||||
|
@ -139,22 +140,22 @@ trait ScriptNumberUtil {
|
|||
|
||||
def isNegative(hex: String): Boolean = isNegative(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def isNegative(bytes: Seq[Byte]): Boolean = {
|
||||
def isNegative(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
if (bytes.isEmpty) false else !isPositive(bytes)
|
||||
}
|
||||
|
||||
def changeSignBitToPositive(hex: String): Seq[Byte] = changeSignBitToPositive(BitcoinSUtil.decodeHex(hex))
|
||||
def changeSignBitToPositive(hex: String): scodec.bits.ByteVector = changeSignBitToPositive(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def changeSignBitToNegative(hex: String): Seq[Byte] = changeSignBitToNegative(BitcoinSUtil.decodeHex(hex))
|
||||
def changeSignBitToNegative(hex: String): scodec.bits.ByteVector = changeSignBitToNegative(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def changeSignBitToNegative(bytes: Seq[Byte]): Seq[Byte] = {
|
||||
def changeSignBitToNegative(bytes: scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
val newByte = (bytes.head | 0x80).toByte
|
||||
(newByte :: bytes.tail.toList)
|
||||
(newByte +: bytes.tail)
|
||||
}
|
||||
|
||||
def firstByteAllZeros(hex: String): Boolean = firstByteAllZeros(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
private def parseLong(byte: Byte): Long = parseLong(List(byte))
|
||||
private def parseLong(byte: Byte): Long = parseLong(ByteVector.fromByte(byte))
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -21,31 +21,31 @@ sealed abstract class CryptoInterpreter {
|
|||
/** The input is hashed twice: first with SHA-256 and then with RIPEMD-160. */
|
||||
def opHash160(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_HASH160), "Script operation must be OP_HASH160")
|
||||
executeHashFunction(program, CryptoUtil.sha256Hash160(_: Seq[Byte]))
|
||||
executeHashFunction(program, CryptoUtil.sha256Hash160(_: scodec.bits.ByteVector))
|
||||
}
|
||||
|
||||
/** The input is hashed using RIPEMD-160. */
|
||||
def opRipeMd160(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_RIPEMD160), "Script operation must be OP_RIPEMD160")
|
||||
executeHashFunction(program, CryptoUtil.ripeMd160(_: Seq[Byte]))
|
||||
executeHashFunction(program, CryptoUtil.ripeMd160(_: scodec.bits.ByteVector))
|
||||
}
|
||||
|
||||
/** The input is hashed using SHA-256. */
|
||||
def opSha256(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_SHA256), "Script operation must be OP_SHA256")
|
||||
executeHashFunction(program, CryptoUtil.sha256(_: Seq[Byte]))
|
||||
executeHashFunction(program, CryptoUtil.sha256(_: scodec.bits.ByteVector))
|
||||
}
|
||||
|
||||
/** The input is hashed two times with SHA-256. */
|
||||
def opHash256(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_HASH256), "Script operation must be OP_HASH256")
|
||||
executeHashFunction(program, CryptoUtil.doubleSHA256(_: Seq[Byte]))
|
||||
executeHashFunction(program, CryptoUtil.doubleSHA256(_: scodec.bits.ByteVector))
|
||||
}
|
||||
|
||||
/** The input is hashed using SHA-1. */
|
||||
def opSha1(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_SHA1), "Script top must be OP_SHA1")
|
||||
executeHashFunction(program, CryptoUtil.sha1(_: Seq[Byte]))
|
||||
executeHashFunction(program, CryptoUtil.sha1(_: scodec.bits.ByteVector))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,7 +249,7 @@ sealed abstract class CryptoInterpreter {
|
|||
* @param hashFunction the hash function which needs to be used on the stack top (sha256,ripemd160,etc..)
|
||||
* @return
|
||||
*/
|
||||
private def executeHashFunction(program: ScriptProgram, hashFunction: Seq[Byte] => HashDigest): ScriptProgram = {
|
||||
private def executeHashFunction(program: ScriptProgram, hashFunction: scodec.bits.ByteVector => HashDigest): ScriptProgram = {
|
||||
if (program.stack.nonEmpty) {
|
||||
val stackTop = program.stack.head
|
||||
val hash = ScriptConstant(hashFunction(stackTop.bytes).bytes)
|
||||
|
@ -293,4 +293,4 @@ sealed abstract class CryptoInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
object CryptoInterpreter extends CryptoInterpreter
|
||||
object CryptoInterpreter extends CryptoInterpreter
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.bitcoins.core.script.crypto
|
|||
import org.bitcoins.core.crypto.ECDigitalSignature
|
||||
import org.bitcoins.core.number.Int32
|
||||
import org.bitcoins.core.util.Factory
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 1/18/16.
|
||||
|
@ -13,12 +14,12 @@ sealed trait HashType {
|
|||
}
|
||||
|
||||
object HashType extends Factory[HashType] {
|
||||
def fromBytes(bytes: Seq[Byte]): HashType = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): HashType = {
|
||||
val num = Int32(bytes)
|
||||
fromNumber(num)
|
||||
}
|
||||
|
||||
def fromByte(byte: Byte): HashType = fromBytes(Seq(byte))
|
||||
def fromByte(byte: Byte): HashType = fromBytes(ByteVector.fromByte(byte))
|
||||
|
||||
def fromNumber(num: Int32): HashType = {
|
||||
if (isSigHashNone(num)) {
|
||||
|
@ -95,7 +96,7 @@ object HashType extends Factory[HashType] {
|
|||
lazy val hashTypes = Seq(sigHashAll, sigHashNone, sigHashSingle, sigHashAnyoneCanPay,
|
||||
sigHashNoneAnyoneCanPay, sigHashAllAnyoneCanPay, sigHashSingleAnyoneCanPay)
|
||||
|
||||
lazy val hashTypeBytes: Seq[Byte] = Seq(sigHashAllByte, sigHashSingleByte, sigHashNoneByte, sigHashAnyoneCanPayByte,
|
||||
lazy val hashTypeBytes: Vector[Byte] = Vector(sigHashAllByte, sigHashSingleByte, sigHashNoneByte, sigHashAnyoneCanPayByte,
|
||||
sigHashNoneAnyoneCanPayByte, sigHashSingleAnyoneCanPayByte, sigHashAllAnyoneCanPayByte)
|
||||
|
||||
def apply(num: Int32): HashType = fromNumber(num)
|
||||
|
|
|
@ -171,7 +171,7 @@ sealed abstract class ScriptInterpreter {
|
|||
redeemScript match {
|
||||
case w: WitnessScriptPubKey =>
|
||||
val pushOp = BitcoinScriptUtil.calculatePushOp(redeemScriptBytes)
|
||||
val expectedScriptBytes = pushOp.flatMap(_.bytes) ++ redeemScriptBytes
|
||||
val expectedScriptBytes = BitcoinSUtil.toByteVector(pushOp) ++ redeemScriptBytes
|
||||
val flags = scriptPubKeyExecutedProgram.flags
|
||||
val segwitEnabled = ScriptFlagUtil.segWitEnabled(flags)
|
||||
if (segwitEnabled && (scriptSig.asmBytes == expectedScriptBytes)) {
|
||||
|
@ -301,11 +301,12 @@ sealed abstract class ScriptInterpreter {
|
|||
private def loop(program: ScriptProgram, opCount: Int): ExecutedScriptProgram = {
|
||||
logger.trace("Stack: " + program.stack)
|
||||
logger.trace("Script: " + program.script)
|
||||
val scriptByteVector = BitcoinSUtil.toByteVector(program.script)
|
||||
if (opCount > maxScriptOps && !program.isInstanceOf[ExecutedScriptProgram]) {
|
||||
logger.error("We have reached the maximum amount of script operations allowed")
|
||||
logger.error("Here are the remaining operations in the script: " + program.script)
|
||||
loop(ScriptProgram(program, ScriptErrorOpCount), opCount)
|
||||
} else if (program.script.flatMap(_.bytes).size > 10000 && !program.isInstanceOf[ExecutedScriptProgram]) {
|
||||
} else if (scriptByteVector.length > 10000 && !program.isInstanceOf[ExecutedScriptProgram]) {
|
||||
logger.error("We cannot run a script that is larger than 10,000 bytes")
|
||||
program match {
|
||||
case p: PreExecutionScriptProgram =>
|
||||
|
|
|
@ -13,11 +13,8 @@ abstract class RawBitcoinSerializer[T] {
|
|||
def read(hex: String): T = read(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/** Reads in bytes and transforms it into the appropriate scala type T. */
|
||||
def read(bytes: List[Byte]): T
|
||||
|
||||
/** Reads in bytes and transforms it into the appropriate scala type T. */
|
||||
def read(bytes: Seq[Byte]): T = read(bytes.toList)
|
||||
def read(bytes: scodec.bits.ByteVector): T
|
||||
|
||||
/** Takes a type T and writes it into the appropriate hexadecimal serialization for type T. */
|
||||
def write(t: T): Seq[Byte]
|
||||
def write(t: T): scodec.bits.ByteVector
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.bitcoins.core.serializers
|
|||
import org.bitcoins.core.number.UInt64
|
||||
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 2/18/16.
|
||||
|
@ -14,10 +15,10 @@ sealed abstract class RawSerializerHelper {
|
|||
* Used parse a byte sequence to a Seq[TransactionInput], Seq[TransactionOutput], etc
|
||||
* Makes sure that we parse the correct amount of elements
|
||||
*/
|
||||
def parseCmpctSizeUIntSeq[T <: NetworkElement](bytes: Seq[Byte], constructor: Seq[Byte] => T): (Seq[T], Seq[Byte]) = {
|
||||
def parseCmpctSizeUIntSeq[T <: NetworkElement](bytes: scodec.bits.ByteVector, constructor: scodec.bits.ByteVector => T): (Seq[T], scodec.bits.ByteVector) = {
|
||||
val count = CompactSizeUInt.parse(bytes)
|
||||
val payload = bytes.splitAt(count.size.toInt)._2
|
||||
def loop(accum: Seq[T], remaining: Seq[Byte]): (Seq[T], Seq[Byte]) = {
|
||||
def loop(accum: Seq[T], remaining: scodec.bits.ByteVector): (Seq[T], scodec.bits.ByteVector) = {
|
||||
if (accum.size == count.num.toInt) {
|
||||
(accum.reverse, remaining)
|
||||
} else {
|
||||
|
@ -32,12 +33,13 @@ sealed abstract class RawSerializerHelper {
|
|||
(parsed, remaining)
|
||||
}
|
||||
|
||||
/** Writes a Seq[TransactionInput]/Seq[TransactionOutput]/Seq[Transaction] -> Seq[Byte] */
|
||||
def writeCmpctSizeUInt[T](ts: Seq[T], serializer: T => Seq[Byte]): Seq[Byte] = {
|
||||
val serialized = ts.flatMap(serializer(_))
|
||||
/** Writes a Seq[TransactionInput]/Seq[TransactionOutput]/Seq[Transaction] -> scodec.bits.ByteVector */
|
||||
def writeCmpctSizeUInt[T](ts: Seq[T], serializer: T => scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
val serializedSeq: Seq[ByteVector] = ts.map(serializer(_))
|
||||
val serialized = serializedSeq.foldLeft(ByteVector.empty)(_ ++ _)
|
||||
val cmpct = CompactSizeUInt(UInt64(ts.size))
|
||||
cmpct.bytes ++ serialized
|
||||
}
|
||||
}
|
||||
|
||||
object RawSerializerHelper extends RawSerializerHelper
|
||||
object RawSerializerHelper extends RawSerializerHelper
|
||||
|
|
|
@ -8,9 +8,9 @@ import org.bitcoins.core.number.Int64
|
|||
*/
|
||||
trait RawSatoshisSerializer extends RawBitcoinSerializer[Satoshis] {
|
||||
|
||||
def read(bytes: List[Byte]): Satoshis = Satoshis(Int64(bytes.reverse))
|
||||
def read(bytes: scodec.bits.ByteVector): Satoshis = Satoshis(Int64(bytes.reverse))
|
||||
|
||||
def write(satoshis: Satoshis): Seq[Byte] = {
|
||||
def write(satoshis: Satoshis): scodec.bits.ByteVector = {
|
||||
Int64(satoshis.toLong).bytes.reverse
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.bitcoins.core.serializers.RawBitcoinSerializer
|
|||
sealed abstract class RawBlockHeaderSerializer extends RawBitcoinSerializer[BlockHeader] {
|
||||
|
||||
/** Converts a list of bytes into a block header */
|
||||
def read(bytes: List[Byte]): BlockHeader = {
|
||||
def read(bytes: scodec.bits.ByteVector): BlockHeader = {
|
||||
//version first 4 bytes
|
||||
val version = Int32(bytes.take(4).reverse)
|
||||
//previous header hash next 32 bytes
|
||||
|
@ -35,7 +35,7 @@ sealed abstract class RawBlockHeaderSerializer extends RawBitcoinSerializer[Bloc
|
|||
}
|
||||
|
||||
/** Serializes the BlockHeader to a byte array */
|
||||
def write(blockHeader: BlockHeader): Seq[Byte] = {
|
||||
def write(blockHeader: BlockHeader): scodec.bits.ByteVector = {
|
||||
val version = blockHeader.version.bytes.reverse
|
||||
|
||||
val prevHash = blockHeader.previousBlockHash.bytes
|
||||
|
@ -50,4 +50,4 @@ sealed abstract class RawBlockHeaderSerializer extends RawBitcoinSerializer[Bloc
|
|||
|
||||
}
|
||||
|
||||
object RawBlockHeaderSerializer extends RawBlockHeaderSerializer
|
||||
object RawBlockHeaderSerializer extends RawBlockHeaderSerializer
|
||||
|
|
|
@ -12,15 +12,15 @@ import org.bitcoins.core.serializers.{ RawBitcoinSerializer, RawSerializerHelper
|
|||
sealed abstract class RawBlockSerializer extends RawBitcoinSerializer[Block] {
|
||||
|
||||
/** Takes a list of bytes and converts it into a Block */
|
||||
def read(bytes: List[Byte]): Block = {
|
||||
def read(bytes: scodec.bits.ByteVector): Block = {
|
||||
val blockHeader: BlockHeader = BlockHeader(bytes.take(80))
|
||||
val txBytes: Seq[Byte] = bytes.splitAt(80)._2
|
||||
val (transactions, _) = RawSerializerHelper.parseCmpctSizeUIntSeq[Transaction](txBytes, Transaction(_: Seq[Byte]))
|
||||
val txBytes: scodec.bits.ByteVector = bytes.splitAt(80)._2
|
||||
val (transactions, _) = RawSerializerHelper.parseCmpctSizeUIntSeq[Transaction](txBytes, Transaction(_: scodec.bits.ByteVector))
|
||||
Block(blockHeader, transactions)
|
||||
}
|
||||
|
||||
/** Takes in a block and converts it to a byte array */
|
||||
def write(block: Block): Seq[Byte] = {
|
||||
def write(block: Block): scodec.bits.ByteVector = {
|
||||
val writtenHeader = block.blockHeader.bytes
|
||||
val txBytes = RawSerializerHelper.writeCmpctSizeUInt(block.transactions, { tx: Transaction => tx.bytes })
|
||||
writtenHeader ++ txBytes
|
||||
|
@ -28,4 +28,4 @@ sealed abstract class RawBlockSerializer extends RawBitcoinSerializer[Block] {
|
|||
|
||||
}
|
||||
|
||||
object RawBlockSerializer extends RawBlockSerializer
|
||||
object RawBlockSerializer extends RawBlockSerializer
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.bitcoins.core.protocol.CompactSizeUInt
|
|||
import org.bitcoins.core.protocol.blockchain.MerkleBlock
|
||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
import scodec.bits.{ BitVector, ByteVector }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -15,7 +17,9 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[MerkleBlock] {
|
||||
|
||||
def read(bytes: List[Byte]): MerkleBlock = {
|
||||
private val logger = LoggerFactory.getLogger(this.getClass.getSimpleName)
|
||||
|
||||
def read(bytes: scodec.bits.ByteVector): MerkleBlock = {
|
||||
val blockHeader = RawBlockHeaderSerializer.read(bytes.take(80))
|
||||
val bytesAfterBlockHeaderParsing = bytes.slice(blockHeader.bytes.size, bytes.size)
|
||||
val transactionCount = UInt32(bytesAfterBlockHeaderParsing.slice(0, 4).reverse)
|
||||
|
@ -27,19 +31,26 @@ sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[Merk
|
|||
val (hashes, bytesAfterTxHashParsing) = parseTransactionHashes(bytesAfterHashCountParsing, hashCount)
|
||||
val flagCount = CompactSizeUInt.parseCompactSizeUInt(bytesAfterTxHashParsing)
|
||||
val flags = bytesAfterTxHashParsing.slice(flagCount.size.toInt, bytesAfterTxHashParsing.size)
|
||||
val matches = BitcoinSUtil.bytesToBitVectors(flags).flatMap(_.reverse)
|
||||
val matches = flags.toArray
|
||||
.map(BitVector(_).reverse)
|
||||
.foldLeft(BitVector.empty)(_ ++ _)
|
||||
MerkleBlock(blockHeader, transactionCount, hashes, matches)
|
||||
}
|
||||
|
||||
def write(merkleBlock: MerkleBlock): Seq[Byte] = {
|
||||
def write(merkleBlock: MerkleBlock): scodec.bits.ByteVector = {
|
||||
val partialMerkleTree = merkleBlock.partialMerkleTree
|
||||
val bitVectors = parseToBytes(partialMerkleTree.bits)
|
||||
val byteVectors: Seq[Byte] = BitcoinSUtil.bitVectorsToBytes(bitVectors)
|
||||
val bitVectors = partialMerkleTree.bits
|
||||
val byteVectors: scodec.bits.ByteVector = {
|
||||
bitVectors.toByteArray
|
||||
.map(BitVector(_).reverse)
|
||||
.foldLeft(ByteVector.empty)(_ ++ _.bytes)
|
||||
}
|
||||
val flagCount = CompactSizeUInt(UInt64(Math.ceil(partialMerkleTree.bits.size.toDouble / 8).toInt))
|
||||
val hashes: ByteVector = BitcoinSUtil.toByteVector(merkleBlock.hashes)
|
||||
merkleBlock.blockHeader.bytes ++
|
||||
merkleBlock.transactionCount.bytes.reverse ++
|
||||
CompactSizeUInt(UInt64(merkleBlock.hashes.size)).bytes ++
|
||||
merkleBlock.hashes.flatMap(_.bytes) ++ flagCount.bytes ++ byteVectors
|
||||
hashes ++ flagCount.bytes ++ byteVectors
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,33 +59,15 @@ sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[Merk
|
|||
* @param hashCount the amount of tx hashes we need to parse from bytes
|
||||
* @return the sequence of tx hashes and the remaining bytes to be parsed into a MerkleBlockMessage
|
||||
*/
|
||||
private def parseTransactionHashes(bytes: Seq[Byte], hashCount: CompactSizeUInt): (Seq[DoubleSha256Digest], Seq[Byte]) = {
|
||||
private def parseTransactionHashes(bytes: scodec.bits.ByteVector, hashCount: CompactSizeUInt): (Seq[DoubleSha256Digest], scodec.bits.ByteVector) = {
|
||||
@tailrec
|
||||
def loop(remainingHashes: Long, remainingBytes: Seq[Byte],
|
||||
accum: List[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Byte]) = {
|
||||
def loop(remainingHashes: Long, remainingBytes: scodec.bits.ByteVector,
|
||||
accum: List[DoubleSha256Digest]): (Seq[DoubleSha256Digest], scodec.bits.ByteVector) = {
|
||||
if (remainingHashes <= 0) (accum.reverse, remainingBytes)
|
||||
else loop(remainingHashes - 1, remainingBytes.slice(32, remainingBytes.size), DoubleSha256Digest(remainingBytes.take(32)) :: accum)
|
||||
}
|
||||
loop(hashCount.num.toInt, bytes, Nil)
|
||||
}
|
||||
|
||||
/** Parses a sequence of bits to a sequence of bit vectors grouped into bytes */
|
||||
private def parseToBytes(bits: Seq[Boolean]): Seq[Seq[Boolean]] = {
|
||||
@tailrec
|
||||
def loop(remainingBits: Seq[Boolean], accum: Seq[Seq[Boolean]]): Seq[Seq[Boolean]] = remainingBits match {
|
||||
case Nil => accum.reverse
|
||||
case h :: t => accum.headOption match {
|
||||
case None => loop(remainingBits, Nil +: accum)
|
||||
case Some(bits) if bits.size == 8 =>
|
||||
//if we have 8 bits in this sequence we need to create a new byte and prepend it to the accum
|
||||
loop(remainingBits, Nil +: accum)
|
||||
case Some(bits) =>
|
||||
val newBits = h +: bits
|
||||
loop(t, newBits +: accum.tail)
|
||||
}
|
||||
}
|
||||
loop(bits, Seq(Nil))
|
||||
}
|
||||
}
|
||||
|
||||
object RawMerkleBlockSerializer extends RawMerkleBlockSerializer
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.bloom.{ BloomFilter, BloomFlag }
|
|||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 8/4/16.
|
||||
|
@ -11,7 +12,7 @@ import org.bitcoins.core.serializers.RawBitcoinSerializer
|
|||
*/
|
||||
sealed abstract class RawBloomFilterSerializer extends RawBitcoinSerializer[BloomFilter] {
|
||||
|
||||
override def read(bytes: List[Byte]): BloomFilter = {
|
||||
override def read(bytes: scodec.bits.ByteVector): BloomFilter = {
|
||||
val filterSize = CompactSizeUInt.parseCompactSizeUInt(bytes)
|
||||
val filter = bytes.slice(filterSize.size.toInt, filterSize.size.toInt + filterSize.num.toInt)
|
||||
val hashFuncsIndex = (filterSize.size + filterSize.num.toInt).toInt
|
||||
|
@ -23,11 +24,11 @@ sealed abstract class RawBloomFilterSerializer extends RawBitcoinSerializer[Bloo
|
|||
|
||||
}
|
||||
|
||||
override def write(bloomFilter: BloomFilter): Seq[Byte] = {
|
||||
override def write(bloomFilter: BloomFilter): scodec.bits.ByteVector = {
|
||||
bloomFilter.filterSize.bytes ++ bloomFilter.data ++
|
||||
bloomFilter.hashFuncs.bytes.reverse ++ bloomFilter.tweak.bytes.reverse ++
|
||||
Seq(bloomFilter.flags.byte)
|
||||
ByteVector.fromByte(bloomFilter.flags.byte)
|
||||
}
|
||||
}
|
||||
|
||||
object RawBloomFilterSerializer extends RawBloomFilterSerializer
|
||||
object RawBloomFilterSerializer extends RawBloomFilterSerializer
|
||||
|
|
|
@ -12,7 +12,7 @@ import scala.util.Try
|
|||
*/
|
||||
trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] {
|
||||
|
||||
override def read(bytes: List[Byte]): ScriptPubKey = {
|
||||
override def read(bytes: scodec.bits.ByteVector): ScriptPubKey = {
|
||||
if (bytes.isEmpty) EmptyScriptPubKey
|
||||
else {
|
||||
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
|
||||
|
@ -25,7 +25,7 @@ trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] {
|
|||
}
|
||||
}
|
||||
|
||||
override def write(scriptPubKey: ScriptPubKey): Seq[Byte] = scriptPubKey.bytes
|
||||
override def write(scriptPubKey: ScriptPubKey): scodec.bits.ByteVector = scriptPubKey.bytes
|
||||
}
|
||||
|
||||
object RawScriptPubKeyParser extends RawScriptPubKeyParser
|
||||
|
|
|
@ -13,7 +13,7 @@ import scala.util.Try
|
|||
*/
|
||||
sealed abstract class RawScriptSignatureParser extends RawBitcoinSerializer[ScriptSignature] {
|
||||
|
||||
def read(bytes: List[Byte]): ScriptSignature = {
|
||||
def read(bytes: scodec.bits.ByteVector): ScriptSignature = {
|
||||
if (bytes.isEmpty) EmptyScriptSignature
|
||||
else {
|
||||
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
|
||||
|
@ -26,7 +26,7 @@ sealed abstract class RawScriptSignatureParser extends RawBitcoinSerializer[Scri
|
|||
}
|
||||
}
|
||||
|
||||
def write(scriptSig: ScriptSignature): Seq[Byte] = scriptSig.bytes
|
||||
def write(scriptSig: ScriptSignature): scodec.bits.ByteVector = scriptSig.bytes
|
||||
}
|
||||
|
||||
object RawScriptSignatureParser extends RawScriptSignatureParser
|
||||
|
|
|
@ -5,6 +5,8 @@ import org.bitcoins.core.protocol.CompactSizeUInt
|
|||
import org.bitcoins.core.protocol.script.ScriptWitness
|
||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -13,12 +15,14 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
sealed abstract class RawScriptWitnessParser extends RawBitcoinSerializer[ScriptWitness] {
|
||||
|
||||
def read(bytes: List[Byte]): ScriptWitness = {
|
||||
private val logger = LoggerFactory.getLogger(this.getClass.getSimpleName)
|
||||
|
||||
def read(bytes: scodec.bits.ByteVector): ScriptWitness = {
|
||||
//first byte is the number of stack items
|
||||
val stackSize = CompactSizeUInt.parseCompactSizeUInt(bytes)
|
||||
val (_, stackBytes) = bytes.splitAt(stackSize.size.toInt)
|
||||
@tailrec
|
||||
def loop(remainingBytes: Seq[Byte], accum: Seq[Seq[Byte]], remainingStackItems: UInt64): Seq[Seq[Byte]] = {
|
||||
def loop(remainingBytes: scodec.bits.ByteVector, accum: Seq[scodec.bits.ByteVector], remainingStackItems: UInt64): Seq[scodec.bits.ByteVector] = {
|
||||
if (remainingStackItems <= UInt64.zero) accum
|
||||
else {
|
||||
val elementSize = CompactSizeUInt.parseCompactSizeUInt(remainingBytes)
|
||||
|
@ -34,19 +38,19 @@ sealed abstract class RawScriptWitnessParser extends RawBitcoinSerializer[Script
|
|||
witness
|
||||
}
|
||||
|
||||
def write(scriptWitness: ScriptWitness): Seq[Byte] = {
|
||||
def write(scriptWitness: ScriptWitness): scodec.bits.ByteVector = {
|
||||
@tailrec
|
||||
def loop(remainingStack: Seq[Seq[Byte]], accum: Seq[Seq[Byte]]): Seq[Seq[Byte]] = {
|
||||
def loop(remainingStack: Seq[scodec.bits.ByteVector], accum: Vector[scodec.bits.ByteVector]): Vector[scodec.bits.ByteVector] = {
|
||||
if (remainingStack.isEmpty) accum.reverse
|
||||
else {
|
||||
val compactSizeUInt: CompactSizeUInt = CompactSizeUInt.calc(remainingStack.head)
|
||||
val serialization: Seq[Byte] = compactSizeUInt.bytes ++ remainingStack.head
|
||||
val serialization: scodec.bits.ByteVector = compactSizeUInt.bytes ++ remainingStack.head
|
||||
loop(remainingStack.tail, serialization +: accum)
|
||||
}
|
||||
}
|
||||
val stackItems: Seq[Seq[Byte]] = loop(scriptWitness.stack.reverse, Nil)
|
||||
val stackItems: Vector[scodec.bits.ByteVector] = loop(scriptWitness.stack.reverse, Vector.empty)
|
||||
val size = CompactSizeUInt(UInt64(stackItems.size))
|
||||
(size.bytes +: stackItems).flatten
|
||||
(size.bytes +: stackItems).fold(ByteVector.empty)(_ ++ _)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bitcoins.core.number.UInt32
|
|||
import org.bitcoins.core.script._
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.Try
|
||||
|
@ -14,7 +15,7 @@ import scala.util.Try
|
|||
sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
||||
|
||||
/** Parses a list of bytes into a list of script tokens */
|
||||
def fromBytes(bytes: Seq[Byte]): List[ScriptToken] = {
|
||||
def fromBytes(bytes: scodec.bits.ByteVector): List[ScriptToken] = {
|
||||
val scriptTokens: List[ScriptToken] = parse(bytes)
|
||||
scriptTokens
|
||||
}
|
||||
|
@ -43,18 +44,21 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
*/
|
||||
private def parse(str: String): List[ScriptToken] = {
|
||||
@tailrec
|
||||
def loop(operations: List[String], accum: List[Byte]): List[Byte] = {
|
||||
def loop(operations: List[String], accum: scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
/* logger.debug("Attempting to parse: " + operations.headOption)
|
||||
logger.debug("Accum: " + accum)*/
|
||||
operations match {
|
||||
//for parsing strings like 'Az', need to remove single quotes
|
||||
//example: [[https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json#L24]]
|
||||
case h :: t if (h.size > 0 && h.head == ''' && h.last == ''') =>
|
||||
case h +: t if (h.size > 0 && h.head == ''' && h.last == ''') =>
|
||||
val strippedQuotes = h.replace("'", "")
|
||||
if (strippedQuotes.size == 0) {
|
||||
loop(t, OP_0.bytes.toList ++ accum)
|
||||
loop(t, OP_0.bytes ++ accum)
|
||||
} else {
|
||||
val bytes: Seq[Byte] = BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(strippedQuotes.getBytes.toList))
|
||||
val bytes: scodec.bits.ByteVector = {
|
||||
val b = ByteVector.apply(strippedQuotes.getBytes)
|
||||
BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(b))
|
||||
}
|
||||
|
||||
val bytesToPushOntoStack: List[ScriptToken] = (bytes.size > 75) match {
|
||||
case true =>
|
||||
|
@ -67,31 +71,33 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
case size if size < Int.MaxValue =>
|
||||
List(scriptNumber, OP_PUSHDATA4)
|
||||
}
|
||||
case false => List(BytesToPushOntoStack(bytes.size))
|
||||
case false => List(BytesToPushOntoStack(bytes.size.toInt))
|
||||
}
|
||||
|
||||
loop(t, bytes.toList ++ bytesToPushOntoStack.flatMap(_.bytes) ++ accum)
|
||||
val pushOpBytes: ByteVector = bytesToPushOntoStack.foldLeft(ByteVector.empty)(_ ++ _.bytes)
|
||||
val aggregation: ByteVector = bytes ++ pushOpBytes ++ accum
|
||||
loop(t, aggregation)
|
||||
}
|
||||
//if we see a byte constant in the form of "0x09adb"
|
||||
case h :: t if (h.size > 1 && h.substring(0, 2) == "0x") =>
|
||||
loop(t, BitcoinSUtil.decodeHex(h.substring(2, h.size).toLowerCase).toList.reverse ++ accum)
|
||||
case h +: t if (h.size > 1 && h.substring(0, 2) == "0x") =>
|
||||
loop(t, BitcoinSUtil.decodeHex(h.substring(2, h.size).toLowerCase).reverse ++ accum)
|
||||
//skip the empty string
|
||||
case h :: t if (h == "") => loop(t, accum)
|
||||
case h :: t if (h == "0") => loop(t, OP_0.bytes.toList ++ accum)
|
||||
case h +: t if (h == "") => loop(t, accum)
|
||||
case h +: t if (h == "0") => loop(t, OP_0.bytes ++ accum)
|
||||
|
||||
case h :: t if (ScriptOperation.fromString(h).isDefined) =>
|
||||
case h +: t if (ScriptOperation.fromString(h).isDefined) =>
|
||||
val op = ScriptOperation.fromString(h).get
|
||||
loop(t, op.bytes.toList ++ accum)
|
||||
case h :: t if (tryParsingLong(h)) =>
|
||||
loop(t, op.bytes ++ accum)
|
||||
case h +: t if (tryParsingLong(h)) =>
|
||||
val hexLong = BitcoinSUtil.flipEndianness(ScriptNumberUtil.longToHex(h.toLong))
|
||||
val bytesToPushOntoStack = BytesToPushOntoStack(hexLong.size / 2)
|
||||
//convert the string to int, then convert to hex
|
||||
loop(t, BitcoinSUtil.decodeHex(hexLong).toList ++ bytesToPushOntoStack.bytes.toList ++ accum)
|
||||
loop(t, BitcoinSUtil.decodeHex(hexLong) ++ bytesToPushOntoStack.bytes ++ accum)
|
||||
//means that it must be a BytesToPushOntoStack followed by a script constant
|
||||
case h :: t =>
|
||||
case h +: t =>
|
||||
//find the size of the string in bytes
|
||||
val bytesToPushOntoStack = BytesToPushOntoStack(h.size / 2)
|
||||
loop(t, BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(h)).toList ++ bytesToPushOntoStack.bytes.toList ++ accum)
|
||||
loop(t, BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(h)) ++ bytesToPushOntoStack.bytes ++ accum)
|
||||
case Nil => accum
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +117,7 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
//take a look at https://github.com/bitcoin/bitcoin/blob/605c17844ea32b6d237db6d83871164dc7d59dab/src/core_read.cpp#L53-L88
|
||||
//for the offical parsing algorithm, for examples of weird formats look inside of
|
||||
//[[https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json]]
|
||||
val parsedBytesFromString = loop(str.split(" ").toList, List()).reverse
|
||||
val parsedBytesFromString = loop(str.split(" ").toList, ByteVector.empty).reverse
|
||||
parse(parsedBytesFromString)
|
||||
}
|
||||
}
|
||||
|
@ -120,24 +126,22 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
* Parses a byte array into a the asm operations for a script
|
||||
* will throw an exception if it fails to parse a op code
|
||||
*/
|
||||
private def parse(bytes: List[Byte]): List[ScriptToken] = {
|
||||
private def parse(bytes: scodec.bits.ByteVector): List[ScriptToken] = {
|
||||
@tailrec
|
||||
def loop(bytes: List[Byte], accum: List[ScriptToken]): List[ScriptToken] = {
|
||||
def loop(bytes: scodec.bits.ByteVector, accum: List[ScriptToken]): List[ScriptToken] = {
|
||||
//logger.debug("Byte to be parsed: " + bytes.headOption)
|
||||
bytes match {
|
||||
case h :: t =>
|
||||
val op = ScriptOperation(h).get
|
||||
val parsingHelper: ParsingHelper[Byte] = parseOperationByte(op, accum, t)
|
||||
loop(parsingHelper.tail, parsingHelper.accum)
|
||||
case Nil => accum
|
||||
if (bytes.nonEmpty) {
|
||||
val op = ScriptOperation(bytes.head).get
|
||||
val parsingHelper: ParsingHelper = parseOperationByte(op, accum, bytes.tail)
|
||||
loop(parsingHelper.tail, parsingHelper.accum)
|
||||
} else {
|
||||
accum
|
||||
}
|
||||
}
|
||||
loop(bytes, List()).reverse
|
||||
loop(bytes, Nil).reverse
|
||||
|
||||
}
|
||||
|
||||
private def parse(bytes: Seq[Byte]): List[ScriptToken] = parse(bytes.toList)
|
||||
|
||||
/** Parses a redeem script from the given script token */
|
||||
def parseRedeemScript(scriptToken: ScriptToken): Try[List[ScriptToken]] = {
|
||||
val redeemScript: Try[List[ScriptToken]] = Try(parse(scriptToken.bytes))
|
||||
|
@ -148,7 +152,7 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
* Slices the amount of bytes specified in the bytesToPushOntoStack parameter and then creates a script constant
|
||||
* from those bytes. Returns the script constant and the byte array without the script constant
|
||||
*/
|
||||
private def sliceConstant[T](bytesToPushOntoStack: BytesToPushOntoStack, data: List[T]): (List[T], List[T]) = {
|
||||
private def sliceConstant(bytesToPushOntoStack: BytesToPushOntoStack, data: ByteVector): (ByteVector, ByteVector) = {
|
||||
val finalIndex = bytesToPushOntoStack.opCode
|
||||
val dataConstant = data.slice(0, finalIndex)
|
||||
(dataConstant, data.slice(finalIndex, data.size))
|
||||
|
@ -162,7 +166,7 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
*/
|
||||
def parseBytesFromString(s: String): List[ScriptConstant] = {
|
||||
//logger.debug("Parsing bytes from string " + s)
|
||||
val scriptConstants: List[ScriptConstant] = (raw"\b0x([0-9a-f]+)\b".r
|
||||
val scriptConstants = (raw"\b0x([0-9a-f]+)\b".r
|
||||
.findAllMatchIn(s.toLowerCase)
|
||||
.map(g =>
|
||||
// 1 hex = 4 bits therefore 16 hex characters * 4 bits = 64
|
||||
|
@ -173,33 +177,33 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
ScriptNumber(g.group(1))
|
||||
} else {
|
||||
ScriptConstant(g.group(1))
|
||||
}).toList)
|
||||
scriptConstants
|
||||
}))
|
||||
scriptConstants.toList
|
||||
}
|
||||
|
||||
private sealed case class ParsingHelper[T](tail: List[T], accum: List[ScriptToken])
|
||||
private sealed case class ParsingHelper(tail: ByteVector, accum: List[ScriptToken])
|
||||
|
||||
/**
|
||||
* Parses an operation if the tail is a List[Byte]
|
||||
* Parses an operation if the tail is a scodec.bits.ByteVector
|
||||
* If the operation is a bytesToPushOntoStack, it pushes the number of bytes onto the stack
|
||||
* specified by the bytesToPushOntoStack
|
||||
* i.e. If the operation was BytesToPushOntoStackImpl(5), it would slice 5 bytes off of the tail and
|
||||
* places them into a ScriptConstant and add them to the accumulator.
|
||||
*/
|
||||
private def parseOperationByte(op: ScriptOperation, accum: List[ScriptToken], tail: List[Byte]): ParsingHelper[Byte] = {
|
||||
private def parseOperationByte(op: ScriptOperation, accum: List[ScriptToken], tail: scodec.bits.ByteVector): ParsingHelper = {
|
||||
op match {
|
||||
case bytesToPushOntoStack: BytesToPushOntoStack =>
|
||||
//logger.debug("Parsing operation byte: " +bytesToPushOntoStack )
|
||||
//means that we need to push x amount of bytes on to the stack
|
||||
val (constant, newTail) = sliceConstant(bytesToPushOntoStack, tail)
|
||||
val scriptConstant = ScriptConstant(constant)
|
||||
ParsingHelper(newTail, scriptConstant :: bytesToPushOntoStack :: accum)
|
||||
ParsingHelper(newTail, scriptConstant +: bytesToPushOntoStack +: accum)
|
||||
case OP_PUSHDATA1 => parseOpPushData(op, accum, tail)
|
||||
case OP_PUSHDATA2 => parseOpPushData(op, accum, tail)
|
||||
case OP_PUSHDATA4 => parseOpPushData(op, accum, tail)
|
||||
case _ =>
|
||||
//means that we need to push the operation onto the stack
|
||||
ParsingHelper(tail, op :: accum)
|
||||
ParsingHelper(tail, op +: accum)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,15 +215,15 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
* @param tail the bytes to be parsed still
|
||||
* @return
|
||||
*/
|
||||
private def parseOpPushData(op: ScriptOperation, accum: List[ScriptToken], tail: List[Byte]): ParsingHelper[Byte] = {
|
||||
private def parseOpPushData(op: ScriptOperation, accum: List[ScriptToken], tail: scodec.bits.ByteVector): ParsingHelper = {
|
||||
|
||||
def parseOpPushDataHelper(numBytes: Int): ParsingHelper[Byte] = {
|
||||
def parseOpPushDataHelper(numBytes: Int): ParsingHelper = {
|
||||
//next numBytes is the size of the script constant
|
||||
val scriptConstantHex = tail.slice(0, numBytes)
|
||||
val uInt32Push = UInt32(BitcoinSUtil.flipEndianness(scriptConstantHex))
|
||||
//need this for the case where we have an OP_PUSHDATA4 with a number larger than a int32 can hold
|
||||
//TODO: Review this more, see this transaction's scriptSig as an example: b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
|
||||
val bytesForPushOp = Try(uInt32Push.toInt).getOrElse(tail.size)
|
||||
val bytesForPushOp: Long = Try(uInt32Push.toLong).getOrElse(tail.length)
|
||||
val bytesToPushOntoStack = ScriptConstant(scriptConstantHex)
|
||||
val endIndex = {
|
||||
val idx = bytesForPushOp + numBytes
|
||||
|
@ -252,15 +256,15 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
|
|||
* @return
|
||||
*/
|
||||
private def buildParsingHelper(op: ScriptOperation, bytesToPushOntoStack: ScriptConstant,
|
||||
scriptConstant: ScriptConstant, restOfBytes: List[Byte], accum: List[ScriptToken]): ParsingHelper[Byte] = {
|
||||
scriptConstant: ScriptConstant, restOfBytes: scodec.bits.ByteVector, accum: List[ScriptToken]): ParsingHelper = {
|
||||
if (bytesToPushOntoStack.hex == "00") {
|
||||
//if we need to push 0 bytes onto the stack we do not add the script constant
|
||||
ParsingHelper[Byte](
|
||||
ParsingHelper(
|
||||
restOfBytes,
|
||||
bytesToPushOntoStack :: op :: accum)
|
||||
} else ParsingHelper[Byte](
|
||||
bytesToPushOntoStack +: op +: accum)
|
||||
} else ParsingHelper(
|
||||
restOfBytes,
|
||||
scriptConstant :: bytesToPushOntoStack :: op :: accum)
|
||||
scriptConstant +: bytesToPushOntoStack +: op +: accum)
|
||||
}
|
||||
|
||||
/** Checks if a string can be cast to an int */
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.bitcoins.core.util.BitcoinSUtil
|
|||
*/
|
||||
sealed abstract class RawTransactionInputParser extends RawBitcoinSerializer[TransactionInput] {
|
||||
|
||||
override def read(bytes: List[Byte]): TransactionInput = {
|
||||
override def read(bytes: scodec.bits.ByteVector): TransactionInput = {
|
||||
val outPoint = TransactionOutPoint(bytes.take(36))
|
||||
val scriptSigBytes = bytes.slice(36, bytes.size)
|
||||
val scriptSig: ScriptSignature = RawScriptSignatureParser.read(scriptSigBytes)
|
||||
|
@ -26,7 +26,7 @@ sealed abstract class RawTransactionInputParser extends RawBitcoinSerializer[Tra
|
|||
}
|
||||
|
||||
/** Writes a single transaction input */
|
||||
def write(input: TransactionInput): Seq[Byte] = {
|
||||
def write(input: TransactionInput): scodec.bits.ByteVector = {
|
||||
input.previousOutput.bytes ++ input.scriptSignature.bytes ++ input.sequence.bytes.reverse
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@ import org.bitcoins.core.serializers.RawBitcoinSerializer
|
|||
*/
|
||||
sealed abstract class RawTransactionOutPointParser extends RawBitcoinSerializer[TransactionOutPoint] {
|
||||
|
||||
override def read(bytes: List[Byte]): TransactionOutPoint = {
|
||||
val txId: List[Byte] = bytes.take(32)
|
||||
override def read(bytes: scodec.bits.ByteVector): TransactionOutPoint = {
|
||||
val txId: scodec.bits.ByteVector = bytes.take(32)
|
||||
val indexBytes = bytes.slice(32, 36)
|
||||
val index = UInt32(indexBytes.reverse)
|
||||
TransactionOutPoint(DoubleSha256Digest(txId), index)
|
||||
}
|
||||
|
||||
def write(outPoint: TransactionOutPoint): Seq[Byte] = {
|
||||
def write(outPoint: TransactionOutPoint): scodec.bits.ByteVector = {
|
||||
//UInt32s cannot hold negative numbers, but sometimes the Bitcoin Protocol requires the vout to be -1, which is serialized
|
||||
//as "0xFFFFFFFF".
|
||||
//https://github.com/bitcoin/bitcoin/blob/d612837814020ae832499d18e6ee5eb919a87907/src/primitives/transaction.h
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.bitcoins.core.serializers.{ RawBitcoinSerializer, RawSatoshisSerializ
|
|||
sealed abstract class RawTransactionOutputParser extends RawBitcoinSerializer[TransactionOutput] {
|
||||
|
||||
/** Writes a single transaction output */
|
||||
override def write(output: TransactionOutput): Seq[Byte] = {
|
||||
override def write(output: TransactionOutput): scodec.bits.ByteVector = {
|
||||
val satoshis: Satoshis = CurrencyUnits.toSatoshis(output.value)
|
||||
satoshis.bytes ++ output.scriptPubKey.bytes
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ sealed abstract class RawTransactionOutputParser extends RawBitcoinSerializer[Tr
|
|||
* Reads a single output from the given bytes, note this is different than [[org.bitcoins.core.serializers.transaction.RawTransactionOutputParser.read]]
|
||||
* because it does NOT expect a [[CompactSizeUInt]] to be the first element in the byte array indicating how many outputs we have
|
||||
*/
|
||||
override def read(bytes: List[Byte]): TransactionOutput = {
|
||||
override def read(bytes: scodec.bits.ByteVector): TransactionOutput = {
|
||||
val satoshisBytes = bytes.take(8)
|
||||
val satoshis = RawSatoshisSerializer.read(satoshisBytes)
|
||||
//it doesn't include itself towards the size, thats why it is incremented by one
|
||||
|
@ -34,4 +34,4 @@ sealed abstract class RawTransactionOutputParser extends RawBitcoinSerializer[Tr
|
|||
|
||||
}
|
||||
|
||||
object RawTransactionOutputParser extends RawTransactionOutputParser
|
||||
object RawTransactionOutputParser extends RawTransactionOutputParser
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.bitcoins.core.serializers.transaction
|
|||
import org.bitcoins.core.protocol.script.ScriptWitness
|
||||
import org.bitcoins.core.protocol.transaction.TransactionWitness
|
||||
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
|
||||
import org.slf4j.LoggerFactory
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -13,14 +15,14 @@ import scala.annotation.tailrec
|
|||
* [[https://github.com/bitcoin/bitcoin/blob/b4e4ba475a5679e09f279aaf2a83dcf93c632bdb/src/primitives/transaction.h#L232-L268]]
|
||||
*/
|
||||
sealed abstract class RawTransactionWitnessParser {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(this.getClass.getSimpleName)
|
||||
/**
|
||||
* We can only tell how many [[ScriptWitness]]
|
||||
* we have if we have the number of inputs the transaction creates
|
||||
*/
|
||||
def read(bytes: Seq[Byte], numInputs: Int): TransactionWitness = {
|
||||
def read(bytes: scodec.bits.ByteVector, numInputs: Int): TransactionWitness = {
|
||||
@tailrec
|
||||
def loop(remainingBytes: Seq[Byte], remainingInputs: Int, accum: Seq[ScriptWitness]): Seq[ScriptWitness] = {
|
||||
def loop(remainingBytes: scodec.bits.ByteVector, remainingInputs: Int, accum: Seq[ScriptWitness]): Seq[ScriptWitness] = {
|
||||
if (remainingInputs != 0) {
|
||||
val w = RawScriptWitnessParser.read(remainingBytes)
|
||||
val (_, newRemainingBytes) = remainingBytes.splitAt(w.bytes.size)
|
||||
|
@ -32,7 +34,9 @@ sealed abstract class RawTransactionWitnessParser {
|
|||
TransactionWitness(witnesses)
|
||||
}
|
||||
|
||||
def write(witness: TransactionWitness): Seq[Byte] = witness.witnesses.flatMap(_.bytes)
|
||||
def write(witness: TransactionWitness): scodec.bits.ByteVector = {
|
||||
witness.witnesses.foldLeft(ByteVector.empty)(_ ++ _.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
object RawTransactionWitnessParser extends RawTransactionWitnessParser
|
||||
object RawTransactionWitnessParser extends RawTransactionWitnessParser
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.util
|
|||
|
||||
import org.bitcoins.core.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.protocol.blockchain._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
@ -16,15 +17,15 @@ sealed abstract class Base58 {
|
|||
val base58Pairs = base58Characters.zipWithIndex.toMap
|
||||
private val logger = BitcoinSLogger.logger
|
||||
/** Verifies a given [[Base58Type]] string against its checksum (last 4 decoded bytes). */
|
||||
def decodeCheck(input: String): Try[Seq[Byte]] = {
|
||||
val decodedTry: Try[Seq[Byte]] = Try(decode(input))
|
||||
def decodeCheck(input: String): Try[scodec.bits.ByteVector] = {
|
||||
val decodedTry: Try[scodec.bits.ByteVector] = Try(decode(input))
|
||||
decodedTry.flatMap { decoded =>
|
||||
if (decoded.length < 4) Failure(new IllegalArgumentException("Invalid input"))
|
||||
else {
|
||||
val splitSeqs = decoded.splitAt(decoded.length - 4)
|
||||
val data: Seq[Byte] = splitSeqs._1
|
||||
val checksum: Seq[Byte] = splitSeqs._2
|
||||
val actualChecksum: Seq[Byte] = CryptoUtil.doubleSHA256(data).bytes.take(4)
|
||||
val data: scodec.bits.ByteVector = splitSeqs._1
|
||||
val checksum: scodec.bits.ByteVector = splitSeqs._2
|
||||
val actualChecksum: scodec.bits.ByteVector = CryptoUtil.doubleSHA256(data).bytes.take(4)
|
||||
if (checksum == actualChecksum) Success(data)
|
||||
else Failure(new IllegalArgumentException("checksums don't validate"))
|
||||
}
|
||||
|
@ -32,8 +33,8 @@ sealed abstract class Base58 {
|
|||
}
|
||||
|
||||
/** Encodes a sequence of bytes to a [[Base58Type]] string. */
|
||||
def encode(bytes: Seq[Byte]): String = {
|
||||
val ones: String = bytes.takeWhile(_ == 0).map(_ => '1').mkString
|
||||
def encode(bytes: scodec.bits.ByteVector): String = {
|
||||
val ones: String = bytes.toSeq.takeWhile(_ == 0).map(_ => '1').mkString
|
||||
@tailrec
|
||||
def loop(current: BigInt, str: String): String = current match {
|
||||
case a if current == BigInt(0) =>
|
||||
|
@ -59,18 +60,18 @@ sealed abstract class Base58 {
|
|||
}
|
||||
|
||||
/** Encodes a [[Byte]] to its [[Base58Type]] representation. */
|
||||
def encode(byte: Byte): String = encode(Seq(byte))
|
||||
def encode(byte: Byte): String = encode(ByteVector.fromByte(byte))
|
||||
|
||||
/**
|
||||
* Takes in [[Base58Type]] string and returns sequence of [[Byte]]s
|
||||
* https://github.com/ACINQ/bitcoin-lib/blob/master/src/main/scala/fr/acinq/bitcoin/Base58.scala.
|
||||
*/
|
||||
def decode(input: String): Seq[Byte] = {
|
||||
val zeroes = input.takeWhile(_ == '1').map(_ => 0: Byte).toArray
|
||||
def decode(input: String): scodec.bits.ByteVector = {
|
||||
val zeroes = ByteVector(input.takeWhile(_ == '1').map(_ => 0: Byte))
|
||||
val trim = input.dropWhile(_ == '1').toList
|
||||
val decoded = trim.foldLeft(BigInt(0))((a, b) =>
|
||||
a.*(BigInt(58L)).+(BigInt(base58Pairs(b))))
|
||||
if (trim.isEmpty) zeroes else zeroes ++ decoded.toByteArray.dropWhile(_ == 0)
|
||||
if (trim.isEmpty) zeroes else zeroes ++ ByteVector(decoded.toByteArray.dropWhile(_ == 0))
|
||||
}
|
||||
|
||||
/** Determines if a string is a valid [[Base58Type]] string. */
|
||||
|
@ -94,10 +95,10 @@ sealed abstract class Base58 {
|
|||
* ('1', '3', 'm', 'n', '2')
|
||||
*/
|
||||
private def isValidAddressPreFixByte(byte: Byte): Boolean = {
|
||||
val validAddressPreFixBytes: Seq[Byte] =
|
||||
val validAddressPreFixBytes: scodec.bits.ByteVector =
|
||||
MainNetChainParams.base58Prefixes(PubKeyAddress) ++ MainNetChainParams.base58Prefixes(ScriptAddress) ++
|
||||
TestNetChainParams.base58Prefixes(PubKeyAddress) ++ TestNetChainParams.base58Prefixes(ScriptAddress)
|
||||
validAddressPreFixBytes.contains(byte)
|
||||
validAddressPreFixBytes.toSeq.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,9 +106,9 @@ sealed abstract class Base58 {
|
|||
* ('5', '9', 'c')
|
||||
*/
|
||||
private def isValidSecretKeyPreFixByte(byte: Byte): Boolean = {
|
||||
val validSecretKeyPreFixBytes: Seq[Byte] =
|
||||
val validSecretKeyPreFixBytes: scodec.bits.ByteVector =
|
||||
MainNetChainParams.base58Prefixes(SecretKey) ++ TestNetChainParams.base58Prefixes(SecretKey)
|
||||
validSecretKeyPreFixBytes.contains(byte)
|
||||
validSecretKeyPreFixBytes.toSeq.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,4 +131,4 @@ sealed abstract class Base58 {
|
|||
}
|
||||
}
|
||||
|
||||
object Base58 extends Base58
|
||||
object Base58 extends Base58
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
package org.bitcoins.core.util
|
||||
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import scodec.bits.{ BitVector, ByteVector }
|
||||
|
||||
import scala.math.BigInt
|
||||
|
||||
/**
|
||||
* Created by chris on 2/26/16.
|
||||
*/
|
||||
trait BitcoinSUtil {
|
||||
|
||||
def decodeHex(hex: String): Seq[Byte] = {
|
||||
hex.replaceAll("[^0-9A-Fa-f]", "").sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte).toList
|
||||
private val logger = BitcoinSLogger.logger
|
||||
def decodeHex(hex: String): scodec.bits.ByteVector = {
|
||||
if (hex.isEmpty) ByteVector.empty else scodec.bits.ByteVector.fromHex(hex).get
|
||||
}
|
||||
|
||||
def encodeHex(bytes: Seq[Byte]): String = bytes.map("%02x".format(_)).mkString
|
||||
def encodeHex(bytes: scodec.bits.ByteVector): String = bytes.toHex
|
||||
|
||||
def encodeHex(byte: Byte): String = encodeHex(Seq(byte))
|
||||
def encodeHex(byte: Byte): String = encodeHex(scodec.bits.ByteVector(byte))
|
||||
|
||||
/**
|
||||
* Encodes a long number to a hex string, pads it with an extra '0' char
|
||||
|
@ -43,7 +46,7 @@ trait BitcoinSUtil {
|
|||
addPadding(4, hex)
|
||||
}
|
||||
|
||||
def encodeHex(bigInt: BigInt): String = BitcoinSUtil.encodeHex(bigInt.toByteArray)
|
||||
def encodeHex(bigInt: BigInt): String = BitcoinSUtil.encodeHex(ByteVector(bigInt.toByteArray))
|
||||
|
||||
/** Tests if a given string is a hexadecimal string. */
|
||||
def isHex(str: String): Boolean = {
|
||||
|
@ -61,8 +64,9 @@ trait BitcoinSUtil {
|
|||
def flipEndianness(hex: String): String = flipEndianness(decodeHex(hex))
|
||||
|
||||
/** Flips the endianness of the given sequence of bytes. */
|
||||
def flipEndianness(bytes: Seq[Byte]): String = encodeHex(bytes.reverse)
|
||||
def flipEndianness(bytes: scodec.bits.ByteVector): String = encodeHex(bytes.reverse)
|
||||
|
||||
def flipEndiannessBytes(bytes: ByteVector): ByteVector = bytes.reverse
|
||||
/**
|
||||
* Adds the amount padding bytes needed to fix the size of the hex string
|
||||
* for instance, ints are required to be 4 bytes. If the number is just 1
|
||||
|
@ -77,29 +81,26 @@ trait BitcoinSUtil {
|
|||
}
|
||||
|
||||
/** Converts a sequence of bytes to a sequence of bit vectors */
|
||||
def bytesToBitVectors(bytes: Seq[Byte]): Seq[Seq[Boolean]] = bytes.map(byteToBitVector)
|
||||
def bytesToBitVectors(bytes: scodec.bits.ByteVector): scodec.bits.BitVector = {
|
||||
bytes.toBitVector
|
||||
}
|
||||
|
||||
/** Converts a byte to a bit vector representing that byte */
|
||||
def byteToBitVector(byte: Byte): Seq[Boolean] = {
|
||||
(0 to 7).map(index => isBitSet(byte, 7 - index))
|
||||
def byteToBitVector(byte: Byte): scodec.bits.BitVector = {
|
||||
BitVector.fromByte(byte)
|
||||
}
|
||||
|
||||
/** Checks if the bit at the given index is set */
|
||||
def isBitSet(byte: Byte, index: Int): Boolean = ((byte >> index) & 1) == 1
|
||||
|
||||
/** Converts a bit vector to a single byte -- the resulting byte is big endian */
|
||||
def bitVectorToByte(bits: Seq[Boolean]): Byte = {
|
||||
require(bits.size <= 8, "Cannot convert a bit vector to a byte when the size of the bit vector is larger than 8, got: " + bits)
|
||||
val b = bits.reverse
|
||||
val result: Seq[Int] = b.zipWithIndex.map {
|
||||
case (b, index) =>
|
||||
if (b) NumberUtil.pow2(index).toInt else 0
|
||||
}
|
||||
result.sum.toByte
|
||||
/** Converts a sequence of bit vectors to a sequence of bytes */
|
||||
def bitVectorToBytes(bits: scodec.bits.BitVector): scodec.bits.ByteVector = {
|
||||
bits.bytes
|
||||
}
|
||||
|
||||
/** Converts a sequence of bit vectors to a sequence of bytes */
|
||||
def bitVectorsToBytes(bits: Seq[Seq[Boolean]]): Seq[Byte] = bits.map(bitVectorToByte)
|
||||
def toByteVector[T <: NetworkElement](h: Seq[T]): ByteVector = {
|
||||
h.foldLeft(ByteVector.empty)(_ ++ _.bytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bitcoins.core.script.crypto.{ OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIF
|
|||
import org.bitcoins.core.script.flag.{ ScriptFlag, ScriptFlagUtil }
|
||||
import org.bitcoins.core.script.result.{ ScriptError, ScriptErrorPubKeyType, ScriptErrorWitnessPubKeyType }
|
||||
import org.bitcoins.core.script.{ ExecutionInProgressScriptProgram, ScriptProgram }
|
||||
import scodec.bits.BitVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -24,7 +25,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
/** Converts a sequence of script tokens to them to their byte values */
|
||||
def asmToBytes(asm: Seq[ScriptToken]): Seq[Byte] = BitcoinSUtil.decodeHex(asmToHex(asm))
|
||||
def asmToBytes(asm: Seq[ScriptToken]): scodec.bits.ByteVector = BitcoinSUtil.decodeHex(asmToHex(asm))
|
||||
|
||||
/**
|
||||
* Filters out push operations in our sequence of script tokens
|
||||
|
@ -113,14 +114,20 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
*/
|
||||
def isPushOnly(script: Seq[ScriptToken]): Boolean = {
|
||||
@tailrec
|
||||
def loop(tokens: Seq[ScriptToken], accum: List[Boolean]): Seq[Boolean] = tokens match {
|
||||
def loop(tokens: Seq[ScriptToken]): Boolean = tokens match {
|
||||
case h :: t => h match {
|
||||
case scriptOp: ScriptOperation => loop(t, (scriptOp.opCode < OP_16.opCode) :: accum)
|
||||
case _: ScriptToken => loop(t, true :: accum)
|
||||
case scriptOp: ScriptOperation =>
|
||||
if (scriptOp.opCode < OP_16.opCode) {
|
||||
loop(t)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
case _: ScriptToken => loop(t)
|
||||
}
|
||||
case Nil => accum
|
||||
case Nil => true
|
||||
}
|
||||
!loop(script, List()).exists(_ == false)
|
||||
loop(script)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +182,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
} else throw new IllegalArgumentException("ScriptToken is to large for pushops, size: " + scriptTokenSize)
|
||||
}
|
||||
|
||||
def calculatePushOp(bytes: Seq[Byte]): Seq[ScriptToken] = calculatePushOp(ScriptConstant(bytes))
|
||||
def calculatePushOp(bytes: scodec.bits.ByteVector): Seq[ScriptToken] = calculatePushOp(ScriptConstant(bytes))
|
||||
|
||||
/**
|
||||
* Whenever a [[ScriptConstant]] is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
|
@ -184,7 +191,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
*/
|
||||
def isShortestEncoding(constant: ScriptConstant): Boolean = isShortestEncoding(constant.bytes)
|
||||
|
||||
def isShortestEncoding(bytes: Seq[Byte]): Boolean = {
|
||||
def isShortestEncoding(bytes: scodec.bits.ByteVector): Boolean = {
|
||||
// If the most-significant-byte - excluding the sign bit - is zero
|
||||
// then we're not minimal. Note how this test also rejects the
|
||||
// negative-zero encoding, 0x80.
|
||||
|
@ -366,7 +373,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
* All bytes in the byte vector must be zero, unless it is the last byte, which can be 0 or 0x80 (negative zero)
|
||||
*/
|
||||
def castToBool(token: ScriptToken): Boolean = {
|
||||
token.bytes.zipWithIndex.exists {
|
||||
token.bytes.toArray.zipWithIndex.exists {
|
||||
case (b, index) =>
|
||||
val byteNotZero = b.toByte != 0
|
||||
val lastByteNotNegativeZero = !(index == token.bytes.size - 1 && b.toByte == 0x80.toByte)
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.bitcoins.core.crypto._
|
|||
import org.bouncycastle.crypto.digests.{ RIPEMD160Digest, SHA512Digest }
|
||||
import org.bouncycastle.crypto.macs.HMac
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/**
|
||||
* Created by chris on 1/14/16.
|
||||
|
@ -16,8 +17,8 @@ trait CryptoUtil {
|
|||
/** Does the following computation: RIPEMD160(SHA256(hex)).*/
|
||||
def sha256Hash160(hex: String): Sha256Hash160Digest = sha256Hash160(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
def sha256Hash160(bytes: Seq[Byte]): Sha256Hash160Digest = {
|
||||
val hash = ripeMd160(sha256(bytes.toArray).bytes).bytes
|
||||
def sha256Hash160(bytes: scodec.bits.ByteVector): Sha256Hash160Digest = {
|
||||
val hash = ripeMd160(sha256(bytes).bytes).bytes
|
||||
Sha256Hash160Digest(hash)
|
||||
}
|
||||
|
||||
|
@ -25,8 +26,8 @@ trait CryptoUtil {
|
|||
def doubleSHA256(hex: String): DoubleSha256Digest = doubleSHA256(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/** Performs sha256(sha256(bytes)). */
|
||||
def doubleSHA256(bytes: Seq[Byte]): DoubleSha256Digest = {
|
||||
val hash: Seq[Byte] = sha256(sha256(bytes).bytes).bytes
|
||||
def doubleSHA256(bytes: scodec.bits.ByteVector): DoubleSha256Digest = {
|
||||
val hash: scodec.bits.ByteVector = sha256(sha256(bytes).bytes).bytes
|
||||
DoubleSha256Digest(hash)
|
||||
}
|
||||
|
||||
|
@ -34,15 +35,15 @@ trait CryptoUtil {
|
|||
def sha256(hex: String): Sha256Digest = sha256(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/** Takes sha256(bytes). */
|
||||
def sha256(bytes: Seq[Byte]): Sha256Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-256").digest(bytes.toArray).toList
|
||||
Sha256Digest(hash)
|
||||
def sha256(bytes: scodec.bits.ByteVector): Sha256Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-256").digest(bytes.toArray)
|
||||
Sha256Digest(ByteVector(hash))
|
||||
}
|
||||
|
||||
/** Performs SHA1(bytes). */
|
||||
def sha1(bytes: Seq[Byte]): Sha1Digest = {
|
||||
def sha1(bytes: scodec.bits.ByteVector): Sha1Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-1").digest(bytes.toArray).toList
|
||||
Sha1Digest(hash)
|
||||
Sha1Digest(ByteVector(hash))
|
||||
}
|
||||
|
||||
/** Performs SHA1(hex). */
|
||||
|
@ -52,25 +53,25 @@ trait CryptoUtil {
|
|||
def ripeMd160(hex: String): RipeMd160Digest = ripeMd160(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/** Performs RIPEMD160(bytes). */
|
||||
def ripeMd160(bytes: Seq[Byte]): RipeMd160Digest = {
|
||||
def ripeMd160(bytes: scodec.bits.ByteVector): RipeMd160Digest = {
|
||||
//from this tutorial http://rosettacode.org/wiki/RIPEMD-160#Scala
|
||||
val messageDigest = new RIPEMD160Digest
|
||||
val raw = bytes.toArray
|
||||
messageDigest.update(raw, 0, raw.length)
|
||||
val out = Array.fill[Byte](messageDigest.getDigestSize())(0)
|
||||
messageDigest.doFinal(out, 0)
|
||||
RipeMd160Digest(out.toList)
|
||||
RipeMd160Digest(ByteVector(out))
|
||||
}
|
||||
|
||||
val emptyDoubleSha256Hash = DoubleSha256Digest("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
def hmac512(key: Seq[Byte], data: Seq[Byte]): Seq[Byte] = {
|
||||
def hmac512(key: scodec.bits.ByteVector, data: scodec.bits.ByteVector): scodec.bits.ByteVector = {
|
||||
val hmac512 = new HMac(new SHA512Digest())
|
||||
hmac512.init(new KeyParameter(key.toArray))
|
||||
hmac512.update(data.toArray, 0, data.size)
|
||||
hmac512.update(data.toArray, 0, data.intSize.get)
|
||||
val output = new Array[Byte](64)
|
||||
hmac512.doFinal(output, 0)
|
||||
output
|
||||
ByteVector(output)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue