Merge branch 'master' into eclair_bench

This commit is contained in:
rorp 2020-01-21 10:17:20 -08:00
commit 3124a1afc5
67 changed files with 2080 additions and 947 deletions

View file

@ -21,4 +21,12 @@ class NetworkParametersTest extends BitcoinSUnitTest {
it must "create the correct magic network bytes for regtest" in {
BitcoinSUtil.encodeHex(RegTest.magicBytes) must be("fabfb5da")
}
it must "get the correct Network from string" in {
assert(Networks.fromString("mainnet").contains(MainNet))
assert(Networks.fromString("testnet").contains(TestNet3))
assert(Networks.fromString("regtest").contains(RegTest))
assert(Networks.fromString("").isEmpty)
assert(Networks.fromString("craig wright is a fraud").isEmpty)
}
}

View file

@ -9,7 +9,8 @@ import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.util.{BitcoinSLogger, TransactionTestUtil}
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.testkit.util.TransactionTestUtil
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.{FlatSpec, MustMatchers}
import scodec.bits.ByteVector

View file

@ -72,7 +72,7 @@ class CurrencyUnitTest extends BitcoinSUnitTest {
}
}
it must "subtract satoshis " in {
it must "subtract satoshis" in {
forAll(CurrencyUnitGenerator.satoshis, CurrencyUnitGenerator.satoshis) {
(num1: Satoshis, num2: Satoshis) =>
val result: Try[Int64] = Try(Int64(num1.toBigInt - num2.toBigInt))
@ -150,6 +150,7 @@ class CurrencyUnitTest extends BitcoinSUnitTest {
forAll(CurrencyUnitGenerator.satoshis) { satoshis =>
val b = Bitcoins(satoshis)
assert(b.satoshis == satoshis)
assert(CurrencyUnits.toSatoshis(b) == satoshis)
}
}
@ -164,4 +165,16 @@ class CurrencyUnitTest extends BitcoinSUnitTest {
else actual.isFailure && expected.isFailure
}
}
it must "correctly compare two currency units" in {
forAll(CurrencyUnitGenerator.satoshis, CurrencyUnitGenerator.satoshis) {
(num1, num2) =>
if (num1 > num2)
assert(num1.compare(num2) == 1)
else if (num1 < num2)
assert(num1.compare(num2) == -1)
else
assert(num1.compare(num2) == 0)
}
}
}

View file

@ -15,5 +15,6 @@ class FilterTypeTest extends BitcoinSUnitTest {
assert(FilterType.getCode(FilterType.Basic) == 0)
assert(FilterType.byCode(0) == FilterType.Basic)
assertThrows[IllegalArgumentException](FilterType.byCode(1))
assertThrows[IllegalArgumentException](FilterType.getCode(FilterType.fromHex("ffff")))
}
}

View file

@ -37,4 +37,15 @@ class ServiceIdentifierTest extends BitcoinSUnitTest {
it must "parse NODE_NETWORK_LIMITED" in {
assert(ServiceIdentifier.NODE_NETWORK_LIMITED.nodeNetworkLimited)
}
it must "correctly get a ServiceIdentifier from string" in {
assert(ServiceIdentifier.fromString("NETWORK") == ServiceIdentifier.NODE_NETWORK)
assert(ServiceIdentifier.fromString("NETWORK_LIMITED") == ServiceIdentifier.NODE_NETWORK_LIMITED)
assert(ServiceIdentifier.fromString("WITNESS") == ServiceIdentifier.NODE_WITNESS)
assert(ServiceIdentifier.fromString("BLOOM") == ServiceIdentifier.NODE_BLOOM)
assert(ServiceIdentifier.fromString("GETUTXO") == ServiceIdentifier.NODE_GET_UTXO)
assert(ServiceIdentifier.fromString("COMPACT_FILTERS") == ServiceIdentifier.NODE_COMPACT_FILTERS)
assert(ServiceIdentifier.fromString("XTHIN") == ServiceIdentifier.NODE_XTHIN)
assertThrows[IllegalArgumentException](ServiceIdentifier.fromString("this is invalid"))
}
}

View file

@ -5,6 +5,10 @@ import org.bitcoins.testkit.util.BitcoinSUnitTest
class TypeIdentifierTest extends BitcoinSUnitTest {
it must "Create a MsgUnassigned" in {
assert(TypeIdentifier(100000).isInstanceOf[MsgUnassigned])
}
"MsgTx" must "serialize to 01000000" in {
MsgTx.hex must be("01000000")
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.util.{Base58, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.Base58
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/30/16.

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
import org.bitcoins.core.crypto.Sha256Hash160Digest
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.crypto.{ECPublicKey, Sha256Hash160Digest}
import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, P2PKHScriptPubKey, P2SHScriptPubKey, P2WPKHWitnessSPKV0, ScriptPubKey, WitnessScriptPubKeyV0}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import scala.util.{Failure, Success, Try}
@ -90,4 +90,36 @@ class BitcoinAddressTest extends BitcoinSUnitTest {
val addr = P2SHAddress(scriptPubKey, MainNet)
addr must be(BitcoinAddress("3P14159f73E4gFr7JterCCQh9QjiTjiZrG").get)
}
it must "create a bech32 address from a WitnessScriptPubKey" in {
val scriptPubKey = P2WPKHWitnessSPKV0(ECPublicKey.freshPublicKey)
assert(Bech32Address.fromScriptPubKey(scriptPubKey, RegTest).isSuccess)
}
it must "fail to create a bech32 address from an invalid ScriptPubKey" in {
assert(Bech32Address.fromScriptPubKey(EmptyScriptPubKey, RegTest).isFailure)
}
it must "create an address from a P2PKHScriptPubKey" in {
val scriptPubKey = P2PKHScriptPubKey(ECPublicKey.freshPublicKey)
assert(P2PKHAddress.fromScriptPubKey(scriptPubKey, RegTest).isSuccess)
}
it must "fail to create a P2PKHAddress address from an invalid ScriptPubKey" in {
assert(P2PKHAddress.fromScriptPubKey(EmptyScriptPubKey, RegTest).isFailure)
}
it must "create an address from a P2SHScriptPubKey" in {
val scriptPubKey = P2SHScriptPubKey(EmptyScriptPubKey)
assert(P2SHAddress.fromScriptPubKey(scriptPubKey, RegTest).isSuccess)
}
it must "fail to create a P2SHScriptPubKey address from an invalid ScriptPubKey" in {
assert(P2SHAddress.fromScriptPubKey(EmptyScriptPubKey, RegTest).isFailure)
}
it must "create an address from a ScriptPubKey" in {
val scriptPubKey = P2SHScriptPubKey(EmptyScriptPubKey)
assert(Address.fromScriptPubKey(scriptPubKey, RegTest).isSuccess)
}
}

View file

@ -2,8 +2,8 @@ package org.bitcoins.core.protocol
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -2,17 +2,11 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPrivateKey
import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant.{
BytesToPushOntoStack,
ScriptConstant,
ScriptNumber,
ScriptToken
}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant, ScriptNumber, ScriptToken}
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP}
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by tom on 9/21/16.

View file

@ -2,17 +2,11 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPrivateKey
import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant.{
BytesToPushOntoStack,
ScriptConstant,
ScriptNumber,
ScriptToken
}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant, ScriptNumber, ScriptToken}
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.locktime.OP_CHECKSEQUENCEVERIFY
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP}
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by tom on 9/21/16.

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/8/16.

View file

@ -1,7 +1,6 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.util.TransactionTestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TransactionTestUtil}
/**
* Created by chris on 3/8/16.

View file

@ -2,8 +2,7 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECDigitalSignature
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 4/1/16.

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECDigitalSignature
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/16/16.

View file

@ -1,12 +1,9 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.script.constant.{
BytesToPushOntoStack,
OP_0,
ScriptConstant
}
import org.bitcoins.core.util.{BitcoinSLogger, TestUtil}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, OP_0, ScriptConstant}
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
/**

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/2/16.

View file

@ -6,8 +6,8 @@ import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.stack.OP_DUP
import org.bitcoins.core.util.{CryptoUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.CryptoUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 1/14/16.

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto._
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -5,15 +5,11 @@ import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.Int32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCase
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
Transaction,
TransactionOutput,
WitnessTransaction
}
import org.bitcoins.core.protocol.transaction.{BaseTransaction, Transaction, TransactionOutput, WitnessTransaction}
import org.bitcoins.core.script.crypto.{HashType, SIGHASH_ALL}
import org.bitcoins.core.serializers.script.RawScriptSignatureParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, TestUtil}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
import scodec.bits.ByteVector
import spray.json._

View file

@ -1,11 +1,7 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.script.{
EmptyScriptSignature,
P2PKScriptSignature
}
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, P2PKScriptSignature}
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/30/16.

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/30/16.

View file

@ -1,10 +1,6 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.crypto.{
BaseTxSigComponent,
WitnessTxSigComponentP2SH,
WitnessTxSigComponentRaw
}
import org.bitcoins.core.crypto.{BaseTxSigComponent, WitnessTxSigComponentP2SH, WitnessTxSigComponentRaw}
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
@ -14,14 +10,13 @@ import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.serializers.transaction.RawBaseTransactionParser
import org.bitcoins.core.util.{CryptoUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.slf4j.LoggerFactory
import spray.json._
import org.bitcoins.core.util.CryptoUtil
import org.bitcoins.testkit.core.gen.TransactionGenerators
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits._
import spray.json._
import scala.io.Source
import org.bitcoins.testkit.core.gen.TransactionGenerators
class TransactionTest extends BitcoinSUnitTest {
behavior of "Transaction"
@ -71,10 +66,11 @@ class TransactionTest extends BitcoinSUnitTest {
"0000000000000000000000000000000000000000000000000000000000000000")
}
it must "calculate the size of a tranaction correctly" in {
it must "calculate the size of a transaction correctly" in {
val rawTx = TestUtil.rawTransaction
val tx = Transaction(rawTx)
//size is in bytes so divide by 2
assert(tx.size == tx.totalSize)
tx.size must be(rawTx.size / 2)
}
@ -135,7 +131,7 @@ class TransactionTest extends BitcoinSUnitTest {
tx2.size must be(216)
tx2.weight must be(534)
tx2.vsize must be(134)
tx2.baseSize must be(106)
}
it must "parse a transaction with an OP_PUSHDATA4 op code but not enough data to push" in {

View file

@ -6,8 +6,7 @@ import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.script.constant.{OP_0, OP_1}
import org.bitcoins.core.script.flag.ScriptFlagFactory
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/31/16.

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.script
import org.bitcoins.core.script.constant._
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 2/6/16.

View file

@ -3,12 +3,9 @@ package org.bitcoins.core.script.arithmetic
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlag
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{
ExecutedScriptProgram,
ExecutionInProgressScriptProgram
}
import org.bitcoins.core.util.{ScriptProgramTestUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram}
import org.bitcoins.core.util.ScriptProgramTestUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scala.util.Try

View file

@ -3,8 +3,7 @@ package org.bitcoins.core.script.bitwise
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result.ScriptErrorInvalidStackOperation
import org.bitcoins.core.script.ExecutedScriptProgram
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 1/6/16.

View file

@ -3,12 +3,9 @@ package org.bitcoins.core.script.constant
import org.bitcoins.core.script.bitwise.OP_EQUAL
import org.bitcoins.core.script.crypto.OP_CHECKMULTISIGVERIFY
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptVerifyMinimalData}
import org.bitcoins.core.script.result.{
ScriptErrorBadOpCode,
ScriptErrorMinimalData
}
import org.bitcoins.core.util.{ScriptProgramTestUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.script.result.{ScriptErrorBadOpCode, ScriptErrorMinimalData}
import org.bitcoins.core.util.ScriptProgramTestUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -1,20 +1,14 @@
package org.bitcoins.core.script.control
import org.bitcoins.core.script.{
ExecutedScriptProgram,
ExecutionInProgressScriptProgram,
StartedScriptProgram
}
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, StartedScriptProgram}
import org.bitcoins.core.script.arithmetic.OP_ADD
import org.bitcoins.core.script.bitwise.OP_EQUAL
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.reserved.{OP_RESERVED, OP_VER}
import org.bitcoins.core.script.result.{
ScriptErrorInvalidStackOperation,
ScriptErrorOpReturn
}
import org.bitcoins.core.script.result.{ScriptErrorInvalidStackOperation, ScriptErrorOpReturn}
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util._
import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
/**

View file

@ -7,13 +7,10 @@ import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.{
ScriptFlagFactory,
ScriptVerifyDerSig,
ScriptVerifyNullDummy
}
import org.bitcoins.core.script.flag.{ScriptFlagFactory, ScriptVerifyDerSig, ScriptVerifyNullDummy}
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, ScriptProgramTestUtil, TestUtil}
import org.bitcoins.core.util.{BitcoinSLogger, ScriptProgramTestUtil}
import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
import scala.util.Try

View file

@ -1,24 +1,14 @@
package org.bitcoins.core.script.interpreter
import org.bitcoins.core.crypto.{
BaseTxSigComponent,
WitnessTxSigComponentP2SH,
WitnessTxSigComponentRaw
}
import org.bitcoins.core.crypto.{BaseTxSigComponent, WitnessTxSigComponentP2SH, WitnessTxSigComponentRaw}
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{
TransactionOutput,
WitnessTransaction
}
import org.bitcoins.core.protocol.transaction.{TransactionOutput, WitnessTransaction}
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.flag.ScriptFlagFactory
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCase
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCaseProtocol._
import org.bitcoins.core.util._
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.slf4j.LoggerFactory
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TransactionTestUtil}
import spray.json._
import scala.io.Source

View file

@ -6,12 +6,9 @@ import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.{OP_0, ScriptNumber}
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{
ExecutedScriptProgram,
PreExecutionScriptProgram
}
import org.bitcoins.core.util.{ScriptProgramTestUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.script.{ExecutedScriptProgram, PreExecutionScriptProgram}
import org.bitcoins.core.util.ScriptProgramTestUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 3/30/16.

View file

@ -3,8 +3,7 @@ package org.bitcoins.core.script.splice
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result.ScriptErrorInvalidStackOperation
import org.bitcoins.core.script.ExecutedScriptProgram
import org.bitcoins.core.util.TestUtil
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 2/4/16.

View file

@ -3,8 +3,8 @@ package org.bitcoins.core.script.stack
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.ExecutedScriptProgram
import org.bitcoins.core.util.{BitcoinSUtil, ScriptProgramTestUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.{BitcoinSUtil, ScriptProgramTestUtil}
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
/**
* Created by chris on 1/6/16.

View file

@ -1,12 +1,12 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey}
import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.stack.OP_DUP
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**
@ -21,6 +21,10 @@ class RawScriptPubKeyParserTest extends BitcoinSUnitTest {
TestUtil.rawScriptPubKey)
}
it must "read an EmptyScriptPubKey" in {
assert(RawScriptPubKeyParser.read(ByteVector.empty) == EmptyScriptPubKey)
}
it must "read a raw scriptPubKey and give us the expected asm" in {
val scriptPubKey = RawScriptPubKeyParser.read(TestUtil.rawP2PKHScriptPubKey)
val expectedAsm: Seq[ScriptToken] =

View file

@ -1,9 +1,9 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptSignature}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**
@ -21,6 +21,10 @@ class RawScriptSignatureParserTest extends BitcoinSUnitTest {
encode(RawScriptSignatureParser.write(scriptSig)) must be(rawScriptSig)
}
it must "read an EmptyScriptSignature" in {
assert(RawScriptSignatureParser.read(ByteVector.empty) == EmptyScriptSignature)
}
it must "read then write a raw script sig" in {
//from this tx
//https://tbtc.blockr.io/api/v1/tx/raw/bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74

View file

@ -4,17 +4,13 @@ import org.bitcoins.core.script.arithmetic.OP_1ADD
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.control.{OP_ELSE, OP_ENDIF, OP_IF, OP_NOTIF}
import org.bitcoins.core.script.crypto.{
OP_CHECKMULTISIG,
OP_CHECKSIG,
OP_HASH160
}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.reserved.OP_NOP
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.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -1,12 +1,9 @@
package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionConstants
}
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionConstants}
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -1,11 +1,9 @@
package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.{
TransactionConstants,
TransactionInput
}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, TestUtil}
import org.bitcoins.core.protocol.transaction.{TransactionConstants, TransactionInput}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.bitcoins.testkit.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
import scodec.bits.ByteVector

View file

@ -10,7 +10,7 @@ import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.reserved.{OP_NOP, OP_RESERVED}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.testkit.core.gen.ScriptGenerators
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/**

View file

@ -21,9 +21,9 @@ import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.{
BitcoinUTXOSpendingInfo,
BitcoinUTXOSpendingInfoFull,
ConditionalPath,
LockTimeSpendingInfo,
LockTimeSpendingInfoFull,
UTXOSpendingInfo
}
import org.bitcoins.core.wallet.fee.SatoshisPerByte
@ -35,6 +35,7 @@ import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder.UTXOMap
import org.bitcoins.testkit.Implicits._
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import org.scalacheck.Gen
class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val tc = TransactionConstants
@ -56,10 +57,10 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
@ -67,14 +68,14 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
)
val listF =
BitcoinTxBuilder(destinations = Seq.empty,
BitcoinTxBuilder(destinations = Vector.empty,
utxos = List(utxo),
feeRate = SatoshisPerByte(1.sat),
changeSPK = EmptyScriptPubKey,
network = RegTest)
val vecF =
BitcoinTxBuilder(destinations = Seq.empty,
BitcoinTxBuilder(destinations = Vector.empty,
utxos = List(utxo),
feeRate = SatoshisPerByte(1.sat),
changeSPK = EmptyScriptPubKey,
@ -91,16 +92,16 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
@ -122,16 +123,16 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
it must "fail to build a transaction when we pass in a negative fee rate" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
@ -152,20 +153,20 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
it must "fail a transaction when the user invariants fail" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
Vector(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
None,
HashType.sigHashAll,
conditionalPath =
ConditionalPath.NoConditionsLeft)
val utxo = BitcoinUTXOSpendingInfoFull(outPoint,
creditingOutput,
Vector(privKey),
None,
None,
HashType.sigHashAll,
conditionalPath =
ConditionalPath.NoConditionsLeft)
val utxoMap: UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(currencyUnit = Satoshis(1))
val txBuilder = BitcoinTxBuilder(destinations = destinations,
@ -174,7 +175,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
changeSPK = EmptyScriptPubKey,
network = TestNet3)
//trivially false
val f = (_: Seq[BitcoinUTXOSpendingInfo], _: Transaction) => false
val f = (_: Seq[BitcoinUTXOSpendingInfoFull], _: Transaction) => false
val resultFuture = txBuilder.flatMap(_.sign(f))
recoverToSucceededIf[IllegalArgumentException] {
resultFuture
@ -185,29 +186,29 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val creditingOutput =
TransactionOutput(value = CurrencyUnits.zero, scriptPubKey = spk)
val destinations = {
Seq(
Vector(
TransactionOutput(value = Satoshis.one,
scriptPubKey = EmptyScriptPubKey))
}
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
conditionalPath = ConditionalPath.NoConditionsLeft
)
val utxoMap: UTXOMap = Map(outPoint -> utxo)
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(
val utxoSpendingInfo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
@ -221,7 +222,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
changeSPK = EmptyScriptPubKey,
network = TestNet3)
val txBuilderTuple = BitcoinTxBuilder(destinations = destinations,
utxos = Seq(utxoSpendingInfo),
utxos = Vector(utxoSpendingInfo),
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
@ -238,14 +239,14 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2sh)
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = Some(EmptyScriptPubKey),
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,
@ -259,14 +260,14 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wsh)
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
Vector(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint,
creditingOutput,
Seq(privKey),
Vector(privKey),
None,
Some(P2WSHWitnessV0(EmptyScriptPubKey)),
HashType.sigHashAll,
@ -279,16 +280,16 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val p2pkh = P2PKHScriptPubKey(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
Vector(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint,
creditingOutput,
Seq(privKey),
Vector(privKey),
None,
Some(P2WSHWitnessV0(EmptyScriptPubKey)),
HashType.sigHashAll,
@ -313,17 +314,17 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val pubKey2 = ECPrivateKey().publicKey
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, vout = UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
val utxo = BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
hashType = HashType.sigHashAll,
@ -349,15 +350,15 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
TransactionOutput(value = CurrencyUnits.zero, scriptPubKey = p2wpkh)
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = tc.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, vout = UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
hashType = HashType.sigHashAll,
@ -371,14 +372,14 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wpkh)
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
Vector(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint,
creditingOutput,
Seq(privKey),
Vector(privKey),
None,
Some(P2WSHWitnessV0(EmptyScriptPubKey)),
HashType.sigHashAll,
@ -396,7 +397,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
CLTVScriptPubKey(ScriptNumber(lockTime),
P2PKScriptPubKey(fundingPrivKey.publicKey))
val cltvSpendingInfo = LockTimeSpendingInfo(
val cltvSpendingInfo = LockTimeSpendingInfoFull(
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero),
Bitcoins.one,
cltvSPK,
@ -430,7 +431,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
CLTVScriptPubKey(ScriptNumber(lockTime),
P2PKScriptPubKey(fundingPrivKey.publicKey))
val cltvSpendingInfo = LockTimeSpendingInfo(
val cltvSpendingInfo = LockTimeSpendingInfoFull(
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero),
Bitcoins.one,
cltvSPK,
@ -469,7 +470,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
CLTVScriptPubKey(ScriptNumber(lockTime2),
P2PKScriptPubKey(fundingPrivKey2.publicKey))
val cltvSpendingInfo1 = LockTimeSpendingInfo(
val cltvSpendingInfo1 = LockTimeSpendingInfoFull(
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.zero),
Bitcoins.one,
cltvSPK1,
@ -478,7 +479,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
ConditionalPath.NoConditionsLeft
)
val cltvSpendingInfo2 = LockTimeSpendingInfo(
val cltvSpendingInfo2 = LockTimeSpendingInfoFull(
TransactionOutPoint(DoubleSha256DigestBE.empty, UInt32.one),
Bitcoins.one,
cltvSPK2,
@ -503,74 +504,79 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
)
}
def verifyScript(tx: Transaction, utxos: Seq[UTXOSpendingInfo]): Boolean = {
val programs: Seq[PreExecutionScriptProgram] = tx.inputs.zipWithIndex.map {
case (input: TransactionInput, idx: Int) =>
val outpoint = input.previousOutput
def verifyScript(tx: Transaction,
utxos: Vector[UTXOSpendingInfo]): 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 creditingTx =
utxos.find(u => u.outPoint.txId == outpoint.txId).get
val output = creditingTx.output
val output = creditingTx.output
val spk = output.scriptPubKey
val spk = output.scriptPubKey
val amount = output.value
val amount = output.value
val txSigComponent = spk match {
case witSPK: WitnessScriptPubKeyV0 =>
val o = TransactionOutput(amount, witSPK)
WitnessTxSigComponentRaw(tx.asInstanceOf[WitnessTransaction],
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 p2sh: 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),
o,
output,
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 p2sh: 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)
}
PreExecutionScriptProgram(txSigComponent)
}
ScriptInterpreter.runAllVerify(programs)
}
private val outputGen = CreditingTxGen.outputs
.flatMap { creditingTxsInfo =>
val creditingOutputs = creditingTxsInfo.map(c => c.output)
val creditingOutputsAmt = creditingOutputs.map(_.value)
val totalAmount = creditingOutputsAmt.fold(CurrencyUnits.zero)(_ + _)
private def outputGen(outputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]]) = {
outputs
.flatMap { creditingTxsInfo =>
val creditingOutputs = creditingTxsInfo.map(c => c.output)
val creditingOutputsAmt = creditingOutputs.map(_.value)
val totalAmount = creditingOutputsAmt.fold(CurrencyUnits.zero)(_ + _)
TransactionGenerators.smallOutputs(totalAmount).map { destinations =>
(creditingTxsInfo, destinations)
TransactionGenerators.smallOutputs(totalAmount).map { destinations =>
(creditingTxsInfo, destinations)
}
}
}
.suchThat(_._1.nonEmpty)
.suchThat(_._1.nonEmpty)
}
it must "sign a mix of spks in a tx and then have it verified" in {
forAllAsync(outputGen,
forAllAsync(outputGen(CreditingTxGen.outputs),
ScriptGenerators.scriptPubKey,
ChainParamsGenerator.bitcoinNetworkParams) {
case ((creditingTxsInfo, destinations), changeSPK, network) =>
@ -583,13 +589,13 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val txF = builder.flatMap(_.sign)
txF.map { tx =>
assert(verifyScript(tx, creditingTxsInfo))
assert(verifyScript(tx, creditingTxsInfo.toVector))
}
}
}
it must "sign a mix of p2sh/p2wsh in a tx and then have it verified" in {
forAllAsync(outputGen,
forAllAsync(outputGen(CreditingTxGen.nestedOutputs),
ScriptGenerators.scriptPubKey,
ChainParamsGenerator.bitcoinNetworkParams) {
case ((creditingTxsInfo, destinations), changeSPK, network) =>
@ -602,7 +608,7 @@ class BitcoinTxBuilderTest extends BitcoinSAsyncTest {
val txF = builder.flatMap(_.sign)
txF.map { tx =>
assert(verifyScript(tx, creditingTxsInfo))
assert(verifyScript(tx, creditingTxsInfo.toVector))
}
}
}

View file

@ -1,24 +1,40 @@
package org.bitcoins.core.wallet.signer
import org.bitcoins.core.protocol.script.WitnessScriptPubKey
import org.bitcoins.core.crypto.ECDigitalSignature
import org.bitcoins.core.currency.{CurrencyUnits, Satoshis}
import org.bitcoins.core.protocol.script.{
EmptyScriptWitness,
P2WPKHWitnessV0,
P2WSHWitnessV0,
WitnessScriptPubKey
}
import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.{
P2WPKHV0SpendingInfo,
P2WSHV0SpendingInfo,
P2WSHV0SpendingInfoFull,
UTXOSpendingInfoSingle,
UnassignedSegwitNativeUTXOSpendingInfo
}
import org.bitcoins.testkit.core.gen.{
ChainParamsGenerator,
CreditingTxGen,
GenUtil,
ScriptGenerators,
TransactionGenerators
}
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import scala.concurrent.ExecutionContext
import scala.concurrent.{ExecutionContext, Future}
class SignerTest extends BitcoinSAsyncTest {
implicit val ec: ExecutionContext = ExecutionContext.global
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
generatorDrivenConfigNewCode
behavior of "Signer"
it should "fail to sign a UnassignedSegwit UTXO" in {
@ -59,10 +75,87 @@ class SignerTest extends BitcoinSAsyncTest {
val dumbSpendingInfo = GenUtil.sample(CreditingTxGen.output)
val p2wsh = GenUtil
.sample(CreditingTxGen.p2wshOutput)
.asInstanceOf[P2WSHV0SpendingInfo]
.asInstanceOf[P2WSHV0SpendingInfoFull]
val tx = GenUtil.sample(TransactionGenerators.baseTransaction)
recoverToSucceededIf[IllegalArgumentException] {
P2WSHSigner.sign(dumbSpendingInfo, tx, isDummySignature = false, p2wsh)
}
}
private val outputGen = CreditingTxGen.outputs
.flatMap { creditingTxsInfo =>
val creditingOutputs = creditingTxsInfo.map(c => c.output)
val creditingOutputsAmt = creditingOutputs.map(_.value)
val totalAmount = creditingOutputsAmt.fold(CurrencyUnits.zero)(_ + _)
TransactionGenerators.smallOutputs(totalAmount).map { destinations =>
(creditingTxsInfo, destinations)
}
}
.suchThat(_._1.nonEmpty)
it must "sign a mix of spks in a tx and then verify that single signing agrees" in {
forAllAsync(outputGen,
ScriptGenerators.scriptPubKey,
ChainParamsGenerator.bitcoinNetworkParams) {
case ((creditingTxsInfos, destinations), changeSPK, network) =>
val fee = SatoshisPerVirtualByte(Satoshis(1000))
for {
builder <- BitcoinTxBuilder(destinations,
creditingTxsInfos,
fee,
changeSPK._1,
network)
unsignedTx <- builder.unsignedTx
signedTx <- builder.sign
singleSigs: Vector[Vector[ECDigitalSignature]] <- {
val singleInfosVec: Vector[Vector[UTXOSpendingInfoSingle]] =
creditingTxsInfos.toVector.map(_.toSingles)
val sigVecFs = singleInfosVec.map { singleInfos =>
val sigFs = singleInfos.map { singleInfo =>
val keyAndSigF =
BitcoinSignerSingle.signSingle(singleInfo,
unsignedTx,
isDummySignature = false)
keyAndSigF.map(_._2)
}
Future.sequence(sigFs)
}
Future.sequence(sigVecFs)
}
} yield {
signedTx.inputs.zipWithIndex.foreach {
case (input, inputIndex) =>
val infoAndIndexOpt = creditingTxsInfos.zipWithIndex
.find(_._1.outPoint == input.previousOutput)
assert(infoAndIndexOpt.isDefined)
val (info, index) = infoAndIndexOpt.get
val sigs = singleSigs(index)
val expectedSigs = if (info.scriptWitnessOpt.isEmpty) {
input.scriptSignature.signatures
} else {
signedTx
.asInstanceOf[WitnessTransaction]
.witness
.witnesses(inputIndex) match {
case p2wpkh: P2WPKHWitnessV0 => Vector(p2wpkh.signature)
case p2wsh: P2WSHWitnessV0 => p2wsh.signatures
case EmptyScriptWitness => Vector.empty
}
}
assert(sigs.length == expectedSigs.length)
assert(sigs.forall(expectedSigs.contains))
}
succeed
}
}
}
}

View file

@ -42,11 +42,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHNoNestSpendingInfo(
P2SHNoNestSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScript = randomRawSPK,
conditionalPath = ConditionalPath.NoConditionsLeft
@ -63,11 +63,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHNestedSegwitV0UTXOSpendingInfo(
P2SHNestedSegwitV0UTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScript = randomWitnessSPK,
scriptWitness = P2WPKHWitnessV0(pubKey),
@ -85,16 +85,16 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHNestedSegwitV0UTXOSpendingInfo(
P2SHNestedSegwitV0UTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScript = P2WPKHWitnessSPKV0(pubKey),
scriptWitness = P2WPKHWitnessV0(ECPrivateKey.freshPrivateKey.publicKey),
@ -112,11 +112,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHNestedSegwitV0UTXOSpendingInfo(
P2SHNestedSegwitV0UTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScript = randomWitnessSPK,
scriptWitness = P2WSHWitnessV0(P2PKScriptPubKey(pubKey)),
@ -134,11 +134,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHNestedSegwitV0UTXOSpendingInfo(
P2SHNestedSegwitV0UTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScript = P2WSHWitnessSPKV0(P2PKScriptPubKey(pubKey)),
scriptWitness = P2WSHWitnessV0(randomRawSPK),
@ -157,11 +157,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
SegwitV0NativeUTXOSpendingInfo(
SegwitV0NativeUTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2wpkh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
scriptWitness = P2WPKHWitnessV0(ECPrivateKey.freshPrivateKey.publicKey),
conditionalPath = ConditionalPath.NoConditionsLeft
@ -179,11 +179,11 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
SegwitV0NativeUTXOSpendingInfo(
SegwitV0NativeUTXOSpendingInfoFull(
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2wsh,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
scriptWitness = P2WSHWitnessV0(randomRawSPK),
conditionalPath = ConditionalPath.NoConditionsLeft
@ -209,10 +209,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2sh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = None,
scriptWitnessOpt = None,
@ -230,15 +230,15 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2sh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = Some(P2WPKHWitnessSPKV0(pubKey)),
scriptWitnessOpt = None,
@ -256,15 +256,15 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val creditingTx = BaseTransaction(version =
TransactionConstants.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
outputs = Vector(creditingOutput),
lockTime = TransactionConstants.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[UnsupportedOperationException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2sh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = Some(P2WPKHWitnessSPKV0(pubKey)),
scriptWitnessOpt = Some(EmptyScriptWitness),
@ -284,10 +284,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[UnsupportedOperationException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2sh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = Some(unassingedWitnessSPK),
scriptWitnessOpt = None,
@ -306,10 +306,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[UnsupportedOperationException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2wpkh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = None,
scriptWitnessOpt = Some(EmptyScriptWitness),
@ -328,10 +328,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, p2wpkh),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = None,
scriptWitnessOpt = None,
@ -340,7 +340,7 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
}
}
it should "successfully return UnassingedSegwitNativeUTXOSpendingInfo" in {
it should "successfully return UnassingedSegwitNativeUTXOSpendingInfoFull" in {
val unassingedWitnessSPK = UnassignedWitnessScriptPubKey.fromAsm(
P2WPKHWitnessSPKV0(ECPublicKey.freshPublicKey).asm)
@ -353,10 +353,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val spendingInfo =
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, unassingedWitnessSPK),
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
redeemScriptOpt = None,
scriptWitnessOpt = None,
@ -368,7 +368,7 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = unassingedWitnessSPK,
signers = Seq(privKey),
signers = Vector(privKey),
hashType = HashType.sigHashAll,
scriptWitness = EmptyScriptWitness,
conditionalPath = ConditionalPath.NoConditionsLeft
@ -386,10 +386,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[UnsupportedOperationException] {
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
outPoint = outPoint,
output = TransactionOutput(CurrencyUnits.zero, spk),
signers = Seq(privKey),
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll,

View file

@ -190,7 +190,7 @@ object Bech32Address extends AddressFactory[Bech32Address] {
np: NetworkParameters): Try[Bech32Address] =
spk match {
case witSPK: WitnessScriptPubKey =>
Bech32Address.fromScriptPubKey(witSPK, np)
Success(Bech32Address(witSPK, np))
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
_: P2SHScriptPubKey | _: LockTimeScriptPubKey |

View file

@ -91,6 +91,17 @@ sealed abstract class P2WSHWitnessV0 extends ScriptWitnessV0 {
val cmpct = CompactSizeUInt.calc(stack.head)
RawScriptPubKey.fromBytes(cmpct.bytes ++ stack.head)
}
lazy val signatures: Vector[ECDigitalSignature] = {
// ECDigital signatures are between 71 and 73 bytes long
// with a exponential decay on the probability of smaller sigs
// [[https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm]]
val relevantStack = stack.toVector.tail.filter(bytes =>
bytes.length >= 67 && bytes.length <= 73)
relevantStack.map(ECDigitalSignature.fromBytes)
}
override def toString =
s"P2WSHWitnessV0(${stack.map(BitcoinSUtil.encodeHex(_)).toString})"
}

View file

@ -1078,6 +1078,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
val inputOutputsNotZero =
!(transaction.inputs.isEmpty || transaction.outputs.isEmpty)
val txNotLargerThanBlock = transaction.bytes.size < Consensus.maxBlockSize
val txNotHeavierThanBlock = transaction.weight < Consensus.maxBlockWeight
val outputsSpendValidAmountsOfMoney = !transaction.outputs.exists(o =>
o.value < CurrencyUnits.zero || o.value > Consensus.maxMoney)
@ -1094,7 +1095,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
} else {
!transaction.inputs.exists(_.previousOutput == EmptyTransactionOutPoint)
}
inputOutputsNotZero && txNotLargerThanBlock && outputsSpendValidAmountsOfMoney &&
inputOutputsNotZero && txNotLargerThanBlock && txNotHeavierThanBlock && outputsSpendValidAmountsOfMoney &&
allOutputsValidMoneyRange && noDuplicateInputs && isValidScriptSigForCoinbaseTx
}

View file

@ -20,18 +20,18 @@ import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.signer._
import org.bitcoins.core.wallet.utxo.{
BitcoinUTXOSpendingInfo,
ConditionalSpendingInfo,
BitcoinUTXOSpendingInfoFull,
ConditionalSpendingInfoFull,
EmptySpendingInfo,
LockTimeSpendingInfo,
MultiSignatureSpendingInfo,
LockTimeSpendingInfoFull,
MultiSignatureSpendingInfoFull,
P2PKHSpendingInfo,
P2PKSpendingInfo,
P2PKWithTimeoutSpendingInfo,
P2SHSpendingInfo,
P2SHSpendingInfoFull,
P2WPKHV0SpendingInfo,
P2WSHV0SpendingInfo,
UTXOSpendingInfo,
P2WSHV0SpendingInfoFull,
UTXOSpendingInfoFull,
UnassignedSegwitNativeUTXOSpendingInfo
}
@ -85,7 +85,7 @@ sealed abstract class TxBuilder {
*/
def utxoMap: TxBuilder.UTXOMap
def utxos: Seq[UTXOSpendingInfo] = utxoMap.values.toSeq
def utxos: Seq[UTXOSpendingInfoFull] = utxoMap.values.toSeq
/** This represents the rate, in [[org.bitcoins.core.wallet.fee.FeeUnit FeeUnit]], we
* should pay for this transaction */
@ -140,8 +140,9 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
override def utxoMap: BitcoinTxBuilder.UTXOMap
override def sign(implicit ec: ExecutionContext): Future[Transaction] = {
val f: (Seq[BitcoinUTXOSpendingInfo], Transaction) => Boolean = { (_, _) =>
true
val f: (Seq[BitcoinUTXOSpendingInfoFull], Transaction) => Boolean = {
(_, _) =>
true
}
sign(f)
}
@ -212,7 +213,8 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
* @param invariants - invariants that should hold true when we are done signing the transaction
* @return the signed transaction, or a [[TxBuilderError]] indicating what went wrong when signing the tx
*/
def sign(invariants: (Seq[BitcoinUTXOSpendingInfo], Transaction) => Boolean)(
def sign(
invariants: (Seq[BitcoinUTXOSpendingInfoFull], Transaction) => Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
val utxos = utxoMap.values.toList
val signedTxWithFee = unsignedTx.flatMap { utx: Transaction =>
@ -237,7 +239,7 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
}
private def loop(
remaining: List[BitcoinUTXOSpendingInfo],
remaining: List[BitcoinUTXOSpendingInfoFull],
txInProgress: Transaction,
dummySignatures: Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = remaining match {
@ -254,7 +256,7 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
* @return either the transaction with the signed input added, or a [[TxBuilderError]]
*/
private def signAndAddInput(
utxo: BitcoinUTXOSpendingInfo,
utxo: BitcoinUTXOSpendingInfoFull,
unsignedTx: Transaction,
dummySignatures: Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
@ -266,7 +268,7 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
utxo match {
case _: UnassignedSegwitNativeUTXOSpendingInfo =>
Future.fromTry(TxBuilderError.NoSigner)
case _: BitcoinUTXOSpendingInfo =>
case _: BitcoinUTXOSpendingInfoFull =>
BitcoinSigner
.sign(utxo, unsignedTx, dummySignatures)
.map(_.transaction)
@ -303,7 +305,8 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
* locktime set to the same value (or higher) than the output it is spending.
* See BIP65 for more info
*/
private def calcLockTime(utxos: Seq[BitcoinUTXOSpendingInfo]): Try[UInt32] = {
private def calcLockTime(
utxos: Seq[BitcoinUTXOSpendingInfoFull]): Try[UInt32] = {
def computeNextLockTime(
currentLockTimeOpt: Option[UInt32],
locktime: Long): Try[UInt32] = {
@ -335,14 +338,14 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
@tailrec
def loop(
remaining: Seq[BitcoinUTXOSpendingInfo],
remaining: Seq[BitcoinUTXOSpendingInfoFull],
currentLockTimeOpt: Option[UInt32]): Try[UInt32] =
remaining match {
case Nil =>
Success(currentLockTimeOpt.getOrElse(TransactionConstants.lockTime))
case spendingInfo +: newRemaining =>
spendingInfo match {
case lockTime: LockTimeSpendingInfo =>
case lockTime: LockTimeSpendingInfoFull =>
lockTime.scriptPubKey match {
case _: CSVScriptPubKey =>
loop(newRemaining, currentLockTimeOpt)
@ -370,17 +373,17 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
case _: Failure[UInt32] => result
}
}
case p2sh: P2SHSpendingInfo =>
case p2sh: P2SHSpendingInfoFull =>
loop(p2sh.nestedSpendingInfo +: newRemaining, currentLockTimeOpt)
case p2wsh: P2WSHV0SpendingInfo =>
case p2wsh: P2WSHV0SpendingInfoFull =>
loop(p2wsh.nestedSpendingInfo +: newRemaining, currentLockTimeOpt)
case conditional: ConditionalSpendingInfo =>
case conditional: ConditionalSpendingInfoFull =>
loop(conditional.nestedSpendingInfo +: newRemaining,
currentLockTimeOpt)
case _: P2WPKHV0SpendingInfo |
_: UnassignedSegwitNativeUTXOSpendingInfo |
_: P2PKSpendingInfo | _: P2PKHSpendingInfo |
_: MultiSignatureSpendingInfo | _: EmptySpendingInfo =>
_: MultiSignatureSpendingInfoFull | _: EmptySpendingInfo =>
// none of these scripts affect the locktime of a tx
loop(newRemaining, currentLockTimeOpt)
}
@ -396,17 +399,17 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
* See BIP68/112 and BIP65 for more info
*/
private def calcSequenceForInputs(
utxos: Seq[UTXOSpendingInfo],
utxos: Seq[UTXOSpendingInfoFull],
isRBFEnabled: Boolean): Seq[TransactionInput] = {
@tailrec
def loop(
remaining: Seq[UTXOSpendingInfo],
remaining: Seq[UTXOSpendingInfoFull],
accum: Seq[TransactionInput]): Seq[TransactionInput] =
remaining match {
case Nil => accum.reverse
case spendingInfo +: newRemaining =>
spendingInfo match {
case lockTime: LockTimeSpendingInfo =>
case lockTime: LockTimeSpendingInfoFull =>
val sequence = lockTime.scriptPubKey match {
case csv: CSVScriptPubKey => solveSequenceForCSV(csv.locktime)
case _: CLTVScriptPubKey => UInt32.zero
@ -431,16 +434,16 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
UInt32.zero)
loop(newRemaining, input +: accum)
}
case p2sh: P2SHSpendingInfo =>
case p2sh: P2SHSpendingInfoFull =>
loop(p2sh.nestedSpendingInfo +: newRemaining, accum)
case p2wsh: P2WSHV0SpendingInfo =>
case p2wsh: P2WSHV0SpendingInfoFull =>
loop(p2wsh.nestedSpendingInfo +: newRemaining, accum)
case conditional: ConditionalSpendingInfo =>
case conditional: ConditionalSpendingInfoFull =>
loop(conditional.nestedSpendingInfo +: newRemaining, accum)
case _: P2WPKHV0SpendingInfo |
_: UnassignedSegwitNativeUTXOSpendingInfo |
_: P2PKSpendingInfo | _: P2PKHSpendingInfo |
_: MultiSignatureSpendingInfo | _: EmptySpendingInfo =>
_: MultiSignatureSpendingInfoFull | _: EmptySpendingInfo =>
//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
@ -462,7 +465,7 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
inputIndex: UInt32,
p2wpkh: P2WPKHWitnessSPKV0,
output: TransactionOutput,
utxo: UTXOSpendingInfo,
utxo: UTXOSpendingInfoFull,
hashType: HashType,
dummySignatures: Boolean): Future[Transaction] = {
//special rule for p2sh(p2wpkh)
@ -520,7 +523,7 @@ object TxBuilder {
/** This contains all the information needed to create a valid
* [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] that spends this utxo */
type UTXOMap = Map[TransactionOutPoint, UTXOSpendingInfo]
type UTXOMap = Map[TransactionOutPoint, UTXOSpendingInfoFull]
private val logger = BitcoinSLogger.logger
/** Runs various sanity checks on the final version of the signed transaction from TxBuilder */
@ -639,7 +642,7 @@ object TxBuilder {
}
object BitcoinTxBuilder {
type UTXOMap = Map[TransactionOutPoint, BitcoinUTXOSpendingInfo]
type UTXOMap = Map[TransactionOutPoint, BitcoinUTXOSpendingInfoFull]
private case class BitcoinTxBuilderImpl(
destinations: Seq[TransactionOutput],
@ -675,16 +678,16 @@ object BitcoinTxBuilder {
def apply(
destinations: Seq[TransactionOutput],
utxos: Seq[BitcoinUTXOSpendingInfo],
utxos: Seq[BitcoinUTXOSpendingInfoFull],
feeRate: FeeUnit,
changeSPK: ScriptPubKey,
network: BitcoinNetwork): Future[BitcoinTxBuilder] = {
@tailrec
def loop(utxos: Seq[UTXOSpendingInfo], accum: UTXOMap): UTXOMap =
def loop(utxos: Seq[UTXOSpendingInfoFull], accum: UTXOMap): UTXOMap =
utxos match {
case Nil => accum
case h +: t =>
val u = BitcoinUTXOSpendingInfo(
val u = BitcoinUTXOSpendingInfoFull(
outPoint = h.outPoint,
output = h.output,
signers = h.signers,

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.wallet.fee
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.protocol.transaction.Transaction
/**
@ -53,3 +53,8 @@ case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit)
*/
case class SatoshisPerVirtualByte(currencyUnit: CurrencyUnit)
extends BitcoinFeeUnit
object SatoshisPerVirtualByte {
val zero: SatoshisPerVirtualByte = SatoshisPerVirtualByte(CurrencyUnits.zero)
val one: SatoshisPerVirtualByte = SatoshisPerVirtualByte(Satoshis.one)
}

View file

@ -13,43 +13,7 @@ import scodec.bits.ByteVector
import scala.concurrent.{ExecutionContext, Future}
/** The class used to represent a signing process for a specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */
sealed abstract class Signer[-SpendingInfo <: UTXOSpendingInfo] {
/**
* The method used to sign a bitcoin unspent transaction output
* @param spendingInfo - The information required for signing
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature - do not sign the tx for real, just use a dummy signature this is useful for fee estimation
* @return
*/
def sign(
spendingInfo: SpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
sign(
spendingInfo,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy = spendingInfo
)
}
/**
* The method used to sign a bitcoin unspent transaction output that is potentially nested
* @param spendingInfo - The information required for signing
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature - do not sign the tx for real, just use a dummy signature this is useful for fee estimation
* @param spendingInfoToSatisfy - specifies the UTXOSpendingInfo whose ScriptPubKey needs a ScriptSignature to be generated
* @return
*/
def sign(
spendingInfo: UTXOSpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: SpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent]
sealed abstract class SignerUtils {
def doSign(
sigComponent: TxSigComponent,
@ -113,11 +77,107 @@ sealed abstract class Signer[-SpendingInfo <: UTXOSpendingInfo] {
WitnessTxSigComponent(wtx, index, spendingInfo.output, flags)
case _: P2SHScriptPubKey =>
P2SHTxSigComponent(unsignedTx, index, spendingInfo.output, flags)
val emptyInput = unsignedTx.inputs(index.toInt)
val newInput = TransactionInput(
emptyInput.previousOutput,
P2SHScriptSignature(EmptyScriptSignature,
spendingInfo.redeemScriptOpt.get),
emptyInput.sequence)
val updatedTx = unsignedTx.updateInput(index.toInt, newInput)
spendingInfo.redeemScriptOpt.get match {
case _: WitnessScriptPubKey =>
val wtx = WitnessTransaction.toWitnessTx(updatedTx)
val updatedWtx =
wtx.updateWitness(index.toInt, spendingInfo.scriptWitnessOpt.get)
WitnessTxSigComponentP2SH(updatedWtx,
index,
spendingInfo.output,
flags)
case _: ScriptPubKey =>
P2SHTxSigComponent(updatedTx, index, spendingInfo.output, flags)
}
case _: ScriptPubKey =>
BaseTxSigComponent(unsignedTx, index, spendingInfo.output, flags)
}
}
}
/** The class used to represent a single key's signing process for a
* specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */
sealed trait SingleSigner[-SpendingInfo <: UTXOSpendingInfoSingle]
extends SignerUtils {
/**
* The method used to sign a bitcoin unspent transaction output with a single key
* @param spendingInfo The information required for creating the signature
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature generates a dummy signature if true, useful for fee estimation
*/
def signSingle(
spendingInfo: SpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean)(implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
signSingle(spendingInfo,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy = spendingInfo)
}
/**
* The method used to sign, with a single key, a bitcoin unspent transaction output that is potentially nested
* @param spendingInfo The information required for creating the signature
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature generates a dummy signature if true, useful for fee estimation
* @param spendingInfoToSatisfy - specifies the UTXOSpendingInfoSingle whose ScriptPubKey needs an ECDigitalSignature to be generated
*/
def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: SpendingInfo)(
implicit ec: ExecutionContext): Future[(ECPublicKey, ECDigitalSignature)]
}
/** The class used to represent a signing process for a specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */
sealed abstract class FullSigner[-SpendingInfo <: UTXOSpendingInfoFull]
extends SignerUtils {
/**
* The method used to sign a bitcoin unspent transaction output
* @param spendingInfo - The information required for signing
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature - do not sign the tx for real, just use a dummy signature this is useful for fee estimation
* @return
*/
def sign(
spendingInfo: SpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
sign(
spendingInfo,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy = spendingInfo
)
}
/**
* The method used to sign a bitcoin unspent transaction output that is potentially nested
* @param spendingInfo - The information required for signing
* @param unsignedTx the external Transaction that needs an input signed
* @param isDummySignature - do not sign the tx for real, just use a dummy signature this is useful for fee estimation
* @param spendingInfoToSatisfy - specifies the UTXOSpendingInfo whose ScriptPubKey needs a ScriptSignature to be generated
* @return
*/
def sign(
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: SpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent]
/** Creates a BaseTxSigComponent by replacing the unsignedTx input at inputIndex
* with a signed one using the given ScriptSignature
@ -151,14 +211,109 @@ sealed abstract class Signer[-SpendingInfo <: UTXOSpendingInfo] {
}
}
sealed trait SingleAndFullSigner[
-SpendingInfo <: UTXOSpendingInfoFull with UTXOSpendingInfoSingle]
/** Represents all signers (for single keys) for the bitcoin protocol,
* we could add another network later like litecoin
*/
sealed trait BitcoinSignerSingle[-SpendingInfo <: BitcoinUTXOSpendingInfoSingle]
extends SingleSigner[SpendingInfo] {
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: SpendingInfo)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
val signatureF = doSign(
sigComponent = sigComponent(spendingInfo, unsignedTx),
sign = spendingInfoToSatisfy.signer.signFunction,
hashType = spendingInfoToSatisfy.hashType,
isDummySignature = isDummySignature
)
signatureF.map { sig =>
(spendingInfoToSatisfy.signer.publicKey, sig)
}
}
}
object BitcoinSignerSingle {
def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean)(implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
signSingle(spendingInfo, unsignedTx, isDummySignature, spendingInfo)
}
def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: UTXOSpendingInfoSingle)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
spendingInfoToSatisfy match {
case p2pk: P2PKSpendingInfo =>
P2PKSigner.signSingle(spendingInfo, unsignedTx, isDummySignature, p2pk)
case p2pkh: P2PKHSpendingInfo =>
P2PKHSigner.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
p2pkh)
case p2pkWithTimeout: P2PKWithTimeoutSpendingInfo =>
P2PKWithTimeoutSigner.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
p2pkWithTimeout)
case multiSig: MultiSignatureSpendingInfoSingle =>
MultiSigSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
multiSig)
case p2sh: P2SHSpendingInfoSingle =>
P2SHSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
p2sh)
case lockTime: LockTimeSpendingInfoSingle =>
LockTimeSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
lockTime)
case conditional: ConditionalSpendingInfoSingle =>
ConditionalSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
conditional)
case p2wpkh: P2WPKHV0SpendingInfo =>
P2WPKHSigner.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
p2wpkh)
case p2wsh: P2WSHV0SpendingInfoSingle =>
P2WSHSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
p2wsh)
case _: UnassignedSegwitNativeUTXOSpendingInfo =>
throw new UnsupportedOperationException("Unsupported Segwit version")
}
}
}
/** Represents all signers for the bitcoin protocol, we could add another network later like litecoin */
sealed abstract class BitcoinSigner[-SpendingInfo <: BitcoinUTXOSpendingInfo]
extends Signer[SpendingInfo]
sealed abstract class BitcoinSignerFull[
-SpendingInfo <: BitcoinUTXOSpendingInfoFull]
extends FullSigner[SpendingInfo]
object BitcoinSigner {
def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
@ -166,10 +321,10 @@ object BitcoinSigner {
}
def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: UTXOSpendingInfo)(
spendingInfoToSatisfy: UTXOSpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
spendingInfoToSatisfy match {
case empty: EmptySpendingInfo =>
@ -183,26 +338,26 @@ object BitcoinSigner {
unsignedTx,
isDummySignature,
p2pKWithTimeout)
case p2sh: P2SHSpendingInfo =>
case p2sh: P2SHSpendingInfoFull =>
P2SHSigner.sign(spendingInfo, unsignedTx, isDummySignature, p2sh)
case multiSig: MultiSignatureSpendingInfo =>
case multiSig: MultiSignatureSpendingInfoFull =>
MultiSigSigner.sign(spendingInfo,
unsignedTx,
isDummySignature,
multiSig)
case lockTime: LockTimeSpendingInfo =>
case lockTime: LockTimeSpendingInfoFull =>
LockTimeSigner.sign(spendingInfo,
unsignedTx,
isDummySignature,
lockTime)
case conditional: ConditionalSpendingInfo =>
case conditional: ConditionalSpendingInfoFull =>
ConditionalSigner.sign(spendingInfo,
unsignedTx,
isDummySignature,
conditional)
case p2wpkh: P2WPKHV0SpendingInfo =>
P2WPKHSigner.sign(spendingInfo, unsignedTx, isDummySignature, p2wpkh)
case pw2sh: P2WSHV0SpendingInfo =>
case pw2sh: P2WSHV0SpendingInfoFull =>
P2WSHSigner.sign(spendingInfo, unsignedTx, isDummySignature, pw2sh)
case _: UnassignedSegwitNativeUTXOSpendingInfo =>
throw new UnsupportedOperationException("Unsupported Segwit version")
@ -210,11 +365,53 @@ object BitcoinSigner {
}
}
/** For signing EmptyScriptPubKeys in tests, should probably not be used in real life. */
sealed abstract class EmptySigner extends BitcoinSigner[EmptySpendingInfo] {
/** Represents a BitcoinSigner for which only a single signature is required */
sealed abstract class SingleKeyBitcoinSigner[
-SpendingInfo <: BitcoinUTXOSpendingInfoFull with BitcoinUTXOSpendingInfoSingle]
extends BitcoinSignerFull[SpendingInfo]
with BitcoinSignerSingle[SpendingInfo]
/** Represents a SingleKeyBitcoinSigner which signs a RawScriptPubKey */
sealed abstract class RawSingleKeyBitcoinSigner[
-SpendingInfo <: RawScriptUTXOSpendingInfoFull with RawScriptUTXOSpendingInfoSingle]
extends SingleKeyBitcoinSigner[SpendingInfo] {
def keyAndSigToScriptSig(
key: ECPublicKey,
sig: ECDigitalSignature,
spendingInfo: SpendingInfo): ScriptSignature
override def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: SpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (_, output, inputIndex, _) =
relevantInfo(spendingInfo, unsignedTx)
val keyAndSigF = signSingle(spendingInfo.toSingle(0),
unsignedTx,
isDummySignature,
spendingInfoToSatisfy)
val scriptSigF = keyAndSigF.map {
case (key, sig) =>
keyAndSigToScriptSig(key, sig, spendingInfoToSatisfy)
}
updateScriptSigInSigComponent(unsignedTx,
inputIndex.toInt,
output,
scriptSigF)
}
}
/** For signing EmptyScriptPubKeys in tests, should probably not be used in real life. */
sealed abstract class EmptySigner extends BitcoinSignerFull[EmptySpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: EmptySpendingInfo)(
@ -235,125 +432,73 @@ sealed abstract class EmptySigner extends BitcoinSigner[EmptySpendingInfo] {
object EmptySigner extends EmptySigner
/** Used to sign a [[org.bitcoins.core.protocol.script.P2PKScriptPubKey]] */
sealed abstract class P2PKSigner extends BitcoinSigner[P2PKSpendingInfo] {
sealed abstract class P2PKSigner
extends RawSingleKeyBitcoinSigner[P2PKSpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2PKSpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (_, output, inputIndex, hashType) =
relevantInfo(spendingInfo, unsignedTx)
val signatureF = doSign(sigComponent(spendingInfo, unsignedTx),
spendingInfoToSatisfy.signer.signFunction,
hashType,
isDummySignature)
val scriptSigF = signatureF.map { signature =>
P2PKScriptSignature(signature)
}
updateScriptSigInSigComponent(unsignedTx,
inputIndex.toInt,
output,
scriptSigF)
override def keyAndSigToScriptSig(
key: ECPublicKey,
sig: ECDigitalSignature,
spendingInfo: P2PKSpendingInfo): ScriptSignature = {
P2PKScriptSignature(sig)
}
}
object P2PKSigner extends P2PKSigner
/** Used to sign a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]] */
sealed abstract class P2PKHSigner extends BitcoinSigner[P2PKHSpendingInfo] {
sealed abstract class P2PKHSigner
extends RawSingleKeyBitcoinSigner[P2PKHSpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2PKHSpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (signers, output, inputIndex, hashType) =
relevantInfo(spendingInfo, unsignedTx)
val sign = signers.head.signFunction
val pubKey = signers.head.publicKey
val signatureF =
doSign(sigComponent(spendingInfo, unsignedTx),
sign,
hashType,
isDummySignature)
val scriptSigF = signatureF.map { signature =>
P2PKHScriptSignature(signature, pubKey)
}
updateScriptSigInSigComponent(unsignedTx,
inputIndex.toInt,
output,
scriptSigF)
override def keyAndSigToScriptSig(
key: ECPublicKey,
sig: ECDigitalSignature,
spendingInfo: P2PKHSpendingInfo): ScriptSignature = {
P2PKHScriptSignature(sig, key)
}
}
object P2PKHSigner extends P2PKHSigner
sealed abstract class P2PKWithTimeoutSigner
extends BitcoinSigner[P2PKWithTimeoutSpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfo,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2PKWithTimeoutSpendingInfo)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (signers, output, inputIndex, hashType) =
relevantInfo(spendingInfo, unsignedTx)
extends RawSingleKeyBitcoinSigner[P2PKWithTimeoutSpendingInfo] {
val sign = signers.head.signFunction
val signatureF = doSign(sigComponent(spendingInfo, unsignedTx),
sign,
hashType,
isDummySignature)
val scriptSigF = signatureF.map { signature =>
P2PKWithTimeoutScriptSignature(spendingInfoToSatisfy.isBeforeTimeout,
signature)
}
updateScriptSigInSigComponent(unsignedTx,
inputIndex.toInt,
output,
scriptSigF)
override def keyAndSigToScriptSig(
key: ECPublicKey,
sig: ECDigitalSignature,
spendingInfo: P2PKWithTimeoutSpendingInfo): ScriptSignature = {
P2PKWithTimeoutScriptSignature(spendingInfo.isBeforeTimeout, sig)
}
}
object P2PKWithTimeoutSigner extends P2PKWithTimeoutSigner
sealed abstract class MultiSigSignerSingle
extends BitcoinSignerSingle[MultiSignatureSpendingInfoSingle]
object MultiSigSignerSingle extends MultiSigSignerSingle
sealed abstract class MultiSigSigner
extends BitcoinSigner[MultiSignatureSpendingInfo] {
extends BitcoinSignerFull[MultiSignatureSpendingInfoFull] {
override def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: MultiSignatureSpendingInfo)(
spendingInfoToSatisfy: MultiSignatureSpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (signersWithPubKeys, output, inputIndex, hashType) =
val (_, output, inputIndex, _) =
relevantInfo(spendingInfo, unsignedTx)
val signers = signersWithPubKeys.map(_.signFunction)
val requiredSigs = spendingInfoToSatisfy.scriptPubKey.requiredSigs
val signatureFs = 0
.until(requiredSigs)
.map(
i =>
doSign(sigComponent(spendingInfo, unsignedTx),
signers(i),
hashType,
isDummySignature))
val keysAndSigsF = spendingInfoToSatisfy.toSingles.zipWithIndex.map {
case (infoSingle, index) =>
MultiSigSignerSingle
.signSingle(spendingInfo.toSingle(index),
unsignedTx,
isDummySignature,
infoSingle)
}
val signaturesF = Future.sequence(signatureFs)
val signaturesF = Future.sequence(keysAndSigsF).map(_.map(_._2))
val scriptSigF = signaturesF.map { sigs =>
MultiSignatureScriptSignature(sigs)
@ -368,13 +513,49 @@ sealed abstract class MultiSigSigner
object MultiSigSigner extends MultiSigSigner
/** Used to sign a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]] */
sealed abstract class P2SHSigner extends BitcoinSigner[P2SHSpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfo,
sealed abstract class P2SHSignerSingle
extends BitcoinSignerSingle[P2SHSpendingInfoSingle] {
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2SHSpendingInfo)(
spendingInfoToSatisfy: P2SHSpendingInfoSingle)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
if (spendingInfoToSatisfy != spendingInfo) {
Future.fromTry(TxBuilderError.WrongSigner)
} else {
val inputIndex = super.inputIndex(spendingInfo, unsignedTx)
val oldInput = unsignedTx.inputs(inputIndex.toInt)
val input =
TransactionInput(spendingInfo.outPoint,
EmptyScriptSignature,
oldInput.sequence)
val updatedTx =
unsignedTx.updateInput(inputIndex.toInt, input)
BitcoinSignerSingle
.signSingle(spendingInfo,
updatedTx,
isDummySignature,
spendingInfoToSatisfy.nestedSpendingInfo)
}
}
}
object P2SHSignerSingle extends P2SHSignerSingle
/** Used to sign a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]] */
sealed abstract class P2SHSigner
extends BitcoinSignerFull[P2SHSpendingInfoFull] {
override def sign(
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2SHSpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
if (spendingInfoToSatisfy != spendingInfo) {
Future.fromTry(TxBuilderError.WrongSigner)
@ -434,10 +615,41 @@ sealed abstract class P2SHSigner extends BitcoinSigner[P2SHSpendingInfo] {
object P2SHSigner extends P2SHSigner
sealed abstract class P2WPKHSigner extends BitcoinSigner[P2WPKHV0SpendingInfo] {
sealed abstract class P2WPKHSigner
extends SingleKeyBitcoinSigner[P2WPKHV0SpendingInfo] {
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2WPKHV0SpendingInfo)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
for {
sigComponent <- sign(spendingInfoToSatisfy,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy)
} yield {
val sig = sigComponent match {
case witnessSigComp: WitnessTxSigComponent =>
witnessSigComp.witness match {
case p2wpkhWit: P2WPKHWitnessV0 => p2wpkhWit.signature
case _: ScriptWitness =>
throw new RuntimeException(
"P2WPKHSigner.sign must generate a P2WPKHWitness")
}
case _: TxSigComponent =>
throw new RuntimeException(
"P2WPKHSigner.sign must return a WitnessTxSigComponent")
}
(spendingInfoToSatisfy.signer.publicKey, sig)
}
}
override def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2WPKHV0SpendingInfo)(
@ -504,13 +716,34 @@ sealed abstract class P2WPKHSigner extends BitcoinSigner[P2WPKHV0SpendingInfo] {
}
object P2WPKHSigner extends P2WPKHSigner
sealed abstract class P2WSHSigner extends BitcoinSigner[P2WSHV0SpendingInfo] {
override def sign(
spendingInfo: UTXOSpendingInfo,
sealed abstract class P2WSHSignerSingle
extends BitcoinSignerSingle[P2WSHV0SpendingInfoSingle] {
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2WSHV0SpendingInfo)(
spendingInfoToSatisfy: P2WSHV0SpendingInfoSingle)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
val wtx = WitnessTransaction.toWitnessTx(unsignedTx)
BitcoinSignerSingle.signSingle(spendingInfo,
wtx,
isDummySignature,
spendingInfoToSatisfy.nestedSpendingInfo)
}
}
object P2WSHSignerSingle extends P2WSHSignerSingle
sealed abstract class P2WSHSigner
extends BitcoinSignerFull[P2WSHV0SpendingInfoFull] {
override def sign(
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: P2WSHV0SpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
if (spendingInfoToSatisfy != spendingInfo) {
Future.fromTry(TxBuilderError.WrongSigner)
@ -545,14 +778,33 @@ sealed abstract class P2WSHSigner extends BitcoinSigner[P2WSHV0SpendingInfo] {
}
object P2WSHSigner extends P2WSHSigner
sealed abstract class LockTimeSigner
extends BitcoinSigner[LockTimeSpendingInfo] {
sealed abstract class LockTimeSignerSingle
extends BitcoinSignerSingle[LockTimeSpendingInfoSingle] {
override def sign(
spendingInfo: UTXOSpendingInfo,
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: LockTimeSpendingInfo)(
spendingInfoToSatisfy: LockTimeSpendingInfoSingle)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
BitcoinSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy.nestedSpendingInfo)
}
}
object LockTimeSignerSingle extends LockTimeSignerSingle
sealed abstract class LockTimeSigner
extends BitcoinSignerFull[LockTimeSpendingInfoFull] {
override def sign(
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: LockTimeSpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
BitcoinSigner.sign(spendingInfo,
unsignedTx,
@ -562,17 +814,36 @@ sealed abstract class LockTimeSigner
}
object LockTimeSigner extends LockTimeSigner
sealed abstract class ConditionalSignerSingle
extends BitcoinSignerSingle[ConditionalSpendingInfoSingle] {
override def signSingle(
spendingInfo: UTXOSpendingInfoSingle,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: ConditionalSpendingInfoSingle)(
implicit ec: ExecutionContext): Future[
(ECPublicKey, ECDigitalSignature)] = {
BitcoinSignerSingle.signSingle(spendingInfo,
unsignedTx,
isDummySignature,
spendingInfoToSatisfy.nestedSpendingInfo)
}
}
object ConditionalSignerSingle extends ConditionalSignerSingle
/** Delegates to get a ScriptSignature for the case being
* spent and then adds an OP_TRUE or OP_FALSE
*/
sealed abstract class ConditionalSigner
extends BitcoinSigner[ConditionalSpendingInfo] {
extends BitcoinSignerFull[ConditionalSpendingInfoFull] {
override def sign(
spendingInfo: UTXOSpendingInfo,
spendingInfo: UTXOSpendingInfoFull,
unsignedTx: Transaction,
isDummySignature: Boolean,
spendingInfoToSatisfy: ConditionalSpendingInfo)(
spendingInfoToSatisfy: ConditionalSpendingInfoFull)(
implicit ec: ExecutionContext): Future[TxSigComponent] = {
val (_, output, inputIndex, _) = relevantInfo(spendingInfo, unsignedTx)

View file

@ -55,15 +55,15 @@ val destinations = {
val destination1 = TransactionOutput(value = destinationAmount,
scriptPubKey = destinationSPK)
List(destination1)
Vector(destination1)
}
// we have to fabricate a transaction that contains the
// UTXO we are trying to spend. If this were a real blockchain
// we would need to reference the UTXO set
val creditingTx = BaseTransaction(version = Int32.one,
inputs = List.empty,
outputs = List(utxo),
inputs = Vector.empty,
outputs = Vector(utxo),
lockTime = UInt32.zero)
// this is the information we need from the crediting TX
@ -72,19 +72,19 @@ val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
// this contains all the information we need to
// validly sign the UTXO above
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = utxo,
signers = List(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType =
HashType.sigHashAll,
conditionalPath =
ConditionalPath.NoConditionsLeft)
val utxoSpendingInfo = BitcoinUTXOSpendingInfoFull(outPoint = outPoint,
output = utxo,
signers = Vector(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType =
HashType.sigHashAll,
conditionalPath =
ConditionalPath.NoConditionsLeft)
// all of the UTXO spending information, since we are only
//spending one UTXO, this is just one element
val utxos: List[BitcoinUTXOSpendingInfo] = List(utxoSpendingInfo)
val utxos: Vector[BitcoinUTXOSpendingInfoFull] = Vector(utxoSpendingInfo)
// this is how much we are going to pay as a fee to the network
// for this example, we are going to pay 1 satoshi per byte

View file

@ -47,7 +47,7 @@ object Deps {
// obviously has to be changed before this is
// merged.
val sourcecodeV = "0.1.9"
val sourcecodeV = "0.2.0"
// CLI deps
val scoptV = "4.0.0-RC2"

View file

@ -1 +1 @@
sbt.version=1.3.6
sbt.version=1.3.7

View file

@ -7,9 +7,9 @@ import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.wallet.utxo.{
BitcoinUTXOSpendingInfo,
BitcoinUTXOSpendingInfoFull,
ConditionalPath,
P2SHNestedSegwitV0UTXOSpendingInfo
P2SHNestedSegwitV0UTXOSpendingInfoFull
}
import org.scalacheck.Gen
@ -28,7 +28,7 @@ sealed abstract class CreditingTxGen {
}
/** Generator for non-script hash based output */
def nonSHOutput: Gen[BitcoinUTXOSpendingInfo] = {
def nonSHOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
Gen.oneOf(
p2pkOutput,
p2pkhOutput,
@ -40,14 +40,14 @@ sealed abstract class CreditingTxGen {
)
}
def nonSHOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def nonSHOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, nonSHOutput))
def basicOutput: Gen[BitcoinUTXOSpendingInfo] = {
def basicOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
Gen.oneOf(p2pkOutput, p2pkhOutput, multiSigOutput)
}
def nonP2WSHOutput: Gen[BitcoinUTXOSpendingInfo] = {
def nonP2WSHOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
//note, cannot put a p2wpkh here
Gen.oneOf(p2pkOutput,
p2pkhOutput,
@ -58,7 +58,7 @@ sealed abstract class CreditingTxGen {
}
/** Only for use in constructing P2SH outputs */
private def nonP2SHOutput: Gen[BitcoinUTXOSpendingInfo] = {
private def nonP2SHOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
Gen
.oneOf(
p2pkOutput,
@ -73,14 +73,14 @@ sealed abstract class CreditingTxGen {
.suchThat(output =>
!ScriptGenerators.redeemScriptTooBig(output.scriptPubKey))
.suchThat {
case P2SHNestedSegwitV0UTXOSpendingInfo(_,
_,
_,
_,
_,
_,
witness: P2WSHWitnessV0,
_) =>
case P2SHNestedSegwitV0UTXOSpendingInfoFull(_,
_,
_,
_,
_,
_,
witness: P2WSHWitnessV0,
_) =>
witness.stack.exists(_.length > ScriptInterpreter.MAX_PUSH_SIZE)
case _ => true
}
@ -100,11 +100,11 @@ sealed abstract class CreditingTxGen {
private val cltvOutputGens = Vector(p2pkWithTimeoutOutput, cltvOutput)
def output: Gen[BitcoinUTXOSpendingInfo] =
def output: Gen[BitcoinUTXOSpendingInfoFull] =
Gen.oneOf(nonCltvOutputGens).flatMap(identity)
/** Either a list of non-CLTV outputs or a single CLTV output, with proportional probability */
def outputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def outputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
val cltvGen = Gen
.oneOf(cltvOutput, p2pkWithTimeoutOutput)
.map { output =>
@ -119,23 +119,23 @@ sealed abstract class CreditingTxGen {
}
/** Generates a crediting tx with a p2pk spk at the returned index */
def p2pkOutput: Gen[BitcoinUTXOSpendingInfo] =
def p2pkOutput: Gen[BitcoinUTXOSpendingInfoFull] =
ScriptGenerators.p2pkScriptPubKey.flatMap { p2pk =>
build(p2pk._1, Seq(p2pk._2), None, None)
}
/** Generates multiple crediting txs with p2pk spks at the returned index */
def p2pkOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def p2pkOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2pkOutput))
}
def p2pkWithTimeoutOutput: Gen[BitcoinUTXOSpendingInfo] = {
def p2pkWithTimeoutOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
ScriptGenerators.p2pkWithTimeoutScriptPubKey.flatMap { p2pkWithTimeout =>
build(p2pkWithTimeout._1, Seq(p2pkWithTimeout._2.head), None, None)
}
}
def p2pkWithTimeoutOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def p2pkWithTimeoutOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2pkWithTimeoutOutput))
}
@ -143,39 +143,39 @@ sealed abstract class CreditingTxGen {
* Generates a transaction that has a p2pkh output at the returned index. This
* output can be spent by the returned ECPrivateKey
*/
def p2pkhOutput: Gen[BitcoinUTXOSpendingInfo] =
def p2pkhOutput: Gen[BitcoinUTXOSpendingInfoFull] =
ScriptGenerators.p2pkhScriptPubKey.flatMap { p2pkh =>
build(p2pkh._1, Seq(p2pkh._2), None, None)
}
/** Generates a sequence of p2pkh outputs at the returned index */
def p2pkhOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def p2pkhOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2pkhOutput))
}
def multiSigOutput: Gen[BitcoinUTXOSpendingInfo] =
def multiSigOutput: Gen[BitcoinUTXOSpendingInfoFull] =
ScriptGenerators.multiSigScriptPubKey.flatMap { multisig =>
build(multisig._1, multisig._2, None, None)
}
def multiSigOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def multiSigOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, multiSigOutput))
}
def multiSignatureWithTimeoutOutput: Gen[BitcoinUTXOSpendingInfo] = {
def multiSignatureWithTimeoutOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
ScriptGenerators.multiSignatureWithTimeoutScriptPubKey.flatMap {
case (conditional, keys) =>
build(conditional, keys, None, None)
}
}
def multiSignatureWithTimeoutOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def multiSignatureWithTimeoutOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen
.choose(min, max)
.flatMap(n => Gen.listOfN(n, multiSignatureWithTimeoutOutput))
}
def conditionalOutput: Gen[BitcoinUTXOSpendingInfo] = {
def conditionalOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
ScriptGenerators
.nonLocktimeConditionalScriptPubKey(ScriptGenerators.defaultMaxDepth)
.flatMap {
@ -184,33 +184,34 @@ sealed abstract class CreditingTxGen {
}
}
def conditionalOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def conditionalOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, conditionalOutput))
}
def p2shOutput: Gen[BitcoinUTXOSpendingInfo] = nonP2SHOutput.flatMap { o =>
CryptoGenerators.hashType.map { hashType =>
val oldOutput = o.output
val redeemScript = o.output.scriptPubKey
val p2sh = P2SHScriptPubKey(redeemScript)
val updatedOutput = TransactionOutput(oldOutput.value, p2sh)
BitcoinUTXOSpendingInfo(
TransactionOutPoint(o.outPoint.txId, o.outPoint.vout),
updatedOutput,
o.signers,
Some(redeemScript),
o.scriptWitnessOpt,
hashType,
computeAllTrueConditionalPath(redeemScript, None, o.scriptWitnessOpt)
)
}
def p2shOutput: Gen[BitcoinUTXOSpendingInfoFull] = nonP2SHOutput.flatMap {
o =>
CryptoGenerators.hashType.map { hashType =>
val oldOutput = o.output
val redeemScript = o.output.scriptPubKey
val p2sh = P2SHScriptPubKey(redeemScript)
val updatedOutput = TransactionOutput(oldOutput.value, p2sh)
BitcoinUTXOSpendingInfoFull(
TransactionOutPoint(o.outPoint.txId, o.outPoint.vout),
updatedOutput,
o.signers,
Some(redeemScript),
o.scriptWitnessOpt,
hashType,
computeAllTrueConditionalPath(redeemScript, None, o.scriptWitnessOpt)
)
}
}
def p2shOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] = {
def p2shOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] = {
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2shOutput))
}
def cltvOutput: Gen[BitcoinUTXOSpendingInfo] =
def cltvOutput: Gen[BitcoinUTXOSpendingInfoFull] =
TransactionGenerators.spendableCLTVValues.flatMap {
case (scriptNum, _) =>
basicOutput.flatMap { o =>
@ -218,7 +219,7 @@ sealed abstract class CreditingTxGen {
val oldOutput = o.output
val csvSPK = CLTVScriptPubKey(scriptNum, oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value, csvSPK)
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
TransactionOutPoint(o.outPoint.txId, o.outPoint.vout),
updatedOutput,
o.signers,
@ -231,10 +232,10 @@ sealed abstract class CreditingTxGen {
}
}
def cltvOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def cltvOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, cltvOutput))
def csvOutput: Gen[BitcoinUTXOSpendingInfo] =
def csvOutput: Gen[BitcoinUTXOSpendingInfoFull] =
TransactionGenerators.spendableCSVValues.flatMap {
case (scriptNum, _) =>
basicOutput.flatMap { o =>
@ -242,7 +243,7 @@ sealed abstract class CreditingTxGen {
val oldOutput = o.output
val csvSPK = CSVScriptPubKey(scriptNum, oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value, csvSPK)
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
TransactionOutPoint(o.outPoint.txId, o.outPoint.vout),
updatedOutput,
o.signers,
@ -255,24 +256,24 @@ sealed abstract class CreditingTxGen {
}
}
def csvOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def csvOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, csvOutput))
def p2wpkhOutput: Gen[BitcoinUTXOSpendingInfo] =
def p2wpkhOutput: Gen[BitcoinUTXOSpendingInfoFull] =
ScriptGenerators.p2wpkhSPKV0.flatMap { witSPK =>
val scriptWit = P2WPKHWitnessV0(witSPK._2.head.publicKey)
build(witSPK._1, witSPK._2, None, Some(scriptWit))
}
def p2wpkhOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def p2wpkhOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2wpkhOutput))
def p2wshOutput: Gen[BitcoinUTXOSpendingInfo] =
def p2wshOutput: Gen[BitcoinUTXOSpendingInfoFull] =
nonP2WSHOutput
.suchThat(output =>
!ScriptGenerators.redeemScriptTooBig(output.scriptPubKey))
.flatMap {
case BitcoinUTXOSpendingInfo(_, txOutput, signer, _, _, _, _) =>
case BitcoinUTXOSpendingInfoFull(_, txOutput, signer, _, _, _, _) =>
val spk = txOutput.scriptPubKey
spk match {
case rspk: RawScriptPubKey =>
@ -285,18 +286,18 @@ sealed abstract class CreditingTxGen {
}
}
def p2wshOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def p2wshOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2wshOutput))
/** A nested output is a p2sh/p2wsh wrapped output */
def nestedOutput: Gen[BitcoinUTXOSpendingInfo] = {
def nestedOutput: Gen[BitcoinUTXOSpendingInfoFull] = {
Gen.oneOf(p2wshOutput, p2shOutput)
}
def nestedOutputs: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def nestedOutputs: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, nestedOutput))
def random: Gen[BitcoinUTXOSpendingInfo] = nonEmptyOutputs.flatMap {
def random: Gen[BitcoinUTXOSpendingInfoFull] = nonEmptyOutputs.flatMap {
outputs =>
Gen.choose(0, outputs.size - 1).flatMap { outputIndex: Int =>
ScriptGenerators.scriptPubKey.flatMap {
@ -304,12 +305,12 @@ sealed abstract class CreditingTxGen {
WitnessGenerators.scriptWitness.flatMap { wit: ScriptWitness =>
CryptoGenerators.hashType.map { hashType: HashType =>
val tc = TransactionConstants
val signers: Seq[Sign] = keys
val signers: Vector[Sign] = keys.toVector
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
outputs,
tc.lockTime)
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
TransactionOutPoint(creditingTx.txId,
UInt32.apply(outputIndex)),
TransactionOutput(
@ -327,7 +328,7 @@ sealed abstract class CreditingTxGen {
}
}
def randoms: Gen[Seq[BitcoinUTXOSpendingInfo]] =
def randoms: Gen[Seq[BitcoinUTXOSpendingInfoFull]] =
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, random))
private def computeAllTrueConditionalPath(
@ -371,7 +372,7 @@ sealed abstract class CreditingTxGen {
spk: ScriptPubKey,
signers: Seq[Sign],
redeemScript: Option[ScriptPubKey],
scriptWitness: Option[ScriptWitness]): Gen[BitcoinUTXOSpendingInfo] =
scriptWitness: Option[ScriptWitness]): Gen[BitcoinUTXOSpendingInfoFull] =
nonEmptyOutputs.flatMap { outputs =>
CryptoGenerators.hashType.flatMap { hashType =>
Gen.choose(0, outputs.size - 1).map { idx =>
@ -379,10 +380,10 @@ sealed abstract class CreditingTxGen {
val updated = outputs.updated(idx, TransactionOutput(old.value, spk))
val tc = TransactionConstants
val btx = BaseTransaction(tc.version, Nil, updated, tc.lockTime)
BitcoinUTXOSpendingInfo(
BitcoinUTXOSpendingInfoFull(
TransactionOutPoint(btx.txId, UInt32.apply(idx)),
TransactionOutput(old.value, spk),
signers,
signers.toVector,
redeemScript,
scriptWitness,
hashType,

View file

@ -20,7 +20,7 @@ import org.bitcoins.core.wallet.signer.{
P2PKWithTimeoutSigner
}
import org.bitcoins.core.wallet.utxo.{
MultiSignatureSpendingInfo,
MultiSignatureSpendingInfoFull,
P2PKHSpendingInfo,
P2PKSpendingInfo,
P2PKWithTimeoutSpendingInfo
@ -660,7 +660,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
creditingTx,
scriptSig,
outputIndex)
spendingInfo = MultiSignatureSpendingInfo(
spendingInfo = MultiSignatureSpendingInfoFull(
TransactionOutPoint(creditingTx.txIdBE, inputIndex),
creditingTx.outputs(outputIndex.toInt).value,
multiSigScriptPubKey,

View file

@ -1,16 +1,22 @@
package org.bitcoins.core.util
package org.bitcoins.testkit.util
import org.bitcoins.core.crypto.BaseTxSigComponent
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.script.{
EmptyScriptPubKey,
P2SHScriptSignature,
ScriptPubKey,
ScriptSignature
}
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionInput,
TransactionOutput
}
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{
@ -19,7 +25,6 @@ import org.bitcoins.core.script.crypto.{
OP_HASH160
}
import org.bitcoins.core.script.stack.OP_DUP
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.serializers.script.RawScriptPubKeyParser
import org.bitcoins.core.serializers.transaction.RawTransactionInputParser

View file

@ -1,10 +1,11 @@
package org.bitcoins.core.util
package org.bitcoins.testkit.util
import org.bitcoins.core.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.util.BitcoinSLogger
/**
* Created by chris on 2/12/16.
@ -239,6 +240,14 @@ trait TransactionTestUtil extends BitcoinSLogger {
Seq(EmptyTransactionInput),
Nil,
EmptyTransaction.lockTime)
/** Builds a dummy transaction that sends money to the given output */
def buildTransactionTo(output: TransactionOutput): Transaction = {
BaseTransaction(version = Int32.one,
inputs = Vector(EmptyTransactionInput),
outputs = Vector(output),
lockTime = TransactionConstants.lockTime)
}
}
object TransactionTestUtil extends TransactionTestUtil

View file

@ -8,6 +8,7 @@ import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
import org.bitcoins.core.currency._
import org.bitcoins.core.gcs.BlockFilter
import org.bitcoins.core.protocol.BlockStamp
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.db.AppConfig
import org.bitcoins.keymanager.KeyManagerTestUtil
@ -17,11 +18,11 @@ import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.server.BitcoinSAppConfig._
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.util.FileUtil
import org.bitcoins.wallet.api.UnlockedWalletApi
import org.bitcoins.testkit.util.{FileUtil, TransactionTestUtil}
import org.bitcoins.wallet.api.{LockedWalletApi, UnlockedWalletApi}
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.db.WalletDbManagement
import org.bitcoins.wallet.{Wallet, WalletLogger}
import org.bitcoins.wallet.{LockedWallet, Wallet, WalletLogger}
import org.scalatest._
import scala.concurrent.{ExecutionContext, Future}
@ -127,6 +128,18 @@ trait BitcoinSWalletTest extends BitcoinSFixture with WalletLogger {
)
}
/** Creates a wallet that is funded with some bitcoin, this wallet is NOT
* peered with a bitcoind so the funds in the wallet are not tied to an
* underlying blockchain */
def withFundedWallet(test: OneArgAsyncTest): FutureOutcome = {
makeDependentFixture(
build = () => createFundedWallet(nodeApi, chainQueryApi),
destroy = { funded: FundedWallet =>
destroyWallet(funded.wallet)
}
)(test)
}
/** Fixture for an initialized wallet which produce legacy addresses */
def withLegacyWallet(test: OneArgAsyncTest): FutureOutcome = {
val confOverride =
@ -293,6 +306,22 @@ object BitcoinSWalletTest extends WalletLogger {
chainQueryApi = chainQueryApi)(config, ec)() // get the standard config
}
/** This wallet should have a total of 6 bitcoin in it
* spread across 3 utxos that have values 1, 2, 3 bitcoins */
case class FundedWallet(wallet: LockedWallet)
/** This creates a wallet that is funded that is not paired to a bitcoind instance. */
def createFundedWallet(nodeApi: NodeApi, chainQueryApi: ChainQueryApi)(
implicit config: BitcoinSAppConfig,
system: ActorSystem): Future[FundedWallet] = {
import system.dispatcher
for {
wallet <- createDefaultWallet(nodeApi, chainQueryApi)
funded <- fundWallet(wallet)
} yield funded
}
/** Pairs the given wallet with a bitcoind instance that has money in the bitcoind wallet */
def createWalletWithBitcoind(
wallet: UnlockedWalletApi
@ -347,6 +376,48 @@ object BitcoinSWalletTest extends WalletLogger {
} yield funded
}
/** Funds a bitcoin-s wallet with 3 utxos with 1, 2 and 3 bitcoin in the utxos */
def fundWallet(wallet: UnlockedWalletApi)(
implicit ec: ExecutionContext): Future[FundedWallet] = {
//get three addresses
val addressesF = Future.sequence(Vector.fill(3) {
//this Thread.sleep is needed because of
//https://github.com/bitcoin-s/bitcoin-s/issues/1009
//once that is resolved we should be able to remove this
Thread.sleep(500)
wallet.getNewAddress()
})
//construct three txs that send money to these addresses
//these are "fictional" transactions in the sense that the
//outpoints do not exist on a blockchain anywhere
val amounts = Vector(1.bitcoin, 2.bitcoin, 3.bitcoin)
val expectedAmt = amounts.fold(CurrencyUnits.zero)(_ + _)
val txsF = for {
addresses <- addressesF
} yield {
addresses.zip(amounts).map {
case (addr, amt) =>
val output =
TransactionOutput(value = amt, scriptPubKey = addr.scriptPubKey)
TransactionTestUtil.buildTransactionTo(output)
}
}
val fundedWalletF =
txsF.flatMap(txs =>
wallet.processTransactions(transactions = txs, blockHash = None))
//sanity check to make sure we have money
for {
fundedWallet <- fundedWalletF
balance <- fundedWallet.getBalance()
_ = require(
balance == 6.bitcoin,
s"Funding wallet fixture failed ot fund the wallet, got balance=${balance} expected=${expectedAmt}")
} yield FundedWallet(fundedWallet.asInstanceOf[LockedWallet])
}
/** Funds the given wallet with money from the given bitcoind */
def fundWalletWithBitcoind(pair: WalletWithBitcoind)(
implicit ec: ExecutionContext): Future[WalletWithBitcoind] = {
@ -378,10 +449,14 @@ object BitcoinSWalletTest extends WalletLogger {
def destroyWallet(wallet: UnlockedWalletApi)(
implicit ec: ExecutionContext): Future[Unit] = {
destroyWallet(wallet.lock())
}
def destroyWallet(wallet: LockedWalletApi)(
implicit ec: ExecutionContext): Future[Unit] = {
val destroyWalletF = WalletDbManagement
.dropAll()(config = wallet.walletConfig, ec = ec)
.map(_ => ())
destroyWalletF
}

View file

@ -0,0 +1,107 @@
package org.bitcoins.wallet
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.testkit.util.TestUtil
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.FundedWallet
import org.scalatest.FutureOutcome
class FundTransactionHandlingTest extends BitcoinSWalletTest {
override type FixtureParam = FundedWallet
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withFundedWallet(test)
}
val destination = TransactionOutput(Bitcoins(0.5),TestUtil.p2pkhScriptPubKey)
val feeRate = SatoshisPerVirtualByte.one
it must "fund a simple raw transaction that requires one utxo" in { fundedWallet : FundedWallet =>
val wallet = fundedWallet.wallet
val fundedTxF = wallet.fundRawTransaction(
destinations = Vector(destination),
feeRate = feeRate)
for {
fundedTx <- fundedTxF
} yield {
assert(fundedTx.inputs.length == 1, s"We should only need one input to fund this tx")
assert(fundedTx.outputs.contains(destination))
assert(fundedTx.outputs.length == 2, s"We must have a single destination output and a change output")
}
}
it must "fund a transaction that requires all utxos in our wallet" in { fundedWallet: FundedWallet =>
val amt = Bitcoins(5.5)
val newDestination = destination.copy(value = amt)
val wallet = fundedWallet.wallet
val fundedTxF = wallet.fundRawTransaction(
destinations = Vector(newDestination),
feeRate = feeRate)
for {
fundedTx <- fundedTxF
} yield {
assert(fundedTx.inputs.length == 3, s"We should need 3 inputs to fund this tx")
assert(fundedTx.outputs.contains(newDestination))
assert(fundedTx.outputs.length == 2, s"We must have a 2 destination output and a change output")
}
}
it must "not care about the number of destinations" in { fundedWallet: FundedWallet =>
val destinations = Vector.fill(5)(destination)
val wallet = fundedWallet.wallet
val fundedTxF = wallet.fundRawTransaction(
destinations = destinations,
feeRate = feeRate)
for {
fundedTx <- fundedTxF
} yield {
assert(fundedTx.inputs.length == 1, s"We should only need one input to fund this tx")
destinations.foreach(d =>
assert(fundedTx.outputs.contains(d))
)
assert(fundedTx.outputs.length == 6, s"We must have a 6 destination output and a change output")
}
}
it must "fail to fund a raw transaction if we don't have enough money in our wallet" in { fundedWallet: FundedWallet =>
//our wallet should only have 6 bitcoin in it
val tooMuchMoney = Bitcoins(10)
val tooBigOutput = destination.copy(value = tooMuchMoney)
val wallet = fundedWallet.wallet
val fundedTxF = wallet.fundRawTransaction(
destinations = Vector(tooBigOutput),
feeRate = feeRate)
recoverToSucceededIf[RuntimeException] {
fundedTxF
}
}
it must "fail to fund a raw transaction if we have the _exact_ amount of money in the wallet because of the fee" in {fundedWallet: FundedWallet =>
//our wallet should only have 6 bitcoin in it
val tooMuchMoney = Bitcoins(6)
val tooBigOutput = destination.copy(value = tooMuchMoney)
val wallet = fundedWallet.wallet
//6 bitcoin destination + 1 sat/vbyte fee means we should
//not have enough money for this
val fundedTxF = wallet.fundRawTransaction(
destinations = Vector(tooBigOutput),
feeRate = feeRate)
recoverToSucceededIf[RuntimeException] {
fundedTxF
}
}
it must "fund from a specific account" ignore { _: FundedWallet =>
assert(false)
}
}

View file

@ -173,5 +173,4 @@ class WalletUnitTest extends BitcoinSWalletTest {
blockHeight = 1)) == matched)
}
}
}

View file

@ -18,6 +18,7 @@ abstract class LockedWallet
with UtxoHandling
with AddressHandling
with AccountHandling
with FundTransactionHandling
with TransactionProcessing
with RescanHandling {

View file

@ -1,15 +1,12 @@
package org.bitcoins.wallet
import org.bitcoins.core.api.{ChainQueryApi, NodeApi}
import org.bitcoins.core.config.BitcoinNetwork
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
import org.bitcoins.core.hd._
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.BitcoinUTXOSpendingInfo
import org.bitcoins.keymanager.KeyManagerParams
import org.bitcoins.keymanager.bip39.BIP39KeyManager
import org.bitcoins.keymanager.util.HDUtil
@ -39,50 +36,15 @@ sealed abstract class Wallet extends LockedWallet with UnlockedWalletApi {
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] = {
logger.info(s"Sending $amount to $address at feerate $feeRate")
val destination = TransactionOutput(amount, address.scriptPubKey)
for {
change <- getNewChangeAddress(fromAccount)
walletUtxos <- listUtxos()
txBuilder <- {
val destinations = Vector(
TransactionOutput(amount, address.scriptPubKey))
// currencly just grabs the biggest utxos until it finds enough
val utxos: Vector[BitcoinUTXOSpendingInfo] =
CoinSelector
.accumulateLargest(walletUtxos, destinations, feeRate)
.map(
_.toUTXOSpendingInfo(account = fromAccount,
keyManager = keyManager,
network = networkParameters))
logger.info({
val utxosStr = utxos
.map { utxo =>
import utxo.outPoint
s"${outPoint.txId.hex}:${outPoint.vout.toInt}"
}
.mkString(", ")
s"Spending UTXOs: $utxosStr"
})
utxos.zipWithIndex.foreach {
case (utxo, index) =>
logger.info(s"UTXO $index details: ${utxo.output}")
}
networkParameters match {
case b: BitcoinNetwork =>
BitcoinTxBuilder(destinations = destinations,
utxos = utxos,
feeRate = feeRate,
changeSPK = change.scriptPubKey,
network = b)
}
}
txBuilder <- fundRawTransactionInternal(
destinations = Vector(destination),
feeRate = feeRate,
fromAccount = fromAccount,
keyManagerOpt = Some(keyManager))
signed <- txBuilder.sign
ourOuts <- findOurOuts(signed)
// TODO internal
_ <- processOurTransaction(signed, blockHashOpt = None)
} yield {
logger.debug(

View file

@ -10,8 +10,9 @@ import org.bitcoins.core.gcs.{GolombFilter, SimpleFilterMatcher}
import org.bitcoins.core.hd.{AddressType, HDPurpose}
import org.bitcoins.core.protocol.blockchain.{Block, ChainParams}
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.keymanager._
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
@ -22,6 +23,7 @@ import org.bitcoins.wallet.models.{AccountDb, AddressDb, SpendingInfoDb}
import scala.annotation.tailrec
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
/**
* API for the wallet project.
@ -63,6 +65,15 @@ trait LockedWalletApi extends WalletApi {
transaction: Transaction,
blockHash: Option[DoubleSha256DigestBE]): Future[LockedWalletApi]
def processTransactions(
transactions: Vector[Transaction],
blockHash: Option[DoubleSha256DigestBE]): Future[LockedWalletApi] = {
transactions.foldLeft(Future.successful(this)) {
case (wallet, tx) =>
wallet.flatMap(_.processTransaction(tx, blockHash))
}
}
/**
* Processes the give block, updating our DB state if it's relevant to us.
* @param block The block we're processing
@ -155,6 +166,19 @@ trait LockedWalletApi extends WalletApi {
*/
def getAddressInfo(address: BitcoinAddress): Future[Option[AddressInfo]]
def getAddressInfo(
spendingInfoDb: SpendingInfoDb): Future[Option[AddressInfo]] = {
val addressT = BitcoinAddress.fromScriptPubKey(
spk = spendingInfoDb.output.scriptPubKey,
np = networkParameters)
addressT match {
case Success(addr) =>
getAddressInfo(addr)
case Failure(_) =>
FutureUtil.none
}
}
/** Generates a new change address */
protected[wallet] def getNewChangeAddress(
account: AccountDb): Future[BitcoinAddress]

View file

@ -0,0 +1,129 @@
package org.bitcoins.wallet.internal
import org.bitcoins.core.config.BitcoinNetwork
import org.bitcoins.core.crypto.Sign
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.keymanager.bip39.BIP39KeyManager
import org.bitcoins.wallet.WalletLogger
import org.bitcoins.wallet.api.{AddressInfo, CoinSelector, LockedWalletApi}
import org.bitcoins.wallet.models.{AccountDb, SpendingInfoDb}
import scala.concurrent.Future
trait FundTransactionHandling extends WalletLogger { self: LockedWalletApi =>
def fundRawTransaction(
destinations: Vector[TransactionOutput],
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
funded <- fundRawTransaction(destinations = destinations,
feeRate = feeRate,
fromAccount = account)
} yield funded
}
def fundRawTransaction(
destinations: Vector[TransactionOutput],
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] = {
val txBuilderF =
fundRawTransactionInternal(destinations = destinations,
feeRate = feeRate,
fromAccount = fromAccount,
keyManagerOpt = None)
txBuilderF.flatMap(_.unsignedTx)
}
/** This returns a [[BitcoinTxBuilder]] that can be used to generate a unsigned transaction with [[BitcoinTxBuilder.unsignedTx unsignedTx]]
* which can be used with signing, or you can just directly call [[BitcoinTxBuilder.sign sign]] to sign the transaction with this instance
* of [[BitcoinTxBuilder]]
*
* If you pass in a [[org.bitcoins.keymanager.KeyManager]], the [[org.bitcoins.core.wallet.utxo.UTXOSpendingInfo.signers signers]]
* will be populated with valid signers that can be used to produce valid [[org.bitcoins.core.crypto.ECDigitalSignature signatures]]
*
* If you do not pass in a key manager, the transaction built by [[BitcoinTxBuilder txbuilder]] will contain [[org.bitcoins.core.protocol.script.EmptyScriptSignature EmptyScriptSignature]]
*
* Currently utxos are funded with [[CoinSelector.accumulateLargest() accumulateLargest]] coin seleciton algorithm
* */
private[wallet] def fundRawTransactionInternal(
destinations: Vector[TransactionOutput],
feeRate: FeeUnit,
fromAccount: AccountDb,
keyManagerOpt: Option[BIP39KeyManager]): Future[BitcoinTxBuilder] = {
val utxosF = listUtxos()
val changeAddrF = getNewChangeAddress(fromAccount)
val selectedUtxosF = for {
walletUtxos <- utxosF
change <- changeAddrF
//currently just grab the biggest utxos
selectedUtxos = CoinSelector
.accumulateLargest(walletUtxos, destinations, feeRate)
} yield selectedUtxos
val addrInfosWithUtxoF: Future[Vector[(SpendingInfoDb, AddressInfo)]] =
for {
selectedUtxos <- selectedUtxosF
addrInfoOptF = selectedUtxos.map { utxo =>
val addrInfoOptF = getAddressInfo(utxo)
//.get should be safe here because of foreign key at the database level
addrInfoOptF.map(addrInfoOpt => (utxo, addrInfoOpt.get))
}
vec <- Future.sequence(addrInfoOptF)
} yield vec
val txBuilderF = for {
addrInfosWithUtxo <- addrInfosWithUtxoF
change <- changeAddrF
utxoSpendingInfos = {
addrInfosWithUtxo.map {
case (utxo, addrInfo) =>
keyManagerOpt match {
case Some(km) =>
utxo.toUTXOSpendingInfo(account = fromAccount,
keyManager = km,
networkParameters = networkParameters)
case None =>
utxo.toUTXOSpendingInfo(account = fromAccount,
sign = Sign.dummySign(addrInfo.pubkey),
networkParameters = networkParameters)
}
}
}
txBuilder <- {
logger.info({
val utxosStr = utxoSpendingInfos
.map { utxo =>
import utxo.outPoint
s"${outPoint.txId.hex}:${outPoint.vout.toInt}"
}
.mkString(", ")
s"Spending UTXOs: $utxosStr"
})
utxoSpendingInfos.zipWithIndex.foreach {
case (utxo, index) =>
logger.info(s"UTXO $index details: ${utxo.output}")
}
networkParameters match {
case b: BitcoinNetwork =>
BitcoinTxBuilder(destinations = destinations,
utxos = utxoSpendingInfos,
feeRate = feeRate,
changeSPK = change.scriptPubKey,
network = b)
}
}
} yield {
txBuilder
}
txBuilderF
}
}

View file

@ -135,10 +135,10 @@ class AddressTable(tag: Tag) extends Table[AddressDb](tag, "addresses") {
def purpose: Rep[HDPurpose] = column("hd_purpose")
def accountIndex: Rep[Int] = column("account_index")
def accountCoin: Rep[HDCoinType] = column("hd_coin")
def accountIndex: Rep[Int] = column("account_index")
def accountChainType: Rep[HDChainType] = column("hd_chain_type")
def addressIndex: Rep[Int] = column("address_index")

View file

@ -11,7 +11,7 @@ import org.bitcoins.core.protocol.transaction.{
}
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.utxo.{
BitcoinUTXOSpendingInfo,
BitcoinUTXOSpendingInfoFull,
ConditionalPath,
TxoState
}
@ -133,14 +133,24 @@ sealed trait SpendingInfoDb extends DbRowAutoInc[SpendingInfoDb] {
def toUTXOSpendingInfo(
account: AccountDb,
keyManager: BIP39KeyManager,
network: NetworkParameters): BitcoinUTXOSpendingInfo = {
networkParameters: NetworkParameters): BitcoinUTXOSpendingInfoFull = {
val sign: Sign = keyManager.toSign(privKeyPath = privKeyPath)
BitcoinUTXOSpendingInfo(
toUTXOSpendingInfo(account = account,
sign = sign,
networkParameters = networkParameters)
}
def toUTXOSpendingInfo(
account: AccountDb,
sign: Sign,
networkParameters: NetworkParameters
): BitcoinUTXOSpendingInfoFull = {
BitcoinUTXOSpendingInfoFull(
outPoint,
output,
List(sign),
Vector(sign),
redeemScriptOpt,
scriptWitnessOpt,
hashType,