TxBuilder Refactor (#1426)

* Refactored Transaction

Created RawTxBuilder

Created RawTxFinalizer as layer on top of RawTxBuilder

Introduced RawTxSigner and replaced TxBuilder!

Deleted TxBuilder!

* fixed things after rebase

* Made RawTxBuilder compatible with older versions of scala

* Began responding to review

* Made Finalizer take a Builder rather than the other way around

* Added composition for finalizers

* Ran scalafmt

* Updated txbuilder example documentation

* Moved tests from old TxBuilderTest files to relevant new test files

* Added scaladocs
This commit is contained in:
Nadav Kohen 2020-05-21 09:47:08 -05:00 committed by GitHub
parent f8c60cc72f
commit dfd3353cc4
115 changed files with 2549 additions and 455 deletions

View file

@ -11,7 +11,8 @@ import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
import org.bitcoins.core.protocol.script.ScriptPubKey import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput} import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
import org.bitcoins.core.script.crypto._ import org.bitcoins.core.script.crypto._
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest, DoubleSha256DigestBE} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
import play.api.libs.json._ import play.api.libs.json._
import scala.collection.mutable import scala.collection.mutable

View file

@ -99,13 +99,17 @@ trait MempoolRpc { self: Client =>
getMemPoolEntry(txid.flip) getMemPoolEntry(txid.flip)
} }
def getMemPoolEntryOpt(txid: DoubleSha256Digest): Future[Option[GetMemPoolEntryResult]] = { def getMemPoolEntryOpt(
txid: DoubleSha256Digest): Future[Option[GetMemPoolEntryResult]] = {
getMemPoolEntryOpt(txid.flip) getMemPoolEntryOpt(txid.flip)
} }
def getMemPoolEntryOpt(txid: DoubleSha256DigestBE): Future[Option[GetMemPoolEntryResult]] = { def getMemPoolEntryOpt(
getMemPoolEntry(txid).map(Some(_)) txid: DoubleSha256DigestBE): Future[Option[GetMemPoolEntryResult]] = {
.recover { case _: BitcoindException.InvalidAddressOrKey => getMemPoolEntry(txid)
.map(Some(_))
.recover {
case _: BitcoindException.InvalidAddressOrKey =>
None None
} }
} }

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.config package org.bitcoins.core.config
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -2,7 +2,8 @@ package org.bitcoins.core.consensus
import org.bitcoins.core.protocol.blockchain.{Block, MainNetChainParams} import org.bitcoins.core.protocol.blockchain.{Block, MainNetChainParams}
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction} import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
import org.bitcoins.crypto.{BytesUtil, CryptoUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{CryptoUtil, DoubleSha256Digest}
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scala.util.Try import scala.util.Try

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.crypto._ import org.bitcoins.core.script.crypto._
import org.bitcoins.core.serializers.script.ScriptParser import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util._ import org.bitcoins.core.util._
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.crypto.DoubleSha256Digest
import org.scalatest.{FlatSpec, MustMatchers} import org.scalatest.{FlatSpec, MustMatchers}
import scala.util.Try import scala.util.Try

View file

@ -5,7 +5,8 @@ import org.bitcoins.core.number.UInt32
import org.bitcoins.testkit.node.NodeTestUtil import org.bitcoins.testkit.node.NodeTestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.config.MainNet import org.bitcoins.core.config.MainNet
import org.bitcoins.crypto.{BytesUtil, CryptoUtil} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.CryptoUtil
import scala.util.Random import scala.util.Random
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.protocol
import org.bitcoins.core.number.UInt64 import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.script.ScriptSignature import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -10,7 +10,7 @@ import org.bitcoins.core.protocol.transaction.{
TransactionConstants, TransactionConstants,
TransactionOutput TransactionOutput
} }
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -3,7 +3,8 @@ package org.bitcoins.core.protocol.blockchain
import org.bitcoins.core.bloom._ import org.bitcoins.core.bloom._
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.TransactionOutPoint import org.bitcoins.core.protocol.transaction.TransactionOutPoint
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest, ECPublicKey} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKey}
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.protocol.blockchain
import org.bitcoins.core.bloom.{BloomFilter, BloomUpdateAll} import org.bitcoins.core.bloom.{BloomFilter, BloomUpdateAll}
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.util.{Leaf, Node} import org.bitcoins.core.util.{BytesUtil, Leaf, Node}
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.BitVector import scodec.bits.BitVector

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.protocol.script package org.bitcoins.core.protocol.script
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/** /**

View file

@ -1,6 +1,7 @@
package org.bitcoins.core.protocol.script package org.bitcoins.core.protocol.script
import org.bitcoins.crypto.{BytesUtil, ECDigitalSignature, ECPublicKey} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -7,14 +7,15 @@ import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCase import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCase
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
BaseTransaction, BaseTransaction,
NonWitnessTransaction,
Transaction, Transaction,
TransactionOutput, TransactionOutput,
WitnessTransaction WitnessTransaction
} }
import org.bitcoins.core.script.crypto.{HashType, SIGHASH_ALL} import org.bitcoins.core.script.crypto.{HashType, SIGHASH_ALL}
import org.bitcoins.core.serializers.script.RawScriptSignatureParser import org.bitcoins.core.serializers.script.RawScriptSignatureParser
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest, ECDigitalSignature} import org.bitcoins.crypto.{DoubleSha256Digest, ECDigitalSignature}
import org.bitcoins.testkit.util.TestUtil import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers} import org.scalatest.{FlatSpec, MustMatchers}
import scodec.bits.ByteVector import scodec.bits.ByteVector
@ -147,7 +148,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
Transaction(testCase.transaction.hex) must be(testCase.transaction) Transaction(testCase.transaction.hex) must be(testCase.transaction)
val output = TransactionOutput(CurrencyUnits.zero, testCase.script) val output = TransactionOutput(CurrencyUnits.zero, testCase.script)
val txSigComponent = testCase.transaction match { val txSigComponent = testCase.transaction match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTxSigComponent(btx, BaseTxSigComponent(btx,
testCase.inputIndex, testCase.inputIndex,
output, output,

View file

@ -13,7 +13,6 @@ import org.bitcoins.core.protocol.transaction.testprotocol.CoreTransactionTestCa
import org.bitcoins.core.script.PreExecutionScriptProgram import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.serializers.transaction.RawBaseTransactionParser
import org.bitcoins.crypto.CryptoUtil import org.bitcoins.crypto.CryptoUtil
import org.bitcoins.testkit.core.gen.TransactionGenerators import org.bitcoins.testkit.core.gen.TransactionGenerators
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
@ -55,7 +54,7 @@ class TransactionTest extends BitcoinSUnitTest {
it must "derive the correct txid from the transaction contents" in { it must "derive the correct txid from the transaction contents" in {
//https://btc.blockr.io/api/v1/tx/raw/cddda897b0e9322937ee1f4fd5d6147d60f04a0f4d3b461e4f87066ac3918f2a //https://btc.blockr.io/api/v1/tx/raw/cddda897b0e9322937ee1f4fd5d6147d60f04a0f4d3b461e4f87066ac3918f2a
val tx = RawBaseTransactionParser.read( val tx = BaseTransaction(
"01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000") "01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000")
tx.txId.flip.bytes must be( tx.txId.flip.bytes must be(
@ -195,7 +194,7 @@ class TransactionTest extends BitcoinSUnitTest {
scriptPubKey match { scriptPubKey match {
case p2sh: P2SHScriptPubKey => case p2sh: P2SHScriptPubKey =>
tx match { tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTxSigComponent(transaction = btx, BaseTxSigComponent(transaction = btx,
inputIndex = UInt32(inputIndex), inputIndex = UInt32(inputIndex),
output = TransactionOutput(amount, p2sh), output = TransactionOutput(amount, p2sh),
@ -209,7 +208,7 @@ class TransactionTest extends BitcoinSUnitTest {
} }
case wit: WitnessScriptPubKey => case wit: WitnessScriptPubKey =>
tx match { tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTxSigComponent(transaction = btx, BaseTxSigComponent(transaction = btx,
inputIndex = UInt32(inputIndex), inputIndex = UInt32(inputIndex),
output = TransactionOutput(amount, wit), output = TransactionOutput(amount, wit),
@ -275,7 +274,7 @@ class TransactionTest extends BitcoinSUnitTest {
scriptPubKey match { scriptPubKey match {
case p2sh: P2SHScriptPubKey => case p2sh: P2SHScriptPubKey =>
tx match { tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTxSigComponent( BaseTxSigComponent(
transaction = btx, transaction = btx,
inputIndex = UInt32(inputIndex), inputIndex = UInt32(inputIndex),
@ -290,7 +289,7 @@ class TransactionTest extends BitcoinSUnitTest {
} }
case wit: WitnessScriptPubKey => case wit: WitnessScriptPubKey =>
tx match { tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTxSigComponent( BaseTxSigComponent(
transaction = btx, transaction = btx,
inputIndex = UInt32(inputIndex), inputIndex = UInt32(inputIndex),

View file

@ -0,0 +1,73 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.currency._
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{
EmptyScriptPubKey,
EmptyScriptSignature,
NonStandardScriptPubKey
}
import org.bitcoins.core.script.control.OP_RETURN
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.EmptyInputInfo
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.testkit.util.BitcoinSUnitTest
class TxUtilTest extends BitcoinSUnitTest {
behavior of "TxUtil"
private val outPoint =
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero)
private val input =
TransactionInput(outPoint, EmptyScriptSignature, UInt32.zero)
private val output = TransactionOutput(Bitcoins.one, EmptyScriptPubKey)
private val tx = BaseTransaction(TransactionConstants.validLockVersion,
Vector(input),
Vector(output),
UInt32.zero)
private val inputInfos = Vector(
EmptyInputInfo(outPoint, Bitcoins.one + Bitcoins.one))
private val feeRate = SatoshisPerVirtualByte(Satoshis.one)
it should "detect a bad fee on the tx" in {
val estimatedFee = 1000.sats
val actualFee = 1.sat
val feeRate = SatoshisPerVirtualByte(1.sat)
TxUtil
.isValidFeeRange(estimatedFee, actualFee, feeRate)
.isFailure must be(true)
TxUtil
.isValidFeeRange(actualFee, estimatedFee, feeRate)
.isFailure must be(true)
}
it should "detect impossibly high fees" in {
val newOutput = TransactionOutput(Bitcoins.zero, EmptyScriptPubKey)
val highFeeTx =
BaseTransaction(tx.version, tx.inputs, Vector(newOutput), tx.lockTime)
assert(
TxUtil
.sanityAmountChecks(isSigned = true, inputInfos, feeRate, highFeeTx)
.isFailure)
}
it should "detect dust outputs" in {
val newOutput = TransactionOutput(Satoshis(999), EmptyScriptPubKey)
val ignoredOutput =
TransactionOutput(Bitcoins.one,
NonStandardScriptPubKey(Vector(OP_RETURN)))
val dustTx = BaseTransaction(tx.version,
tx.inputs,
Vector(ignoredOutput, newOutput),
tx.lockTime)
assert(
TxUtil
.sanityAmountChecks(isSigned = true, inputInfos, feeRate, dustTx)
.isFailure)
}
}

View file

@ -56,7 +56,7 @@ class PSBTTest extends BitcoinSAsyncTest {
it must "correctly update PSBTs' inputs" in { it must "correctly update PSBTs' inputs" in {
forAllAsync(PSBTGenerators.psbtToBeSigned)(_.flatMap { forAllAsync(PSBTGenerators.psbtToBeSigned)(_.flatMap {
case (fullPsbt, utxos) => case (fullPsbt, utxos, _) =>
val emptyPsbt = PSBT.fromUnsignedTx(fullPsbt.transaction) val emptyPsbt = PSBT.fromUnsignedTx(fullPsbt.transaction)
val infoAndTxs = PSBTGenerators val infoAndTxs = PSBTGenerators
@ -93,7 +93,7 @@ class PSBTTest extends BitcoinSAsyncTest {
it must "correctly construct and sign a PSBT" in { it must "correctly construct and sign a PSBT" in {
forAllAsync(PSBTGenerators.psbtToBeSigned) { psbtWithBuilderF => forAllAsync(PSBTGenerators.psbtToBeSigned) { psbtWithBuilderF =>
psbtWithBuilderF.flatMap { psbtWithBuilderF.flatMap {
case (psbtNoSigs, utxos) => case (psbtNoSigs, utxos, _) =>
val infos = utxos.toVector.zipWithIndex.map { val infos = utxos.toVector.zipWithIndex.map {
case (utxo: ScriptSignatureParams[InputInfo], index) => case (utxo: ScriptSignatureParams[InputInfo], index) =>
(index, utxo) (index, utxo)
@ -173,19 +173,17 @@ class PSBTTest extends BitcoinSAsyncTest {
val maxFee = crediting - spending val maxFee = crediting - spending
val fee = GenUtil.sample(CurrencyUnitGenerator.feeUnit(maxFee)) val fee = GenUtil.sample(CurrencyUnitGenerator.feeUnit(maxFee))
for { for {
(psbt, _) <- PSBTGenerators.psbtAndBuilderFromInputs( (psbt, _, _) <- PSBTGenerators.psbtAndBuilderFromInputs(
finalized = false, finalized = false,
creditingTxsInfo = creditingTxsInfo, creditingTxsInfo = creditingTxsInfo,
destinations = destinations, destinations = destinations,
changeSPK = changeSPK, changeSPK = changeSPK,
network = network,
fee = fee) fee = fee)
(expected, _) <- PSBTGenerators.psbtAndBuilderFromInputs( (expected, _, _) <- PSBTGenerators.psbtAndBuilderFromInputs(
finalized = true, finalized = true,
creditingTxsInfo = creditingTxsInfo, creditingTxsInfo = creditingTxsInfo,
destinations = destinations, destinations = destinations,
changeSPK = changeSPK, changeSPK = changeSPK,
network = network,
fee = fee) fee = fee)
} yield { } yield {
val finalizedPsbtOpt = psbt.finalizePSBT val finalizedPsbtOpt = psbt.finalizePSBT
@ -198,8 +196,8 @@ class PSBTTest extends BitcoinSAsyncTest {
it must "agree with TxBuilder.sign given UTXOSpendingInfos" in { it must "agree with TxBuilder.sign given UTXOSpendingInfos" in {
forAllAsync(PSBTGenerators.finalizedPSBTWithBuilder) { psbtAndBuilderF => forAllAsync(PSBTGenerators.finalizedPSBTWithBuilder) { psbtAndBuilderF =>
for { for {
(psbt, builder) <- psbtAndBuilderF (psbt, builder, fee) <- psbtAndBuilderF
signedTx <- builder.sign signedTx <- builder.sign(fee)
} yield { } yield {
val txT = psbt.extractTransactionAndValidate val txT = psbt.extractTransactionAndValidate
assert(txT.isSuccess, txT.failed) assert(txT.isSuccess, txT.failed)

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.script package org.bitcoins.core.script
import org.bitcoins.core.script.constant.ScriptConstant import org.bitcoins.core.script.constant.ScriptConstant
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.script.crypto.OP_RIPEMD160
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.splice.OP_SUBSTR import org.bitcoins.core.script.splice.OP_SUBSTR
import org.bitcoins.core.script.stack.OP_TOALTSTACK import org.bitcoins.core.script.stack.OP_TOALTSTACK
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.script.constant package org.bitcoins.core.script.constant
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.script.constant package org.bitcoins.core.script.constant
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -3,8 +3,7 @@ package org.bitcoins.core.script.stack
import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result._ import org.bitcoins.core.script.result._
import org.bitcoins.core.script.ExecutedScriptProgram import org.bitcoins.core.script.ExecutedScriptProgram
import org.bitcoins.core.util.ScriptProgramTestUtil import org.bitcoins.core.util.{BytesUtil, ScriptProgramTestUtil}
import org.bitcoins.crypto.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/** /**

View file

@ -1,7 +1,8 @@
package org.bitcoins.core.serializers.blockchain package org.bitcoins.core.serializers.blockchain
import org.bitcoins.core.number.{Int32, UInt32} import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -3,7 +3,8 @@ package org.bitcoins.core.serializers.blockchain
import org.bitcoins.core.number.{Int32, UInt32, UInt64} import org.bitcoins.core.number.{Int32, UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.transaction.Transaction import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -6,8 +6,8 @@ import org.bitcoins.core.protocol.blockchain.{
MerkleBlock, MerkleBlock,
PartialMerkleTree PartialMerkleTree
} }
import org.bitcoins.core.util.{Leaf, Node} import org.bitcoins.core.util.{BytesUtil, Leaf, Node}
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.BitVector import scodec.bits.BitVector

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.serializers.p2p.messages package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
class RawFilterAddMessageSerializerTest extends BitcoinSUnitTest { class RawFilterAddMessageSerializerTest extends BitcoinSUnitTest {

View file

@ -3,7 +3,7 @@ package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.bloom.BloomUpdateNone import org.bitcoins.core.bloom.BloomUpdateNone
import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -3,7 +3,8 @@ package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.number.UInt64 import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.p2p._ import org.bitcoins.core.p2p._
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -1,7 +1,8 @@
package org.bitcoins.core.serializers.p2p.messages package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.p2p.TypeIdentifier.MsgTx import org.bitcoins.core.p2p.TypeIdentifier.MsgTx
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -2,7 +2,8 @@ package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.BitVector import scodec.bits.BitVector

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.number.UInt64 import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
/** /**

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.serializers.p2p.messages package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.node.NodeTestUtil import org.bitcoins.testkit.node.NodeTestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest

View file

@ -5,7 +5,7 @@ import java.net.InetSocketAddress
import org.bitcoins.core.number.{Int32, Int64, UInt64} import org.bitcoins.core.number.{Int32, Int64, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.p2p._ import org.bitcoins.core.p2p._
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
class RawVersionMessageSerializerTest extends BitcoinSUnitTest { class RawVersionMessageSerializerTest extends BitcoinSUnitTest {

View file

@ -5,7 +5,7 @@ import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160} import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.stack.OP_DUP import org.bitcoins.core.script.stack.OP_DUP
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptSignature} import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptSignature}
import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.constant._
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -13,7 +13,7 @@ import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.reserved.OP_NOP import org.bitcoins.core.script.reserved.OP_NOP
import org.bitcoins.core.script.splice.OP_SIZE import org.bitcoins.core.script.splice.OP_SIZE
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP, OP_PICK, OP_SWAP} import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP, OP_PICK, OP_SWAP}
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -2,10 +2,11 @@ package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.{Int32, UInt32} import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
Transaction, Transaction,
TransactionConstants TransactionConstants
} }
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil} import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector import scodec.bits.ByteVector
@ -15,7 +16,7 @@ import scodec.bits.ByteVector
class RawBaseTransactionParserTest extends BitcoinSUnitTest { class RawBaseTransactionParserTest extends BitcoinSUnitTest {
val encode = BytesUtil.encodeHex(_: ByteVector) val encode = BytesUtil.encodeHex(_: ByteVector)
"RawBaseTransactionParser" must "parse a raw transaction" in { "RawBaseTransactionParser" must "parse a raw transaction" in {
val tx: Transaction = RawBaseTransactionParser.read(TestUtil.rawTransaction) val tx: Transaction = BaseTransaction(TestUtil.rawTransaction)
tx.version must be(Int32.one) tx.version must be(Int32.one)
tx.inputs.size must be(2) tx.inputs.size must be(2)
tx.outputs.size must be(2) tx.outputs.size must be(2)
@ -29,7 +30,7 @@ class RawBaseTransactionParserTest extends BitcoinSUnitTest {
//txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74 //txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawTx = val rawTx =
"0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655" "0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655"
val tx: Transaction = RawBaseTransactionParser.read(rawTx) val tx: Transaction = BaseTransaction(rawTx)
tx.txId.hex must be( tx.txId.hex must be(
BytesUtil.flipEndianness( BytesUtil.flipEndianness(
"bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74")) "bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74"))
@ -40,8 +41,8 @@ class RawBaseTransactionParserTest extends BitcoinSUnitTest {
//txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74 //txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawTxWithLockTime = val rawTxWithLockTime =
"0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655" "0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655"
val tx = RawBaseTransactionParser.read(rawTxWithLockTime) val tx = BaseTransaction(rawTxWithLockTime)
val serializedTx = RawBaseTransactionParser.write(tx) val serializedTx = tx.bytes
encode(serializedTx) must be(rawTxWithLockTime) encode(serializedTx) must be(rawTxWithLockTime)
} }
@ -50,20 +51,20 @@ class RawBaseTransactionParserTest extends BitcoinSUnitTest {
val rawTx = val rawTx =
"01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000" "01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000"
val tx = RawBaseTransactionParser.read(rawTx) val tx = BaseTransaction(rawTx)
val serializedTx = RawBaseTransactionParser.write(tx) val serializedTx = tx.bytes
encode(serializedTx) must be(rawTx) encode(serializedTx) must be(rawTx)
} }
it must "read then write a simple raw transaction with one input and two outputs" in { it must "read then write a simple raw transaction with one input and two outputs" in {
val rawTx = TestUtil.simpleRawTransaction val rawTx = TestUtil.simpleRawTransaction
val tx = RawBaseTransactionParser.read(rawTx) val tx = BaseTransaction(rawTx)
val serializedTx = RawBaseTransactionParser.write(tx) val serializedTx = tx.bytes
encode(serializedTx) must be(rawTx) encode(serializedTx) must be(rawTx)
} }
it must "parse a transaction with one input and two outputs" in { it must "parse a transaction with one input and two outputs" in {
val tx = RawBaseTransactionParser.read(TestUtil.parentSimpleRawTransaction) val tx = BaseTransaction(TestUtil.parentSimpleRawTransaction)
tx.inputs.size must be(1) tx.inputs.size must be(1)
tx.inputs.head.scriptSignature.hex must be( tx.inputs.head.scriptSignature.hex must be(
"6a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b") "6a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b")

View file

@ -5,8 +5,7 @@ import org.bitcoins.core.protocol.transaction.{
TransactionConstants, TransactionConstants,
TransactionInput TransactionInput
} }
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import org.bitcoins.crypto.BytesUtil
import org.bitcoins.testkit.util.TestUtil import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers} import org.scalatest.{FlatSpec, MustMatchers}
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.serializers.transaction package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -9,7 +9,7 @@ import org.bitcoins.core.protocol.transaction.{
import org.bitcoins.core.script.bitwise.OP_EQUAL import org.bitcoins.core.script.bitwise.OP_EQUAL
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant} import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant}
import org.bitcoins.core.script.crypto.OP_HASH160 import org.bitcoins.core.script.crypto.OP_HASH160
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.ByteVector import scodec.bits.ByteVector

View file

@ -1,6 +1,7 @@
package org.bitcoins.core.serializers.transaction package org.bitcoins.core.serializers.transaction
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
class RawWitnessTransactionParserTest extends BitcoinSUnitTest { class RawWitnessTransactionParserTest extends BitcoinSUnitTest {
@ -8,8 +9,8 @@ class RawWitnessTransactionParserTest extends BitcoinSUnitTest {
"RawWitnessTransactionParser" must "serialize and deserialize a wtx" in { "RawWitnessTransactionParser" must "serialize and deserialize a wtx" in {
val hex = val hex =
"0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f85603000000171600141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b928ffffffff019caef505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02483045022100f764287d3e99b1474da9bec7f7ed236d6c81e793b20c4b5aa1f3051b9a7daa63022016a198031d5554dbb855bdbe8534776a4be6958bd8d530dc001c32b828f6f0ab0121038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac00000000" "0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f85603000000171600141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b928ffffffff019caef505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02483045022100f764287d3e99b1474da9bec7f7ed236d6c81e793b20c4b5aa1f3051b9a7daa63022016a198031d5554dbb855bdbe8534776a4be6958bd8d530dc001c32b828f6f0ab0121038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac00000000"
val wtx = RawWitnessTransactionParser.read(hex) val wtx = WitnessTransaction(hex)
val bytes = RawWitnessTransactionParser.write(wtx) val bytes = wtx.bytes
BytesUtil.encodeHex(bytes) must be(hex) BytesUtil.encodeHex(bytes) must be(hex)
} }
} }

View file

@ -1,7 +1,6 @@
package org.bitcoins.core.util package org.bitcoins.core.util
import org.bitcoins.core.util.testprotocol._ import org.bitcoins.core.util.testprotocol._
import org.bitcoins.crypto.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import spray.json._ import spray.json._

View file

@ -1,6 +1,5 @@
package org.bitcoins.core.util package org.bitcoins.core.util
import org.bitcoins.crypto.BytesUtil
import org.bitcoins.testkit.core.gen.StringGenerators import org.bitcoins.testkit.core.gen.StringGenerators
import org.scalacheck.{Prop, Properties} import org.scalacheck.{Prop, Properties}

View file

@ -1,6 +1,5 @@
package org.bitcoins.core.util package org.bitcoins.core.util
import org.bitcoins.crypto.BytesUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits.BitVector import scodec.bits.BitVector

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.util package org.bitcoins.core.util
import org.bitcoins.crypto.{BytesUtil, CryptoUtil} import org.bitcoins.crypto.CryptoUtil
import org.bitcoins.testkit.core.gen.CryptoGenerators import org.bitcoins.testkit.core.gen.CryptoGenerators
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits._ import scodec.bits._

View file

@ -2,7 +2,6 @@ package org.bitcoins.core.util
import org.bitcoins.testkit.core.gen.NumberGenerator import org.bitcoins.testkit.core.gen.NumberGenerator
import org.bitcoins.core.number.UInt8 import org.bitcoins.core.number.UInt8
import org.bitcoins.crypto.BytesUtil
import org.scalacheck.{Prop, Properties} import org.scalacheck.{Prop, Properties}
/** /**

View file

@ -0,0 +1,195 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{
EmptyScriptPubKey,
EmptyScriptSignature,
P2PKHScriptPubKey,
P2WPKHWitnessSPKV0,
P2WPKHWitnessV0
}
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
TransactionConstants,
TransactionInput,
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.{
ConditionalPath,
InputInfo,
ScriptSignatureParams,
UnassignedSegwitNativeInputInfo
}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey, ECPublicKey}
import org.bitcoins.testkit.Implicits._
import org.bitcoins.testkit.core.gen.ScriptGenerators
import org.bitcoins.testkit.util.BitcoinSAsyncTest
class NonInteractiveWithChangeFinalizerTest extends BitcoinSAsyncTest {
behavior of "NonInteractiveWithChangeFinalizerTest"
private val (spk, privKey) = ScriptGenerators.p2pkhScriptPubKey.sampleSome
private val outPoint =
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero)
private val input =
TransactionInput(outPoint, EmptyScriptSignature, UInt32.zero)
private val output = TransactionOutput(Bitcoins.one, EmptyScriptPubKey)
private val tx = BaseTransaction(TransactionConstants.validLockVersion,
Vector(input),
Vector(output),
UInt32.zero)
private val changeSPK = P2PKHScriptPubKey(ECPublicKey.freshPublicKey)
it should "detect a missing destination" in {
val missingOutputTx = BaseTransaction(tx.version,
tx.inputs,
Vector.empty[TransactionOutput],
tx.lockTime)
assert(
NonInteractiveWithChangeFinalizer
.sanityDestinationChecks(Vector(outPoint),
Vector(output),
changeSPK,
missingOutputTx)
.isFailure)
}
it should "detect extra outputs added" in {
val newOutput = TransactionOutput(Bitcoins.max, EmptyScriptPubKey)
val extraOutputTx = BaseTransaction(tx.version,
tx.inputs,
Vector(output, newOutput),
tx.lockTime)
assert(
NonInteractiveWithChangeFinalizer
.sanityDestinationChecks(Vector(outPoint),
Vector(output),
changeSPK,
extraOutputTx)
.isFailure)
}
it should "detect extra outpoints added" in {
val newOutPoint =
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.one)
val newInput =
TransactionInput(newOutPoint, EmptyScriptSignature, UInt32.zero)
val extraOutPointTx = BaseTransaction(tx.version,
Vector(input, newInput),
tx.outputs,
tx.lockTime)
assert(
NonInteractiveWithChangeFinalizer
.sanityDestinationChecks(Vector(outPoint),
Vector(output),
changeSPK,
extraOutPointTx)
.isFailure)
}
it should "failed to build a transaction that mints money out of thin air" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = None,
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
signer = privKey,
hashType = HashType.sigHashAll
)
val utxos = Vector(utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
recoverToSucceededIf[IllegalArgumentException] {
NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = utxos,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey)
}
}
it should "fail to build a transaction when we pass in a negative fee rate" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = None,
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
signer = privKey,
hashType = HashType.sigHashAll
)
val utxos = Vector(utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(-1))
recoverToSucceededIf[IllegalArgumentException] {
NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = utxos,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey)
}
}
it should "fail to construct a tx given an UnassignedSegwitNativeUTXOSpendingInfo" in {
val outPoint = TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero)
val privKey = ECPrivateKey.freshPrivateKey
val pubKey = privKey.publicKey
val spendingInfo =
ScriptSignatureParams(
UnassignedSegwitNativeInputInfo(
outPoint = outPoint,
amount = Bitcoins.one + CurrencyUnits.oneMBTC,
scriptPubKey = P2WPKHWitnessSPKV0(pubKey),
scriptWitness = P2WPKHWitnessV0(pubKey),
conditionalPath = ConditionalPath.NoCondition,
Vector(pubKey)
),
signers = Vector(privKey),
hashType = HashType.sigHashAll
)
recoverToSucceededIf[UnsupportedOperationException] {
NonInteractiveWithChangeFinalizer.txFrom(
Vector(TransactionOutput(Bitcoins.one, EmptyScriptPubKey)),
Vector(spendingInfo),
SatoshisPerVirtualByte(Satoshis.one),
EmptyScriptPubKey
)
}
}
}

View file

@ -0,0 +1,395 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.crypto.{
BaseTxSigComponent,
WitnessTxSigComponentP2SH,
WitnessTxSigComponentRaw
}
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.{
CLTVScriptPubKey,
CSVScriptPubKey,
ConditionalScriptPubKey,
EmptyScriptPubKey,
MultiSignatureScriptPubKey,
NonStandardScriptPubKey,
P2PKHScriptPubKey,
P2PKScriptPubKey,
P2PKWithTimeoutScriptPubKey,
P2SHScriptPubKey,
P2SHScriptSignature,
P2WSHWitnessV0,
UnassignedWitnessScriptPubKey,
WitnessCommitment,
WitnessScriptPubKey,
WitnessScriptPubKeyV0
}
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
Transaction,
TransactionConstants,
TransactionInput,
TransactionOutPoint,
TransactionOutput,
WitnessTransaction
}
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerVirtualByte}
import org.bitcoins.core.wallet.utxo.{
ConditionalPath,
InputInfo,
InputSigningInfo,
LockTimeInputInfo,
ScriptSignatureParams
}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
import org.bitcoins.testkit.Implicits._
import org.bitcoins.testkit.core.gen.{CreditingTxGen, ScriptGenerators}
import org.bitcoins.testkit.util.BitcoinSAsyncTest
class RawTxSignerTest extends BitcoinSAsyncTest {
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
generatorDrivenConfigNewCode
behavior of "RawTxSigner"
private val (spk, privKey) = ScriptGenerators.p2pkhScriptPubKey.sampleSome
it should "fail a transaction when the user invariants fail" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(TransactionConstants.validLockVersion,
Nil,
Vector(creditingOutput),
TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo =
ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = None,
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
val utxos = Vector(utxo)
val feeUnit = SatoshisPerVirtualByte(currencyUnit = Satoshis(1))
val utxF = NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = utxos,
feeRate = feeUnit,
changeSPK =
EmptyScriptPubKey)
//trivially false
val f = (_: Seq[ScriptSignatureParams[InputInfo]], _: Transaction) => false
recoverToSucceededIf[IllegalArgumentException] {
utxF.flatMap(utx => RawTxSigner.sign(utx, utxos, feeUnit, f))
}
}
it should "fail to sign a p2pkh if we don't pass in the public key" in {
val p2pkh = P2PKHScriptPubKey(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(TransactionConstants.validLockVersion,
Nil,
Vector(creditingOutput),
TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
val utxos = Vector(utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val utxF = NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = utxos,
feeRate = feeUnit,
changeSPK =
EmptyScriptPubKey)
recoverToSucceededIf[IllegalArgumentException] {
utxF.flatMap(utx => RawTxSigner.sign(utx, utxos, feeUnit))
}
}
it should "fail to sign a p2pkh if we pass in the wrong public key" in {
val p2pkh = P2PKHScriptPubKey(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, vout = UInt32.zero)
val utxo = ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
signer = privKey,
hashType = HashType.sigHashAll
)
val utxos = Vector(utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val utxF = NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = utxos,
feeRate = feeUnit,
changeSPK =
EmptyScriptPubKey)
recoverToSucceededIf[IllegalArgumentException] {
utxF.flatMap(utx => RawTxSigner.sign(utx, utxos, feeUnit))
}
}
it should "succeed to sign a cltv spk that uses a second-based locktime" in {
val fundingPrivKey = ECPrivateKey.freshPrivateKey
val lockTime = System.currentTimeMillis / 1000
val cltvSPK =
CLTVScriptPubKey(ScriptNumber(lockTime),
P2PKScriptPubKey(fundingPrivKey.publicKey))
val cltvSpendingInfo = ScriptSignatureParams(
LockTimeInputInfo(TransactionOutPoint(DoubleSha256DigestBE.empty,
UInt32.zero),
Bitcoins.one,
cltvSPK,
ConditionalPath.NoCondition),
Vector(fundingPrivKey),
HashType.sigHashAll
)
val utxos = Vector(cltvSpendingInfo)
val feeUnit = SatoshisPerByte(Satoshis.one)
val utxF =
NonInteractiveWithChangeFinalizer.txFrom(
outputs = Vector(
TransactionOutput(Bitcoins.one - CurrencyUnits.oneMBTC,
EmptyScriptPubKey)),
utxos = utxos,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey
)
utxF
.flatMap(utx => RawTxSigner.sign(utx, utxos, feeUnit))
.map(tx => assert(tx.lockTime == UInt32(lockTime)))
}
it should "succeed to sign a cltv spk that uses a block height locktime" in {
val fundingPrivKey = ECPrivateKey.freshPrivateKey
val lockTime = 1000
val cltvSPK =
CLTVScriptPubKey(ScriptNumber(lockTime),
P2PKScriptPubKey(fundingPrivKey.publicKey))
val cltvSpendingInfo = ScriptSignatureParams(
LockTimeInputInfo(TransactionOutPoint(DoubleSha256DigestBE.empty,
UInt32.zero),
Bitcoins.one,
cltvSPK,
ConditionalPath.NoCondition),
Vector(fundingPrivKey),
HashType.sigHashAll
)
val utxos = Vector(cltvSpendingInfo)
val feeUnit = SatoshisPerByte(Satoshis.one)
val utxF =
NonInteractiveWithChangeFinalizer.txFrom(
outputs = Vector(
TransactionOutput(Bitcoins.one - CurrencyUnits.oneMBTC,
EmptyScriptPubKey)),
utxos = utxos,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey
)
utxF
.flatMap(utx => RawTxSigner.sign(utx, utxos, feeUnit))
.map(tx => assert(tx.lockTime == UInt32(lockTime)))
}
it should "fail to sign a cltv spk that uses both a second-based and a block height locktime" in {
val fundingPrivKey1 = ECPrivateKey.freshPrivateKey
val fundingPrivKey2 = ECPrivateKey.freshPrivateKey
val lockTime1 = System.currentTimeMillis / 1000
val lockTime2 = 1000
val cltvSPK1 =
CLTVScriptPubKey(ScriptNumber(lockTime1),
P2PKScriptPubKey(fundingPrivKey1.publicKey))
val cltvSPK2 =
CLTVScriptPubKey(ScriptNumber(lockTime2),
P2PKScriptPubKey(fundingPrivKey2.publicKey))
val cltvSpendingInfo1 = ScriptSignatureParams(
LockTimeInputInfo(TransactionOutPoint(DoubleSha256DigestBE.empty,
UInt32.zero),
Bitcoins.one,
cltvSPK1,
ConditionalPath.NoCondition),
Vector(fundingPrivKey1),
HashType.sigHashAll
)
val cltvSpendingInfo2 = ScriptSignatureParams(
LockTimeInputInfo(TransactionOutPoint(DoubleSha256DigestBE.empty,
UInt32.one),
Bitcoins.one,
cltvSPK2,
ConditionalPath.NoCondition),
Vector(fundingPrivKey2),
HashType.sigHashAll
)
val utxos = Vector(cltvSpendingInfo1, cltvSpendingInfo2)
val feeRate = SatoshisPerByte(Satoshis.one)
val utxF =
NonInteractiveWithChangeFinalizer.txFrom(
Vector(
TransactionOutput(Bitcoins.one + Bitcoins.one - CurrencyUnits.oneMBTC,
EmptyScriptPubKey)),
utxos,
feeRate,
EmptyScriptPubKey
)
recoverToSucceededIf[IllegalArgumentException](
utxF.flatMap(utx => RawTxSigner.sign(utx, utxos, feeRate))
)
}
def verifyScript(
tx: Transaction,
utxos: Vector[InputSigningInfo[InputInfo]]): Boolean = {
val programs: Vector[PreExecutionScriptProgram] =
tx.inputs.zipWithIndex.toVector.map {
case (input: TransactionInput, idx: Int) =>
val outpoint = input.previousOutput
val creditingTx =
utxos.find(u => u.outPoint.txId == outpoint.txId).get
val output = creditingTx.output
val spk = output.scriptPubKey
val amount = output.value
val txSigComponent = spk match {
case witSPK: WitnessScriptPubKeyV0 =>
val o = TransactionOutput(amount, witSPK)
WitnessTxSigComponentRaw(tx.asInstanceOf[WitnessTransaction],
UInt32(idx),
o,
Policy.standardFlags)
case _: UnassignedWitnessScriptPubKey => ???
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
_: WitnessCommitment | _: CSVScriptPubKey |
_: CLTVScriptPubKey | _: ConditionalScriptPubKey |
_: NonStandardScriptPubKey | EmptyScriptPubKey) =>
val o = TransactionOutput(CurrencyUnits.zero, x)
BaseTxSigComponent(tx, UInt32(idx), o, Policy.standardFlags)
case _: P2SHScriptPubKey =>
val p2shScriptSig =
tx.inputs(idx).scriptSignature.asInstanceOf[P2SHScriptSignature]
p2shScriptSig.redeemScript match {
case _: WitnessScriptPubKey =>
WitnessTxSigComponentP2SH(
transaction = tx.asInstanceOf[WitnessTransaction],
inputIndex = UInt32(idx),
output = output,
flags = Policy.standardFlags)
case _ =>
BaseTxSigComponent(tx,
UInt32(idx),
output,
Policy.standardFlags)
}
}
PreExecutionScriptProgram(txSigComponent)
}
ScriptInterpreter.runAllVerify(programs)
}
it should "sign a mix of spks in a tx and then have it verified" in {
forAllAsync(CreditingTxGen.inputsAndOutputs(),
ScriptGenerators.scriptPubKey) {
case ((creditingTxsInfo, destinations), (changeSPK, _)) =>
val fee = SatoshisPerVirtualByte(Satoshis(1000))
val utxF =
NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = creditingTxsInfo,
feeRate = fee,
changeSPK = changeSPK)
val txF = utxF.flatMap(utx =>
RawTxSigner.sign(utx, creditingTxsInfo.toVector, fee))
txF.map { tx =>
assert(verifyScript(tx, creditingTxsInfo.toVector))
}
}
}
it should "sign a mix of p2sh/p2wsh in a tx and then have it verified" in {
forAllAsync(CreditingTxGen.inputsAndOutputs(CreditingTxGen.nestedOutputs),
ScriptGenerators.scriptPubKey) {
case ((creditingTxsInfo, destinations), (changeSPK, _)) =>
val fee = SatoshisPerByte(Satoshis(1000))
val utxF =
NonInteractiveWithChangeFinalizer.txFrom(outputs = destinations,
utxos = creditingTxsInfo,
feeRate = fee,
changeSPK = changeSPK)
val txF = utxF.flatMap(utx =>
RawTxSigner.sign(utx, creditingTxsInfo.toVector, fee))
txF.map { tx =>
assert(verifyScript(tx, creditingTxsInfo.toVector))
}
}
}
}

View file

@ -39,7 +39,10 @@ import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.psbt.PSBT import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.script.PreExecutionScriptProgram import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder import org.bitcoins.core.wallet.builder.{
NonInteractiveWithChangeFinalizer,
RawTxSigner
}
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.{ import org.bitcoins.core.wallet.utxo.{
ECSignatureParams, ECSignatureParams,
@ -52,7 +55,6 @@ import org.bitcoins.core.wallet.utxo.{
} }
import org.bitcoins.crypto.ECDigitalSignature import org.bitcoins.crypto.ECDigitalSignature
import org.bitcoins.testkit.core.gen.{ import org.bitcoins.testkit.core.gen.{
ChainParamsGenerator,
CreditingTxGen, CreditingTxGen,
GenUtil, GenUtil,
ScriptGenerators, ScriptGenerators,
@ -121,19 +123,19 @@ class SignerTest extends BitcoinSAsyncTest {
it must "sign a mix of spks in a tx and then verify that single signing agrees" in { it must "sign a mix of spks in a tx and then verify that single signing agrees" in {
forAllAsync(CreditingTxGen.inputsAndOutputs(), forAllAsync(CreditingTxGen.inputsAndOutputs(),
ScriptGenerators.scriptPubKey, ScriptGenerators.scriptPubKey) {
ChainParamsGenerator.bitcoinNetworkParams) { case ((creditingTxsInfos, destinations), (changeSPK, _)) =>
case ((creditingTxsInfos, destinations), changeSPK, network) =>
val fee = SatoshisPerVirtualByte(Satoshis(1000)) val fee = SatoshisPerVirtualByte(Satoshis(1000))
for { for {
builder <- BitcoinTxBuilder(destinations, unsignedTx <- NonInteractiveWithChangeFinalizer.txFrom(
destinations,
creditingTxsInfos, creditingTxsInfos,
fee, fee,
changeSPK._1, changeSPK)
network) signedTx <- RawTxSigner.sign(unsignedTx,
unsignedTx <- builder.unsignedTx creditingTxsInfos.toVector,
signedTx <- builder.sign fee)
singleSigs: Vector[Vector[ECDigitalSignature]] <- { singleSigs: Vector[Vector[ECDigitalSignature]] <- {
val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] = val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] =
@ -257,18 +259,16 @@ class SignerTest extends BitcoinSAsyncTest {
it must "sign p2wsh inputs correctly when provided no witness data" in { it must "sign p2wsh inputs correctly when provided no witness data" in {
forAllAsync(CreditingTxGen.inputsAndOutputs(CreditingTxGen.p2wshOutputs), forAllAsync(CreditingTxGen.inputsAndOutputs(CreditingTxGen.p2wshOutputs),
ScriptGenerators.scriptPubKey, ScriptGenerators.scriptPubKey) {
ChainParamsGenerator.bitcoinNetworkParams) { case ((creditingTxsInfos, destinations), (changeSPK, _)) =>
case ((creditingTxsInfos, destinations), changeSPK, network) =>
val fee = SatoshisPerVirtualByte(Satoshis(100)) val fee = SatoshisPerVirtualByte(Satoshis(100))
for { for {
builder <- BitcoinTxBuilder(destinations, unsignedTx <- NonInteractiveWithChangeFinalizer.txFrom(
destinations,
creditingTxsInfos, creditingTxsInfos,
fee, fee,
changeSPK._1, changeSPK)
network)
unsignedTx <- builder.unsignedTx
singleSigs: Vector[Vector[PartialSignature]] <- { singleSigs: Vector[Vector[PartialSignature]] <- {
val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] = val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] =

View file

@ -0,0 +1,175 @@
package org.bitcoins.core.wallet.utxo
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{
EmptyScriptPubKey,
P2SHScriptPubKey,
P2WPKHWitnessSPKV0,
P2WSHWitnessSPKV0,
P2WSHWitnessV0
}
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
TransactionConstants,
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.testkit.Implicits._
import org.bitcoins.testkit.core.gen.ScriptGenerators
import org.bitcoins.testkit.util.BitcoinSUnitTest
class InputSigningInfoTest extends BitcoinSUnitTest {
behavior of "InputSigningInfo"
private val (spk, privKey) = ScriptGenerators.p2pkhScriptPubKey.sampleSome
it should "fail to build a tx if you have the wrong redeemscript" in {
val p2sh = P2SHScriptPubKey(spk)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2sh)
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val inputInfo = InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = Some(EmptyScriptPubKey),
scriptWitnessOpt = None,
conditionalPath = ConditionalPath.NoCondition
)
assertThrows[RuntimeException] {
ScriptSignatureParams(
inputInfo = inputInfo,
signer = privKey,
hashType = HashType.sigHashAll
)
}
assertThrows[RuntimeException] {
ECSignatureParams(
inputInfo = inputInfo,
signer = privKey,
hashType = HashType.sigHashAll
)
}
}
it should "fail to build a tx if you have the wrong script witness" in {
val p2wsh = P2WSHWitnessSPKV0(spk)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wsh)
val creditingTx = BaseTransaction(TransactionConstants.validLockVersion,
Nil,
Vector(creditingOutput),
TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
}
assertThrows[IllegalArgumentException] {
ECSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
}
}
it should "fail to sign a p2wpkh if we don't pass in the public key" in {
val p2wpkh = P2WPKHWitnessSPKV0(pubKey = privKey.publicKey)
val creditingOutput =
TransactionOutput(value = CurrencyUnits.zero, scriptPubKey = p2wpkh)
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, vout = UInt32.zero)
val inputInfo = InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition
)
assertThrows[IllegalArgumentException] {
ScriptSignatureParams(
inputInfo = inputInfo,
signer = privKey,
hashType = HashType.sigHashAll
)
}
assertThrows[IllegalArgumentException] {
ECSignatureParams(
inputInfo = inputInfo,
signer = privKey,
hashType = HashType.sigHashAll
)
}
}
it should "fail to sign a p2wpkh if we pass in the wrong public key" in {
val p2wpkh = P2WPKHWitnessSPKV0(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wpkh)
val creditingTx = BaseTransaction(TransactionConstants.validLockVersion,
Nil,
Vector(creditingOutput),
TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
ScriptSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
}
assertThrows[IllegalArgumentException] {
ECSignatureParams(
InputInfo(
outPoint = outPoint,
output = creditingOutput,
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
conditionalPath = ConditionalPath.NoCondition,
hashPreImages = Vector(privKey.publicKey)
),
privKey,
HashType.sigHashAll
)
}
}
}

View file

@ -10,13 +10,12 @@ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken} import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.core.serializers.bloom.RawBloomFilterSerializer import org.bitcoins.core.serializers.bloom.RawBloomFilterSerializer
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import scodec.bits.{BitVector, ByteVector} import scodec.bits.{BitVector, ByteVector}
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.util.hashing.MurmurHash3 import scala.util.hashing.MurmurHash3
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BytesUtil,
CryptoUtil, CryptoUtil,
DoubleSha256Digest, DoubleSha256Digest,
ECPublicKey, ECPublicKey,

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.consensus
import org.bitcoins.core.protocol.blockchain.Block import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
BaseTransaction, NonWitnessTransaction,
Transaction, Transaction,
WitnessTransaction WitnessTransaction
} }
@ -95,7 +95,7 @@ trait Merkle extends BitcoinSLogger {
val coinbaseWTxId = DoubleSha256Digest.empty val coinbaseWTxId = DoubleSha256Digest.empty
val hashes = block.transactions.tail.map { val hashes = block.transactions.tail.map {
case wtx: WitnessTransaction => wtx.wTxId case wtx: WitnessTransaction => wtx.wTxId
case btx: BaseTransaction => btx.txId case btx: NonWitnessTransaction => btx.txId
} }
build(coinbaseWTxId +: hashes) build(coinbaseWTxId +: hashes)
} }

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.crypto package org.bitcoins.core.crypto
import org.bitcoins.core.config.{NetworkParameters, Networks} import org.bitcoins.core.config.{NetworkParameters, Networks}
import org.bitcoins.core.util.Base58 import org.bitcoins.core.util.{Base58, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, CryptoUtil, ECPrivateKey} import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}

View file

@ -7,7 +7,6 @@ import org.bitcoins.core.util._
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BaseECKey, BaseECKey,
BouncyCastleUtil, BouncyCastleUtil,
BytesUtil,
CryptoUtil, CryptoUtil,
ECDigitalSignature, ECDigitalSignature,
ECPrivateKey, ECPrivateKey,

View file

@ -8,13 +8,8 @@ import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil} import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.core.policy.Policy import org.bitcoins.core.policy.Policy
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil} import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{DERSignatureUtil, ECDigitalSignature, ECPublicKey}
BytesUtil,
DERSignatureUtil,
ECDigitalSignature,
ECPublicKey
}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -7,8 +7,8 @@ import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.ScriptToken import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.crypto._ import org.bitcoins.core.script.crypto._
import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil} import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, CryptoUtil, DoubleSha256Digest} import org.bitcoins.crypto.{CryptoUtil, DoubleSha256Digest}
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -53,7 +53,7 @@ object TxSigComponent {
output.scriptPubKey match { output.scriptPubKey match {
case _: WitnessScriptPubKey => case _: WitnessScriptPubKey =>
transaction match { transaction match {
case _: BaseTransaction => case _: NonWitnessTransaction =>
throw new IllegalArgumentException( throw new IllegalArgumentException(
s"Cannot spend from segwit output ($output) with a base transaction ($transaction)") s"Cannot spend from segwit output ($output) with a base transaction ($transaction)")
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
@ -64,7 +64,7 @@ object TxSigComponent {
if (WitnessScriptPubKey.isWitnessScriptPubKey( if (WitnessScriptPubKey.isWitnessScriptPubKey(
p2shScriptSig.redeemScript.asm)) { p2shScriptSig.redeemScript.asm)) {
transaction match { transaction match {
case _: BaseTransaction => case _: NonWitnessTransaction =>
throw new IllegalArgumentException( throw new IllegalArgumentException(
s"Cannot spend from segwit output ($output) with a base transaction ($transaction)") s"Cannot spend from segwit output ($output) with a base transaction ($transaction)")
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
@ -77,6 +77,19 @@ object TxSigComponent {
BaseTxSigComponent(transaction, inputIndex, output, flags) BaseTxSigComponent(transaction, inputIndex, output, flags)
} }
} }
def getScriptWitness(
txSigComponent: TxSigComponent): Option[ScriptWitnessV0] = {
txSigComponent.transaction match {
case _: NonWitnessTransaction => None
case wtx: WitnessTransaction =>
val witness = wtx.witness.witnesses(txSigComponent.inputIndex.toInt)
witness match {
case EmptyScriptWitness => None
case witness: ScriptWitnessV0 => Some(witness)
}
}
}
} }
/** /**

View file

@ -5,7 +5,8 @@ import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey} import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey}
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput} import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
import org.bitcoins.core.script.control.OP_RETURN import org.bitcoins.core.script.control.OP_RETURN
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import scodec.bits.ByteVector import scodec.bits.ByteVector
object BlockFilter { object BlockFilter {

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.number package org.bitcoins.core.number
import org.bitcoins.core.util.NumberUtil import org.bitcoins.core.util.{BytesUtil, NumberUtil}
import org.bitcoins.crypto.{BytesUtil, Factory, NetworkElement} import org.bitcoins.crypto.{Factory, NetworkElement}
import scodec.bits.{ByteOrdering, ByteVector} import scodec.bits.{ByteOrdering, ByteVector}
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}

View file

@ -10,9 +10,9 @@ import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, MerkleBlock}
import org.bitcoins.core.protocol.transaction.Transaction import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.serializers.p2p.messages._ import org.bitcoins.core.serializers.p2p.messages._
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerKiloByte} import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerKiloByte}
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BytesUtil,
DoubleSha256Digest, DoubleSha256Digest,
Factory, Factory,
HashDigest, HashDigest,

View file

@ -1,11 +1,11 @@
package org.bitcoins.core.protocol package org.bitcoins.core.protocol
import org.bitcoins.core.config.{MainNet, TestNet3, _} import org.bitcoins.core.config.{MainNet, TestNet3, _}
import org.bitcoins.core.number.{UInt5, UInt8} import org.bitcoins.core.number.{UInt5, UInt8}
import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.script._
import org.bitcoins.core.script.constant.ScriptConstant import org.bitcoins.core.script.constant.ScriptConstant
import org.bitcoins.core.util._ import org.bitcoins.core.util._
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BytesUtil,
CryptoUtil, CryptoUtil,
ECPublicKey, ECPublicKey,
HashDigest, HashDigest,

View file

@ -2,7 +2,8 @@ package org.bitcoins.core.protocol
import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.script.ScriptSignature import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.crypto.{BytesUtil, Factory, NetworkElement} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{Factory, NetworkElement}
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -9,7 +9,8 @@ import org.bitcoins.core.protocol.ln.fee.{
FeeBaseMSat, FeeBaseMSat,
FeeProportionalMillionths FeeProportionalMillionths
} }
import org.bitcoins.crypto.{BytesUtil, ECPublicKey, NetworkElement} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{ECPublicKey, NetworkElement}
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant.ScriptToken import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.util.BitcoinScriptUtil import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, Factory} import org.bitcoins.crypto.Factory
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -18,7 +18,6 @@ import org.bitcoins.core.script.reserved.UndefinedOP_NOP
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP} import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP}
import org.bitcoins.core.util._ import org.bitcoins.core.util._
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BytesUtil,
CryptoUtil, CryptoUtil,
DoubleSha256Digest, DoubleSha256Digest,
ECPublicKey, ECPublicKey,

View file

@ -4,7 +4,7 @@ import org.bitcoins.core.script.constant._
import org.bitcoins.core.serializers.script.ScriptParser import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util._ import org.bitcoins.core.util._
import org.bitcoins.core.wallet.utxo.ConditionalPath import org.bitcoins.core.wallet.utxo.ConditionalPath
import org.bitcoins.crypto.{BytesUtil, ECDigitalSignature, ECPublicKey} import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
@ -125,7 +125,8 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
_: ScriptConstant, _: ScriptConstant,
_: BytesToPushOntoStack, _: BytesToPushOntoStack,
z: ScriptConstant) => z: ScriptConstant) =>
if ((z.bytes.length == 33 || z.bytes.length == 65) && ECPublicKey.isFullyValid(z.bytes)) true if ((z.bytes.length == 33 || z.bytes.length == 65) && ECPublicKey
.isFullyValid(z.bytes)) true
else !P2SHScriptSignature.isRedeemScript(z) else !P2SHScriptSignature.isRedeemScript(z)
case _ => false case _ => false
} }

View file

@ -2,9 +2,8 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.serializers.script.RawScriptWitnessParser import org.bitcoins.core.serializers.script.RawScriptWitnessParser
import org.bitcoins.core.util.BitcoinScriptUtil import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
BytesUtil,
ECDigitalSignature, ECDigitalSignature,
ECPublicKey, ECPublicKey,
EmptyDigitalSignature, EmptyDigitalSignature,

View file

@ -3,13 +3,8 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result._ import org.bitcoins.core.script.result._
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{CryptoUtil, Sha256Digest, Sha256Hash160Digest}
BytesUtil,
CryptoUtil,
Sha256Digest,
Sha256Hash160Digest
}
/** /**
* Created by chris on 11/10/16. * Created by chris on 11/10/16.

View file

@ -0,0 +1,122 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{
CLTVScriptPubKey,
CSVScriptPubKey,
EmptyScriptSignature
}
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.core.script.locktime.LockTimeInterpreter
import org.bitcoins.core.wallet.utxo.{
ConditionalInputInfo,
EmptyInputInfo,
InputInfo,
InputSigningInfo,
LockTimeInputInfo,
MultiSignatureInputInfo,
P2PKHInputInfo,
P2PKInputInfo,
P2PKWithTimeoutInputInfo,
P2SHInputInfo,
P2WPKHV0InputInfo,
P2WSHV0InputInfo,
UnassignedSegwitNativeInputInfo
}
import scala.annotation.tailrec
object InputUtil {
/**
* Returns a valid sequence number for the given [[ScriptNumber]]
* A transaction needs a valid sequence number to spend a OP_CHECKSEQUENCEVERIFY script.
* See BIP68/112 for more information
* [[https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki]]
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki]]
*/
private def solveSequenceForCSV(scriptNum: ScriptNumber): UInt32 =
if (LockTimeInterpreter.isCSVLockByBlockHeight(scriptNum)) {
val blocksPassed = scriptNum.toLong & TransactionConstants.sequenceLockTimeMask.toLong
UInt32(blocksPassed)
} else {
val n = scriptNum.toLong
val sequence = UInt32(
n & TransactionConstants.sequenceLockTimeMask.toLong)
//set sequence number to indicate this is relative locktime
sequence | TransactionConstants.sequenceLockTimeTypeFlag
}
/**
* This helper function calculates the appropriate sequence number for each transaction input.
* [[CLTVScriptPubKey]] and [[CSVScriptPubKey]]'s need certain sequence numbers on the inputs
* to make them spendable.
* See BIP68/112 and BIP65 for more info
*/
def calcSequenceForInputs(
utxos: Seq[InputSigningInfo[InputInfo]],
isRBFEnabled: Boolean): Seq[TransactionInput] = {
@tailrec
def loop(
remaining: Seq[InputSigningInfo[InputInfo]],
accum: Seq[TransactionInput]): Seq[TransactionInput] =
remaining match {
case Nil => accum.reverse
case spendingInfo +: newRemaining =>
spendingInfo.inputInfo match {
case lockTime: LockTimeInputInfo =>
val sequence = lockTime.scriptPubKey match {
case csv: CSVScriptPubKey => solveSequenceForCSV(csv.locktime)
case _: CLTVScriptPubKey => UInt32.zero
}
val input = TransactionInput(lockTime.outPoint,
EmptyScriptSignature,
sequence)
loop(newRemaining, input +: accum)
case p2pkWithTimeout: P2PKWithTimeoutInputInfo =>
if (p2pkWithTimeout.isBeforeTimeout) {
val sequence =
if (isRBFEnabled) UInt32.zero
else TransactionConstants.sequence
val input =
TransactionInput(spendingInfo.outPoint,
EmptyScriptSignature,
sequence)
loop(newRemaining, input +: accum)
} else {
val input = TransactionInput(p2pkWithTimeout.outPoint,
EmptyScriptSignature,
UInt32.zero)
loop(newRemaining, input +: accum)
}
case p2sh: P2SHInputInfo =>
val nestedSpendingInfo =
p2sh.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, accum)
case p2wsh: P2WSHV0InputInfo =>
val nestedSpendingInfo =
p2wsh.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, accum)
case conditional: ConditionalInputInfo =>
val nestedSpendingInfo =
conditional.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, accum)
case _: P2WPKHV0InputInfo | _: UnassignedSegwitNativeInputInfo |
_: P2PKInputInfo | _: P2PKHInputInfo |
_: MultiSignatureInputInfo | _: EmptyInputInfo =>
//none of these script types affect the sequence number of a tx
//the sequence only needs to be adjustd if we have replace by fee (RBF) enabled
//see BIP125 for more information
val sequence =
if (isRBFEnabled) UInt32.zero else TransactionConstants.sequence
val input =
TransactionInput(spendingInfo.outPoint,
EmptyScriptSignature,
sequence)
loop(newRemaining, input +: accum)
}
}
loop(utxos, Nil)
}
}

View file

@ -1,8 +1,9 @@
package org.bitcoins.core.protocol.transaction package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.{Int32, UInt32} import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script.ScriptWitness import org.bitcoins.core.protocol.script.{EmptyScriptWitness, ScriptWitness}
import org.bitcoins.core.serializers.transaction.{RawBaseTransactionParser, RawWitnessTransactionParser} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.core.wallet.builder.RawTxBuilder
import org.bitcoins.crypto._ import org.bitcoins.crypto._
import scodec.bits.ByteVector import scodec.bits.ByteVector
@ -64,7 +65,7 @@ sealed abstract class Transaction extends NetworkElement {
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]] * [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
*/ */
def baseSize: Long = this match { def baseSize: Long = this match {
case btx: BaseTransaction => btx.byteSize case btx: NonWitnessTransaction => btx.byteSize
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime).baseSize BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime).baseSize
} }
@ -85,7 +86,7 @@ sealed abstract class Transaction extends NetworkElement {
def updateInput(idx: Int, i: TransactionInput): Transaction = { def updateInput(idx: Int, i: TransactionInput): Transaction = {
val updatedInputs = inputs.updated(idx, i) val updatedInputs = inputs.updated(idx, i)
this match { this match {
case _: BaseTransaction => case _: NonWitnessTransaction =>
BaseTransaction(version, updatedInputs, outputs, lockTime) BaseTransaction(version, updatedInputs, outputs, lockTime)
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
WitnessTransaction(version, WitnessTransaction(version,
@ -97,39 +98,101 @@ sealed abstract class Transaction extends NetworkElement {
} }
} }
sealed abstract class BaseTransaction extends Transaction { object Transaction extends Factory[Transaction] {
override def bytes = RawBaseTransactionParser.write(this) def newBuilder: RawTxBuilder = RawTxBuilder()
override def weight = byteSize * 4
override def fromBytes(bytes: ByteVector): Transaction = {
//see BIP141 for marker/flag bytes
//https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id
if (bytes(4) == WitnessTransaction.marker && bytes(5) == WitnessTransaction.flag) {
//this throw/catch is _still_ necessary for the case where we have unsigned base transactions
//with zero inputs and 1 output which is serialized as "0001" at bytes 4 and 5.
//these transactions will not have a script witness associated with them making them invalid
//witness transactions (you need to have a witness to be considered a witness tx)
//see: https://github.com/bitcoin-s/bitcoin-s/blob/01d89df1b7c6bc4b1594406d54d5e6019705c654/core-test/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionTest.scala#L88
try {
WitnessTransaction.fromBytes(bytes)
} catch {
case scala.util.control.NonFatal(_) =>
BaseTransaction.fromBytes(bytes)
}
} else {
BaseTransaction.fromBytes(bytes)
}
}
} }
case object EmptyTransaction extends BaseTransaction { sealed abstract class NonWitnessTransaction extends Transaction {
override def txId = DoubleSha256Digest.empty override def weight: Long = byteSize * 4
override def version = TransactionConstants.version
override def inputs = Nil override def bytes: ByteVector = {
override def outputs = Nil val versionBytes = version.bytes.reverse
override def lockTime = TransactionConstants.lockTime val inputBytes = BytesUtil.writeCmpctSizeUInt(inputs)
val outputBytes = BytesUtil.writeCmpctSizeUInt(outputs)
val lockTimeBytes = lockTime.bytes.reverse
versionBytes ++ inputBytes ++ outputBytes ++ lockTimeBytes
}
} }
sealed abstract class WitnessTransaction extends Transaction { case class BaseTransaction(
version: Int32,
inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput],
lockTime: UInt32)
extends NonWitnessTransaction
object BaseTransaction extends Factory[BaseTransaction] {
override def fromBytes(bytes: ByteVector): BaseTransaction = {
val versionBytes = bytes.take(4)
val version = Int32(versionBytes.reverse)
val txInputBytes = bytes.slice(4, bytes.size)
val (inputs, outputBytes) =
BytesUtil.parseCmpctSizeUIntSeq(txInputBytes, TransactionInput)
val (outputs, lockTimeBytes) =
BytesUtil.parseCmpctSizeUIntSeq(outputBytes, TransactionOutput)
val lockTime = UInt32(lockTimeBytes.take(4).reverse)
BaseTransaction(version, inputs, outputs, lockTime)
}
def unapply(tx: NonWitnessTransaction): Option[
(Int32, Seq[TransactionInput], Seq[TransactionOutput], UInt32)] = {
Some(tx.version, tx.inputs, tx.outputs, tx.lockTime)
}
}
case object EmptyTransaction extends NonWitnessTransaction {
override def txId: DoubleSha256Digest = DoubleSha256Digest.empty
override def version: Int32 = TransactionConstants.version
override def inputs: Vector[TransactionInput] = Vector.empty
override def outputs: Vector[TransactionOutput] = Vector.empty
override def lockTime: UInt32 = TransactionConstants.lockTime
def toBaseTx: BaseTransaction =
BaseTransaction(version, inputs, outputs, lockTime)
}
case class WitnessTransaction(
version: Int32,
inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput],
lockTime: UInt32,
witness: TransactionWitness)
extends Transaction {
require( require(
inputs.length == witness.length, inputs.length == witness.length,
s"Must have same amount of inputs and witnesses in witness tx, inputs=${inputs.length} witnesses=${witness.length}" s"Must have same amount of inputs and witnesses in witness tx, inputs=${inputs.length} witnesses=${witness.length}"
) )
/** The txId for the witness transaction from satoshi's original serialization */ def toBaseTx: BaseTransaction = {
override def txId: DoubleSha256Digest = { BaseTransaction(version, inputs, outputs, lockTime)
val btx = BaseTransaction(version, inputs, outputs, lockTime)
btx.txId
} }
/** /** The txId for the witness transaction from satoshi's original serialization */
* The witness used to evaluate override def txId: DoubleSha256Digest = {
* [[org.bitcoins.core.protocol.script.ScriptSignature ScriptSignature]]/ toBaseTx.txId
* [[org.bitcoins.core.protocol.script.ScriptPubKey ScriptPubKey]]s inside of a SegWit tx. }
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki BIP141]]
*/
def witness: TransactionWitness
/** /**
* The witness transaction id as defined by * The witness transaction id as defined by
@ -145,10 +208,32 @@ sealed abstract class WitnessTransaction extends Transaction {
* [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]] * [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]]
*/ */
override def weight: Long = { override def weight: Long = {
val base = BaseTransaction(version, inputs, outputs, lockTime) toBaseTx.byteSize * 3 + byteSize
base.byteSize * 3 + byteSize }
/**
* Writes a [[org.bitcoins.core.protocol.transaction.WitnessTransaction WitnessTransaction]] to a hex string
* This is unique from BaseTransaction.bytes in the fact
* that it adds a 'marker' and 'flag' to indicate that this tx is a
* [[org.bitcoins.core.protocol.transaction.WitnessTransaction WitnessTransaction]] and has extra
* witness data attached to it.
* See [[https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki BIP144]] for more info.
* Functionality inside of Bitcoin Core:
* [[https://github.com/bitcoin/bitcoin/blob/e8cfe1ee2d01c493b758a67ad14707dca15792ea/src/primitives/transaction.h#L282-L287s]]
*/
override def bytes: ByteVector = {
val versionBytes = version.bytes.reverse
val inputBytes = BytesUtil.writeCmpctSizeUInt(inputs)
val outputBytes = BytesUtil.writeCmpctSizeUInt(outputs)
val witnessBytes = witness.bytes
val lockTimeBytes = lockTime.bytes.reverse
// notice we use the old serialization format if all witnesses are empty
// https://github.com/bitcoin/bitcoin/blob/e8cfe1ee2d01c493b758a67ad14707dca15792ea/src/primitives/transaction.h#L276-L281
if (witness.exists(_ != EmptyScriptWitness)) {
val witConstant = ByteVector(0.toByte, 1.toByte)
versionBytes ++ witConstant ++ inputBytes ++ outputBytes ++ witnessBytes ++ lockTimeBytes
} else toBaseTx.bytes
} }
override def bytes: ByteVector = RawWitnessTransactionParser.write(this)
/** /**
* Updates the [[org.bitcoins.core.protocol.script.ScriptWitness ScriptWitness]] at the given index and * Updates the [[org.bitcoins.core.protocol.script.ScriptWitness ScriptWitness]] at the given index and
@ -159,73 +244,43 @@ sealed abstract class WitnessTransaction extends Transaction {
val txWit = witness.updated(idx, scriptWit) val txWit = witness.updated(idx, scriptWit)
WitnessTransaction(version, inputs, outputs, lockTime, txWit) WitnessTransaction(version, inputs, outputs, lockTime, txWit)
} }
}
object Transaction extends Factory[Transaction] {
override def fromBytes(bytes: ByteVector): Transaction = {
//see BIP141 for marker/flag bytes
//https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id
if (bytes(4) == WitnessTransaction.marker && bytes(5) == WitnessTransaction.flag) {
//this throw/catch is _still_ necessary for the case where we have unsigned base transactions
//with zero inputs and 1 output which is serialized as "0001" at bytes 4 and 5.
//these transactions will not have a script witness associated with them making them invalid
//witness transactions (you need to have a witness to be considered a witness tx)
//see: https://github.com/bitcoin-s/bitcoin-s/blob/01d89df1b7c6bc4b1594406d54d5e6019705c654/core-test/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionTest.scala#L88
try {
RawWitnessTransactionParser.read(bytes)
} catch {
case scala.util.control.NonFatal(_) =>
RawBaseTransactionParser.read(bytes)
}
} else {
RawBaseTransactionParser.read(bytes)
}
}
}
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: ByteVector): BaseTransaction =
RawBaseTransactionParser.read(bytes)
def apply(
version: Int32,
inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput],
lockTime: UInt32): BaseTransaction =
BaseTransactionImpl(version, inputs, outputs, lockTime)
} }
object WitnessTransaction extends Factory[WitnessTransaction] { object WitnessTransaction extends Factory[WitnessTransaction] {
private case class WitnessTransactionImpl(
version: Int32,
inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput],
lockTime: UInt32,
witness: TransactionWitness)
extends WitnessTransaction
def apply( /**
version: Int32, * This read function is unique to BaseTransaction.fromBytes
inputs: Seq[TransactionInput], * in the fact that it reads a 'marker' and 'flag' byte to indicate that this tx is a
outputs: Seq[TransactionOutput], * [[org.bitcoins.core.protocol.transaction.WitnessTransaction WitnessTransaction]].
lockTime: UInt32, * See [[https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki BIP144 ]] for more details.
witness: TransactionWitness): WitnessTransaction = * Functionality inside of Bitcoin Core:
WitnessTransactionImpl(version, inputs, outputs, lockTime, witness) * [[https://github.com/bitcoin/bitcoin/blob/e8cfe1ee2d01c493b758a67ad14707dca15792ea/src/primitives/transaction.h#L244-L251]]
*/
override def fromBytes(bytes: ByteVector): WitnessTransaction = {
val versionBytes = bytes.take(4)
val version = Int32(versionBytes.reverse)
val marker = bytes(4)
require(
marker.toInt == 0,
"Incorrect marker for witness transaction, the marker MUST be 0 for the marker according to BIP141, got: " + marker)
val flag = bytes(5)
require(
flag.toInt != 0,
"Incorrect flag for witness transaction, this must NOT be 0 according to BIP141, got: " + flag)
val txInputBytes = bytes.slice(6, bytes.size)
val (inputs, outputBytes) =
BytesUtil.parseCmpctSizeUIntSeq(txInputBytes, TransactionInput)
val (outputs, witnessBytes) =
BytesUtil.parseCmpctSizeUIntSeq(outputBytes, TransactionOutput)
val witness = TransactionWitness(witnessBytes, inputs.size)
val lockTimeBytes = witnessBytes.drop(witness.byteSize)
val lockTime = UInt32(lockTimeBytes.take(4).reverse)
override def fromBytes(bytes: ByteVector): WitnessTransaction = WitnessTransaction(version, inputs, outputs, lockTime, witness)
RawWitnessTransactionParser.read(bytes) }
def toWitnessTx(tx: Transaction): WitnessTransaction = tx match { def toWitnessTx(tx: Transaction): WitnessTransaction = tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
WitnessTransaction(btx.version, WitnessTransaction(btx.version,
btx.inputs, btx.inputs,
btx.outputs, btx.outputs,

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.script.{EmptyScriptWitness, ScriptWitness} import org.bitcoins.core.protocol.script.{EmptyScriptWitness, ScriptWitness}
import org.bitcoins.core.serializers.transaction.RawTransactionWitnessParser import org.bitcoins.core.serializers.transaction.RawTransactionWitnessParser
import org.bitcoins.core.util.SeqWrapper import org.bitcoins.core.util.{BytesUtil, SeqWrapper}
import org.bitcoins.crypto.{BytesUtil, NetworkElement} import org.bitcoins.crypto.NetworkElement
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -0,0 +1,321 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.{
CLTVScriptPubKey,
CSVScriptPubKey,
EmptyScriptSignature,
EmptyScriptWitness,
ScriptWitnessV0
}
import org.bitcoins.core.script.control.OP_RETURN
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.builder.RawTxSigner.logger
import org.bitcoins.core.wallet.builder.TxBuilderError
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.signer.BitcoinSigner
import org.bitcoins.core.wallet.utxo.{
ConditionalInputInfo,
EmptyInputInfo,
InputInfo,
InputSigningInfo,
LockTimeInputInfo,
MultiSignatureInputInfo,
P2PKHInputInfo,
P2PKInputInfo,
P2PKWithTimeoutInputInfo,
P2SHInputInfo,
P2WPKHV0InputInfo,
P2WSHV0InputInfo,
UnassignedSegwitNativeInputInfo
}
import org.bitcoins.crypto.{DummyECDigitalSignature, Sign}
import scala.annotation.tailrec
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
object TxUtil {
/**
* This helper function calculates the appropriate locktime for a transaction.
* To be able to spend [[CLTVScriptPubKey]]'s you need to have the transaction's
* locktime set to the same value (or higher) than the output it is spending.
* See BIP65 for more info
*/
def calcLockTime(utxos: Seq[InputSigningInfo[InputInfo]]): Try[UInt32] = {
def computeNextLockTime(
currentLockTimeOpt: Option[UInt32],
locktime: Long): Try[UInt32] = {
val lockTimeT =
if (locktime > UInt32.max.toLong || locktime < 0) {
TxBuilderError.IncompatibleLockTimes
} else Success(UInt32(locktime))
lockTimeT.flatMap { lockTime: UInt32 =>
currentLockTimeOpt match {
case Some(currentLockTime) =>
val lockTimeThreshold = TransactionConstants.locktimeThreshold
if (currentLockTime < lockTime) {
if (currentLockTime < lockTimeThreshold && lockTime >= lockTimeThreshold) {
//means that we spend two different locktime types, one of the outputs spends a
//OP_CLTV script by block height, the other spends one by time stamp
TxBuilderError.IncompatibleLockTimes
} else Success(lockTime)
} else if (currentLockTime >= lockTimeThreshold && lockTime < lockTimeThreshold) {
//means that we spend two different locktime types, one of the outputs spends a
//OP_CLTV script by block height, the other spends one by time stamp
TxBuilderError.IncompatibleLockTimes
} else {
Success(currentLockTime)
}
case None => Success(lockTime)
}
}
}
@tailrec
def loop(
remaining: Seq[InputSigningInfo[InputInfo]],
currentLockTimeOpt: Option[UInt32]): Try[UInt32] =
remaining match {
case Nil =>
Success(currentLockTimeOpt.getOrElse(TransactionConstants.lockTime))
case spendingInfo +: newRemaining =>
spendingInfo.inputInfo match {
case lockTime: LockTimeInputInfo =>
lockTime.scriptPubKey match {
case _: CSVScriptPubKey =>
loop(newRemaining, currentLockTimeOpt)
case cltv: CLTVScriptPubKey =>
val result = computeNextLockTime(currentLockTimeOpt,
cltv.locktime.toLong)
result match {
case Success(newLockTime) =>
loop(newRemaining, Some(newLockTime))
case _: Failure[UInt32] => result
}
}
case p2pkWithTimeout: P2PKWithTimeoutInputInfo =>
if (p2pkWithTimeout.isBeforeTimeout) {
loop(newRemaining, currentLockTimeOpt)
} else {
val result = computeNextLockTime(
currentLockTimeOpt,
p2pkWithTimeout.scriptPubKey.lockTime.toLong)
result match {
case Success(newLockTime) =>
loop(newRemaining, Some(newLockTime))
case _: Failure[UInt32] => result
}
}
case p2sh: P2SHInputInfo =>
val nestedSpendingInfo =
p2sh.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, currentLockTimeOpt)
case p2wsh: P2WSHV0InputInfo =>
val nestedSpendingInfo =
p2wsh.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, currentLockTimeOpt)
case conditional: ConditionalInputInfo =>
val nestedSpendingInfo =
conditional.nestedInputInfo.genericWithSignFrom(spendingInfo)
loop(nestedSpendingInfo +: newRemaining, currentLockTimeOpt)
case _: P2WPKHV0InputInfo | _: UnassignedSegwitNativeInputInfo |
_: P2PKInputInfo | _: P2PKHInputInfo |
_: MultiSignatureInputInfo | _: EmptyInputInfo =>
// none of these scripts affect the locktime of a tx
loop(newRemaining, currentLockTimeOpt)
}
}
loop(utxos, None)
}
/** Inserts script signatures and (potentially) witness data to a given
* transaction using DummyECDigitalSignatures for all sigs in order
* to produce a transaction roughly the size of the expected fully signed
* transaction. Useful during fee estimation.
*
* Note that the resulting dummy-signed Transaction will have populated
* (dummy) witness data when applicable.
*/
def addDummySigs(utx: Transaction, inputInfos: Vector[InputInfo])(
implicit ec: ExecutionContext): Future[Transaction] = {
val dummyInputAndWitnnessFs = inputInfos.zipWithIndex.map {
case (inputInfo, index) =>
val mockSigners = inputInfo.pubKeys.take(inputInfo.requiredSigs).map {
pubKey =>
Sign(_ => Future.successful(DummyECDigitalSignature), pubKey)
}
val mockSpendingInfo =
inputInfo.toSpendingInfo(mockSigners, HashType.sigHashAll)
BitcoinSigner
.sign(mockSpendingInfo, utx, isDummySignature = true)
.map(_.transaction)
.map { tx =>
val witnessOpt = tx match {
case _: NonWitnessTransaction => None
case wtx: WitnessTransaction =>
wtx.witness.witnesses(index) match {
case EmptyScriptWitness => None
case wit: ScriptWitnessV0 => Some(wit)
}
}
(tx.inputs(index), witnessOpt)
}
}
Future.sequence(dummyInputAndWitnnessFs).map { inputsAndWitnesses =>
val inputs = inputsAndWitnesses.map(_._1)
val txWitnesses = inputsAndWitnesses.map(_._2)
TransactionWitness.fromWitOpt(txWitnesses) match {
case _: EmptyWitness =>
BaseTransaction(utx.version, inputs, utx.outputs, utx.lockTime)
case wit: TransactionWitness =>
WitnessTransaction(utx.version,
inputs,
utx.outputs,
utx.lockTime,
wit)
}
}
}
/**
* Sets the ScriptSignature for every input in the given transaction to an EmptyScriptSignature
* as well as sets the witness to an EmptyWitness
* @param tx Transaction to empty signatures
* @return Transaction with no signatures
*/
def emptyAllScriptSigs(tx: Transaction): Transaction = {
val newInputs = tx.inputs.map { input =>
TransactionInput(input.previousOutput,
EmptyScriptSignature,
input.sequence)
}
tx match {
case btx: NonWitnessTransaction =>
BaseTransaction(version = btx.version,
inputs = newInputs,
outputs = btx.outputs,
lockTime = btx.lockTime)
case wtx: WitnessTransaction =>
WitnessTransaction(version = wtx.version,
inputs = newInputs,
outputs = wtx.outputs,
lockTime = wtx.lockTime,
witness = EmptyWitness.fromInputs(newInputs))
}
}
/** Runs various sanity checks on a transaction */
def sanityChecks(
isSigned: Boolean,
inputInfos: Vector[InputInfo],
expectedFeeRate: FeeUnit,
tx: Transaction): Try[Unit] = {
val dustT = if (isSigned) {
sanityDustCheck(tx)
} else {
Success(())
}
dustT.flatMap { _ =>
sanityAmountChecks(isSigned, inputInfos, expectedFeeRate, tx)
}
}
/** Checks that no output is beneath the dust threshold */
def sanityDustCheck(tx: Transaction): Try[Unit] = {
val belowDustOutputs = tx.outputs
.filterNot(_.scriptPubKey.asm.contains(OP_RETURN))
.filter(_.value < Policy.dustThreshold)
if (belowDustOutputs.nonEmpty) {
TxBuilderError.OutputBelowDustThreshold(belowDustOutputs)
} else {
Success(())
}
}
/**
* Checks that the creditingAmount >= destinationAmount
* and then does a sanity check on the transaction's fee
*/
def sanityAmountChecks(
isSigned: Boolean,
inputInfos: Vector[InputInfo],
expectedFeeRate: FeeUnit,
tx: Transaction): Try[Unit] = {
val spentAmount = tx.outputs.foldLeft(CurrencyUnits.zero)(_ + _.value)
val creditingAmount =
inputInfos.foldLeft(CurrencyUnits.zero)(_ + _.amount)
if (spentAmount > creditingAmount) {
TxBuilderError.MintsMoney
} else {
val expectedTx = if (isSigned) {
tx
} else {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
Await.result(TxUtil.addDummySigs(tx, inputInfos), 5.seconds)
}
val actualFee = creditingAmount - spentAmount
val estimatedFee = expectedFeeRate * expectedTx
isValidFeeRange(estimatedFee, actualFee, expectedFeeRate)
}
}
/**
* Checks if the fee is within a 'valid' range
* @param estimatedFee the estimated amount of fee we should pay
* @param actualFee the actual amount of fee the transaction pays
* @param feeRate the fee rate in satoshis/vbyte we paid per byte on this tx
* @return
*/
def isValidFeeRange(
estimatedFee: CurrencyUnit,
actualFee: CurrencyUnit,
feeRate: FeeUnit): Try[Unit] = {
//what the number '40' represents is the allowed variance -- in bytes -- between the size of the two
//versions of signed tx. I believe the two signed version can vary in size because the digital
//signature might have changed in size. It could become larger or smaller depending on the digital
//signatures produced.
//Personally I think 40 seems like a little high. As you shouldn't vary more than a 2 bytes per input in the tx i think?
//bumping for now though as I don't want to spend time debugging
//I think there is something incorrect that errors to the low side of fee estimation
//for p2sh(p2wpkh) txs
//See this link for more info on variance in size on ECDigitalSignatures
//https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
val acceptableVariance = 40 * feeRate.toLong
val min = Satoshis(-acceptableVariance)
val max = Satoshis(acceptableVariance)
val difference = estimatedFee - actualFee
if (difference <= min) {
logger.error(
s"Fee was too high. Estimated fee $estimatedFee, actualFee $actualFee, difference $difference, acceptableVariance $acceptableVariance")
TxBuilderError.HighFee
} else if (difference >= max) {
logger.error(
s"Fee was too low. Estimated fee $estimatedFee, actualFee $actualFee, difference $difference, acceptableVariance $acceptableVariance")
TxBuilderError.LowFee
} else {
Success(())
}
}
}

View file

@ -11,7 +11,6 @@ import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.script.interpreter.ScriptInterpreter import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
import org.bitcoins.core.wallet.signer.BitcoinSigner import org.bitcoins.core.wallet.signer.BitcoinSigner
import org.bitcoins.core.wallet.utxo._ import org.bitcoins.core.wallet.utxo._
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
@ -582,7 +581,7 @@ case class PSBT(
new RuntimeException( new RuntimeException(
s"Input $index was invalid: $inputResult")) s"Input $index was invalid: $inputResult"))
} }
case (Some(_), _: BaseTransaction) => case (Some(_), _: NonWitnessTransaction) =>
Failure(new RuntimeException( Failure(new RuntimeException(
s"Extracted program is not witness transaction, but input $index has WitnessUTXO record")) s"Extracted program is not witness transaction, but input $index has WitnessUTXO record"))
case (None, _) => case (None, _) =>
@ -647,7 +646,7 @@ case class PSBT(
witness) witness)
} else { } else {
transaction match { transaction match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTransaction(btx.version, newInputs, btx.outputs, btx.lockTime) BaseTransaction(btx.version, newInputs, btx.outputs, btx.lockTime)
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
WitnessTransaction(wtx.version, WitnessTransaction(wtx.version,
@ -761,7 +760,7 @@ object PSBT extends Factory[PSBT] {
val btx = unsignedTx match { val btx = unsignedTx match {
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime) BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime)
case base: BaseTransaction => base case base: NonWitnessTransaction => base
} }
val globalMap = GlobalPSBTMap( val globalMap = GlobalPSBTMap(
Vector(GlobalPSBTRecord.UnsignedTransaction(btx))) Vector(GlobalPSBTRecord.UnsignedTransaction(btx)))
@ -829,11 +828,11 @@ object PSBT extends Factory[PSBT] {
spendingInfoAndNonWitnessTxs.matchesInputs(unsignedTx.inputs), spendingInfoAndNonWitnessTxs.matchesInputs(unsignedTx.inputs),
"NewSpendingInfos must correspond to transaction inputs" "NewSpendingInfos must correspond to transaction inputs"
) )
val emptySigTx = BitcoinTxBuilder.emptyAllScriptSigs(unsignedTx) val emptySigTx = TxUtil.emptyAllScriptSigs(unsignedTx)
val btx = emptySigTx match { val btx = emptySigTx match {
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime) BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime)
case base: BaseTransaction => base case base: NonWitnessTransaction => base
} }
val globalMap = GlobalPSBTMap( val globalMap = GlobalPSBTMap(

View file

@ -4,7 +4,7 @@ import org.bitcoins.core.byteVectorOrdering
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
BaseTransaction, NonWitnessTransaction,
Transaction, Transaction,
TransactionInput, TransactionInput,
TransactionOutput, TransactionOutput,
@ -657,7 +657,8 @@ object InputPSBTMap extends PSBTMapFactory[InputPSBTRecord, InputPSBTMap] {
val scriptSig = val scriptSig =
FinalizedScriptSig(sigComponent.scriptSignature) FinalizedScriptSig(sigComponent.scriptSignature)
sigComponent.transaction match { sigComponent.transaction match {
case _: BaseTransaction => InputPSBTMap(utxos ++ Vector(scriptSig)) case _: NonWitnessTransaction =>
InputPSBTMap(utxos ++ Vector(scriptSig))
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
val witness = wtx.witness(sigComponent.inputIndex.toInt) val witness = wtx.witness(sigComponent.inputIndex.toInt)
val scriptWitness = FinalizedScriptWitness(witness) val scriptWitness = FinalizedScriptWitness(witness)

View file

@ -7,6 +7,7 @@ import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
BaseTransaction, BaseTransaction,
NonWitnessTransaction,
Transaction, Transaction,
TransactionOutput TransactionOutput
} }
@ -62,7 +63,7 @@ sealed trait GlobalPSBTRecord extends PSBTRecord {
object GlobalPSBTRecord extends Factory[GlobalPSBTRecord] { object GlobalPSBTRecord extends Factory[GlobalPSBTRecord] {
import org.bitcoins.core.psbt.PSBTGlobalKeyId._ import org.bitcoins.core.psbt.PSBTGlobalKeyId._
case class UnsignedTransaction(transaction: BaseTransaction) case class UnsignedTransaction(transaction: NonWitnessTransaction)
extends GlobalPSBTRecord { extends GlobalPSBTRecord {
require( require(
transaction.inputs.forall(_.scriptSignature == EmptyScriptSignature), transaction.inputs.forall(_.scriptSignature == EmptyScriptSignature),

View file

@ -9,8 +9,7 @@ import org.bitcoins.core.script.locktime.LocktimeOperation
import org.bitcoins.core.script.reserved.ReservedOperation import org.bitcoins.core.script.reserved.ReservedOperation
import org.bitcoins.core.script.splice.SpliceOperation import org.bitcoins.core.script.splice.SpliceOperation
import org.bitcoins.core.script.stack.StackOperation import org.bitcoins.core.script.stack.StackOperation
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.{BitcoinSLogger, BytesUtil}
import org.bitcoins.crypto.BytesUtil
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -6,8 +6,7 @@ import org.bitcoins.core.script.{
} }
import org.bitcoins.core.script.flag.ScriptFlagUtil import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._ import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil} import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.BytesUtil
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.script.constant
import org.bitcoins.core.number.Int64 import org.bitcoins.core.number.Int64
import org.bitcoins.core.script.ScriptOperationFactory import org.bitcoins.core.script.ScriptOperationFactory
import org.bitcoins.core.util.BitcoinScriptUtil import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{BytesUtil, Factory, NetworkElement} import org.bitcoins.crypto.{Factory, NetworkElement}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.util.{Failure, Try} import scala.util.{Failure, Try}

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.script.constant package org.bitcoins.core.script.constant
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -24,8 +24,7 @@ import org.bitcoins.core.script.reserved._
import org.bitcoins.core.script.result._ import org.bitcoins.core.script.result._
import org.bitcoins.core.script.splice._ import org.bitcoins.core.script.splice._
import org.bitcoins.core.script.stack._ import org.bitcoins.core.script.stack._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil} import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.BytesUtil
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
@ -1160,7 +1159,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
b.transaction match { b.transaction match {
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
wtx.witness(txSigComponent.inputIndex.toInt).stack.nonEmpty wtx.witness(txSigComponent.inputIndex.toInt).stack.nonEmpty
case _: BaseTransaction => false case _: NonWitnessTransaction => false
} }
case r: WitnessTxSigComponentRebuilt => case r: WitnessTxSigComponentRebuilt =>
r.transaction.witness(txSigComponent.inputIndex.toInt).stack.nonEmpty r.transaction.witness(txSigComponent.inputIndex.toInt).stack.nonEmpty

View file

@ -39,5 +39,6 @@ case object OP_CHECKSEQUENCEVERIFY extends LocktimeOperation {
} }
object LocktimeOperation extends ScriptOperationFactory[LocktimeOperation] { object LocktimeOperation extends ScriptOperationFactory[LocktimeOperation] {
override val operations = Vector(OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY) override val operations =
Vector(OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY)
} }

View file

@ -29,5 +29,6 @@ case object OP_SIZE extends SpliceOperation {
} }
object SpliceOperation extends ScriptOperationFactory[SpliceOperation] { object SpliceOperation extends ScriptOperationFactory[SpliceOperation] {
override val operations = Vector(OP_CAT, OP_LEFT, OP_RIGHT, OP_SIZE, OP_SUBSTR) override val operations =
Vector(OP_CAT, OP_LEFT, OP_RIGHT, OP_SIZE, OP_SUBSTR)
} }

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.serializers package org.bitcoins.core.serializers
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.crypto.BytesUtil import org.bitcoins.core.util.BytesUtil
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** /**

View file

@ -4,7 +4,8 @@ import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.blockchain.MerkleBlock import org.bitcoins.core.protocol.blockchain.MerkleBlock
import org.bitcoins.core.serializers.RawBitcoinSerializer import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.crypto.{BytesUtil, DoubleSha256Digest} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import scodec.bits.{BitVector, ByteVector} import scodec.bits.{BitVector, ByteVector}
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -3,7 +3,8 @@ package org.bitcoins.core.serializers.script
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.script._ import org.bitcoins.core.script._
import org.bitcoins.core.script.constant._ import org.bitcoins.core.script.constant._
import org.bitcoins.crypto.{BytesUtil, Factory} import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.Factory
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.util
import org.bitcoins.core.crypto.ECPrivateKeyUtil import org.bitcoins.core.crypto.ECPrivateKeyUtil
import org.bitcoins.core.protocol.blockchain._ import org.bitcoins.core.protocol.blockchain._
import org.bitcoins.crypto.{BytesUtil, CryptoUtil} import org.bitcoins.crypto.CryptoUtil
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -25,7 +25,7 @@ import org.bitcoins.core.script.result.{
} }
import org.bitcoins.core.script.ExecutionInProgressScriptProgram import org.bitcoins.core.script.ExecutionInProgressScriptProgram
import org.bitcoins.core.serializers.script.ScriptParser import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.crypto.{BytesUtil, ECDigitalSignature, ECPublicKey} import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec import scala.annotation.tailrec

View file

@ -0,0 +1,53 @@
package org.bitcoins.core.util
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.crypto.{CryptoBytesUtil, Factory, NetworkElement}
import scodec.bits.ByteVector
import scala.annotation.tailrec
trait BytesUtil extends CryptoBytesUtil {
def writeCmpctSizeUInt[T <: NetworkElement](ts: Seq[T]): ByteVector = {
val serialized = ts.map(_.bytes).foldLeft(ByteVector.empty)(_ ++ _)
val cmpct = CompactSizeUInt(UInt64(ts.size))
cmpct.bytes ++ serialized
}
/**
* 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: ByteVector,
factory: Factory[T]): (Vector[T], ByteVector) = {
val count = CompactSizeUInt.parse(bytes)
val payload = bytes.drop(count.byteSize.toInt)
val builder = Vector.newBuilder[T]
@tailrec
def loop(remaining: ByteVector, counter: Int = 0): ByteVector = {
if (counter == count.num.toInt) {
remaining
} else {
val parsed = factory.fromBytes(remaining)
val newRemaining = remaining.drop(parsed.byteSize)
builder.+=(parsed)
loop(newRemaining, counter + 1)
}
}
val remaining = loop(payload)
val result = builder.result()
require(
result.size == count.num.toInt,
s"Could not parse the amount of required elements, got: ${result.size} required: ${count}")
(result, remaining)
}
}
object BytesUtil extends BytesUtil

View file

@ -5,7 +5,6 @@ import java.math.BigInteger
import org.bitcoins.core.number._ import org.bitcoins.core.number._
import org.bitcoins.core.protocol.blockchain.BlockHeader import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.blockchain.BlockHeader.TargetDifficultyHelper import org.bitcoins.core.protocol.blockchain.BlockHeader.TargetDifficultyHelper
import org.bitcoins.crypto.BytesUtil
import scodec.bits.{BitVector, ByteVector} import scodec.bits.{BitVector, ByteVector}
import scala.math.BigInt import scala.math.BigInt

View file

@ -0,0 +1,28 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.{InputInfo, ScriptSignatureParams}
import scala.concurrent.{ExecutionContext, Future}
/** Contains a finalized tx (output from [[RawTxFinalizer.buildTx]]) and the
* ScriptSignatureParams needed to sign that transaction. */
case class FinalizedTxWithSigningInfo(
finalizedTx: Transaction,
infos: Vector[ScriptSignatureParams[InputInfo]]) {
def sign(expectedFeeRate: FeeUnit)(
implicit ec: ExecutionContext): Future[Transaction] = {
RawTxSigner.sign(this, expectedFeeRate)
}
def sign(
expectedFeeRate: FeeUnit,
invariants: (
Vector[ScriptSignatureParams[InputInfo]],
Transaction) => Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
RawTxSigner.sign(this, expectedFeeRate, invariants)
}
}

View file

@ -0,0 +1,182 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.transaction._
import scala.collection.mutable
import scala.concurrent.{ExecutionContext, Future}
/** The mutable transaction builder which collects:
* - Unsigned inputs (script signature will be ignored)
* - Destination outputs (change is dealt with in the finalizer)
* - A version number (default is [[TransactionConstants.validLockVersion]])
* - A lock time (default is [[TransactionConstants.lockTime]])
*
* At a high level, RawTxBuilder is responsible only for the funding inputs
* and logical outputs (outputs that are intended by this transaction, and not
* outputs computed from the logical outputs such as change outputs, which are
* the responsibility of the RawTxFinalizer) of a transaction.
*
* RawTxBuilder supports inline calls to += and ++= for adding inputs
* and outputs. Note that RawTxBuilder respects the order in which inputs
* and outputs are added when generating a RawTxBuilderResult. If you wish
* to have some other (computed) order, this is the responsibility of either
* the calling code (to add in the correct order) or of the RawTxFinalizer which
* may alter the order of inputs and outputs (and should only do so if it is
* clearly documented that this will occur, otherwise it is safe to assume that
* RawTxFinalizers will not alter the order of inputs and outputs).
*
* Once a transaction is done being built and is ready to be passed to a
* RawTransactionFinalizer, call the result method to receive a
* [[RawTxBuilderResult]] which can be passed into [[RawTxFinalizer.buildTx]].
*
* If you have access to a finalizer before you are ready to call result,
* you may call the setFinalizer method to receive an instance of type
* [[RawTxBuilderWithFinalizer]] which is described below, and where
* you may continue to build and then call buildTx directly.
*
* Note: RawTxBuilder is not thread safe.
*/
case class RawTxBuilder() {
private var version: Int32 = TransactionConstants.validLockVersion
private val inputsBuilder: mutable.Builder[
TransactionInput,
Vector[TransactionInput]] = Vector.newBuilder
private val outputsBuilder: mutable.Builder[
TransactionOutput,
Vector[TransactionOutput]] = Vector.newBuilder
private var lockTime: UInt32 = TransactionConstants.lockTime
/** Returns a RawTxBuilderResult ready for a RawTxFinalizer. */
def result(): RawTxBuilderResult = {
RawTxBuilderResult(version,
inputsBuilder.result(),
outputsBuilder.result(),
lockTime)
}
/** Returns a RawTxBuilderWithFinalizer where building can continue
* and where buildTx can be called once building is completed. */
def setFinalizer(finalizer: RawTxFinalizer): RawTxBuilderWithFinalizer = {
RawTxBuilderWithFinalizer(this, finalizer)
}
/** Resets the RawTxBuilder as if it was just constructed. */
def clear(): Unit = {
version = TransactionConstants.validLockVersion
inputsBuilder.clear()
outputsBuilder.clear()
lockTime = TransactionConstants.lockTime
}
/** Adds a TransactionInput to be the next input. No ScriptSignature is required
* and any given ScriptSignature will be ignored (we recommend EmptyScriptSignature). */
def addInput(input: TransactionInput): this.type = {
inputsBuilder += input
this
}
@inline final def +=(input: TransactionInput): this.type = addInput(input)
def addInputs(inputs: Iterable[TransactionInput]): this.type = {
inputsBuilder ++= inputs
this
}
/** Adds a TransactionOuput to be the next output.
*
* Note that outputs like a change
* output which are computed from other inputs and outputs should not be added here
* and are instead the responsibility of the RawTxFinalizer.
*/
def addOutput(output: TransactionOutput): this.type = {
outputsBuilder += output
this
}
@inline final def +=(output: TransactionOutput): this.type = addOutput(output)
def addOutputs(outputs: Iterable[TransactionOutput]): this.type = {
outputsBuilder ++= outputs
this
}
/** Adds a collection of inputs and/or outputs to the
* input and/or output lists
*/
@inline final def ++=[T >: TransactionInput with TransactionOutput](
inputsOrOutputs: Iterable[T]): this.type = {
val vec = inputsOrOutputs.iterator.toVector
val inputs = vec.collect {
case input: TransactionInput => input
}
val outputs = vec.collect {
case output: TransactionOutput => output
}
addInputs(inputs)
addOutputs(outputs)
}
/** Sets the transaction version */
def setVersion(version: Int32): this.type = {
this.version = version
this
}
/** Sets the transaction nLockTime */
def setLockTime(lockTime: UInt32): this.type = {
this.lockTime = lockTime
this
}
}
/** Wraps a RawTxBuilder and RawTxFinalizer pair.
*
* Provides access to builder methods for continuing
* to collect inputs and outputs and also offers direct
* access to the RawTxFinalizer's buildTx method which
* completes the RawTxBuilder and then finalized the result.
*/
case class RawTxBuilderWithFinalizer(
builder: RawTxBuilder,
finalizer: RawTxFinalizer) {
/** Completes the builder and finalizes the result */
def buildTx()(implicit ec: ExecutionContext): Future[Transaction] = {
finalizer.buildTx(builder.result())
}
def clearBuilder(): Unit = {
builder.clear()
}
@inline final def +=(input: TransactionInput): this.type = {
builder += input
this
}
@inline final def +=(output: TransactionOutput): this.type = {
builder += output
this
}
@inline final def ++=[T >: TransactionInput with TransactionOutput](
inputsOrOutputs: Iterable[T]): this.type = {
builder ++= inputsOrOutputs
this
}
def setVersion(version: Int32): this.type = {
builder.setVersion(version)
this
}
def setLockTime(lockTime: UInt32): this.type = {
builder.setLockTime(lockTime)
this
}
}

View file

@ -0,0 +1,31 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
Transaction,
TransactionInput,
TransactionOutput
}
/** Raw Transaction to be finalized by a RawTxFinalizer */
case class RawTxBuilderResult(
version: Int32,
inputs: Vector[TransactionInput],
outputs: Vector[TransactionOutput],
lockTime: UInt32) {
def toBaseTransaction: BaseTransaction = {
BaseTransaction(version, inputs, outputs, lockTime)
}
}
object RawTxBuilderResult {
def fromTransaction(tx: Transaction): RawTxBuilderResult = {
RawTxBuilderResult(tx.version,
tx.inputs.toVector,
tx.outputs.toVector,
tx.lockTime)
}
}

View file

@ -0,0 +1,205 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.{InputInfo, InputSigningInfo}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Success, Try}
/** This trait is responsible for converting RawTxBuilderResults into
* finalized (unsigned) transactions. This process usually includes
* such things as computation on inputs and outputs to generate
* things like change outputs, or to reorder inputs or outputs.
*
* Once a transaction is done being finalized, its txid/wtxid should
* not change as the RawTxSigner's only responsibility is adding signature
* data not included in the txid/wtxid.
*
* RawTxFinalizer may (but is not required to) generate witness data
* other than signatures (i.e. public keys for P2WPKH and redeem scripts
* for P2WSH). RawTxFinalizer may not otherwise populate any other kind
* of script signature or witness data.
*
* RawTxFinalizers are compose-able through the andThen method which will
* turn the first RawTxFinalizer's finalized transaction into a RawTxBuilderResult
* by taking that transactions inputs (in order), outputs (in order), locktime and
* version and this RawTxBuilderResult is then given to the second RawTxFinalizer.
*/
sealed trait RawTxFinalizer {
/** Constructs a finalized (unsigned) transaction */
def buildTx(txBuilderResult: RawTxBuilderResult)(
implicit ec: ExecutionContext): Future[Transaction]
/** The result of buildTx is converted into a RawTxBuilderResult
* by taking that transactions inputs (in order), outputs (in order),
* locktime and version and this RawTxBuilderResult is then passed to
* the other RawTxFinalizer's buildTx
*/
def andThen(other: RawTxFinalizer): RawTxFinalizer = new RawTxFinalizer {
override def buildTx(txBuilderResult: RawTxBuilderResult)(
implicit ec: ExecutionContext): Future[Transaction] = {
for {
firstFinalizedTx <- this.buildTx(txBuilderResult)
composedFinalizedTx <- other.buildTx(
RawTxBuilderResult.fromTransaction(firstFinalizedTx))
} yield composedFinalizedTx
}
}
}
/** A trivial finalizer that does no processing */
case object RawFinalizer extends RawTxFinalizer {
override def buildTx(txBuilderResult: RawTxBuilderResult)(
implicit ec: ExecutionContext): Future[Transaction] = {
Future.successful(txBuilderResult.toBaseTransaction)
}
}
/** A simple finalizer that only removes outputs beneath the dust
* threshold and does nothing else
*/
case object FilterDustFinalizer extends RawTxFinalizer {
override def buildTx(txBuilderResult: RawTxBuilderResult)(
implicit ec: ExecutionContext): Future[Transaction] = {
val filteredOutputs =
txBuilderResult.outputs.filter(_.value >= Policy.dustThreshold)
Future.successful(
txBuilderResult.toBaseTransaction.copy(outputs = filteredOutputs))
}
}
/** A finalizer which takes as parameters an input info for each input, as well
* as a fee rate and change scriptpubkey and adds a change output resulting in
* the expected fee (after fee estimation).
*/
case class NonInteractiveWithChangeFinalizer(
inputInfos: Vector[InputInfo],
feeRate: FeeUnit,
changeSPK: ScriptPubKey)
extends RawTxFinalizer {
override def buildTx(txBuilderResult: RawTxBuilderResult)(
implicit ec: ExecutionContext): Future[Transaction] = {
val RawTxBuilderResult(version, inputs, outputs, lockTime) = txBuilderResult
val outputsWithChange = outputs :+ TransactionOutput(Satoshis.zero,
changeSPK)
val totalCrediting = inputInfos
.map(_.output.value)
.foldLeft[CurrencyUnit](Satoshis.zero)(_ + _)
val totalSpending =
outputs.map(_.value).foldLeft[CurrencyUnit](Satoshis.zero)(_ + _)
val witnesses = inputInfos.map(InputInfo.getScriptWitness)
val txNoChangeFee = TransactionWitness.fromWitOpt(witnesses) match {
case _: EmptyWitness =>
BaseTransaction(version, inputs, outputsWithChange, lockTime)
case wit: TransactionWitness =>
WitnessTransaction(version, inputs, outputsWithChange, lockTime, wit)
}
val dummyTxF = TxUtil.addDummySigs(txNoChangeFee, inputInfos)
val txF = dummyTxF.map { dummyTx =>
val fee = feeRate.calc(dummyTx)
val change = totalCrediting - totalSpending - fee
val newChangeOutput = TransactionOutput(change, changeSPK)
val newOutputs = if (change <= Policy.dustThreshold) {
outputs
} else {
outputs :+ newChangeOutput
}
txNoChangeFee match {
case btx: NonWitnessTransaction =>
BaseTransaction(btx.version, btx.inputs, newOutputs, btx.lockTime)
case WitnessTransaction(version, inputs, _, lockTime, witness) =>
WitnessTransaction(version, inputs, newOutputs, lockTime, witness)
}
}
txF.flatMap { tx =>
val passInOutChecksT =
NonInteractiveWithChangeFinalizer.sanityDestinationChecks(
expectedOutPoints = inputs.map(_.previousOutput),
expectedOutputs = outputs,
changeSPK = changeSPK,
finalizedTx = tx)
val passChecksT = passInOutChecksT.flatMap { _ =>
TxUtil.sanityChecks(isSigned = false,
inputInfos = inputInfos,
expectedFeeRate = feeRate,
tx = tx)
}
Future.fromTry(passChecksT.map(_ => tx))
}
}
}
object NonInteractiveWithChangeFinalizer {
/** Checks that a finalized transaction contains the expected
* inputs and outputs. */
def sanityDestinationChecks(
expectedOutPoints: Vector[TransactionOutPoint],
expectedOutputs: Vector[TransactionOutput],
changeSPK: ScriptPubKey,
finalizedTx: Transaction): Try[Unit] = {
//make sure we send coins to the appropriate destinations
val isMissingDestination =
!expectedOutputs.forall(finalizedTx.outputs.contains)
val hasExtraOutputs =
if (finalizedTx.outputs.size == expectedOutputs.size) {
false
} else {
//the extra output should be the changeOutput
!(finalizedTx.outputs.size == (expectedOutputs.size + 1) &&
finalizedTx.outputs.map(_.scriptPubKey).contains(changeSPK))
}
val spendingTxOutPoints = finalizedTx.inputs.map(_.previousOutput)
val hasExtraOutPoints =
!spendingTxOutPoints.forall(expectedOutPoints.contains) ||
expectedOutPoints.length != spendingTxOutPoints.length
if (isMissingDestination) {
TxBuilderError.MissingDestinationOutput
} else if (hasExtraOutputs) {
TxBuilderError.ExtraOutputsAdded
} else if (hasExtraOutPoints) {
TxBuilderError.ExtraOutPoints
} else {
Success(())
}
}
def txBuilderFrom(
outputs: Seq[TransactionOutput],
utxos: Seq[InputSigningInfo[InputInfo]],
feeRate: FeeUnit,
changeSPK: ScriptPubKey): RawTxBuilderWithFinalizer = {
val inputs = InputUtil.calcSequenceForInputs(utxos, Policy.isRBFEnabled)
val lockTime = TxUtil.calcLockTime(utxos).get
val builder = RawTxBuilder().setLockTime(lockTime) ++= outputs ++= inputs
val finalizer = NonInteractiveWithChangeFinalizer(
utxos.toVector.map(_.inputInfo),
feeRate,
changeSPK)
builder.setFinalizer(finalizer)
}
def txFrom(
outputs: Seq[TransactionOutput],
utxos: Seq[InputSigningInfo[InputInfo]],
feeRate: FeeUnit,
changeSPK: ScriptPubKey)(
implicit ec: ExecutionContext): Future[Transaction] = {
val builderF = Future(txBuilderFrom(outputs, utxos, feeRate, changeSPK))
builderF.flatMap(_.buildTx())
}
}

View file

@ -0,0 +1,148 @@
package org.bitcoins.core.wallet.builder
import org.bitcoins.core.crypto.TxSigComponent
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.protocol.transaction.{
EmptyWitness,
Transaction,
TransactionWitness,
TxUtil,
WitnessTransaction
}
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.signer.BitcoinSigner
import org.bitcoins.core.wallet.utxo.{
InputInfo,
ScriptSignatureParams,
UnassignedSegwitNativeInputInfo
}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
/** Transactions that have been finalized by a RawTxFinalizer are passed as inputs
* to a sign function here in order to generate fully signed transactions.
*
* In the future sign methods specific to multi-party protocols will be added
* here to support PSBT and signed transaction construction between multiple parties.
*/
object RawTxSigner extends BitcoinSLogger {
def sign(txWithInfo: FinalizedTxWithSigningInfo, expectedFeeRate: FeeUnit)(
implicit ec: ExecutionContext): Future[Transaction] = {
sign(txWithInfo.finalizedTx, txWithInfo.infos, expectedFeeRate)
}
def sign(
utx: Transaction,
utxoInfos: Vector[ScriptSignatureParams[InputInfo]],
expectedFeeRate: FeeUnit)(
implicit ec: ExecutionContext): Future[Transaction] = {
sign(utx, utxoInfos, expectedFeeRate, (_, _) => true)
}
def sign(
txWithInfo: FinalizedTxWithSigningInfo,
expectedFeeRate: FeeUnit,
invariants: (
Vector[ScriptSignatureParams[InputInfo]],
Transaction) => Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
sign(txWithInfo.finalizedTx, txWithInfo.infos, expectedFeeRate, invariants)
}
def sign(
utx: Transaction,
utxoInfos: Vector[ScriptSignatureParams[InputInfo]],
expectedFeeRate: FeeUnit,
invariants: (
Vector[ScriptSignatureParams[InputInfo]],
Transaction) => Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
require(utxoInfos.length == utx.inputs.length,
"Must provide exactly one UTXOSatisfyingInfo per input.")
require(utxoInfos.distinct.length == utxoInfos.length,
"All UTXOSatisfyingInfos must be unique. ")
require(utxoInfos.forall(utxo =>
utx.inputs.exists(_.previousOutput == utxo.outPoint)),
"All UTXOSatisfyingInfos must correspond to an input.")
val signedTxF =
if (utxoInfos.exists(
_.inputInfo.isInstanceOf[UnassignedSegwitNativeInputInfo])) {
Future.fromTry(TxBuilderError.NoSigner)
} else {
val builder = RawTxBuilder()
.setVersion(utx.version)
.setLockTime(utx.lockTime) ++= utx.outputs
val inputAndWitnessFs = utxoInfos.map { utxo =>
val txSigCompF =
BitcoinSigner.sign(utxo, utx, isDummySignature = false)
txSigCompF.map { txSigComp =>
val scriptWitnessOpt = TxSigComponent.getScriptWitness(txSigComp)
if (scriptWitnessOpt.isEmpty && InputInfo
.getScriptWitness(utxo.inputInfo)
.isDefined) {
println(utxo.inputInfo)
}
(txSigComp.input, scriptWitnessOpt)
}
}
val witnessesBuilder = Vector.newBuilder[Option[ScriptWitness]]
val inputsAddedToBuilderF = Future.sequence(inputAndWitnessFs).map {
inputsAndWitnesses =>
utx.inputs.foreach { unsignedInput =>
val (input, witnessOpt) = inputsAndWitnesses
.find(_._1.previousOutput == unsignedInput.previousOutput)
.get
witnessesBuilder += witnessOpt
builder += input
}
}
for {
_ <- inputsAddedToBuilderF
btx <- builder.setFinalizer(RawFinalizer).buildTx()
} yield {
val txWitness =
TransactionWitness.fromWitOpt(witnessesBuilder.result())
txWitness match {
case EmptyWitness(_) => btx
case _: TransactionWitness =>
WitnessTransaction(btx.version,
btx.inputs,
btx.outputs,
btx.lockTime,
txWitness)
}
}
}
signedTxF.flatMap { signedTx =>
val txT: Try[Transaction] = {
if (invariants(utxoInfos, signedTx)) {
//final sanity checks
TxUtil.sanityChecks(isSigned = true,
inputInfos = utxoInfos.map(_.inputInfo),
expectedFeeRate = expectedFeeRate,
tx = signedTx) match {
case Success(_) => Success(signedTx)
case Failure(err) => Failure(err)
}
} else {
TxBuilderError.FailedUserInvariants
}
}
Future.fromTry(txT)
}
}
}

View file

@ -1,5 +1,7 @@
package org.bitcoins.core.wallet.builder package org.bitcoins.core.wallet.builder
import org.bitcoins.core.protocol.transaction.TransactionOutput
import scala.util.Failure import scala.util.Failure
/** /**
@ -183,8 +185,10 @@ object TxBuilderError {
/** Means we have a output on this transaction below /** Means we have a output on this transaction below
* [[org.bitcoins.core.policy.Policy.dustThreshold Policy.dustThreshold]] */ * [[org.bitcoins.core.policy.Policy.dustThreshold Policy.dustThreshold]] */
val OutputBelowDustThreshold = Failure(new IllegalArgumentException( def OutputBelowDustThreshold(
"The p2p network discourages outputs below the dustThreshold, this tx won't be relayed")) belowDustOutputs: Seq[TransactionOutput]): Failure[Nothing] =
Failure(new IllegalArgumentException(
s"The p2p network discourages outputs below the dustThreshold, $belowDustOutputs, this tx won't be relayed"))
val UnknownError = Failure(new IllegalArgumentException) val UnknownError = Failure(new IllegalArgumentException)
} }

View file

@ -178,7 +178,7 @@ sealed abstract class Signer[-InputType <: InputInfo] extends SignerUtils {
unsignedInput.sequence) unsignedInput.sequence)
val signedInputs = unsignedTx.inputs.updated(inputIndex, signedInput) val signedInputs = unsignedTx.inputs.updated(inputIndex, signedInput)
val signedTx = unsignedTx match { val signedTx = unsignedTx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTransaction(btx.version, signedInputs, btx.outputs, btx.lockTime) BaseTransaction(btx.version, signedInputs, btx.outputs, btx.lockTime)
case wtx: WitnessTransaction => case wtx: WitnessTransaction =>
WitnessTransaction(wtx.version, WitnessTransaction(wtx.version,
@ -306,7 +306,7 @@ object BitcoinSigner extends SignerUtils {
val txToSign = spendingInfo.output.scriptPubKey match { val txToSign = spendingInfo.output.scriptPubKey match {
case _: WitnessScriptPubKey => case _: WitnessScriptPubKey =>
tx match { tx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
val witnesses = psbt.inputMaps.map { map => val witnesses = psbt.inputMaps.map { map =>
map.witnessScriptOpt.map(scriptWit => map.witnessScriptOpt.map(scriptWit =>
P2WSHWitnessV0(scriptWit.witnessScript)) P2WSHWitnessV0(scriptWit.witnessScript))
@ -518,7 +518,7 @@ sealed abstract class P2SHSigner extends Signer[P2SHInputInfo] {
signedTx.inputs.updated(inputIndex.toInt, signedInput) signedTx.inputs.updated(inputIndex.toInt, signedInput)
val finalTx = signedTx match { val finalTx = signedTx match {
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
BaseTransaction(version = btx.version, BaseTransaction(version = btx.version,
inputs = signedInputs, inputs = signedInputs,
outputs = btx.outputs, outputs = btx.outputs,
@ -603,7 +603,7 @@ sealed abstract class P2WPKHSigner extends Signer[P2WPKHV0InputInfo] {
} }
} }
case btx: BaseTransaction => case btx: NonWitnessTransaction =>
val wtx = WitnessTransaction.toWitnessTx(btx) val wtx = WitnessTransaction.toWitnessTx(btx)
sign(spendingInfoToSatisfy, wtx, isDummySignature) sign(spendingInfoToSatisfy, wtx, isDummySignature)

View file

@ -36,6 +36,8 @@ sealed trait InputInfo {
def pubKeys: Vector[ECPublicKey] def pubKeys: Vector[ECPublicKey]
def requiredSigs: Int
def toSpendingInfo( def toSpendingInfo(
signers: Vector[Sign], signers: Vector[Sign],
hashType: HashType): ScriptSignatureParams[InputInfo] = { hashType: HashType): ScriptSignatureParams[InputInfo] = {
@ -48,6 +50,15 @@ sealed trait InputInfo {
ECSignatureParams(this, signer, hashType) ECSignatureParams(this, signer, hashType)
} }
def genericWithSignFrom(
signerMaterial: InputSigningInfo[InputInfo]): InputSigningInfo[
this.type] = {
signerMaterial match {
case info: ScriptSignatureParams[InputInfo] => withSignFrom(info)
case info: ECSignatureParams[InputInfo] => withSignFrom(info)
}
}
def withSignFrom( def withSignFrom(
signerMaterial: ScriptSignatureParams[InputInfo]): ScriptSignatureParams[ signerMaterial: ScriptSignatureParams[InputInfo]): ScriptSignatureParams[
this.type] = { this.type] = {
@ -245,6 +256,7 @@ case class EmptyInputInfo(outPoint: TransactionOutPoint, amount: CurrencyUnit)
override def conditionalPath: ConditionalPath = override def conditionalPath: ConditionalPath =
ConditionalPath.NoCondition ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = Vector.empty override def pubKeys: Vector[ECPublicKey] = Vector.empty
override def requiredSigs: Int = 0
} }
case class P2PKInputInfo( case class P2PKInputInfo(
@ -256,6 +268,8 @@ case class P2PKInputInfo(
ConditionalPath.NoCondition ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = Vector(scriptPubKey.publicKey) override def pubKeys: Vector[ECPublicKey] = Vector(scriptPubKey.publicKey)
override def requiredSigs: Int = 1
} }
case class P2PKHInputInfo( case class P2PKHInputInfo(
@ -269,6 +283,8 @@ case class P2PKHInputInfo(
ConditionalPath.NoCondition ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = Vector(pubKey) override def pubKeys: Vector[ECPublicKey] = Vector(pubKey)
override def requiredSigs: Int = 1
} }
case class P2PKWithTimeoutInputInfo( case class P2PKWithTimeoutInputInfo(
@ -287,6 +303,8 @@ case class P2PKWithTimeoutInputInfo(
override def pubKeys: Vector[ECPublicKey] = override def pubKeys: Vector[ECPublicKey] =
Vector(scriptPubKey.pubKey, scriptPubKey.timeoutPubKey) Vector(scriptPubKey.pubKey, scriptPubKey.timeoutPubKey)
override def requiredSigs: Int = 1
} }
case class MultiSignatureInputInfo( case class MultiSignatureInputInfo(
@ -298,6 +316,8 @@ case class MultiSignatureInputInfo(
ConditionalPath.NoCondition ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = scriptPubKey.publicKeys.toVector override def pubKeys: Vector[ECPublicKey] = scriptPubKey.publicKeys.toVector
override def requiredSigs: Int = scriptPubKey.requiredSigs
} }
case class ConditionalInputInfo( case class ConditionalInputInfo(
@ -332,6 +352,8 @@ case class ConditionalInputInfo(
} }
override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys
override def requiredSigs: Int = nestedInputInfo.requiredSigs
} }
case class LockTimeInputInfo( case class LockTimeInputInfo(
@ -350,6 +372,8 @@ case class LockTimeInputInfo(
hashPreImages) hashPreImages)
override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys
override def requiredSigs: Int = nestedInputInfo.requiredSigs
} }
sealed trait SegwitV0NativeInputInfo extends InputInfo { sealed trait SegwitV0NativeInputInfo extends InputInfo {
@ -390,6 +414,8 @@ case class P2WPKHV0InputInfo(
ConditionalPath.NoCondition ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = Vector(pubKey) override def pubKeys: Vector[ECPublicKey] = Vector(pubKey)
override def requiredSigs: Int = 1
} }
case class P2WSHV0InputInfo( case class P2WSHV0InputInfo(
@ -410,6 +436,8 @@ case class P2WSHV0InputInfo(
hashPreImages) hashPreImages)
override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys
override def requiredSigs: Int = nestedInputInfo.requiredSigs
} }
case class UnassignedSegwitNativeInputInfo( case class UnassignedSegwitNativeInputInfo(
@ -419,7 +447,9 @@ case class UnassignedSegwitNativeInputInfo(
scriptWitness: ScriptWitness, scriptWitness: ScriptWitness,
conditionalPath: ConditionalPath, conditionalPath: ConditionalPath,
pubKeys: Vector[ECPublicKey]) pubKeys: Vector[ECPublicKey])
extends InputInfo extends InputInfo {
override def requiredSigs: Int = pubKeys.length
}
sealed trait P2SHInputInfo extends InputInfo { sealed trait P2SHInputInfo extends InputInfo {
def hashPreImages: Vector[NetworkElement] def hashPreImages: Vector[NetworkElement]
@ -431,6 +461,8 @@ sealed trait P2SHInputInfo extends InputInfo {
def nestedInputInfo: InputInfo def nestedInputInfo: InputInfo
override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys override def pubKeys: Vector[ECPublicKey] = nestedInputInfo.pubKeys
override def requiredSigs: Int = nestedInputInfo.requiredSigs
} }
case class P2SHNonSegwitInputInfo( case class P2SHNonSegwitInputInfo(

View file

@ -57,7 +57,7 @@ object BouncyCastleUtil {
val pubBytes = ByteVector(point.getEncoded(privateKey.isCompressed)) val pubBytes = ByteVector(point.getEncoded(privateKey.isCompressed))
require( require(
ECPublicKey.isFullyValid(pubBytes), ECPublicKey.isFullyValid(pubBytes),
s"Bouncy Castle failed to generate a valid public key, got: ${BytesUtil s"Bouncy Castle failed to generate a valid public key, got: ${CryptoBytesUtil
.encodeHex(pubBytes)}") .encodeHex(pubBytes)}")
ECPublicKey(pubBytes) ECPublicKey(pubBytes)
} }

View file

@ -7,7 +7,7 @@ import scala.math.BigInt
/** /**
* Created by chris on 2/26/16. * Created by chris on 2/26/16.
*/ */
trait BytesUtil { trait CryptoBytesUtil {
def decodeHex(hex: String): ByteVector = { def decodeHex(hex: String): ByteVector = {
if (hex.isEmpty) ByteVector.empty else ByteVector.fromHex(hex).get if (hex.isEmpty) ByteVector.empty else ByteVector.fromHex(hex).get
@ -43,7 +43,7 @@ trait BytesUtil {
} }
def encodeHex(bigInt: BigInt): String = def encodeHex(bigInt: BigInt): String =
BytesUtil.encodeHex(ByteVector(bigInt.toByteArray)) CryptoBytesUtil.encodeHex(ByteVector(bigInt.toByteArray))
/** Tests if a given string is a hexadecimal string. */ /** Tests if a given string is a hexadecimal string. */
def isHex(str: String): Boolean = { def isHex(str: String): Boolean = {
@ -54,7 +54,7 @@ trait BytesUtil {
/** Converts a two character hex string to its byte representation. */ /** Converts a two character hex string to its byte representation. */
def hexToByte(hex: String): Byte = { def hexToByte(hex: String): Byte = {
require(hex.length == 2) require(hex.length == 2)
BytesUtil.decodeHex(hex).head CryptoBytesUtil.decodeHex(hex).head
} }
/** Flips the endianness of the give hex string. */ /** Flips the endianness of the give hex string. */
@ -94,4 +94,4 @@ trait BytesUtil {
} }
} }
object BytesUtil extends BytesUtil object CryptoBytesUtil extends CryptoBytesUtil

View file

@ -8,7 +8,7 @@ import scodec.bits.ByteVector
sealed abstract class ECDigitalSignature { sealed abstract class ECDigitalSignature {
require(r.signum == 1 || r.signum == 0, s"r must not be negative, got $r") require(r.signum == 1 || r.signum == 0, s"r must not be negative, got $r")
require(s.signum == 1 || s.signum == 0, s"s must not be negative, got $s") require(s.signum == 1 || s.signum == 0, s"s must not be negative, got $s")
def hex: String = BytesUtil.encodeHex(bytes) def hex: String = CryptoBytesUtil.encodeHex(bytes)
def bytes: ByteVector def bytes: ByteVector
@ -162,7 +162,7 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
* the second 32 is the value * the second 32 is the value
*/ */
def fromRS(hex: String): ECDigitalSignature = { def fromRS(hex: String): ECDigitalSignature = {
val bytes = BytesUtil.decodeHex(hex) val bytes = CryptoBytesUtil.decodeHex(hex)
fromRS(bytes) fromRS(bytes)
} }
} }

Some files were not shown because too many files have changed in this diff Show more