mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 02:11:40 +01:00
commit
bd2271db63
@ -2,6 +2,8 @@
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2")
|
||||
|
||||
//testOptions in Test += Tests.Argument("-oF")
|
||||
|
||||
coverageExcludedPackages := ".*gen"
|
||||
|
||||
coverageMinimum := 90
|
||||
@ -16,6 +18,5 @@ mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => {
|
||||
|
||||
assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false)
|
||||
|
||||
//testOptions in Test += Tests.Argument("-oF")
|
||||
|
||||
scalacOptions ++= Seq("-Xmax-classfile-name", "140")
|
||||
|
@ -54,7 +54,7 @@ sealed abstract class MainNet extends BitcoinNetwork {
|
||||
|
||||
object MainNet extends MainNet
|
||||
|
||||
sealed abstract class TestNet3 extends NetworkParameters {
|
||||
sealed abstract class TestNet3 extends BitcoinNetwork {
|
||||
override def chainParams = TestNetChainParams
|
||||
override def port = 18333
|
||||
override def rpcPort = 18332
|
||||
@ -67,8 +67,8 @@ sealed abstract class TestNet3 extends NetworkParameters {
|
||||
|
||||
object TestNet3 extends TestNet3
|
||||
|
||||
sealed abstract class RegTest extends NetworkParameters {
|
||||
override def chainParams: ChainParams = RegTestNetChainParams
|
||||
sealed abstract class RegTest extends BitcoinNetwork {
|
||||
override def chainParams = RegTestNetChainParams
|
||||
override def port = 18444
|
||||
override def rpcPort = TestNet3.rpcPort
|
||||
override def dnsSeeds = Nil
|
||||
|
@ -42,7 +42,7 @@ sealed abstract class TransactionSignatureSerializer {
|
||||
val inputSigsRemoved = for {
|
||||
input <- spendingTransaction.inputs
|
||||
s = input.scriptSignature
|
||||
} yield TransactionInput(input,NonStandardScriptSignature(s.compactSizeUInt.hex))
|
||||
} yield TransactionInput(input.previousOutput,NonStandardScriptSignature(s.compactSizeUInt.hex), input.sequence)
|
||||
|
||||
//make sure all scriptSigs have empty asm
|
||||
inputSigsRemoved.map(input =>
|
||||
@ -65,7 +65,8 @@ sealed abstract class TransactionSignatureSerializer {
|
||||
// Set the input to the script of its output. Bitcoin Core does this but the step has no obvious purpose as
|
||||
// the signature covers the hash of the prevout transaction which obviously includes the output script
|
||||
// already. Perhaps it felt safer to him in some way, or is another leftover from how the code was written.
|
||||
val inputWithConnectedScript = TransactionInput(inputToSign,scriptWithOpCodeSeparatorsRemoved)
|
||||
val scriptSig = ScriptSignature.fromAsm(scriptWithOpCodeSeparatorsRemoved)
|
||||
val inputWithConnectedScript = TransactionInput(inputToSign.previousOutput,scriptSig, inputToSign.sequence)
|
||||
|
||||
//update the input at index i with inputWithConnectScript
|
||||
val updatedInputs = for {
|
||||
@ -232,7 +233,7 @@ sealed abstract class TransactionSignatureSerializer {
|
||||
(input,index) <- inputs.zipWithIndex
|
||||
} yield {
|
||||
if (UInt32(index) == inputIndex) input
|
||||
else TransactionInput(input,UInt32.zero)
|
||||
else TransactionInput(input.previousOutput, input.scriptSignature,UInt32.zero)
|
||||
}
|
||||
|
||||
/** Executes the [[SIGHASH_NONE]] procedure on a spending transaction for the input specified by inputIndex. */
|
||||
|
@ -9,7 +9,7 @@ import org.bitcoins.core.script.flag._
|
||||
* Mimics the policy files found in bitcoin core
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.h
|
||||
*/
|
||||
trait Policy {
|
||||
sealed abstract class Policy {
|
||||
|
||||
/**
|
||||
* Mandatory script verification flags that all new blocks must comply with for
|
||||
@ -49,6 +49,8 @@ trait Policy {
|
||||
|
||||
/** Max fee for a transaction is set to 10 mBTC right now */
|
||||
def maxFee: CurrencyUnit = Satoshis(Int64(10)) * CurrencyUnits.oneMBTC
|
||||
|
||||
def isRBFEnabled: Boolean = true
|
||||
}
|
||||
|
||||
object Policy extends Policy
|
||||
|
@ -77,7 +77,7 @@ sealed abstract class ChainParams {
|
||||
val const = ScriptConstant(timestampBytes)
|
||||
val scriptSignature = ScriptSignature.fromAsm(Seq(BytesToPushOntoStack(4), ScriptNumber(486604799),
|
||||
BytesToPushOntoStack(1), ScriptNumber(4)) ++ BitcoinScriptUtil.calculatePushOp(const) ++ Seq(const))
|
||||
val input = TransactionInput(scriptSignature)
|
||||
val input = CoinbaseInput(scriptSignature)
|
||||
val output = TransactionOutput(amount,scriptPubKey)
|
||||
val tx = BaseTransaction(TransactionConstants.version,Seq(input), Seq(output), TransactionConstants.lockTime)
|
||||
val prevBlockHash = DoubleSha256Digest("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
@ -122,34 +122,6 @@ object Transaction extends Factory[Transaction] {
|
||||
btx
|
||||
}
|
||||
}
|
||||
@deprecated("", "2018/02/16")
|
||||
def apply(oldTx : Transaction, lockTime : UInt32): Transaction = oldTx match {
|
||||
case btx: BaseTransaction =>
|
||||
BaseTransaction(btx.version,btx.inputs,btx.outputs,lockTime)
|
||||
case wtx: WitnessTransaction =>
|
||||
WitnessTransaction(wtx.version,wtx.inputs,wtx.outputs,lockTime,wtx.witness)
|
||||
}
|
||||
|
||||
@deprecated("", "2018/02/16")
|
||||
def apply(oldTx : Transaction, updatedInputs : UpdateTransactionInputs): Transaction = oldTx match {
|
||||
case btx: BaseTransaction =>
|
||||
BaseTransaction(btx.version,updatedInputs.inputs,btx.outputs,btx.lockTime)
|
||||
case wtx: WitnessTransaction =>
|
||||
WitnessTransaction(wtx.version,updatedInputs.inputs,wtx.outputs,wtx.lockTime,wtx.witness)
|
||||
}
|
||||
@deprecated("", "2018/02/16")
|
||||
def apply(oldTx : Transaction, updatedOutputs : UpdateTransactionOutputs) : Transaction = oldTx match {
|
||||
case btx: BaseTransaction =>
|
||||
BaseTransaction(btx.version,btx.inputs,updatedOutputs.outputs,btx.lockTime)
|
||||
case wtx: WitnessTransaction =>
|
||||
WitnessTransaction(wtx.version,wtx.inputs,updatedOutputs.outputs,wtx.lockTime,wtx.witness)
|
||||
}
|
||||
|
||||
@deprecated("Dangerous was you can lose TransactionWitness, use BaseTransaction", "2018/02/16")
|
||||
def apply(version : UInt32, inputs : Seq[TransactionInput],
|
||||
outputs : Seq[TransactionOutput], lockTime : UInt32) : Transaction = {
|
||||
BaseTransaction(version,inputs,outputs,lockTime)
|
||||
}
|
||||
}
|
||||
|
||||
object BaseTransaction extends Factory[BaseTransaction] {
|
||||
|
@ -2,8 +2,7 @@ package org.bitcoins.core.protocol.transaction
|
||||
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
|
||||
import org.bitcoins.core.script.constant.ScriptToken
|
||||
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptSignature}
|
||||
import org.bitcoins.core.serializers.transaction.RawTransactionInputParser
|
||||
import org.bitcoins.core.util.Factory
|
||||
|
||||
@ -11,31 +10,26 @@ import org.bitcoins.core.util.Factory
|
||||
* Created by chris on 12/26/15.
|
||||
* Algebraic data type that represents a transaction input
|
||||
*/
|
||||
sealed trait TransactionInput extends NetworkElement {
|
||||
sealed abstract class TransactionInput extends NetworkElement {
|
||||
|
||||
def previousOutput : TransactionOutPoint
|
||||
|
||||
def scriptSignature : ScriptSignature
|
||||
|
||||
def sequence : UInt32
|
||||
|
||||
//https://bitcoin.org/en/developer-reference#txin
|
||||
override def size = previousOutput.size + scriptSignature.size + 4
|
||||
|
||||
override def bytes = RawTransactionInputParser.write(this)
|
||||
}
|
||||
|
||||
case object EmptyTransactionInput extends TransactionInput {
|
||||
override def previousOutput = TransactionInput.empty.previousOutput
|
||||
override def scriptSignature = TransactionInput.empty.scriptSignature
|
||||
override def sequence = TransactionInput.empty.sequence
|
||||
override def previousOutput = EmptyTransactionOutPoint
|
||||
override def scriptSignature = EmptyScriptSignature
|
||||
override def sequence = TransactionConstants.sequence
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a coinbase input - these always have a EmptyTransactionOutPoint
|
||||
* and arbitrary data inside the script signature
|
||||
*/
|
||||
sealed trait CoinbaseInput extends TransactionInput {
|
||||
sealed abstract class CoinbaseInput extends TransactionInput {
|
||||
override def previousOutput = EmptyTransactionOutPoint
|
||||
override def sequence = TransactionConstants.sequence
|
||||
}
|
||||
@ -43,66 +37,25 @@ sealed trait CoinbaseInput extends TransactionInput {
|
||||
|
||||
|
||||
object TransactionInput extends Factory[TransactionInput] {
|
||||
private def factory(oldInput : TransactionInput, scriptSig : ScriptSignature) : TransactionInput = {
|
||||
apply(oldInput.previousOutput,scriptSig,oldInput.sequence)
|
||||
}
|
||||
private sealed case class TransactionInputImpl(previousOutput : TransactionOutPoint,
|
||||
private case class TransactionInputImpl(previousOutput : TransactionOutPoint,
|
||||
scriptSignature : ScriptSignature, sequence : UInt32) extends TransactionInput
|
||||
|
||||
private sealed case class CoinbaseInputImpl(
|
||||
scriptSignature : ScriptSignature) extends CoinbaseInput
|
||||
|
||||
private def factory(oldInput : TransactionInput, scriptPubKey: ScriptPubKey) : TransactionInput = {
|
||||
val scriptSig = ScriptSignature(scriptPubKey.hex)
|
||||
factory(oldInput,scriptSig)
|
||||
}
|
||||
|
||||
private def factory(oldInput : TransactionInput,sequenceNumber : UInt32) : TransactionInput = {
|
||||
TransactionInputImpl(oldInput.previousOutput, oldInput.scriptSignature,sequenceNumber)
|
||||
}
|
||||
|
||||
/** Creates a transaction input from a given output and the output's transaction */
|
||||
private def factory(oldInput : TransactionInput,output : TransactionOutput, outputsTransaction : Transaction) : TransactionInput = {
|
||||
val outPoint = TransactionOutPoint(output,outputsTransaction)
|
||||
factory(oldInput,outPoint)
|
||||
}
|
||||
|
||||
private def factory(oldInput : TransactionInput, outPoint: TransactionOutPoint) : TransactionInput = {
|
||||
TransactionInputImpl(outPoint,oldInput.scriptSignature,oldInput.sequence)
|
||||
}
|
||||
|
||||
|
||||
private def factory(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature, sequenceNumber : UInt32) : TransactionInput = {
|
||||
outPoint match {
|
||||
case EmptyTransactionOutPoint => CoinbaseInputImpl(scriptSignature)
|
||||
case _ : TransactionOutPoint => TransactionInputImpl(outPoint, scriptSignature, sequenceNumber)
|
||||
}
|
||||
}
|
||||
|
||||
def empty : TransactionInput = {
|
||||
TransactionInputImpl(EmptyTransactionOutPoint,
|
||||
ScriptSignature.empty,TransactionConstants.sequence)
|
||||
}
|
||||
def empty: TransactionInput = EmptyTransactionInput
|
||||
|
||||
def fromBytes(bytes : Seq[Byte]) : TransactionInput = RawTransactionInputParser.read(bytes)
|
||||
|
||||
def apply(oldInput : TransactionInput, scriptSig : ScriptSignature) : TransactionInput = factory(oldInput,scriptSig)
|
||||
|
||||
def apply(oldInput : TransactionInput, script : Seq[ScriptToken]) : TransactionInput = {
|
||||
val scriptSig = ScriptSignature.fromAsm(script)
|
||||
apply(oldInput,scriptSig)
|
||||
def apply(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature,
|
||||
sequenceNumber : UInt32) : TransactionInput = outPoint match {
|
||||
case EmptyTransactionOutPoint => CoinbaseInput(scriptSignature)
|
||||
case _: TransactionOutPoint => TransactionInputImpl(outPoint,scriptSignature,sequenceNumber)
|
||||
}
|
||||
|
||||
def apply(oldInput : TransactionInput, scriptPubKey: ScriptPubKey) : TransactionInput = factory(oldInput, scriptPubKey)
|
||||
|
||||
def apply(oldInput : TransactionInput,sequenceNumber : UInt32) : TransactionInput = factory(oldInput, sequenceNumber)
|
||||
|
||||
def apply(oldInput : TransactionInput,output : TransactionOutput, outputsTransaction : Transaction) : TransactionInput = factory(oldInput,output,outputsTransaction)
|
||||
}
|
||||
|
||||
def apply(oldInput : TransactionInput, outPoint: TransactionOutPoint) : TransactionInput = factory(oldInput,outPoint)
|
||||
|
||||
def apply(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature, sequenceNumber : UInt32) : TransactionInput = factory(outPoint,scriptSignature,sequenceNumber)
|
||||
object CoinbaseInput {
|
||||
|
||||
private case class CoinbaseInputImpl(scriptSignature : ScriptSignature) extends CoinbaseInput
|
||||
/**
|
||||
* Creates a coinbase input - coinbase inputs always have an empty outpoint
|
||||
* @param scriptSignature this can contain anything, miners use this to signify support for various protocol BIPs
|
||||
|
@ -9,16 +9,13 @@ import org.bitcoins.core.util.{BitcoinSUtil, Factory}
|
||||
* Created by chris on 12/26/15.
|
||||
*
|
||||
*/
|
||||
sealed trait TransactionOutPoint extends NetworkElement {
|
||||
sealed abstract class TransactionOutPoint extends NetworkElement {
|
||||
/** The transaction id for the crediting transaction for this input */
|
||||
def txId : DoubleSha256Digest
|
||||
|
||||
/** The output index in the parent transaction for the output we are spending */
|
||||
def vout : UInt32
|
||||
|
||||
//https://bitcoin.org/en/developer-reference#outpoint
|
||||
override def size = 36
|
||||
|
||||
override def bytes = RawTransactionOutPointParser.write(this)
|
||||
}
|
||||
|
||||
@ -36,29 +33,14 @@ case object EmptyTransactionOutPoint extends TransactionOutPoint {
|
||||
|
||||
object TransactionOutPoint extends Factory[TransactionOutPoint] {
|
||||
|
||||
private sealed case class TransactionOutPointImpl(txId : DoubleSha256Digest, vout : UInt32) extends TransactionOutPoint
|
||||
/**
|
||||
* Creates a transaction outpoint from a TransactionOutput & it's parent transaction
|
||||
*
|
||||
* @param output
|
||||
* @return
|
||||
*/
|
||||
private def factory(output : TransactionOutput, parentTransaction : Transaction) : TransactionOutPoint = {
|
||||
val indexOfOutput = UInt32(parentTransaction.outputs.indexOf(output))
|
||||
if (indexOfOutput.toInt == (-1)) throw new RuntimeException("This output is not contained in the parent transaction")
|
||||
else factory(parentTransaction.txId,indexOfOutput)
|
||||
}
|
||||
|
||||
private def factory(txId : DoubleSha256Digest, index : UInt32) = {
|
||||
if (txId == EmptyTransactionOutPoint.txId && index == EmptyTransactionOutPoint.vout) {
|
||||
EmptyTransactionOutPoint
|
||||
} else TransactionOutPointImpl(txId, index)
|
||||
}
|
||||
private case class TransactionOutPointImpl(txId : DoubleSha256Digest, vout : UInt32) extends TransactionOutPoint
|
||||
|
||||
def fromBytes(bytes : Seq[Byte]) : TransactionOutPoint = RawTransactionOutPointParser.read(bytes)
|
||||
|
||||
def apply(output : TransactionOutput,parentTransaction : Transaction) : TransactionOutPoint = factory(output,parentTransaction)
|
||||
|
||||
def apply(txId : DoubleSha256Digest, index: UInt32) : TransactionOutPoint = factory(txId,index)
|
||||
def apply(txId : DoubleSha256Digest, index: UInt32) : TransactionOutPoint = {
|
||||
if (txId == EmptyTransactionOutPoint.txId && index == EmptyTransactionOutPoint.vout) {
|
||||
EmptyTransactionOutPoint
|
||||
} else TransactionOutPointImpl(txId,index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,23 +25,12 @@ case object EmptyTransactionOutput extends TransactionOutput {
|
||||
override def scriptPubKey = ScriptPubKey.empty
|
||||
}
|
||||
|
||||
|
||||
case class TransactionOutputImpl(value : CurrencyUnit, scriptPubKey: ScriptPubKey) extends TransactionOutput
|
||||
|
||||
object TransactionOutput extends Factory[TransactionOutput] {
|
||||
private case class TransactionOutputImpl(value : CurrencyUnit, scriptPubKey: ScriptPubKey) extends TransactionOutput
|
||||
|
||||
def fromBytes(bytes : Seq[Byte]) : TransactionOutput = RawTransactionOutputParser.read(bytes)
|
||||
|
||||
def apply(oldOutput : TransactionOutput, newCurrencyUnit: CurrencyUnit) : TransactionOutput = {
|
||||
TransactionOutput(newCurrencyUnit,oldOutput.scriptPubKey)
|
||||
}
|
||||
|
||||
def apply(oldOutput : TransactionOutput, newScriptPubKey : ScriptPubKey) : TransactionOutput = {
|
||||
TransactionOutput(oldOutput.value,newScriptPubKey)
|
||||
}
|
||||
|
||||
def apply(currencyUnit: CurrencyUnit, scriptPubKey: ScriptPubKey) : TransactionOutput = {
|
||||
TransactionOutputImpl(currencyUnit,scriptPubKey)
|
||||
}
|
||||
|
||||
}
|
@ -25,8 +25,13 @@ case object EmptyWitness extends TransactionWitness {
|
||||
object TransactionWitness {
|
||||
private case class TransactionWitnessImpl(witnesses: Seq[ScriptWitness]) extends TransactionWitness
|
||||
|
||||
def apply(witnesses: Seq[ScriptWitness]): TransactionWitness = TransactionWitnessImpl(witnesses)
|
||||
|
||||
def apply(witnesses: Seq[ScriptWitness]): TransactionWitness = {
|
||||
if (witnesses.exists(_ != EmptyScriptWitness)) {
|
||||
TransactionWitnessImpl(witnesses)
|
||||
} else {
|
||||
EmptyWitness
|
||||
}
|
||||
}
|
||||
/** Creates a [[TransactionWitness]] from a Seq[Option[ScriptWitness]].
|
||||
* This constructor is for convinience if a certain input does not spend a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
|
||||
* It simply transforms the `None` types to [[EmptyScriptWitness]] and then calls the normal TransactionWitness constructor
|
||||
|
@ -20,7 +20,4 @@ abstract class RawBitcoinSerializer[T] {
|
||||
|
||||
/** Takes a type T and writes it into the appropriate hexadecimal serialization for type T. */
|
||||
def write(t : T) : Seq[Byte]
|
||||
|
||||
def logger: Logger = BitcoinSLogger.logger
|
||||
|
||||
}
|
||||
|
@ -9,22 +9,6 @@ import org.bitcoins.core.util.BitcoinSLogger
|
||||
*/
|
||||
sealed abstract class RawSerializerHelper {
|
||||
private val logger = BitcoinSLogger.logger
|
||||
/** Adds the amount padding bytes needed to fix the size of the hex string
|
||||
* for instance, vouts are required to be 4 bytes. If the number is just 1
|
||||
* it will only take 1 byte. We need to pad the byte with an extra 3 bytes so the result is
|
||||
* 01000000 instead of just 01. */
|
||||
def addPadding(charactersNeeded : Int, hex : String) : String = {
|
||||
val paddingNeeded = charactersNeeded - hex.size
|
||||
val padding = for { i <- 0 until paddingNeeded} yield "0"
|
||||
val paddedHex = hex + padding.mkString
|
||||
paddedHex
|
||||
}
|
||||
|
||||
/** Adds a preceding zero to a hex string.
|
||||
* Example: if '1' was passed in, it would return the hex string '01'.*/
|
||||
def addPrecedingZero(hex : String) = {
|
||||
if (hex.size == 1) "0" + hex else hex
|
||||
}
|
||||
|
||||
/** Used parse a byte sequence to a Seq[TransactionInput], Seq[TransactionOutput], etc
|
||||
* Makes sure that we parse the correct amount of elements
|
||||
|
@ -0,0 +1,121 @@
|
||||
package org.bitcoins.core.wallet.builder
|
||||
|
||||
/** Represents an error that can be returned by the [[org.bitcoins.core.wallet.builder.TxBuilder]]
|
||||
* if it failed to sign a set of utxos
|
||||
*/
|
||||
sealed abstract class TxBuilderError
|
||||
|
||||
object TxBuilderError {
|
||||
/** This error indicates that the transaction failed to pass the invariants the user wanted to hold
|
||||
* true after the signing process was complete. An example of this is the transaction is too big,
|
||||
* or the fee level was too high or low.
|
||||
*/
|
||||
case object FailedUserInvariants extends TxBuilderError
|
||||
|
||||
/** Means that we gave too many [[org.bitcoins.core.wallet.signer.Signer.Sign]] for the TxBuilder
|
||||
* to use during the signing process for a utxo.
|
||||
* An example of this occurring is if we gave 2 private keys to sign a p2pkh spk.
|
||||
* A p2pkh only requires one private key to sign the utxo.
|
||||
*/
|
||||
case object TooManySigners extends TxBuilderError
|
||||
|
||||
/** Means that you are using the wrong [[org.bitcoins.core.wallet.signer.Signer]] to
|
||||
* sign the given [[org.bitcoins.core.protocol.script.ScriptPubKey]]
|
||||
*/
|
||||
case object WrongSigner extends TxBuilderError
|
||||
|
||||
/** Means that the [[org.bitcoins.core.protocol.script.ScriptWitnessV0]] you passed as an argument does
|
||||
* not hash to the commitment inside of [[org.bitcoins.core.protocol.script.P2WSHWitnessSPKV0]]
|
||||
*/
|
||||
case object WrongWitness extends TxBuilderError
|
||||
|
||||
/** Means that the redeem script you passed as an argument does not hash to the commitment
|
||||
* inside of the [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]]
|
||||
*/
|
||||
case object WrongRedeemScript extends TxBuilderError
|
||||
|
||||
/** Means that you passed the wrong public key for a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]] or a
|
||||
* [[org.bitcoins.core.protocol.script.P2WPKHWitnessSPKV0]] that you are trying to spend
|
||||
*/
|
||||
case object WrongPublicKey extends TxBuilderError
|
||||
|
||||
/** Can occurr when we are trying to sign a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]] but
|
||||
* we do not have a redeem script for that p2sh spk.
|
||||
*/
|
||||
case object NoRedeemScript extends TxBuilderError
|
||||
|
||||
/** Can occurr when we are trying to sign a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
|
||||
* but we do not have a [[org.bitcoins.core.protocol.script.ScriptWitness]] for that witness spk
|
||||
*/
|
||||
case object NoWitness extends TxBuilderError
|
||||
|
||||
/** We expected a [[org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0]], but got a non witness spk type */
|
||||
case object NonWitnessSPK extends TxBuilderError
|
||||
|
||||
/** We cannot have a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]] nested inside of another [[org.bitcoins.core.protocol.script.ScriptPubKey]] */
|
||||
case object NestedWitnessSPK extends TxBuilderError
|
||||
|
||||
/** We cannot have a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]] nested inside of another spk */
|
||||
case object NestedP2SHSPK extends TxBuilderError
|
||||
|
||||
/** Means that there is no signer defined for the given [[org.bitcoins.core.protocol.script.ScriptPubKey]] type.
|
||||
* An example of a spk with no signer that is defined is [[org.bitcoins.core.protocol.script.WitnessCommitment]]
|
||||
*/
|
||||
case object NoSigner extends TxBuilderError
|
||||
|
||||
/** Means that you specified a fee that was too large for the change output you provided.
|
||||
* This may happen if you have a transaction with a lot of inputs, or the change output you provided
|
||||
* is a output that contains a very small amount of bitcoin.
|
||||
* */
|
||||
case object FeeToLarge extends TxBuilderError
|
||||
|
||||
/** Means that the [[TxBuilder.destinations]] outputs you specified when creating the [[TxBuilder]] are NOT
|
||||
* all included in the final signed tx
|
||||
*/
|
||||
case object MissingDestinationOutput extends TxBuilderError
|
||||
|
||||
/** Means that you provided a outpoint in the [[TxBuilder.utxoMap]] that does not
|
||||
* exist inside of [[TxBuilder.creditingTxs]]. You cannot spend an outpoint that was not
|
||||
* passed into the txbuilder originally
|
||||
*/
|
||||
case object MissingCreditingTx extends TxBuilderError
|
||||
|
||||
/** Means that the script we are signing for requires a public key, but we did not pass one in
|
||||
* as a parameter inside of [[org.bitcoins.core.wallet.signer.Signer.Sign]]
|
||||
*/
|
||||
case object MissingPublicKey extends TxBuilderError
|
||||
|
||||
case object MissingOutPoint extends TxBuilderError
|
||||
/** Means that the signed version of this transaction has MORE outputs than what was specified
|
||||
* when building the [[TxBuilder]]. [[TxBuilder.destinations]] && [[TxBuilder.changeOutput]] should
|
||||
* be the only outputs in the signedTx
|
||||
*/
|
||||
case object ExtraOutputsAdded extends TxBuilderError
|
||||
|
||||
/** Means that the transaction spends outpoints that were not given when creating
|
||||
* the [[TxBuilder]], aka, we should only spend outpoints in [[TxBuilder.outPoints]]
|
||||
*/
|
||||
case object ExtraOutPoints extends TxBuilderError
|
||||
|
||||
/** Means that this transaction attempts to print satoshis out of thin air */
|
||||
case object MintsMoney extends TxBuilderError
|
||||
|
||||
/** Means that the fee was too low for [[TxBuilder.feeRate]] */
|
||||
case object LowFee extends TxBuilderError
|
||||
|
||||
/** Means tha this transaction pays too high of a fee for [[TxBuilder.feeRate]] */
|
||||
case object HighFee extends TxBuilderError
|
||||
|
||||
/** Indicates we are spending multiple [[org.bitcoins.core.protocol.script.CLTVScriptPubKey]],
|
||||
* and that one of those spk's outputs are locked by block height, while the other is locked by
|
||||
* a time stamp. Since there is only one locktime field on a transaction, we cannot satisfy both of these
|
||||
* locktimes simultaneously.
|
||||
*/
|
||||
case object IncompatibleLockTimes extends TxBuilderError
|
||||
|
||||
/** Means we have a output on this transaction below [[org.bitcoins.core.policy.Policy.dustThreshold]] */
|
||||
case object OutputBelowDustThreshold extends TxBuilderError
|
||||
|
||||
case object UnknownError extends TxBuilderError
|
||||
|
||||
}
|
29
src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala
Normal file
29
src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala
Normal file
@ -0,0 +1,29 @@
|
||||
package org.bitcoins.core.wallet.fee
|
||||
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.number.Int64
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
|
||||
/** This is meant to be an abstract type that represents different fee unit measurements for
|
||||
* blockchains
|
||||
*/
|
||||
sealed abstract class FeeUnit {
|
||||
def currencyUnit: CurrencyUnit
|
||||
def *(tx: Transaction): CurrencyUnit = calc(tx)
|
||||
def calc(tx: Transaction): CurrencyUnit = Satoshis(Int64(tx.size * toLong))
|
||||
def toLong: Long = currencyUnit.satoshis.toLong
|
||||
}
|
||||
|
||||
/** Meant to represent the different fee unit types for the bitcoin protocol
|
||||
* [[https://en.bitcoin.it/wiki/Weight_units]]
|
||||
*/
|
||||
sealed abstract class BitcoinFeeUnit
|
||||
|
||||
case class SatoshisPerByte(currencyUnit: CurrencyUnit) extends FeeUnit
|
||||
|
||||
/** A 'virtual byte' (also known as virtual size) is a new weight measurement that
|
||||
* was created with segregated witness (BIP141). Now 1 'virtual byte'
|
||||
* has the weight of 4 bytes in the [[org.bitcoins.core.protocol.transaction.TransactionWitness]]
|
||||
* of a [[org.bitcoins.core.protocol.transaction.WitnessTransaction]]
|
||||
*/
|
||||
case class SatoshisPerVirtualByte(currencyUnit: CurrencyUnit) extends FeeUnit
|
410
src/main/scala/org/bitcoins/core/wallet/signer/Signer.scala
Normal file
410
src/main/scala/org/bitcoins/core/wallet/signer/Signer.scala
Normal file
@ -0,0 +1,410 @@
|
||||
package org.bitcoins.core.wallet.signer
|
||||
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.policy.Policy
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.wallet.builder.TxBuilderError
|
||||
|
||||
/** The class used to represent a signing process for a specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */
|
||||
sealed abstract class Signer {
|
||||
|
||||
/**
|
||||
* The method used to sign a bitcoin unspent transaction output
|
||||
* @param signers the [[Signer.Sign]] needed to sign the utxo
|
||||
* @param output the utxo we are spending
|
||||
* @param unsignedTx the unsigned transaction which is spending the utxo
|
||||
* @param inputIndex the input index inside of the unsigned transaction which spends the utxo
|
||||
* @param hashType the signature hashing algorithm we should use to sign the utxo
|
||||
* @return
|
||||
*/
|
||||
def sign(signers: Seq[Signer.Sign], output: TransactionOutput, unsignedTx: Transaction,
|
||||
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError]
|
||||
}
|
||||
|
||||
object Signer {
|
||||
/** This is meant to be an abstraction for a [[org.bitcoins.core.crypto.ECPrivateKey]], sometimes we will not
|
||||
* have direct access to a private key in memory -- for instance if that key is on a hardware device -- so we need to create an
|
||||
* abstraction of the signing process. Fundamentally a private key takes in a Seq[Byte] and returns a [[ECDigitalSignature]]
|
||||
* That is what this abstraction is meant to represent. If you have a [[ECPrivateKey]] in your application, you can get it's
|
||||
* [[Sign]] type by doing this:
|
||||
*
|
||||
* val key = ECPrivateKey()
|
||||
* val sign: Seq[Byte] => ECDigitalSignature = (key.sign(_: Seq[Byte]), key.publicKey)
|
||||
*
|
||||
* If you have a hardware wallet, you will need to implement the protocol to send a message to the hardware device. The
|
||||
* type signature of the function you implement must be Seq[Byte] => ECDigitalSignature
|
||||
*
|
||||
* TODO: Investigate turning this into Seq[Byte] => Future[ECDigitalSignature]
|
||||
*/
|
||||
type Sign = (Seq[Byte] => ECDigitalSignature, Option[ECPublicKey])
|
||||
}
|
||||
|
||||
/** Represents all signers for the bitcoin protocol, we could add another network later like litecoin */
|
||||
sealed abstract class BitcoinSigner extends Signer
|
||||
|
||||
/** Used to sign a [[org.bitcoins.core.protocol.script.P2PKScriptPubKey]] */
|
||||
sealed abstract class P2PKSigner extends BitcoinSigner {
|
||||
|
||||
override def sign(signers: Seq[Signer.Sign], output: TransactionOutput, unsignedTx: Transaction,
|
||||
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
|
||||
val spk = output.scriptPubKey
|
||||
if (signers.size != 1) {
|
||||
Right(TxBuilderError.TooManySigners)
|
||||
} else {
|
||||
val signer = signers.head._1
|
||||
val unsignedInput = unsignedTx.inputs(inputIndex.toInt)
|
||||
val flags = Policy.standardFlags
|
||||
val amount = output.value
|
||||
val signed: Either[TxSigComponent,TxBuilderError] = spk match {
|
||||
case p2wshSPK: P2WSHWitnessSPKV0 =>
|
||||
val wtx = unsignedTx match {
|
||||
case btx: BaseTransaction => WitnessTransaction(btx.version, btx.inputs,
|
||||
btx.outputs, btx.lockTime, EmptyWitness)
|
||||
case wtx: WitnessTransaction => wtx
|
||||
}
|
||||
val redeemScript = wtx.witness.witnesses(inputIndex.toInt) match {
|
||||
case x: P2WSHWitnessV0 => Left(x.redeemScript)
|
||||
case _: P2WPKHWitnessV0 => Right(TxBuilderError.NoRedeemScript)
|
||||
case EmptyScriptWitness => Right(TxBuilderError.NoWitness)
|
||||
}
|
||||
val sigComponent = WitnessTxSigComponentRaw(wtx,inputIndex,p2wshSPK,flags,amount)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent,signer,hashType)
|
||||
val p2pkScriptSig = P2PKScriptSignature(signature)
|
||||
val scriptWit = redeemScript.left.map(s => P2WSHWitnessV0(s,p2pkScriptSig))
|
||||
val signedWitness = scriptWit.left.map(s => TransactionWitness(wtx.witness.witnesses.updated(inputIndex.toInt,s)))
|
||||
val signedWTx = signedWitness.left.map { wit =>
|
||||
WitnessTransaction(wtx.version,wtx.inputs,wtx.outputs,wtx.lockTime,wit)
|
||||
}
|
||||
signedWTx.left.map(wtx => WitnessTxSigComponentRaw(wtx,inputIndex,p2wshSPK,flags,amount))
|
||||
case _: P2PKScriptPubKey =>
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx,inputIndex,spk,flags)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent,signer,hashType)
|
||||
val p2pkScriptSig = P2PKScriptSignature(signature)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput,p2pkScriptSig,unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt,signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs,btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs,wtx.lockTime, wtx.witness)
|
||||
}
|
||||
Left(BaseTxSigComponent(signedTx,inputIndex,spk, flags))
|
||||
case lock: LockTimeScriptPubKey =>
|
||||
lock.nestedScriptPubKey match {
|
||||
case _: P2PKScriptPubKey =>
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx,inputIndex,lock,flags)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent,signer,hashType)
|
||||
val p2pkScriptSig = P2PKScriptSignature(signature)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput,p2pkScriptSig,unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt,signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs,btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs,wtx.lockTime, wtx.witness)
|
||||
}
|
||||
Left(BaseTxSigComponent(signedTx, inputIndex, lock, flags))
|
||||
case _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: CLTVScriptPubKey | _:CSVScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
case _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
signed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object P2PKSigner extends P2PKSigner
|
||||
|
||||
/** Used to sign a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]] */
|
||||
sealed abstract class P2PKHSigner extends BitcoinSigner {
|
||||
|
||||
override def sign(signers: Seq[Signer.Sign], output: TransactionOutput, unsignedTx: Transaction,
|
||||
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
|
||||
val spk = output.scriptPubKey
|
||||
if (signers.size != 1) {
|
||||
Right(TxBuilderError.TooManySigners)
|
||||
} else if (signers.head._2.isEmpty) {
|
||||
Right(TxBuilderError.MissingPublicKey)
|
||||
} else {
|
||||
val signer = signers.head._1
|
||||
val pubKey = signers.head._2.get
|
||||
val unsignedInput = unsignedTx.inputs(inputIndex.toInt)
|
||||
val flags = Policy.standardFlags
|
||||
val amount = output.value
|
||||
val signed: Either[TxSigComponent, TxBuilderError] = spk match {
|
||||
case p2wshSPK: P2WSHWitnessSPKV0 =>
|
||||
val wtx = unsignedTx match {
|
||||
case btx: BaseTransaction => WitnessTransaction(btx.version, btx.inputs,
|
||||
btx.outputs, btx.lockTime, EmptyWitness)
|
||||
case wtx: WitnessTransaction => wtx
|
||||
}
|
||||
val redeemScript = wtx.witness.witnesses(inputIndex.toInt) match {
|
||||
case EmptyScriptWitness | _: P2WPKHWitnessV0 => Right(TxBuilderError.WrongWitness)
|
||||
case p2wsh: P2WSHWitnessV0 => Left(p2wsh.redeemScript)
|
||||
}
|
||||
val sigComponent = WitnessTxSigComponentRaw(wtx, inputIndex, p2wshSPK, flags, amount)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent, signer, hashType)
|
||||
val p2pkhScriptSig = P2PKHScriptSignature(signature,pubKey)
|
||||
val scriptWit = redeemScript.left.flatMap {
|
||||
case p2pkh: P2PKHScriptPubKey =>
|
||||
if (p2pkh != P2PKHScriptPubKey(pubKey)) {
|
||||
Right(TxBuilderError.WrongPublicKey)
|
||||
} else Left(P2WSHWitnessV0(p2pkh, p2pkhScriptSig))
|
||||
case lock: LockTimeScriptPubKey =>
|
||||
lock.nestedScriptPubKey match {
|
||||
case p2pkh: P2PKHScriptPubKey =>
|
||||
if (p2pkh != P2PKHScriptPubKey(pubKey)) {
|
||||
Right(TxBuilderError.WrongPublicKey)
|
||||
} else {
|
||||
Left(P2WSHWitnessV0(lock,p2pkhScriptSig))
|
||||
}
|
||||
case _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: CLTVScriptPubKey | _: CSVScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
case _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
val signedWitness = scriptWit.left.map(wit => TransactionWitness(wtx.witness.witnesses.updated(inputIndex.toInt, wit)))
|
||||
val signedWTx = signedWitness.left.map(txWit => WitnessTransaction(wtx.version, wtx.inputs,
|
||||
wtx.outputs, wtx.lockTime, txWit))
|
||||
signedWTx.left.map(wtx => WitnessTxSigComponentRaw(wtx, inputIndex, p2wshSPK, flags, amount))
|
||||
case p2pkh: P2PKHScriptPubKey =>
|
||||
if (p2pkh != P2PKHScriptPubKey(pubKey)) {
|
||||
Right(TxBuilderError.WrongPublicKey)
|
||||
} else {
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx, inputIndex, p2pkh, flags)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent, signer, hashType)
|
||||
val p2pkhScriptSig = P2PKHScriptSignature(signature, pubKey)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput, p2pkhScriptSig, unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt, signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs, btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs, wtx.lockTime, wtx.witness)
|
||||
}
|
||||
Left(BaseTxSigComponent(signedTx, inputIndex, p2pkh, flags))
|
||||
}
|
||||
case lock : LockTimeScriptPubKey =>
|
||||
lock.nestedScriptPubKey match {
|
||||
case p2pkh: P2PKHScriptPubKey =>
|
||||
if (p2pkh != P2PKHScriptPubKey(pubKey)) {
|
||||
Right(TxBuilderError.WrongPublicKey)
|
||||
} else {
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx, inputIndex, lock, flags)
|
||||
val signature = TransactionSignatureCreator.createSig(sigComponent, signer, hashType)
|
||||
val p2pkhScriptSig = P2PKHScriptSignature(signature, pubKey)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput, p2pkhScriptSig, unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt, signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs, btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs, wtx.lockTime, wtx.witness)
|
||||
}
|
||||
Left(BaseTxSigComponent(signedTx, inputIndex, lock, flags))
|
||||
}
|
||||
case _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: CLTVScriptPubKey | _: CSVScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
case _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
signed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object P2PKHSigner extends P2PKHSigner
|
||||
|
||||
sealed abstract class MultiSigSigner extends BitcoinSigner {
|
||||
private val logger = BitcoinSLogger.logger
|
||||
|
||||
override def sign(signersWithPubKeys: Seq[Signer.Sign], output: TransactionOutput, unsignedTx: Transaction,
|
||||
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
|
||||
val spk = output.scriptPubKey
|
||||
val signers = signersWithPubKeys.map(_._1)
|
||||
val unsignedInput = unsignedTx.inputs(inputIndex.toInt)
|
||||
val flags = Policy.standardFlags
|
||||
val amount = output.value
|
||||
val signed: Either[TxSigComponent, TxBuilderError] = spk match {
|
||||
case p2wshSPK: P2WSHWitnessSPKV0 =>
|
||||
val wtx = unsignedTx match {
|
||||
case btx: BaseTransaction => WitnessTransaction(btx.version, btx.inputs,
|
||||
btx.outputs, btx.lockTime, EmptyWitness)
|
||||
case wtx: WitnessTransaction => wtx
|
||||
}
|
||||
val witness = wtx.witness.witnesses(inputIndex.toInt)
|
||||
val multiSigSPK: Either[(MultiSignatureScriptPubKey,ScriptPubKey), TxBuilderError] = witness match {
|
||||
case _: P2WPKHWitnessV0 => Right(TxBuilderError.WrongSigner)
|
||||
case EmptyScriptWitness => Right(TxBuilderError.NoWitness)
|
||||
case p2wsh: P2WSHWitnessV0 =>
|
||||
p2wsh.redeemScript match {
|
||||
case lock: LockTimeScriptPubKey =>
|
||||
lock.nestedScriptPubKey match {
|
||||
case m: MultiSignatureScriptPubKey => Left((m,lock))
|
||||
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: P2SHScriptPubKey | _: P2WPKHWitnessV0
|
||||
| _: P2WSHWitnessSPKV0 | _: WitnessCommitment | _: EscrowTimeoutScriptPubKey | _: CSVScriptPubKey
|
||||
| _: CLTVScriptPubKey | _: NonStandardScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | EmptyScriptPubKey =>
|
||||
Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
case m: MultiSignatureScriptPubKey => Left((m,m))
|
||||
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: P2SHScriptPubKey | _: P2WPKHWitnessV0
|
||||
| _: P2WSHWitnessSPKV0 | _: WitnessCommitment | _: EscrowTimeoutScriptPubKey
|
||||
| _: NonStandardScriptPubKey | _: P2WPKHWitnessSPKV0 | _: UnassignedWitnessScriptPubKey
|
||||
| EmptyScriptPubKey =>
|
||||
Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
}
|
||||
val requiredSigs = multiSigSPK.left.map(_._1.requiredSigs)
|
||||
val sigComponent = WitnessTxSigComponentRaw(wtx, inputIndex, p2wshSPK, flags, amount)
|
||||
val signatures = requiredSigs.left.map { r =>
|
||||
0.until(r).map(i => TransactionSignatureCreator.createSig(sigComponent, signers(i), hashType))
|
||||
}
|
||||
val multiSigScriptSig = signatures.left.map(s => MultiSignatureScriptSignature(s))
|
||||
val scriptWit = multiSigSPK.left.flatMap { case (_,redeem) =>
|
||||
multiSigScriptSig.left.map { scriptSig =>
|
||||
P2WSHWitnessV0(redeem, scriptSig)
|
||||
}
|
||||
}
|
||||
val signedWitness = scriptWit.left.map(wit => TransactionWitness(wtx.witness.witnesses.updated(inputIndex.toInt, wit)))
|
||||
val signedWTx: Either[WitnessTransaction,TxBuilderError] = signedWitness.left.map { txWit =>
|
||||
WitnessTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime, txWit)
|
||||
}
|
||||
signedWTx.left.map { wtx =>
|
||||
WitnessTxSigComponentRaw(wtx,inputIndex,p2wshSPK,flags,amount)
|
||||
}
|
||||
case multiSigSPK: MultiSignatureScriptPubKey =>
|
||||
val requiredSigs = multiSigSPK.requiredSigs
|
||||
if (signers.size < requiredSigs) {
|
||||
Right(TxBuilderError.WrongSigner)
|
||||
} else {
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx, inputIndex, multiSigSPK, flags)
|
||||
val signatures = 0.until(requiredSigs).map(i => TransactionSignatureCreator.createSig(sigComponent, signers(i), hashType))
|
||||
val multiSigScriptSig = MultiSignatureScriptSignature(signatures)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput, multiSigScriptSig, unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt, signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs, btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs, wtx.lockTime, wtx.witness)
|
||||
}
|
||||
Left(BaseTxSigComponent(signedTx, inputIndex, multiSigSPK, Policy.standardFlags))
|
||||
}
|
||||
case lock : LockTimeScriptPubKey =>
|
||||
val nested = lock.nestedScriptPubKey
|
||||
val multiSigSPK = nested match {
|
||||
case m: MultiSignatureScriptPubKey => Left(m)
|
||||
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: CLTVScriptPubKey | _: CSVScriptPubKey
|
||||
| _: UnassignedWitnessScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment
|
||||
| _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
val requiredSigs = multiSigSPK.left.map(_.requiredSigs)
|
||||
val sigComponent = BaseTxSigComponent(unsignedTx, inputIndex, lock, flags)
|
||||
val signatures = requiredSigs.left.flatMap { n =>
|
||||
if (signers.size < n) {
|
||||
Right(TxBuilderError.WrongSigner)
|
||||
} else {
|
||||
val sigs = 0.until(n).map { i =>
|
||||
TransactionSignatureCreator.createSig(sigComponent, signers(i), hashType)
|
||||
}
|
||||
Left(sigs)
|
||||
}
|
||||
}
|
||||
val tx = signatures.left.map { sigs =>
|
||||
val multiSigScriptSig = MultiSignatureScriptSignature(sigs)
|
||||
val signedInput = TransactionInput(unsignedInput.previousOutput, multiSigScriptSig, unsignedInput.sequence)
|
||||
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt, signedInput)
|
||||
val signedTx = unsignedTx match {
|
||||
case btx: BaseTransaction => BaseTransaction(btx.version, signedInputs,
|
||||
btx.outputs, btx.lockTime)
|
||||
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, signedInputs,
|
||||
wtx.outputs, wtx.lockTime, wtx.witness)
|
||||
}
|
||||
signedTx
|
||||
}
|
||||
|
||||
tx.left.flatMap { t =>
|
||||
multiSigSPK.left.map { m =>
|
||||
BaseTxSigComponent(t, inputIndex, m, flags)
|
||||
}
|
||||
}
|
||||
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WPKHWitnessSPKV0 | _: NonStandardScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey =>
|
||||
Right(TxBuilderError.WrongSigner)
|
||||
}
|
||||
signed
|
||||
}
|
||||
}
|
||||
|
||||
object MultiSigSigner extends MultiSigSigner
|
||||
|
||||
sealed abstract class P2WPKHSigner extends BitcoinSigner {
|
||||
override def sign(signers: Seq[Signer.Sign], output: TransactionOutput, unsignedTx: Transaction,
|
||||
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = unsignedTx match {
|
||||
case wtx: WitnessTransaction =>
|
||||
if (signers.size != 1) {
|
||||
Right(TxBuilderError.TooManySigners)
|
||||
} else if (signers.head._2.isEmpty) {
|
||||
Right(TxBuilderError.MissingPublicKey)
|
||||
} else {
|
||||
val signer = signers.head._1
|
||||
val pubKey = signers.head._2.get
|
||||
val unsignedScriptWit = P2WPKHWitnessV0(pubKey)
|
||||
val unsignedTxWitness = TransactionWitness(wtx.witness.witnesses.updated(inputIndex.toInt, unsignedScriptWit))
|
||||
val unsignedWtx = WitnessTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime, unsignedTxWitness)
|
||||
val witSPK = output.scriptPubKey match {
|
||||
case p2wpkh: P2WPKHWitnessSPKV0 =>
|
||||
if (p2wpkh != P2WPKHWitnessSPKV0(pubKey)) {
|
||||
Right(TxBuilderError.WrongPublicKey)
|
||||
} else Left(p2wpkh)
|
||||
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
|
||||
| _: P2WSHWitnessSPKV0 | _: NonStandardScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey
|
||||
| _: WitnessCommitment | EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey
|
||||
| _: EscrowTimeoutScriptPubKey =>
|
||||
Right(TxBuilderError.NonWitnessSPK)
|
||||
}
|
||||
val result = witSPK.left.map { w =>
|
||||
val wtxComp = WitnessTxSigComponentRaw(unsignedWtx, inputIndex, w, Policy.standardFlags, output.value)
|
||||
val signature = TransactionSignatureCreator.createSig(wtxComp,signer,hashType)
|
||||
val scriptWitness = P2WPKHWitnessV0(pubKey,signature)
|
||||
val signedTxWitness = TransactionWitness(unsignedWtx.witness.witnesses.updated(inputIndex.toInt,scriptWitness))
|
||||
val signedTx = WitnessTransaction(unsignedWtx.version, unsignedWtx.inputs, unsignedWtx.outputs,
|
||||
unsignedWtx.lockTime, signedTxWitness)
|
||||
WitnessTxSigComponentRaw(signedTx,inputIndex,w,Policy.standardFlags,output.value)
|
||||
}
|
||||
result
|
||||
}
|
||||
case btx: BaseTransaction =>
|
||||
val wtx = WitnessTransaction(btx.version, btx.inputs, btx.outputs, btx.lockTime, EmptyWitness)
|
||||
sign(signers,output,wtx,inputIndex,hashType)
|
||||
}
|
||||
}
|
||||
|
||||
object P2WPKHSigner extends P2WPKHSigner
|
@ -22,14 +22,6 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
|
||||
result == ScriptOk
|
||||
}
|
||||
|
||||
property("generate a valid signature for a p2pkh transaction") =
|
||||
Prop.forAll(TransactionGenerators.signedP2PKHTransaction) {
|
||||
case (txSignatureComponent: TxSigComponent, _) =>
|
||||
//run it through the interpreter
|
||||
val program = ScriptProgram(txSignatureComponent)
|
||||
val result = ScriptInterpreter.run(program)
|
||||
result == ScriptOk
|
||||
}
|
||||
property("generate valid signatures for a multisignature transaction") =
|
||||
Prop.forAllNoShrink(TransactionGenerators.signedMultiSigTransaction) {
|
||||
case (txSignatureComponent: TxSigComponent, _) =>
|
||||
|
@ -3,7 +3,7 @@ package org.bitcoins.core.protocol.blockchain
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.number.Int64
|
||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
|
||||
import org.bitcoins.core.protocol.transaction.{TransactionConstants, TransactionInput, TransactionOutput}
|
||||
import org.bitcoins.core.protocol.transaction.{CoinbaseInput, TransactionConstants, TransactionOutput}
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
@ -17,7 +17,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers {
|
||||
val genesisTransaction = genesisBlock.transactions.head
|
||||
|
||||
val expectedGenesisScriptSig = ScriptSignature("4D04FFFF001D0104455468652054696D65732030332F4A616E2F32303039204368616E63656C6C6F72206F6E206272696E6B206F66207365636F6E64206261696C6F757420666F722062616E6B73".toLowerCase())
|
||||
val expectedGenesisInput = TransactionInput(expectedGenesisScriptSig)
|
||||
val expectedGenesisInput = CoinbaseInput(expectedGenesisScriptSig)
|
||||
val expectedGenesisScriptPubKey = ScriptPubKey("434104678AFDB0FE5548271967F1A67130B7105CD6A828E03909A67962E0EA1F61DEB649F6BC3F4CEF38C4F35504E51EC112DE5C384DF7BA0B8D578A4C702B6BF11D5FAC".toLowerCase)
|
||||
val expectedGenesisOutput = TransactionOutput(Satoshis(Int64(5000000000L)),expectedGenesisScriptPubKey)
|
||||
"ChainParams" must "generate correct block hex for genesis block" in {
|
||||
|
@ -1,16 +1,19 @@
|
||||
package org.bitcoins.core.protocol.transaction
|
||||
|
||||
import org.bitcoins.core.gen.TransactionGenerators
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.scalacheck.{Prop, Properties}
|
||||
|
||||
/**
|
||||
* Created by chris on 6/24/16.
|
||||
*/
|
||||
class TransactionInputSpec extends Properties("TranactionInputSpec") {
|
||||
|
||||
property("Serialization symmetry") =
|
||||
private val logger = BitcoinSLogger.logger
|
||||
property("Serialization symmetry") = {
|
||||
Prop.forAllNoShrink(TransactionGenerators.input) { input =>
|
||||
val result = TransactionInput(input.hex) == input
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.bitcoins.core.protocol.transaction
|
||||
|
||||
import org.bitcoins.core.protocol.script.EmptyScriptSignature
|
||||
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, P2PKHScriptSignature, P2PKScriptSignature}
|
||||
import org.bitcoins.core.util.TestUtil
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
@ -37,5 +37,11 @@ class TransactionInputTest extends FlatSpec with MustMatchers {
|
||||
(TransactionInput(input.hex) == input) must be (true)
|
||||
}
|
||||
|
||||
it must "serialize and deserialize a coinbase input" in {
|
||||
val c= CoinbaseInput(P2PKScriptSignature("4847304502210092d4e6183970b5e082d87563afbcfb3e1f38e801d89f036fd2935c394d6cc364022032b2a419e19f00b6f32f88c4427cf5e2a97f298b7d4e45efb5f723d84257ca03"))
|
||||
TransactionInput(c.previousOutput, c.scriptSignature, c.sequence) must be (c)
|
||||
c.hex must be (TransactionInput(c.previousOutput,c.scriptSignature,c.sequence).hex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -173,9 +173,7 @@ class TransactionTest extends FlatSpec with MustMatchers {
|
||||
//use this to represent a single test case from script_valid.json
|
||||
/* val lines =
|
||||
"""
|
||||
|[ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x02 0x0001", 2000]],
|
||||
| "01000000010001000000000000000000000000000000000000000000000000000000000000000000000151ffffffff010000000000000000015100000000", "P2SH,WITNESS"]
|
||||
|]
|
||||
|[[[["0000000000000000000000000000000000000000000000000000000000000000",-1,"1"]], "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "P2SH"]]
|
||||
""".stripMargin*/
|
||||
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
|
||||
val json = lines.parseJson
|
||||
|
@ -33,7 +33,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as invalid if the stack top is negative" in {
|
||||
val stack = Seq(ScriptNumber(-1))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version,Seq(txInputAdjustedSequenceNumber), emptyTx.outputs, emptyTx.lockTime)
|
||||
val adjustedLockTimeTx = BaseTransaction(txAdjustedSequenceNumber.version,txAdjustedSequenceNumber.inputs,txAdjustedSequenceNumber.outputs,UInt32.zero)
|
||||
@ -47,7 +48,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as invalid if the locktime on the tx is < 500000000 && stack top is >= 500000000" in {
|
||||
val stack = Seq(ScriptNumber(500000000))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version,Seq(txInputAdjustedSequenceNumber), emptyTx.outputs, emptyTx.lockTime)
|
||||
val adjustedLockTimeTx = BaseTransaction(txAdjustedSequenceNumber.version,txAdjustedSequenceNumber.inputs,txAdjustedSequenceNumber.outputs,UInt32.zero)
|
||||
@ -61,7 +63,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as invalid if the locktime on the tx is >= 500000000 && stack top is < 500000000" in {
|
||||
val stack = Seq(ScriptNumber(499999999))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version,Seq(txInputAdjustedSequenceNumber), emptyTx.outputs, emptyTx.lockTime)
|
||||
val adjustedLockTimeTx = BaseTransaction(txAdjustedSequenceNumber.version,txAdjustedSequenceNumber.inputs,txAdjustedSequenceNumber.outputs,UInt32.zero)
|
||||
@ -76,7 +79,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as invalid if the stack top item is greater than the tx locktime" in {
|
||||
val stack = Seq(ScriptNumber(499999999))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version,Seq(txInputAdjustedSequenceNumber), emptyTx.outputs, emptyTx.lockTime)
|
||||
val adjustedLockTimeTx = BaseTransaction(txAdjustedSequenceNumber.version,txAdjustedSequenceNumber.inputs,txAdjustedSequenceNumber.outputs,UInt32.zero)
|
||||
@ -94,7 +98,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as valid if the locktime on the tx is < 500000000 && stack top is < 500000000" in {
|
||||
val stack = Seq(ScriptNumber(0))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version,Seq(txInputAdjustedSequenceNumber),
|
||||
emptyTx.outputs, emptyTx.lockTime)
|
||||
@ -113,7 +118,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers {
|
||||
it must "mark the transaction as valid if the locktime on the tx is >= 500000000 && stack top is >= 500000000" in {
|
||||
val stack = Seq(ScriptNumber(500000000))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
|
||||
val oldInput = TestUtil.transaction.inputs(0)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(oldInput.previousOutput, oldInput.scriptSignature, UInt32.zero)
|
||||
val emptyTx = EmptyTransaction
|
||||
val txAdjustedSequenceNumber = BaseTransaction(emptyTx.version, Seq(txInputAdjustedSequenceNumber),
|
||||
emptyTx.outputs, emptyTx.lockTime)
|
||||
|
Loading…
Reference in New Issue
Block a user