mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 06:57:51 +01:00
Merge pull request #8 from TomMcCabe/segwit
[Minor] Revise/improve doc notes to reduce unnecessary white spaces and add references
This commit is contained in:
commit
8183d3764c
29 changed files with 490 additions and 1463 deletions
|
@ -23,52 +23,32 @@ import org.bitcoins.core.util.BitcoinSUtil
|
|||
*/
|
||||
sealed trait ChainParams {
|
||||
|
||||
/**
|
||||
* Return the BIP70 network string (main, test or regtest)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** Return the BIP70 network string ([[MainNetChainParams]], [[TestNetChainParams]] or [[RegTestNetChainParams]].) */
|
||||
def networkId : String
|
||||
|
||||
/**
|
||||
* The genesis block in the blockchain
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** The Genesis [[Block]] in the blockchain. */
|
||||
def genesisBlock : Block
|
||||
|
||||
/**
|
||||
* Filter transactions that do not match well-defined patterns
|
||||
* inside of Policy
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** Filter transactions that do not match well-defined patterns
|
||||
* inside of [[org.bitcoins.core.policy.Policy]]. */
|
||||
def requireStandardTransaction : Boolean
|
||||
|
||||
/**
|
||||
* Takes in a Base58Type and returns its base58 prefix
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
/** Takes in a [[Base58Type]] and returns its base58 prefix. */
|
||||
def base58Prefix(base58 : Base58Type) : Seq[Byte] = base58Prefixes(base58)
|
||||
|
||||
/**
|
||||
* The mapping from a Base58Type to a String
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** The mapping from a [[Base58Type]]to a String.
|
||||
* Base58 prefixes for various keys/hashes on the network.
|
||||
* See: [[https://en.bitcoin.it/wiki/List_of_address_prefixes]]. */
|
||||
def base58Prefixes : Map[Base58Type,Seq[Byte]]
|
||||
|
||||
/**
|
||||
* Creates the genesis block for this blockchain
|
||||
* Mimics this function in bitcoin core
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L51
|
||||
/** Creates the Genesis [[Block]] for this blockchain.
|
||||
* Mimics this function in bitcoin core:
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L51]]
|
||||
* @param time the time when the miner started hashing the block header
|
||||
* @param nonce the nonce to mine the block
|
||||
* @param nBits An encoded version of the target threshold this block’s header hash must be less than or equal to.
|
||||
* @param version the block version
|
||||
* @param amount the block reward for the gensis block (50 BTC in Bitcoin)
|
||||
* @param amount the block reward for the genesis block (50 BTC in Bitcoin)
|
||||
* @return the newly minted genesis block
|
||||
*/
|
||||
def createGenesisBlock(time : UInt32, nonce : UInt32, nBits : UInt32, version : UInt32, amount : CurrencyUnit) : Block = {
|
||||
|
@ -90,7 +70,6 @@ sealed trait ChainParams {
|
|||
*/
|
||||
def createGenesisBlock(timestamp : String, scriptPubKey : ScriptPubKey, time : UInt32, nonce : UInt32, nBits : UInt32,
|
||||
version : UInt32, amount : CurrencyUnit) : Block = {
|
||||
|
||||
val timestampHex = timestamp.toCharArray.map(_.toByte)
|
||||
//see https://bitcoin.stackexchange.com/questions/13122/scriptsig-coinbase-structure-of-the-genesis-block
|
||||
//for a full breakdown of the genesis block & its script signature
|
||||
|
@ -107,23 +86,15 @@ sealed trait ChainParams {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the main network parameters
|
||||
*/
|
||||
/** The Main Network parameters. */
|
||||
object MainNetChainParams extends ChainParams {
|
||||
|
||||
override def networkId = "main"
|
||||
|
||||
override def genesisBlock = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
override def genesisBlock : Block = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
|
||||
override def requireStandardTransaction = true
|
||||
override def requireStandardTransaction : Boolean = true
|
||||
|
||||
/**
|
||||
* Base58 prefixes for various keys/hashes on the network
|
||||
* See: https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
* @return
|
||||
*/
|
||||
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
|
||||
PubKeyAddress -> BitcoinSUtil.decodeHex("00"),
|
||||
ScriptAddress -> BitcoinSUtil.decodeHex("05"),
|
||||
|
@ -138,15 +109,10 @@ object TestNetChainParams extends ChainParams {
|
|||
|
||||
override def networkId = "test"
|
||||
|
||||
override def genesisBlock = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
|
||||
override def requireStandardTransaction = true
|
||||
override def requireStandardTransaction : Boolean = true
|
||||
|
||||
/**
|
||||
* Base58 prefixes for various keys/hashes on the network
|
||||
* See: https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
* @return
|
||||
*/
|
||||
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
|
||||
PubKeyAddress -> BitcoinSUtil.decodeHex("6f"),
|
||||
ScriptAddress -> BitcoinSUtil.decodeHex("c4"),
|
||||
|
@ -160,14 +126,8 @@ object TestNetChainParams extends ChainParams {
|
|||
|
||||
object RegTestNetChainParams extends ChainParams {
|
||||
override def networkId = "regtest"
|
||||
override def genesisBlock = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
override def requireStandardTransaction = TestNetChainParams.requireStandardTransaction
|
||||
|
||||
/**
|
||||
* Base58 prefixes for various keys/hashes on the network
|
||||
* See: https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
* @return
|
||||
*/
|
||||
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), UInt32.one, Satoshis(Int64(5000000000L)))
|
||||
override def requireStandardTransaction : Boolean = TestNetChainParams.requireStandardTransaction
|
||||
override def base58Prefixes : Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes
|
||||
}
|
||||
|
||||
|
|
|
@ -569,7 +569,7 @@ object WitnessScriptPubKeyV0 extends ScriptFactory[WitnessScriptPubKeyV0] {
|
|||
/** Mimics the function to determine if a [[ScriptPubKey]] contains a witness
|
||||
* A witness program is any valid [[ScriptPubKey]] that consists of a 1 byte push op and then a data push
|
||||
* between 2 and 40 bytes
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpL215-L229]]
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpp#L215-L229]]
|
||||
* Returns None if it is not a witness program, else returns the script and script version
|
||||
* */
|
||||
def isWitnessScriptPubKeyV0(asm: Seq[ScriptToken]): Boolean = {
|
||||
|
|
|
@ -19,20 +19,11 @@ import org.slf4j.LoggerFactory
|
|||
*/
|
||||
trait ScriptOperationFactory[T <: ScriptOperation] extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* All of the script operations for a particular T
|
||||
*
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** All of the [[ScriptOperation]]s for a particular T. */
|
||||
def operations : Seq[T]
|
||||
|
||||
/**
|
||||
* Finds a script operation from a given string
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
* Finds a [[ScriptOperation]] from a given string */
|
||||
def fromString(str : String) : Option[T] = {
|
||||
val result : Option[T] = operations.find(_.toString == str)
|
||||
if (result.isEmpty) {
|
||||
|
@ -42,30 +33,18 @@ trait ScriptOperationFactory[T <: ScriptOperation] extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds a script operation from its hexadecimal representation
|
||||
*
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
* Finds a [[ScriptOperation]] from its hexadecimal representation. */
|
||||
def fromHex(hex : String) : Option[T] = operations.find(_.hex == hex.toLowerCase)
|
||||
|
||||
/**
|
||||
* Removes the 'OP_' prefix from a given operation.
|
||||
* Example: OP_EQUALVERIFY would be transformed into EQUALVERIFY
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
private def removeOP_Prefix(str : String) : String = {
|
||||
str.replace("OP_","")
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a script operation from a given byte
|
||||
*
|
||||
* @param byte
|
||||
* @return
|
||||
*/
|
||||
/** Finds a [[ScriptOperation]] from a given [[Byte]]. */
|
||||
def fromByte(byte : Byte) : Option[T] = {
|
||||
val hex = BitcoinSUtil.encodeHex(byte)
|
||||
fromHex(hex)
|
||||
|
|
|
@ -193,10 +193,7 @@ object ScriptProgram {
|
|||
require(updatedScript.script == scriptTokens)
|
||||
updatedScript
|
||||
}
|
||||
/**
|
||||
* Updates the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index.
|
||||
* @return
|
||||
*/
|
||||
/** Updates the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index. */
|
||||
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
|
||||
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
|
||||
|
@ -204,10 +201,7 @@ object ScriptProgram {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the [[ScriptToken]]s in either the stack or script and the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index
|
||||
* @return
|
||||
*/
|
||||
/** Updates the [[ScriptToken]]s in either the stack or script and the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index */
|
||||
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
|
||||
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
|
||||
val updatedIndicator = ScriptProgram(oldProgram, tokens, indicator)
|
||||
|
@ -326,29 +320,19 @@ object ScriptProgram {
|
|||
PreExecutionScriptProgramImpl(t,Nil,script.toList,script.toList,Nil,t.flags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a [[ScriptProgram]] that is a [[ExecutionInProgressScriptProgram]] and changes it to an [[ExecutedScriptProgram]].
|
||||
* @param executionInProgressScriptProgram
|
||||
* @return
|
||||
*/
|
||||
/** Changes a [[ScriptProgram]] that is a [[ExecutionInProgressScriptProgram]] and changes it to an [[ExecutedScriptProgram]].*/
|
||||
def toExecutedProgram(executionInProgressScriptProgram: ExecutionInProgressScriptProgram) : ExecutedScriptProgram = {
|
||||
ExecutedScriptProgramImpl(executionInProgressScriptProgram.txSignatureComponent, executionInProgressScriptProgram.stack,
|
||||
executionInProgressScriptProgram.script,executionInProgressScriptProgram.originalScript,executionInProgressScriptProgram.altStack,
|
||||
executionInProgressScriptProgram.flags,None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]].
|
||||
* @param preExecutionScriptProgram
|
||||
* @return
|
||||
*/
|
||||
/** Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]].*/
|
||||
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram) : ExecutionInProgressScriptProgram = {
|
||||
toExecutionInProgress(preExecutionScriptProgram,None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]] given the stack state.
|
||||
*/
|
||||
/** Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]] given the stack state. */
|
||||
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[Seq[ScriptToken]]) : ExecutionInProgressScriptProgram = {
|
||||
stack match {
|
||||
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,stackTokens.toList,preExecutionScriptProgram.script,
|
||||
|
|
|
@ -14,150 +14,84 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
||||
|
||||
|
||||
/**
|
||||
* a is added to b
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** a is added to b. */
|
||||
def opAdd(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ADD, "Script top must be OP_ADD")
|
||||
require(program.script.headOption.contains(OP_ADD), "Script top must be OP_ADD")
|
||||
performBinaryArithmeticOperation(program, (x,y) => x + y)
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the stack top by 1
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Increments the stack top by 1. */
|
||||
def op1Add(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_1ADD, "Script top must be OP_1ADD")
|
||||
require(program.script.headOption.contains(OP_1ADD), "Script top must be OP_1ADD")
|
||||
performUnaryArithmeticOperation(program, x => x + ScriptNumber.one)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the stack top by 1
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Decrements the stack top by 1. */
|
||||
def op1Sub(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_1SUB, "Script top must be OP_1SUB")
|
||||
require(program.script.headOption.contains(OP_1SUB), "Script top must be OP_1SUB")
|
||||
performUnaryArithmeticOperation(program, x => x - ScriptNumber.one )
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* b is subtracted from a.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** b is subtracted from a. */
|
||||
def opSub(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SUB, "Script top must be OP_SUB")
|
||||
require(program.script.headOption.contains(OP_SUB), "Script top must be OP_SUB")
|
||||
performBinaryArithmeticOperation(program, (x,y) => y - x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the absolute value of the stack top
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Takes the absolute value of the stack top. */
|
||||
def opAbs(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ABS, "Script top must be OP_ABS")
|
||||
require(program.script.headOption.contains(OP_ABS), "Script top must be OP_ABS")
|
||||
performUnaryArithmeticOperation(program, x => x match {
|
||||
case ScriptNumber.zero => ScriptNumber.zero
|
||||
case _ : ScriptNumber => ScriptNumber(x.underlying.abs)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the stack top
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Negates the stack top. */
|
||||
def opNegate(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NEGATE, "Script top must be OP_NEGATE")
|
||||
require(program.script.headOption.contains(OP_NEGATE), "Script top must be OP_NEGATE")
|
||||
performUnaryArithmeticOperation(program, x => x -)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** If the input is 0 or 1, it is flipped. Otherwise the output will be 0. */
|
||||
def opNot(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NOT, "Script top must be OP_NOT")
|
||||
|
||||
require(program.script.headOption.contains(OP_NOT), "Script top must be OP_NOT")
|
||||
performUnaryArithmeticOperation(program, x => if (program.stackTopIsFalse) OP_TRUE else OP_FALSE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if the input is 0. 1 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 0 if the input is 0. 1 otherwise. */
|
||||
def op0NotEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_0NOTEQUAL, "Script top must be OP_0NOTEQUAL")
|
||||
require(program.script.headOption.contains(OP_0NOTEQUAL), "Script top must be OP_0NOTEQUAL")
|
||||
performUnaryArithmeticOperation(program, x => if(x.underlying == 0) OP_FALSE else OP_TRUE)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If both a and b are not 0, the output is 1. Otherwise 0.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** If both a and b are not 0, the output is 1. Otherwise 0. */
|
||||
def opBoolAnd(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_BOOLAND, "Script top must be OP_BOOLAND")
|
||||
require(program.script.headOption.contains(OP_BOOLAND), "Script top must be OP_BOOLAND")
|
||||
performBinaryBooleanOperation(program,(x,y) => {
|
||||
val xIsFalse = (x == ScriptNumber.zero || x == OP_0)
|
||||
val yIsFalse = (y == ScriptNumber.zero || y == OP_0)
|
||||
if (xIsFalse || yIsFalse) false else true
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a or b is not 0, the output is 1. Otherwise 0.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** If a or b is not 0, the output is 1. Otherwise 0. */
|
||||
def opBoolOr(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_BOOLOR, "Script top must be OP_BOOLOR")
|
||||
require(program.script.headOption.contains(OP_BOOLOR), "Script top must be OP_BOOLOR")
|
||||
performBinaryBooleanOperation(program, (x,y) => {
|
||||
if (x == y && (x == ScriptNumber.zero || x == OP_0)) false else true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the numbers are equal, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if the numbers are equal, 0 otherwise. */
|
||||
def opNumEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NUMEQUAL, "Script top must be OP_NUMEQUAL")
|
||||
require(program.script.headOption.contains(OP_NUMEQUAL), "Script top must be OP_NUMEQUAL")
|
||||
performBinaryBooleanOperation(program,(x,y) => x.numEqual(y))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Same as [[OP_NUMEQUAL]], but runs [[OP_VERIFY]] afterward. */
|
||||
def opNumEqualVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NUMEQUALVERIFY,
|
||||
require(program.script.headOption.contains(OP_NUMEQUALVERIFY),
|
||||
"Script top must be OP_NUMEQUALVERIFY")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
|
||||
|
@ -176,100 +110,58 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns 1 if the numbers are not equal, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if the numbers are not equal, 0 otherwise. */
|
||||
def opNumNotEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NUMNOTEQUAL,
|
||||
require(program.script.headOption.contains(OP_NUMNOTEQUAL),
|
||||
"Script top must be OP_NUMNOTEQUAL")
|
||||
performBinaryBooleanOperation(program, (x,y) => {
|
||||
x.underlying != y.underlying
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns 1 if a is less than b, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if a is less than b, 0 otherwise. */
|
||||
def opLessThan(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_LESSTHAN,
|
||||
require(program.script.headOption.contains(OP_LESSTHAN),
|
||||
"Script top must be OP_LESSTHAN")
|
||||
performBinaryBooleanOperation(program, (x,y) => y < x)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns 1 if a is greater than b, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if a is greater than b, 0 otherwise. */
|
||||
def opGreaterThan(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_GREATERTHAN,
|
||||
require(program.script.headOption.contains(OP_GREATERTHAN),
|
||||
"Script top must be OP_GREATERTHAN")
|
||||
performBinaryBooleanOperation(program, (x,y) => y > x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is less than or equal to b, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if a is less than or equal to b, 0 otherwise. */
|
||||
def opLessThanOrEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_LESSTHANOREQUAL,
|
||||
require(program.script.headOption.contains(OP_LESSTHANOREQUAL),
|
||||
"Script top must be OP_LESSTHANOREQUAL")
|
||||
performBinaryBooleanOperation(program, (x,y) => y <= x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is greater than or equal to b, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if a is greater than or equal to b, 0 otherwise. */
|
||||
def opGreaterThanOrEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_GREATERTHANOREQUAL,
|
||||
require(program.script.headOption.contains(OP_GREATERTHANOREQUAL),
|
||||
"Script top must be OP_GREATERTHANOREQUAL")
|
||||
performBinaryBooleanOperation(program, (x,y) => y >= x)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the smaller of a and b.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns the smaller of a and b. */
|
||||
def opMin(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_MIN,
|
||||
require(program.script.headOption.contains(OP_MIN),
|
||||
"Script top must be OP_MIN")
|
||||
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
performComparisonOnTwoStackTopItems(program, (x : ScriptNumber, y : ScriptNumber) => if (x < y) x else y)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the larger of a and b.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns the larger of a and b. */
|
||||
def opMax(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_MAX,
|
||||
require(program.script.headOption.contains(OP_MAX),
|
||||
"Script top must be OP_MAX")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
|
@ -279,15 +171,9 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. */
|
||||
def opWithin(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_WITHIN,
|
||||
require(program.script.headOption.contains(OP_WITHIN),
|
||||
"Script top must be OP_WITHIN")
|
||||
if (program.stack.size < 3) {
|
||||
logger.error("OP_WITHIN requires at least 3 elements on the stack")
|
||||
|
@ -296,10 +182,8 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val c = ScriptNumber(program.stack.head.bytes)
|
||||
val b = ScriptNumber(program.stack.tail.head.bytes)
|
||||
val a = ScriptNumber(program.stack.tail.tail.head.bytes)
|
||||
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) && (!BitcoinScriptUtil.isShortestEncoding(c) ||
|
||||
!BitcoinScriptUtil.isShortestEncoding(b) || !BitcoinScriptUtil.isShortestEncoding(a))) {
|
||||
|
||||
logger.error("The constant you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
} else if (isLargerThan4Bytes(c) || isLargerThan4Bytes(b) || isLargerThan4Bytes(a)) {
|
||||
|
@ -313,27 +197,15 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
|
||||
ScriptProgram(program, newStackTop :: program.stack.tail.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function checks if a number is <= 4 bytes in size
|
||||
/** This function checks if a number is <= 4 bytes in size
|
||||
* We cannot perform arithmetic operations on bitcoin numbers that are larger than 4 bytes.
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L214-L239
|
||||
*
|
||||
* @param scriptNumber the script number to be checked
|
||||
* @return false if the number is larger than 4 bytes
|
||||
*/
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L214-L239. */
|
||||
private def isLargerThan4Bytes(scriptNumber : ScriptNumber) : Boolean = scriptNumber.bytes.size > 4
|
||||
|
||||
|
||||
/**
|
||||
* Performs the given arithmetic operation on the stack head
|
||||
*
|
||||
/** Performs the given arithmetic operation on the stack head
|
||||
* @param program the program whose stack top is used as an argument for the arithmetic operation
|
||||
* @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1
|
||||
* @return the program with the result from performing the arithmetic operation pushed onto the top of the stack
|
||||
|
@ -374,9 +246,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the given arithmetic operation on the top two stack items
|
||||
*
|
||||
/** Performs the given arithmetic operation on the top two stack items
|
||||
* @param program the program whose stack top is used as an argument for the arithmetic operation
|
||||
* @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1
|
||||
* @return the program with the result from performing the arithmetic operation pushed onto the top of the stack
|
||||
|
@ -425,9 +295,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two script numbers with the given boolean operation
|
||||
*
|
||||
/** Compares two script numbers with the given boolean operation
|
||||
* @param program the program whose two top stack elements are used for the comparison
|
||||
* @param op the operation which compares the two script numbers
|
||||
* @return the program with either OP_FALSE or OP_TRUE on the stack top
|
||||
|
@ -437,7 +305,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
logger.error("We need two stack elements for a binary boolean operation")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
|
||||
val (x,y) = parseTopTwoStackElementsAsScriptNumbers(program)
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) &&
|
||||
(!BitcoinScriptUtil.isShortestEncoding(x) || !BitcoinScriptUtil.isShortestEncoding(y))) {
|
||||
|
@ -455,11 +322,8 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes the top two stack items, parses them to numbers then executes the op function on them and places the result
|
||||
/** Takes the top two stack items, parses them to numbers then executes the op function on them and places the result
|
||||
* onto the stack top
|
||||
*
|
||||
* @param program the script program whose two top stack items are used as arguments for op
|
||||
* @param op the operation that needs to be executed on the two stack top items
|
||||
* @return the program with the result of op pushed onto the top of the stack
|
||||
|
@ -469,10 +333,8 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
performBinaryArithmeticOperation(program,op)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes the top two stack elements and parses them as script numbers
|
||||
*
|
||||
* @param program the program whose top two stack elements are being parsed as script numbers
|
||||
* @return the tuple with the first element being the first stack element, the second element in the tuple being the second stack element
|
||||
*/
|
||||
|
@ -491,7 +353,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val interpretedNumberX = ScriptNumber(x.hex)
|
||||
val interpretedNumberY = ScriptNumber(y.hex)
|
||||
(interpretedNumberX,interpretedNumberY)
|
||||
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
|
|
|
@ -3,201 +3,143 @@ package org.bitcoins.core.script.arithmetic
|
|||
import org.bitcoins.core.script.ScriptOperationFactory
|
||||
import org.bitcoins.core.script.constant.ScriptOperation
|
||||
|
||||
/**
|
||||
* Created by chris on 1/6/16.
|
||||
*/
|
||||
/** Created by chris on 1/6/16. */
|
||||
sealed trait ArithmeticOperation extends ScriptOperation
|
||||
|
||||
|
||||
/**
|
||||
* 1 is added to the input.
|
||||
*/
|
||||
/** 1 is added to the input. */
|
||||
case object OP_1ADD extends ArithmeticOperation {
|
||||
override def opCode = 139
|
||||
}
|
||||
|
||||
/**
|
||||
* 1 is subtracted from the input.
|
||||
*/
|
||||
/** 1 is subtracted from the input. */
|
||||
case object OP_1SUB extends ArithmeticOperation {
|
||||
override def opCode = 140
|
||||
}
|
||||
|
||||
/**
|
||||
* The sign of the input is flipped.
|
||||
*/
|
||||
/** The sign of the input is flipped. */
|
||||
case object OP_NEGATE extends ArithmeticOperation {
|
||||
override def opCode = 143
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is made positive.
|
||||
*/
|
||||
/** The input is made positive. */
|
||||
case object OP_ABS extends ArithmeticOperation {
|
||||
override def opCode = 144
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
||||
*/
|
||||
/** If the input is 0 or 1, it is flipped. Otherwise the output will be 0. */
|
||||
|
||||
case object OP_NOT extends ArithmeticOperation {
|
||||
override def opCode = 145
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if the input is 0. 1 otherwise.
|
||||
*/
|
||||
/** Returns 0 if the input is 0. 1 otherwise. */
|
||||
case object OP_0NOTEQUAL extends ArithmeticOperation {
|
||||
override def opCode = 146
|
||||
}
|
||||
|
||||
/**
|
||||
* a is added to b.
|
||||
*/
|
||||
/** a is added to b. */
|
||||
case object OP_ADD extends ArithmeticOperation {
|
||||
override def opCode = 147
|
||||
}
|
||||
|
||||
/**
|
||||
* b is subtracted from a.
|
||||
*/
|
||||
/** b is subtracted from a. */
|
||||
case object OP_SUB extends ArithmeticOperation {
|
||||
override def opCode = 148
|
||||
}
|
||||
|
||||
/**
|
||||
* If both a and b are not 0, the output is 1. Otherwise 0.
|
||||
*/
|
||||
/** If both a and b are not 0, the output is 1. Otherwise 0. */
|
||||
case object OP_BOOLAND extends ArithmeticOperation {
|
||||
override def opCode = 154
|
||||
}
|
||||
|
||||
/**
|
||||
* If a or b is not 0, the output is 1. Otherwise 0.
|
||||
*/
|
||||
/** If a or b is not 0, the output is 1. Otherwise 0. */
|
||||
case object OP_BOOLOR extends ArithmeticOperation {
|
||||
override def opCode = 155
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the numbers are equal, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if the numbers are equal, 0 otherwise. */
|
||||
case object OP_NUMEQUAL extends ArithmeticOperation {
|
||||
override def opCode = 156
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
||||
*/
|
||||
/** Same as OP_NUMEQUAL, but runs OP_VERIFY afterward. */
|
||||
case object OP_NUMEQUALVERIFY extends ArithmeticOperation {
|
||||
override def opCode= 157
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the numbers are not equal, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if the numbers are not equal, 0 otherwise. */
|
||||
case object OP_NUMNOTEQUAL extends ArithmeticOperation {
|
||||
override def opCode = 158
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is less than b, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if a is less than b, 0 otherwise. */
|
||||
case object OP_LESSTHAN extends ArithmeticOperation {
|
||||
override def opCode = 159
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is greater than b, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if a is greater than b, 0 otherwise. */
|
||||
case object OP_GREATERTHAN extends ArithmeticOperation {
|
||||
override def opCode = 160
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is less than or equal to b, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if a is less than or equal to b, 0 otherwise. */
|
||||
case object OP_LESSTHANOREQUAL extends ArithmeticOperation {
|
||||
override def opCode = 161
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if a is greater than or equal to b, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if a is greater than or equal to b, 0 otherwise. */
|
||||
case object OP_GREATERTHANOREQUAL extends ArithmeticOperation {
|
||||
override def opCode = 162
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smaller of a and b.
|
||||
*/
|
||||
/** Returns the smaller of a and b. */
|
||||
case object OP_MIN extends ArithmeticOperation {
|
||||
override def opCode = 163
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the larger of a and b.
|
||||
*/
|
||||
/** Returns the larger of a and b. */
|
||||
case object OP_MAX extends ArithmeticOperation {
|
||||
override def opCode = 164
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. */
|
||||
case object OP_WITHIN extends ArithmeticOperation {
|
||||
override def opCode = 165
|
||||
}
|
||||
|
||||
|
||||
//currently disabled operations
|
||||
|
||||
/**
|
||||
* The input is multiplied by 2. disabled.
|
||||
*/
|
||||
/** The input is multiplied by 2. disabled. */
|
||||
case object OP_2MUL extends ArithmeticOperation {
|
||||
override def opCode = 141
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is divided by 2. disabled.
|
||||
*/
|
||||
/** The input is divided by 2. disabled. */
|
||||
case object OP_2DIV extends ArithmeticOperation {
|
||||
override def opCode = 142
|
||||
}
|
||||
|
||||
/**
|
||||
* a is multiplied by b. disabled.
|
||||
*/
|
||||
/** a is multiplied by b. disabled. */
|
||||
case object OP_MUL extends ArithmeticOperation {
|
||||
override def opCode = 149
|
||||
}
|
||||
|
||||
/**
|
||||
* a is divided by b. disabled.
|
||||
*/
|
||||
/** a is divided by b. disabled. */
|
||||
case object OP_DIV extends ArithmeticOperation {
|
||||
override def opCode = 150
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remainder after dividing a by b. disabled.
|
||||
*/
|
||||
/** Returns the remainder after dividing a by b. disabled. */
|
||||
case object OP_MOD extends ArithmeticOperation {
|
||||
override def opCode = 151
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a left b bits, preserving sign. disabled.
|
||||
*/
|
||||
/** Shifts a left b bits, preserving sign. disabled. */
|
||||
case object OP_LSHIFT extends ArithmeticOperation {
|
||||
override def opCode = 152
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a right b bits, preserving sign. disabled.
|
||||
*/
|
||||
/** Shifts a right b bits, preserving sign. disabled. */
|
||||
case object OP_RSHIFT extends ArithmeticOperation {
|
||||
override def opCode = 153
|
||||
}
|
||||
|
|
|
@ -1,27 +1,19 @@
|
|||
package org.bitcoins.core.script.bitwise
|
||||
|
||||
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
|
||||
|
||||
/**
|
||||
* Created by chris on 1/6/16.
|
||||
*/
|
||||
trait BitwiseInterpreter extends ControlOperationsInterpreter {
|
||||
|
||||
/**
|
||||
* Returns 1 if the inputs are exactly equal, 0 otherwise.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Returns 1 if the inputs are exactly equal, 0 otherwise. */
|
||||
def opEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_EQUAL, "Script operation must be OP_EQUAL")
|
||||
|
||||
require(program.script.headOption.contains(OP_EQUAL), "Script operation must be OP_EQUAL")
|
||||
if (program.stack.size < 2) {
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
|
@ -41,35 +33,26 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
|
|||
val scriptBoolean = if (result) OP_TRUE else OP_FALSE
|
||||
ScriptProgram(program,scriptBoolean :: program.stack.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Same as OP_EQUAL, but runs OP_VERIFY afterward.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Same as [[OP_EQUAL]], but runs [[OP_VERIFY]] afterward. */
|
||||
def opEqualVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_EQUALVERIFY, "Script operation must be OP_EQUALVERIFY")
|
||||
program.stack.size > 1 match {
|
||||
case true =>
|
||||
//first replace OP_EQUALVERIFY with OP_EQUAL and OP_VERIFY
|
||||
val simpleScript = OP_EQUAL :: OP_VERIFY :: program.script.tail
|
||||
val newProgram: ScriptProgram = opEqual(ScriptProgram(program, program.stack, simpleScript))
|
||||
opVerify(newProgram) match {
|
||||
case p : ExecutedScriptProgram if (p.error.isDefined) =>
|
||||
//need to switch the error set on this to ScriptErrorEqualVerify instead of ScriptErrorVerify
|
||||
ScriptProgram(p,ScriptErrorEqualVerify)
|
||||
case p : PreExecutionScriptProgram => p
|
||||
case p : ExecutedScriptProgram => p
|
||||
case p : ExecutionInProgressScriptProgram => p
|
||||
|
||||
}
|
||||
case false =>
|
||||
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
require(program.script.headOption.contains(OP_EQUALVERIFY), "Script operation must be OP_EQUALVERIFY")
|
||||
if (program.stack.size > 1) {
|
||||
//first replace OP_EQUALVERIFY with OP_EQUAL and OP_VERIFY
|
||||
val simpleScript = OP_EQUAL :: OP_VERIFY :: program.script.tail
|
||||
val newProgram: ScriptProgram = opEqual(ScriptProgram(program, program.stack, simpleScript))
|
||||
opVerify(newProgram) match {
|
||||
case p: ExecutedScriptProgram if (p.error.isDefined) =>
|
||||
//need to switch the error set on this to ScriptErrorEqualVerify instead of ScriptErrorVerify
|
||||
ScriptProgram(p, ScriptErrorEqualVerify)
|
||||
case p: PreExecutionScriptProgram => p
|
||||
case p: ExecutedScriptProgram => p
|
||||
case p: ExecutionInProgressScriptProgram => p
|
||||
}
|
||||
} else{
|
||||
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,44 +8,32 @@ import org.bitcoins.core.script.constant.ScriptOperation
|
|||
*/
|
||||
sealed trait BitwiseOperation extends ScriptOperation
|
||||
|
||||
/**
|
||||
* Returns 1 if the inputs are exactly equal, 0 otherwise.
|
||||
*/
|
||||
/** Returns 1 if the inputs are exactly equal, 0 otherwise. */
|
||||
case object OP_EQUAL extends BitwiseOperation {
|
||||
override def opCode = 135
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as OP_EQUAL, but runs OP_VERIFY afterward.
|
||||
*/
|
||||
/** Same as [[OP_EQUAL]], but runs [[org.bitcoins.core.script.control.OP_VERIFY]] afterward. */
|
||||
case object OP_EQUALVERIFY extends BitwiseOperation {
|
||||
override def opCode = 136
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips all of the bits in the input. disabled.
|
||||
*/
|
||||
/** Flips all of the bits in the input. disabled. */
|
||||
case object OP_INVERT extends BitwiseOperation {
|
||||
override def opCode = 131
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean and between each bit in the inputs. disabled.
|
||||
*/
|
||||
/** Boolean and between each bit in the inputs. disabled. */
|
||||
case object OP_AND extends BitwiseOperation {
|
||||
override def opCode = 132
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean or between each bit in the inputs. disabled.
|
||||
*/
|
||||
/** Boolean or between each bit in the inputs. disabled. */
|
||||
case object OP_OR extends BitwiseOperation {
|
||||
override def opCode = 133
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean exclusive or between each bit in the inputs. disabled.
|
||||
*/
|
||||
/** Boolean exclusive or between each bit in the inputs. disabled. */
|
||||
case object OP_XOR extends BitwiseOperation {
|
||||
override def opCode = 134
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package org.bitcoins.core.script.constant
|
||||
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.script.flag.{ScriptFlagUtil, ScriptVerifyMinimalData}
|
||||
import org.bitcoins.core.script.ScriptProgram
|
||||
import org.bitcoins.core.util.{BitcoinScriptUtil, BitcoinSLogger, BitcoinSUtil}
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.bitcoins.core.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -13,46 +12,25 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
trait ConstantInterpreter extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* The next byte contains the number of bytes to be pushed onto the stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The next byte contains the number of bytes to be pushed onto the stack. */
|
||||
def opPushData1(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_PUSHDATA1, "Top of script stack must be OP_PUSHDATA1")
|
||||
require(program.script.headOption.contains(OP_PUSHDATA1), "Top of script stack must be OP_PUSHDATA1")
|
||||
opPushData(program)
|
||||
}
|
||||
|
||||
/**
|
||||
* The next two bytes contain the number of bytes to be pushed onto the stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The next two bytes contain the number of bytes to be pushed onto the stack. */
|
||||
def opPushData2(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_PUSHDATA2, "Top of script stack must be OP_PUSHDATA2")
|
||||
require(program.script.headOption.contains(OP_PUSHDATA2), "Top of script stack must be OP_PUSHDATA2")
|
||||
opPushData(program)
|
||||
}
|
||||
|
||||
/**
|
||||
* The next four bytes contain the number of bytes to be pushed onto the stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The next four bytes contain the number of bytes to be pushed onto the stack. */
|
||||
def opPushData4(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_PUSHDATA4, "Top of script stack must be OP_PUSHDATA4")
|
||||
require(program.script.headOption.contains(OP_PUSHDATA4), "Top of script stack must be OP_PUSHDATA4")
|
||||
opPushData(program)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pushes the number of bytes onto the stack that is specified by script number on the script stack
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Pushes the number of bytes onto the stack that is specified by script number on the script stack. */
|
||||
def pushScriptNumberBytesToStack(program : ScriptProgram) : ScriptProgram = {
|
||||
val bytesNeeded : Long = program.script.head match {
|
||||
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 =>
|
||||
|
@ -60,18 +38,12 @@ trait ConstantInterpreter extends BitcoinSLogger {
|
|||
case _ : ScriptToken => bytesNeededForPushOp(program.script.head)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the script tokens that need to be pushed onto our stack
|
||||
*
|
||||
* @param scriptTokens
|
||||
* @param accum
|
||||
* @return
|
||||
*/
|
||||
/** Parses the script tokens that need to be pushed onto our stack. */
|
||||
@tailrec
|
||||
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], accum : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
|
||||
val bytesSum = accum.map(_.bytes.size).sum
|
||||
if (bytesSum == bytesNeeded) (scriptTokens,accum)
|
||||
else if (scriptTokens.size == 0) (Nil,accum)
|
||||
else if (scriptTokens.isEmpty) (Nil,accum)
|
||||
else if (bytesSum > bytesNeeded) throw new RuntimeException("We cannot have more bytes than what our script number specified")
|
||||
else {
|
||||
//for the case when a ScriptNumberImpl(x) was parsed as a ByteToPushOntoStackImpl(x)
|
||||
|
@ -111,16 +83,9 @@ trait ConstantInterpreter extends BitcoinSLogger {
|
|||
} else ScriptProgram.apply(program, constant :: program.stack, newScript)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the MINIMALDATA script flag is set, if so checks if we are using the minimal push operation
|
||||
* if we are, then we push the bytes onto the stack
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Checks if the MINIMALDATA script flag is set, if so checks if we are using the minimal push operation
|
||||
* if we are, then we push the bytes onto the stack. */
|
||||
private def opPushData(program : ScriptProgram) : ScriptProgram = {
|
||||
|
||||
//for the case when we have the minimal data flag and the bytes to push onto stack is represented by the
|
||||
//constant telling OP_PUSHDATA how many bytes need to go onto the stack
|
||||
//for instance OP_PUSHDATA1 OP_0
|
||||
|
@ -151,12 +116,7 @@ trait ConstantInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the bytes needed for a push op (for instance OP_PUSHDATA1)
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
/** Parses the bytes needed for a push op (for instance OP_PUSHDATA1). */
|
||||
private def bytesNeededForPushOp(token : ScriptToken) : Long = token match {
|
||||
case scriptNumber: BytesToPushOntoStack => scriptNumber.opCode
|
||||
case scriptNumber: ScriptNumber => scriptNumber.underlying
|
||||
|
|
|
@ -15,53 +15,32 @@ import scala.util.{Failure, Success, Try}
|
|||
* ScriptToken - think of this the same way you think about Object in Java.
|
||||
*/
|
||||
sealed trait ScriptToken {
|
||||
/**
|
||||
* The hexadecimal representation of this script token
|
||||
* @return
|
||||
*/
|
||||
/** The hexadecimal representation of this [[ScriptToken]]. */
|
||||
def hex : String
|
||||
|
||||
/**
|
||||
* The byte representation of this script token
|
||||
* @return
|
||||
*/
|
||||
/** The byte representation of this [[ScriptToken]]. */
|
||||
def bytes : Seq[Byte] = BitcoinSUtil.decodeHex(hex)
|
||||
|
||||
/**
|
||||
* The conversion from the byte representation of a token to a number
|
||||
* @return
|
||||
*/
|
||||
/** The conversion from the byte representation of a [[ScriptToken]] to a number. */
|
||||
def toLong = ScriptNumberUtil.toLong(hex)
|
||||
}
|
||||
|
||||
/**
|
||||
* A script operation is an instruction that takes an input and gives an output
|
||||
* Think of these as functions
|
||||
*/
|
||||
/** A script operation is an instruction that takes an input and gives an output
|
||||
* Think of these as functions.*/
|
||||
trait ScriptOperation extends ScriptToken {
|
||||
def opCode : Int
|
||||
override def hex : String = BitcoinSUtil.encodeHex(opCode.toByte)
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant in the Script language for instance as String or a number
|
||||
*/
|
||||
/** A constant in the Script language for instance as String or a number. */
|
||||
sealed trait ScriptConstant extends ScriptToken {
|
||||
/**
|
||||
* Returns if the constant is encoded in the shortest possible way
|
||||
* @return
|
||||
*/
|
||||
/** Returns if the [[ScriptConstant]] is encoded in the shortest possible way. */
|
||||
def isShortestEncoding : Boolean = BitcoinScriptUtil.isShortestEncoding(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a number in the Script language
|
||||
*/
|
||||
/** Represents a [[ScriptNumber]] in the Script language. */
|
||||
sealed trait ScriptNumber extends ScriptConstant {
|
||||
/**
|
||||
* The underlying number of the ScriptNumber
|
||||
* @return
|
||||
*/
|
||||
/** The underlying number of the [[ScriptNumber]]. */
|
||||
def underlying : Long
|
||||
|
||||
def + (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying + that.underlying)
|
||||
|
@ -85,13 +64,9 @@ sealed trait ScriptNumber extends ScriptConstant {
|
|||
|
||||
def | (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying | that.underlying)
|
||||
|
||||
/**
|
||||
* This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
|
||||
/** This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
|
||||
* are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true
|
||||
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false
|
||||
* @param that
|
||||
* @return
|
||||
*/
|
||||
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false. */
|
||||
def numEqual(that : ScriptNumber) : Boolean = underlying == that.underlying
|
||||
|
||||
override def toLong = underlying match {
|
||||
|
@ -104,8 +79,7 @@ sealed trait ScriptNumber extends ScriptConstant {
|
|||
|
||||
object ScriptNumber extends Factory[ScriptNumber] {
|
||||
|
||||
/**
|
||||
* This represents a script number inside of bitcoin
|
||||
/** This represents a [[ScriptNumber]] inside of bitcoin
|
||||
*
|
||||
* @param underlying the number being represented
|
||||
* @param hex the hex representation of the number - this can be different than the obvious value for
|
||||
|
@ -113,29 +87,20 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
|||
*/
|
||||
private case class ScriptNumberImpl(underlying : Long, override val hex : String) extends ScriptNumber
|
||||
|
||||
/**
|
||||
* Represents the number zero inside of bitcoin's script language
|
||||
* @return
|
||||
*/
|
||||
/** Represents the number zero inside of bitcoin's script language. */
|
||||
lazy val zero : ScriptNumber = ScriptNumberImpl(0,"")
|
||||
|
||||
/**
|
||||
* Represents the number one inside of bitcoin's script language
|
||||
* @return
|
||||
*/
|
||||
/** Represents the number one inside of bitcoin's script language. */
|
||||
lazy val one : ScriptNumber = ScriptNumberImpl(1)
|
||||
/**
|
||||
* Represents the number negative one inside of bitcoin's script language
|
||||
*/
|
||||
|
||||
/** Represents the number negative one inside of bitcoin's script language. */
|
||||
lazy val negativeOne : ScriptNumber = ScriptNumberImpl(-1)
|
||||
|
||||
/**
|
||||
* Bitcoin has a numbering system which has a negative zero
|
||||
*/
|
||||
/** Bitcoin has a numbering system which has a negative zero. */
|
||||
lazy val negativeZero : ScriptNumber = fromHex("80")
|
||||
|
||||
def fromBytes(bytes : Seq[Byte]) = {
|
||||
if (bytes.size == 0) zero
|
||||
if (bytes.isEmpty) zero
|
||||
else ScriptNumberImpl(ScriptNumberUtil.toLong(bytes), BitcoinSUtil.encodeHex(bytes))
|
||||
}
|
||||
|
||||
|
@ -154,10 +119,8 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
|||
|
||||
def apply(bytes : Seq[Byte], requireMinimal : Boolean) : Try[ScriptNumber] = apply(BitcoinSUtil.encodeHex(bytes),requireMinimal)
|
||||
|
||||
/**
|
||||
* Companion object for ScriptNumberImpl that gives us access to more constructor types for the
|
||||
* ScriptNumberImpl case class
|
||||
*/
|
||||
/** Companion object for [[ScriptNumberImpl]] that gives us access to more constructor types for the
|
||||
* [[ScriptNumberImpl]] case class. */
|
||||
private object ScriptNumberImpl {
|
||||
def apply(underlying : Long) : ScriptNumber = ScriptNumberImpl(underlying, ScriptNumberUtil.longToHex(underlying))
|
||||
def apply(hex : String) : ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(hex), hex)
|
||||
|
@ -166,63 +129,43 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The next byte contains the number of bytes to be pushed onto the stack.
|
||||
*/
|
||||
/** The next byte contains the number of bytes to be pushed onto the stack. */
|
||||
case object OP_PUSHDATA1 extends ScriptOperation {
|
||||
override def opCode = 76
|
||||
|
||||
/**
|
||||
* The maximum amount of bytes OP_PUSHDATA1 can push onto the stack
|
||||
*/
|
||||
/** The maximum amount of bytes OP_PUSHDATA1 can push onto the stack. */
|
||||
def max = 255
|
||||
}
|
||||
|
||||
/**
|
||||
* The next two bytes contain the number of bytes to be pushed onto the stack.
|
||||
*/
|
||||
/** The next two bytes contain the number of bytes to be pushed onto the stack. */
|
||||
case object OP_PUSHDATA2 extends ScriptOperation {
|
||||
override def opCode = 77
|
||||
|
||||
/**
|
||||
* The max amount of data that OP_PUSHDATA2 can push onto the stack
|
||||
* @return
|
||||
*/
|
||||
/** The max amount of data that OP_PUSHDATA2 can push onto the stack. */
|
||||
def max = 65535
|
||||
}
|
||||
|
||||
/**
|
||||
* The next four bytes contain the number of bytes to be pushed onto the stack.
|
||||
*/
|
||||
/** The next four bytes contain the number of bytes to be pushed onto the stack. */
|
||||
case object OP_PUSHDATA4 extends ScriptOperation {
|
||||
override def opCode = 78
|
||||
|
||||
/**
|
||||
* The maximum amount of data that OP_PUSHDATA4 can be push on the stack
|
||||
* @return
|
||||
*/
|
||||
/** The maximum amount of data that OP_PUSHDATA4 can be push on the stack. */
|
||||
def max = 4294967295L
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a script number operation where the the number in the operation is pushed onto the stack
|
||||
* i.e. OP_0 would be push 0 onto the stack, OP_1 would be push 1 onto the stack
|
||||
*/
|
||||
/** Represents a [[ScriptNumberOperation]] where the the number in the operation is pushed onto the stack
|
||||
* i.e. OP_0 would be push 0 onto the stack, OP_1 would be push 1 onto the stack. */
|
||||
sealed trait ScriptNumberOperation extends ScriptNumber with ScriptOperation {
|
||||
override def hex = opCode.toHexString
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.)
|
||||
*/
|
||||
/** An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) */
|
||||
case object OP_0 extends ScriptNumberOperation {
|
||||
override def opCode = 0
|
||||
override def hex = "00"
|
||||
override def underlying = 0
|
||||
}
|
||||
/**
|
||||
* An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.)
|
||||
*/
|
||||
/** An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) */
|
||||
case object OP_FALSE extends ScriptNumberOperation {
|
||||
override def opCode = OP_0.opCode
|
||||
override def hex = OP_0.hex
|
||||
|
@ -230,181 +173,132 @@ case object OP_FALSE extends ScriptNumberOperation {
|
|||
override def bytes = OP_0.bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 1 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 1 is pushed onto the stack. */
|
||||
case object OP_TRUE extends ScriptNumberOperation {
|
||||
override def opCode = 81
|
||||
override def underlying = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* The number -1 is pushed onto the stack.
|
||||
*/
|
||||
/** The number -1 is pushed onto the stack. */
|
||||
case object OP_1NEGATE extends ScriptNumberOperation {
|
||||
override def opCode = 79
|
||||
override def underlying = -1
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The number 1 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 1 is pushed onto the stack. */
|
||||
case object OP_1 extends ScriptNumberOperation {
|
||||
override def opCode = OP_TRUE.opCode
|
||||
override def underlying = OP_TRUE.underlying
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 2 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 2 is pushed onto the stack. */
|
||||
case object OP_2 extends ScriptNumberOperation {
|
||||
override def opCode = 82
|
||||
override def underlying = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 3 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 3 is pushed onto the stack. */
|
||||
case object OP_3 extends ScriptNumberOperation {
|
||||
override def opCode = 83
|
||||
override def underlying = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 4 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 4 is pushed onto the stack. */
|
||||
case object OP_4 extends ScriptNumberOperation {
|
||||
override def opCode = 84
|
||||
override def underlying = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 5 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 5 is pushed onto the stack. */
|
||||
case object OP_5 extends ScriptNumberOperation {
|
||||
override def opCode = 85
|
||||
override def underlying = 5
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 6 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 6 is pushed onto the stack. */
|
||||
case object OP_6 extends ScriptNumberOperation {
|
||||
override def opCode = 86
|
||||
override def underlying = 6
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 7 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 7 is pushed onto the stack. */
|
||||
case object OP_7 extends ScriptNumberOperation {
|
||||
override def opCode = 87
|
||||
override def underlying = 7
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 8 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 8 is pushed onto the stack. */
|
||||
case object OP_8 extends ScriptNumberOperation {
|
||||
override def opCode = 88
|
||||
override def underlying = 8
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 9 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 9 is pushed onto the stack. */
|
||||
case object OP_9 extends ScriptNumberOperation {
|
||||
override def opCode = 89
|
||||
override def underlying = 9
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 10 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 10 is pushed onto the stack. */
|
||||
case object OP_10 extends ScriptNumberOperation {
|
||||
override def opCode = 90
|
||||
override def underlying = 10
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 11 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 11 is pushed onto the stack. */
|
||||
case object OP_11 extends ScriptNumberOperation {
|
||||
override def opCode = 91
|
||||
override def underlying = 11
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 12 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 12 is pushed onto the stack. */
|
||||
case object OP_12 extends ScriptNumberOperation {
|
||||
override def opCode = 92
|
||||
override def underlying = 12
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 13 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 13 is pushed onto the stack. */
|
||||
case object OP_13 extends ScriptNumberOperation {
|
||||
override def opCode = 93
|
||||
override def underlying = 13
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 14 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 14 is pushed onto the stack. */
|
||||
case object OP_14 extends ScriptNumberOperation {
|
||||
override def opCode = 94
|
||||
override def underlying = 14
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 15 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 15 is pushed onto the stack. */
|
||||
case object OP_15 extends ScriptNumberOperation {
|
||||
override def opCode = 95
|
||||
override def underlying = 15
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 16 is pushed onto the stack.
|
||||
*/
|
||||
/** The number 16 is pushed onto the stack. */
|
||||
case object OP_16 extends ScriptNumberOperation {
|
||||
override def opCode = 96
|
||||
override def underlying = 16
|
||||
}
|
||||
|
||||
|
||||
object ScriptNumberOperation extends ScriptOperationFactory[ScriptNumberOperation] {
|
||||
|
||||
def operations = Seq(OP_0,OP_1, OP_1NEGATE, OP_2,OP_3,OP_4,OP_5,OP_6,OP_7,OP_8,OP_9,OP_10,OP_11,OP_12,OP_13,OP_14,OP_15,OP_16)
|
||||
|
||||
/**
|
||||
* Finds the script number operation based on the given integer
|
||||
* @param underlying
|
||||
* @return
|
||||
*/
|
||||
/** Finds the [[ScriptNumberOperation]] based on the given integer. */
|
||||
def fromNumber(underlying : Int) : Option[ScriptNumberOperation] = operations.find(_.underlying == underlying)
|
||||
|
||||
}
|
||||
|
||||
object ScriptConstant extends Factory[ScriptConstant] {
|
||||
/**
|
||||
* Represent a pubkey or hash of a pub key on our stack
|
||||
* @param hex
|
||||
*/
|
||||
/** Represent a public key or hash of a public key on our stack.*/
|
||||
private case class ScriptConstantImpl(hex : String) extends ScriptConstant {
|
||||
def this(bytes : List[Byte]) = this(BitcoinSUtil.encodeHex(bytes))
|
||||
}
|
||||
lazy val zero = ScriptConstant("00")
|
||||
lazy val negativeZero = ScriptConstant("80")
|
||||
lazy val negativeOne = ScriptConstant("81")
|
||||
/**
|
||||
* Creates a script constant from a sequence of bytes
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Creates a [[ScriptConstant]] from a sequence of bytes. */
|
||||
def fromBytes(bytes : Seq[Byte]) : ScriptConstant = ScriptConstantImpl(BitcoinSUtil.encodeHex(bytes))
|
||||
|
||||
}
|
||||
|
|
|
@ -10,40 +10,30 @@ sealed trait ControlOperations extends ScriptOperation
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* If the top stack value is not 0, the statements are executed. The top stack value is removed.
|
||||
*/
|
||||
/** If the top stack value is not 0, the statements are executed. The top stack value is removed. */
|
||||
case object OP_IF extends ControlOperations {
|
||||
override def opCode = 99
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the top stack value is 0, the statements are executed. The top stack value is removed.
|
||||
*/
|
||||
/** If the top stack value is 0, the statements are executed. The top stack value is removed. */
|
||||
case object OP_NOTIF extends ControlOperations {
|
||||
override def opCode = 100
|
||||
}
|
||||
|
||||
/**
|
||||
* If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and
|
||||
* if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not.
|
||||
*/
|
||||
/** If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and
|
||||
* if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not. */
|
||||
case object OP_ELSE extends ControlOperations {
|
||||
override def opCode = 103
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an if/else block. All blocks must end, or the transaction is invalid.
|
||||
* An OP_ENDIF without OP_IF earlier is also invalid.
|
||||
*/
|
||||
/** Ends an if/else block. All blocks must end, or the transaction is invalid.
|
||||
* An OP_ENDIF without OP_IF earlier is also invalid. */
|
||||
case object OP_ENDIF extends ControlOperations {
|
||||
override def opCode = 104
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks transaction as invalid if top stack value is not true.
|
||||
*/
|
||||
/** Marks transaction as invalid if top stack value is not true. */
|
||||
case object OP_VERIFY extends ControlOperations {
|
||||
override def opCode = 105
|
||||
}
|
||||
|
|
|
@ -15,11 +15,9 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
trait ControlOperationsInterpreter extends BitcoinSLogger {
|
||||
|
||||
|
||||
|
||||
/** If the top stack value is not 0, the statements are executed. The top stack value is removed. */
|
||||
def opIf(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_IF, "Script top was not OP_IF")
|
||||
require(program.script.headOption.contains(OP_IF), "Script top was not OP_IF")
|
||||
val sigVersion = program.txSignatureComponent.sigVersion
|
||||
val flags = program.flags
|
||||
val minimalIfEnabled = ScriptFlagUtil.minimalIfEnabled(flags)
|
||||
|
@ -54,21 +52,18 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
|
||||
ScriptProgram(program, program.stack.tail,scriptWithoutOpIf.toList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** If the top stack value is 0, the statements are executed. The top stack value is removed. */
|
||||
def opNotIf(program : ScriptProgram) : ScriptProgram = {
|
||||
//TODO: Try and reduce this down to using OP_IF by inverting the stack top
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NOTIF, "Script top was not OP_NOTIF")
|
||||
require(program.script.headOption.contains(OP_NOTIF), "Script top was not OP_NOTIF")
|
||||
val binaryTree = parseBinaryTree(program.script)
|
||||
val sigVersion = program.txSignatureComponent.sigVersion
|
||||
val flags = program.flags
|
||||
val minimalIfEnabled = ScriptFlagUtil.minimalIfEnabled(flags)
|
||||
val stackTop = program.stack.headOption
|
||||
logger.debug("Parsed binary tree: " + binaryTree)
|
||||
|
||||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
logger.error("We do not have a matching OP_ENDIF for every OP_NOTIF we have")
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
|
@ -95,10 +90,9 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/** Evaluates the OP_ELSE operator */
|
||||
/** Evaluates the [[OP_ELSE]] operator. */
|
||||
def opElse(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ELSE, "First script opt must be OP_ELSE")
|
||||
|
||||
require(program.script.headOption.contains(OP_ELSE), "First script opt must be OP_ELSE")
|
||||
if (!program.script.tail.contains(OP_ENDIF)) {
|
||||
logger.error("OP_ELSE does not have a OP_ENDIF")
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
|
@ -121,41 +115,31 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
ScriptProgram(program, program.stack,treeWithNextOpElseRemoved.toList.tail)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Evaluates an OP_ENDIF operator */
|
||||
/** Evaluates an [[OP_ENDIF]] operator. */
|
||||
def opEndIf(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ENDIF, "Script top must be OP_ENDIF")
|
||||
require(program.script.headOption.contains(OP_ENDIF), "Script top must be OP_ENDIF")
|
||||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
//means we do not have a matching OP_IF for our OP_ENDIF
|
||||
logger.error("We do not have a matching OP_IF/OP_NOTIF for every OP_ENDIF we have")
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
} else ScriptProgram(program, program.stack,program.script.tail)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marks transaction as invalid. A standard way of attaching extra data to transactions is to add a zero-value output
|
||||
* with a scriptPubKey consisting of OP_RETURN followed by exactly one pushdata op. Such outputs are provably unspendable,
|
||||
/** Marks transaction as invalid. A standard way of attaching extra data to transactions is to add a zero-value output
|
||||
* with a [[org.bitcoins.core.protocol.script.ScriptPubKey]] consisting of [[OP_RETURN]] followed by exactly one pushdata op. Such outputs are provably unspendable,
|
||||
* reducing their cost to the network. Currently it is usually considered non-standard (though valid) for a transaction to
|
||||
* have more than one OP_RETURN output or an OP_RETURN output with more than one pushdata op.
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
* have more than one OP_RETURN output or an OP_RETURN output with more than one pushdata op. */
|
||||
def opReturn(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_RETURN)
|
||||
require(program.script.headOption.contains(OP_RETURN))
|
||||
ScriptProgram(program,ScriptErrorOpReturn)
|
||||
}
|
||||
|
||||
|
||||
/** Marks transaction as invalid if top stack value is not true. */
|
||||
/** Marks [[org.bitcoins.core.protocol.transaction.Transaction]] as invalid if top stack value is not true. */
|
||||
def opVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
|
||||
program.stack.size > 0 match {
|
||||
require(program.script.headOption.contains(OP_VERIFY), "Script top must be OP_VERIFY")
|
||||
program.stack.nonEmpty match {
|
||||
case true =>
|
||||
logger.debug("Stack for OP_VERIFY: " + program.stack)
|
||||
if (program.stackTopIsFalse) ScriptProgram(program,ScriptErrorVerify)
|
||||
|
@ -164,17 +148,15 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
logger.error("OP_VERIFY requires an element to be on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Parses a list of script tokens into its corresponding binary tree */
|
||||
/** Parses a list of [[ScriptToken]]s into its corresponding [[BinaryTree]] */
|
||||
def parseBinaryTree(script : List[ScriptToken]) : BinaryTree[ScriptToken] = {
|
||||
val bTree = loop(script,Empty)
|
||||
bTree
|
||||
}
|
||||
|
||||
/** The loop that parses a list of script tokens into a binary tree */
|
||||
/** The loop that parses a list of [[ScriptToken]]s into a [[BinaryTree]]. */
|
||||
@tailrec
|
||||
private def loop(script : List[ScriptToken], tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
|
||||
/* logger.debug("Script : " + script)
|
||||
|
@ -203,7 +185,6 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a sub tree into the parse tree of Script.
|
||||
* @param tree the parse tree of the control flow of the Script program
|
||||
|
@ -234,40 +215,37 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
else Node(node.v, insertSubTree(node.l, subTree), node.r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Parses an OP_IF script token */
|
||||
/** Parses an [[OP_IF]] [[ScriptToken]]. */
|
||||
private def parseOpIf(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
|
||||
case OP_IF :: t => (t, Node(OP_IF,Empty,Empty))
|
||||
case h :: t => throw new IllegalArgumentException("Cannot parse " + h + " as an OP_IF")
|
||||
case Nil => (script,Empty)
|
||||
}
|
||||
|
||||
|
||||
/** Parses an OP_NOTIF script token */
|
||||
/** Parses an [[OP_NOTIF]] [[ScriptToken]]. */
|
||||
private def parseOpNotIf(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
|
||||
case OP_NOTIF :: t => (t, Node(OP_NOTIF,Empty,Empty))
|
||||
case h :: t => throw new IllegalArgumentException("Cannot parse " + h + " as an OP_NOTIF")
|
||||
case Nil => (script,Empty)
|
||||
}
|
||||
|
||||
/** Parses and OP_ELSE expression */
|
||||
/** Parses an [[OP_ELSE]] [[ScriptToken]]. */
|
||||
private def parseOpElse(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
|
||||
case OP_ELSE :: t => (t,Node(OP_ELSE,Empty,Empty))
|
||||
case h :: t => throw new RuntimeException("Cannot parse " + h + " as an OP_ELSE")
|
||||
case Nil => (script,Empty)
|
||||
}
|
||||
|
||||
/** Parses an [[OP_ENDIF]] [[ScriptToken]]. */
|
||||
private def parseOpEndIf(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
|
||||
case OP_ENDIF :: t => (t,Leaf(OP_ENDIF))
|
||||
case h :: t => throw new IllegalArgumentException("Cannot parse " + h + " as an OP_ENDIF")
|
||||
case Nil => (script,Empty)
|
||||
}
|
||||
|
||||
|
||||
/** Checks if an [[OP_IF]]/[[OP_NOTIF]] script token has a matching [[OP_ENDIF]] */
|
||||
/** Checks if an [[OP_IF]]/[[OP_NOTIF]] [[ScriptToken]] has a matching [[OP_ENDIF]] */
|
||||
def checkMatchingOpIfOpNotIfOpEndIf(script : List[ScriptToken]) : Boolean = {
|
||||
@tailrec
|
||||
def loop(script : List[ScriptToken], counter : Int) : Boolean = script match {
|
||||
|
@ -281,9 +259,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
loop(script,0)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Returns the first index of an OP_ENDIF */
|
||||
/** Returns the first index of an [[OP_ENDIF]]. */
|
||||
def findFirstOpEndIf(script : List[ScriptToken]) : Option[Int] = {
|
||||
val index = script.indexOf(OP_ENDIF)
|
||||
index match {
|
||||
|
@ -292,15 +268,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** Finds the last OP_ENDIF in the given script */
|
||||
/** Finds the last [[OP_ENDIF]] in the given script. */
|
||||
def findLastOpEndIf(script : List[ScriptToken]) : Option[Int] = {
|
||||
val lastOpEndIf = findFirstOpEndIf(script.reverse)
|
||||
if (lastOpEndIf.isDefined) Some(script.size - lastOpEndIf.get - 1)
|
||||
else None
|
||||
}
|
||||
|
||||
/** Returns the first index of an OP_ENDIF */
|
||||
/** Returns the first index of an [[OP_ENDIF]]. */
|
||||
def findFirstOpElse(script : List[ScriptToken]) : Option[Int] = {
|
||||
val index = script.indexOf(OP_ELSE)
|
||||
index match {
|
||||
|
@ -309,7 +284,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/** Removes the first OP_ELSE expression encountered in the script */
|
||||
/** Removes the first [[OP_ELSE]] expression encountered in the script. */
|
||||
def removeFirstOpElse(script : List[ScriptToken]) : List[ScriptToken] = {
|
||||
if (script.contains(OP_ELSE)) {
|
||||
val firstOpElseIndex = findFirstOpElse(script)
|
||||
|
@ -324,8 +299,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
} else script
|
||||
}
|
||||
|
||||
|
||||
/** Removes the first OP_ELSE {expression} in a binary tree */
|
||||
/** Removes the first [[OP_ELSE]] in a [[BinaryTree]]. */
|
||||
def removeFirstOpElse(tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
|
||||
tree match {
|
||||
case Empty => Empty
|
||||
|
@ -353,16 +327,15 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
Node(node.v,node.l,node.r.right.getOrElse(Empty))
|
||||
} else tree
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Removes the first OP_IF { expression } encountered in the script */
|
||||
/** Removes the first [[OP_IF]] encountered in the script. */
|
||||
def removeFirstOpIf(script : List[ScriptToken]) : List[ScriptToken] = {
|
||||
val firstOpIfIndex = script.indexOf(OP_IF)
|
||||
val matchingOpEndIfIndex = findMatchingOpEndIf(script)
|
||||
|
||||
val opIfExpression = script.slice(firstOpIfIndex,matchingOpEndIfIndex)
|
||||
val hasNestedIfExpression = opIfExpression.filter(_ == OP_IF).size > 1
|
||||
val hasNestedIfExpression = opIfExpression.count(_ == OP_IF) > 1
|
||||
val (firstOpElseIndex,_) = findFirstIndexesOpElseOpEndIf(opIfExpression)
|
||||
|
||||
if (firstOpElseIndex.isDefined && !hasNestedIfExpression) {
|
||||
|
@ -372,25 +345,23 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
//that OP_IF
|
||||
script.slice(0,firstOpIfIndex) ++ script.slice(matchingOpEndIfIndex,script.size)
|
||||
} else script.slice(0,firstOpIfIndex) ++ script.slice(matchingOpEndIfIndex,script.size)
|
||||
|
||||
}
|
||||
|
||||
/** Removes the first occurrence of OP_IF or OP_NOTIF in the binary tree */
|
||||
/** Removes the first occurrence of [[OP_IF]] or [[OP_NOTIF]] in the [[BinaryTree]]. */
|
||||
def removeFirstOpIf(tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
|
||||
require(tree.value.isDefined && (tree.value.get == OP_IF || tree.value.get == OP_NOTIF) , "Top of the tree must be OP_IF or OP_NOTIF to remove the OP_IF or OP_NOTIF")
|
||||
if (tree.right.isDefined && tree.right.get.value == Some(OP_ELSE)) tree.right.getOrElse(Empty)
|
||||
else tree.findFirstDFS[ScriptToken](OP_ENDIF)().getOrElse(Empty)
|
||||
}
|
||||
|
||||
/** Finds the indexes of our OP_ELSE (if it exists) and our OP_ENDIF */
|
||||
/** Finds the indexes of our [[OP_ELSE]] (if it exists) and our [[OP_ENDIF]]. */
|
||||
def findFirstIndexesOpElseOpEndIf(script : List[ScriptToken]) : (Option[Int],Option[Int]) = {
|
||||
val indexOpElse = findFirstOpElse(script)
|
||||
val indexOpEndIf = findFirstOpEndIf(script)
|
||||
(indexOpElse,indexOpEndIf)
|
||||
}
|
||||
|
||||
|
||||
/** Returns the index of the matching OP_ENDIF for the OP_IF statement */
|
||||
/** Returns the index of the matching [[OP_ENDIF]] for the [[OP_IF]] statement. */
|
||||
def findMatchingOpEndIf(script : List[ScriptToken]) : Int = {
|
||||
val matchingOpEndIfIndex = findLastOpEndIf(script)
|
||||
require(matchingOpEndIfIndex.isDefined, "Every OP_IF must have a matching OP_ENDIF: " + script)
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.bitcoins.core.crypto.{SignatureValidationFailureIncorrectSignatures,
|
|||
import org.bitcoins.core.script._
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
|
||||
import org.bitcoins.core.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, CryptoUtil}
|
||||
|
||||
|
@ -18,43 +18,42 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
|
||||
/** The input is hashed twice: first with SHA-256 and then with RIPEMD-160. */
|
||||
def opHash160(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_HASH160, "Script operation must be OP_HASH160")
|
||||
require(program.script.headOption.contains(OP_HASH160), "Script operation must be OP_HASH160")
|
||||
executeHashFunction(program, CryptoUtil.sha256Hash160(_ : Seq[Byte]))
|
||||
}
|
||||
|
||||
|
||||
/** The input is hashed using RIPEMD-160. */
|
||||
def opRipeMd160(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_RIPEMD160, "Script operation must be OP_RIPEMD160")
|
||||
require(program.script.headOption.contains(OP_RIPEMD160), "Script operation must be OP_RIPEMD160")
|
||||
executeHashFunction(program, CryptoUtil.ripeMd160(_ : Seq[Byte]))
|
||||
}
|
||||
|
||||
/** The input is hashed using SHA-256. */
|
||||
def opSha256(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SHA256, "Script operation must be OP_SHA256")
|
||||
require(program.script.headOption.contains(OP_SHA256), "Script operation must be OP_SHA256")
|
||||
executeHashFunction(program, CryptoUtil.sha256(_ : Seq[Byte]))
|
||||
}
|
||||
|
||||
/** The input is hashed two times with SHA-256. */
|
||||
def opHash256(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_HASH256, "Script operation must be OP_HASH256")
|
||||
require(program.script.headOption.contains(OP_HASH256), "Script operation must be OP_HASH256")
|
||||
executeHashFunction(program, CryptoUtil.doubleSHA256(_ : Seq[Byte]))
|
||||
}
|
||||
|
||||
/** The input is hashed using SHA-1. */
|
||||
def opSha1(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SHA1, "Script top must be OP_SHA1")
|
||||
require(program.script.headOption.contains(OP_SHA1), "Script top must be OP_SHA1")
|
||||
executeHashFunction(program, CryptoUtil.sha1(_ : Seq[Byte]))
|
||||
}
|
||||
|
||||
/**
|
||||
* The entire transaction's outputs, inputs, and script (from the most
|
||||
* recently-executed OP_CODESEPARATOR to the end) are hashed.
|
||||
* The signature used by OP_CHECKSIG must be a valid signature for this hash and public key.
|
||||
* The signature used by [[OP_CHECKSIG]] must be a valid signature for this hash and public key.
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L880]]
|
||||
*/
|
||||
def opCheckSig(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
|
||||
require(program.script.headOption.contains(OP_CHECKSIG), "Script top must be OP_CHECKSIG")
|
||||
program match {
|
||||
case preExecutionScriptProgram : PreExecutionScriptProgram =>
|
||||
opCheckSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
|
||||
|
@ -127,9 +126,9 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
}
|
||||
}
|
||||
|
||||
/** Runs OP_CHECKSIG with an OP_VERIFY afterwards */
|
||||
/** Runs [[OP_CHECKSIG]] with an [[OP_VERIFY]] afterwards. */
|
||||
def opCheckSigVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIGVERIFY,
|
||||
require(program.script.headOption.contains(OP_CHECKSIGVERIFY),
|
||||
"Script top must be OP_CHECKSIGVERIFY")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("Stack must contain at least 3 items for OP_CHECKSIGVERIFY")
|
||||
|
@ -147,11 +146,10 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All of the signature checking words will only match signatures to the data
|
||||
* after the most recently-executed OP_CODESEPARATOR. */
|
||||
/** All of the signature checking words will only match signatures to the data
|
||||
* after the most recently-executed [[OP_CODESEPARATOR]]. */
|
||||
def opCodeSeparator(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CODESEPARATOR, "Script top must be OP_CODESEPARATOR")
|
||||
require(program.script.headOption.contains(OP_CODESEPARATOR), "Script top must be OP_CODESEPARATOR")
|
||||
val e = program match {
|
||||
case e : PreExecutionScriptProgram =>
|
||||
opCodeSeparator(ScriptProgram.toExecutionInProgress(e))
|
||||
|
@ -164,7 +162,6 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
e
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the first signature against each public key until it finds an ECDSA match.
|
||||
* Starting with the subsequent public key, it compares the second signature against each remaining
|
||||
|
@ -178,7 +175,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
*/
|
||||
@tailrec
|
||||
final def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
|
||||
require(program.script.headOption.contains(OP_CHECKMULTISIG), "Script top must be OP_CHECKMULTISIG")
|
||||
val flags = program.flags
|
||||
program match {
|
||||
case preExecutionScriptProgram : PreExecutionScriptProgram =>
|
||||
|
@ -244,7 +241,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L966
|
||||
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
|
||||
} else if (ScriptFlagUtil.requireNullDummy(flags) &&
|
||||
(stackWithoutPubKeysAndSignatures.headOption.isDefined && stackWithoutPubKeysAndSignatures.head.bytes.nonEmpty)) {
|
||||
(stackWithoutPubKeysAndSignatures.nonEmpty && stackWithoutPubKeysAndSignatures.head.bytes.nonEmpty)) {
|
||||
logger.error("Script flag null dummy was set however the first element in the script signature was not an OP_0, stackWithoutPubKeysAndSignatures: " + stackWithoutPubKeysAndSignatures)
|
||||
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullDummy)
|
||||
} else {
|
||||
|
@ -298,10 +295,9 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** Runs OP_CHECKMULTISIG with an OP_VERIFY afterwards */
|
||||
/** Runs [[OP_CHECKMULTISIG]] with an [[OP_VERIFY]] afterwards */
|
||||
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY")
|
||||
require(program.script.headOption.contains(OP_CHECKMULTISIGVERIFY), "Script top must be OP_CHECKMULTISIGVERIFY")
|
||||
if (program.stack.size < 3) {
|
||||
logger.error("Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
|
@ -318,7 +314,6 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a higher order function designed to execute a hash function on the stack top of the program
|
||||
* For instance, we could pass in CryptoUtil.sha256 function as the 'hashFunction' argument, which would then
|
||||
|
@ -328,7 +323,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
* @return
|
||||
*/
|
||||
private def executeHashFunction(program : ScriptProgram, hashFunction : Seq[Byte] => HashDigest) : ScriptProgram = {
|
||||
if (program.stack.headOption.isDefined) {
|
||||
if (program.stack.nonEmpty) {
|
||||
val stackTop = program.stack.head
|
||||
val hash = ScriptConstant(hashFunction(stackTop.bytes).bytes)
|
||||
ScriptProgram(program, hash :: program.stack.tail, program.script.tail)
|
||||
|
@ -337,5 +332,4 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,72 +9,54 @@ import org.bitcoins.core.script.constant.ScriptOperation
|
|||
*/
|
||||
sealed trait CryptoOperation extends ScriptOperation
|
||||
|
||||
/**
|
||||
* Represents an operation where ECDSA signatures are evaluated
|
||||
*/
|
||||
/** Represents an operation where ECDSA signatures are evaluated. */
|
||||
sealed trait CryptoSignatureEvaluation extends CryptoOperation
|
||||
/**
|
||||
* The input is hashed using RIPEMD-160.
|
||||
*/
|
||||
|
||||
/** The input is hashed using RIPEMD-160. */
|
||||
case object OP_RIPEMD160 extends CryptoOperation {
|
||||
override def opCode = 166
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed using SHA-1.
|
||||
*/
|
||||
/** The input is hashed using SHA-1. */
|
||||
case object OP_SHA1 extends CryptoOperation {
|
||||
override def opCode = 167
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed using SHA-256.
|
||||
*/
|
||||
/** The input is hashed using SHA-256. */
|
||||
case object OP_SHA256 extends CryptoOperation {
|
||||
override def opCode = 168
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
|
||||
*/
|
||||
/** The input is hashed twice: first with SHA-256 and then with RIPEMD-160. */
|
||||
case object OP_HASH160 extends CryptoOperation {
|
||||
override def opCode = 169
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed two times with SHA-256.
|
||||
*/
|
||||
/** The input is hashed two times with SHA-256. */
|
||||
case object OP_HASH256 extends CryptoOperation {
|
||||
override def opCode = 170
|
||||
}
|
||||
|
||||
/**
|
||||
* All of the signature checking words will only match signatures to
|
||||
* the data after the most recently-executed OP_CODESEPARATOR.
|
||||
*/
|
||||
/** All of the signature checking words will only match signatures to
|
||||
* the data after the most recently-executed OP_CODESEPARATOR. */
|
||||
case object OP_CODESEPARATOR extends CryptoOperation {
|
||||
override def opCode = 171
|
||||
}
|
||||
|
||||
/**
|
||||
* The entire transaction's outputs, inputs, and script
|
||||
/** The entire transaction's outputs, inputs, and script
|
||||
* (from the most recently-executed OP_CODESEPARATOR to the end) are hashed.
|
||||
* The signature used by OP_CHECKSIG must be a valid signature for this hash and public key.
|
||||
* If it is, 1 is returned, 0 otherwise.
|
||||
*/
|
||||
* If it is, 1 is returned, 0 otherwise. */
|
||||
case object OP_CHECKSIG extends CryptoSignatureEvaluation {
|
||||
override def opCode = 172
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
|
||||
*/
|
||||
/** Same as OP_CHECKSIG, but OP_VERIFY is executed afterward. */
|
||||
case object OP_CHECKSIGVERIFY extends CryptoSignatureEvaluation {
|
||||
override def opCode = 173
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the first signature against each public key until it finds an ECDSA match.
|
||||
/** Compares the first signature against each public key until it finds an ECDSA match.
|
||||
* Starting with the subsequent public key, it compares the second signature against each remaining public key
|
||||
* until it finds an ECDSA match.
|
||||
* The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result.
|
||||
|
@ -82,15 +64,12 @@ case object OP_CHECKSIGVERIFY extends CryptoSignatureEvaluation {
|
|||
* Because public keys are not checked again if they fail any signature comparison,
|
||||
* signatures must be placed in the scriptSig using the same order as their corresponding public keys
|
||||
* were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise.
|
||||
* Due to a bug, one extra unused value is removed from the stack.
|
||||
*/
|
||||
* Due to a bug, one extra unused value is removed from the stack. */
|
||||
case object OP_CHECKMULTISIG extends CryptoSignatureEvaluation {
|
||||
override def opCode = 174
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
|
||||
*/
|
||||
/** Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward. */
|
||||
case object OP_CHECKMULTISIGVERIFY extends CryptoSignatureEvaluation {
|
||||
override def opCode = 175
|
||||
}
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
package org.bitcoins.core.script.crypto
|
||||
|
||||
import org.bitcoins.core.script.ScriptOperationFactory
|
||||
import org.bitcoins.core.util.Factory
|
||||
|
||||
/**
|
||||
* Created by chris on 3/24/16.
|
||||
*/
|
||||
trait CryptoSignatureEvaluationFactory extends ScriptOperationFactory[CryptoSignatureEvaluation] {
|
||||
|
||||
/**
|
||||
* The current crypto signature evaluation operations
|
||||
*
|
||||
* @return the sequence of crypto signature evaluation operations
|
||||
*/
|
||||
/** The current [[CryptoSignatureEvaluation]] operations. */
|
||||
def operations = Seq(OP_CHECKMULTISIG,OP_CHECKMULTISIGVERIFY,OP_CHECKSIG, OP_CHECKSIGVERIFY)
|
||||
|
||||
}
|
||||
|
|
|
@ -7,11 +7,8 @@ package org.bitcoins.core.script.flag
|
|||
*/
|
||||
trait ScriptFlagFactory {
|
||||
|
||||
/**
|
||||
* All the script flags found inside of bitcoin core
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31
|
||||
* @return
|
||||
*/
|
||||
/** All the [[ScriptFlag]]s found inside of bitcoin core
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31. */
|
||||
private def flags = Seq(ScriptVerifyNone, ScriptVerifyP2SH, ScriptVerifyStrictEnc,
|
||||
ScriptVerifyDerSig, ScriptVerifyLowS, ScriptVerifySigPushOnly, ScriptVerifyMinimalData,
|
||||
ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack,
|
||||
|
@ -19,39 +16,24 @@ trait ScriptFlagFactory {
|
|||
ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyMinimalIf,ScriptVerifyNullFail,
|
||||
ScriptVerifyWitnessPubKeyType)
|
||||
|
||||
/**
|
||||
* Takes in a string and tries to match it with a script flag
|
||||
* @param str the string to try and match with a script flag
|
||||
* @return the script flag if one is found else none
|
||||
*/
|
||||
/** Takes in a string and tries to match it with a [[ScriptFlag]]. */
|
||||
def fromString(str : String) : Option[ScriptFlag] = {
|
||||
flags.find(_.name == str)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given list into script flags
|
||||
* the strings that do not match a script flag are discarded
|
||||
* @param list the list of strings to parse into script flags
|
||||
* @return the sequence of script flags
|
||||
*/
|
||||
/** Parses the given list into[[ScriptFlag]]s
|
||||
* the strings that do not match a [[ScriptFlag]] are discarded. */
|
||||
def fromList(list : Seq[String]) : Seq[ScriptFlag] = {
|
||||
list.flatMap(fromString(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of script flags that is separated by commas
|
||||
* @param str a string in the format flag1,flag2,...,flagN
|
||||
* @return the sequence of script flags
|
||||
*/
|
||||
/** Parses a list of [[ScriptFlag]]s that is separated by commas. */
|
||||
def fromList(str : String) : Seq[ScriptFlag] = {
|
||||
fromList(str.split(","))
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty script flag
|
||||
* @return
|
||||
*/
|
||||
/** Empty script flag. */
|
||||
def empty : Seq[ScriptFlag] = Nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.bitcoins.core.script.flag
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
|
||||
/**
|
||||
* Created by chris on 3/23/16.
|
||||
* This represents all of the script flags found inside of
|
||||
|
@ -9,158 +7,126 @@ import org.bitcoins.core.util.BitcoinSUtil
|
|||
* these flags indicate how to evaluate a certain script
|
||||
*/
|
||||
sealed trait ScriptFlag {
|
||||
/**
|
||||
* The flag's representation represented as an integer
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** The flag's representation represented as an integer. */
|
||||
def flag : Int
|
||||
|
||||
/**
|
||||
* The name of the flag as found in bitcoin core
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/** The name of the flag as found in bitcoin core. */
|
||||
def name : String
|
||||
}
|
||||
|
||||
|
||||
case object ScriptVerifyNone extends ScriptFlag {
|
||||
override def flag = 0
|
||||
override def name = "NONE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate P2SH subscripts (softfork safe, BIP16).
|
||||
*/
|
||||
/** Evaluate P2SH subscripts (softfork safe, BIP16). */
|
||||
case object ScriptVerifyP2SH extends ScriptFlag {
|
||||
override def flag = 1
|
||||
override def name = "P2SH"
|
||||
}
|
||||
|
||||
/**
|
||||
* Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||
/** Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||
* Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
||||
* (softfork safe, but not used or intended as a consensus rule).
|
||||
*/
|
||||
* (softfork safe, but not used or intended as a consensus rule). */
|
||||
case object ScriptVerifyStrictEnc extends ScriptFlag {
|
||||
override def flag = 1 << 1
|
||||
override def name = "STRICTENC"
|
||||
}
|
||||
|
||||
/**
|
||||
* Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
|
||||
*/
|
||||
/** Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1). */
|
||||
case object ScriptVerifyDerSig extends ScriptFlag {
|
||||
override def flag = 1 << 2
|
||||
override def name = "DERSIG"
|
||||
}
|
||||
|
||||
/**
|
||||
* Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||
* (softfork safe, BIP62 rule 5).
|
||||
*/
|
||||
/** Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||
* (softfork safe, BIP62 rule 5). */
|
||||
case object ScriptVerifyLowS extends ScriptFlag {
|
||||
override def flag = 1 << 3
|
||||
override def name = "LOW_S"
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7)
|
||||
*/
|
||||
/** Verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7). */
|
||||
case object ScriptVerifyNullDummy extends ScriptFlag {
|
||||
override def flag = 1 << 4
|
||||
override def name = "NULLDUMMY"
|
||||
}
|
||||
|
||||
/**
|
||||
* Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
|
||||
*/
|
||||
/** Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2). */
|
||||
case object ScriptVerifySigPushOnly extends ScriptFlag {
|
||||
override def flag = 1 << 5
|
||||
override def name = "SIGPUSHONLY"
|
||||
}
|
||||
|
||||
/**
|
||||
* Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
||||
/** Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
||||
* pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
||||
* any other push causes the script to fail (BIP62 rule 3).
|
||||
* In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
||||
* (softfork safe)
|
||||
*/
|
||||
* (softfork safe). */
|
||||
case object ScriptVerifyMinimalData extends ScriptFlag {
|
||||
override def flag = 1 << 6
|
||||
override def name = "MINIMALDATA"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||
/** Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||
* Provided so that nodes can avoid accepting or mining transactions
|
||||
* containing executed NOP's whose meaning may change after a soft-fork,
|
||||
* thus rendering the script invalid; with this flag set executing
|
||||
* discouraged NOPs fails the script. This verification flag will never be
|
||||
* a mandatory flag applied to scripts in a block. NOPs that are not
|
||||
* executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
||||
*/
|
||||
* executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected. */
|
||||
case object ScriptVerifyDiscourageUpgradableNOPs extends ScriptFlag {
|
||||
override def flag = 1 << 7
|
||||
override def name = "DISCOURAGE_UPGRADABLE_NOPS"
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that only a single stack element remains after evaluation. This changes the success criterion from
|
||||
/** Require that only a single stack element remains after evaluation. This changes the success criterion from
|
||||
* "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
||||
* "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
|
||||
* (softfork safe, BIP62 rule 6)
|
||||
* Note: CLEANSTACK should never be used without P2SH.
|
||||
*/
|
||||
* Note: CLEANSTACK should never be used without P2SH. */
|
||||
case object ScriptVerifyCleanStack extends ScriptFlag {
|
||||
override def flag = 1 << 8
|
||||
override def name = "CLEANSTACK"
|
||||
}
|
||||
|
||||
/**
|
||||
* See BIP65 for details
|
||||
*/
|
||||
/** See [[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki]] for details.*/
|
||||
case object ScriptVerifyCheckLocktimeVerify extends ScriptFlag {
|
||||
override def flag = 1 << 9
|
||||
override def name = "CHECKLOCKTIMEVERIFY"
|
||||
}
|
||||
|
||||
/**
|
||||
* See BIP112 for details
|
||||
*/
|
||||
/** See https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki for details.*/
|
||||
case object ScriptVerifyCheckSequenceVerify extends ScriptFlag {
|
||||
override def flag = 1 << 10
|
||||
override def name = "CHECKSEQUENCEVERIFY"
|
||||
}
|
||||
|
||||
// Support segregated witness
|
||||
/** Support segregated witness. */
|
||||
case object ScriptVerifyWitness extends ScriptFlag {
|
||||
override def flag = 1 << 11
|
||||
override def name = "WITNESS"
|
||||
}
|
||||
|
||||
// Making v1-v16 witness program non-standard
|
||||
/** Making v1-v16 witness program non-standard. */
|
||||
case object ScriptVerifyDiscourageUpgradableWitnessProgram extends ScriptFlag {
|
||||
override def flag = 1 << 12
|
||||
override def name = "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"
|
||||
}
|
||||
|
||||
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
||||
/** Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector. */
|
||||
case object ScriptVerifyMinimalIf extends ScriptFlag {
|
||||
override def flag = 1 << 13
|
||||
override def name = "MINIMALIF"
|
||||
}
|
||||
|
||||
// Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
|
||||
/** Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed. */
|
||||
case object ScriptVerifyNullFail extends ScriptFlag {
|
||||
override def flag = 1 << 14
|
||||
override def name = "NULLFAIL"
|
||||
}
|
||||
|
||||
// Public keys in segregated witness scripts must be compressed
|
||||
/** Public keys in segregated witness scripts must be compressed. */
|
||||
case object ScriptVerifyWitnessPubKeyType extends ScriptFlag {
|
||||
override def flag = 1 << 15
|
||||
override def name = "WITNESS_PUBKEYTYPE"
|
||||
|
|
|
@ -30,7 +30,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter with SpliceInterpreter
|
||||
with LockTimeInterpreter with BitcoinSLogger {
|
||||
|
||||
|
||||
/**
|
||||
* Currently bitcoin core limits the maximum number of non-push operations per script
|
||||
* to 201
|
||||
|
@ -82,21 +81,18 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
logger.debug("Executed Script Program: " + executedProgram)
|
||||
if (executedProgram.error.isDefined) executedProgram.error.get
|
||||
else if (hasUnexpectedWitness(program)) {
|
||||
//note: the 'program' value we pass above is intetional, we need to check the original program
|
||||
//note: the 'program' value we pass above is intentional, we need to check the original program
|
||||
//as the 'executedProgram' may have had the scriptPubKey value changed to the rebuilt ScriptPubKey of the witness program
|
||||
ScriptErrorWitnessUnexpected
|
||||
}
|
||||
else if (executedProgram.stackTopIsTrue && flags.contains(ScriptVerifyCleanStack)) {
|
||||
//require that the stack after execution has exactly one element on it
|
||||
executedProgram.stack.size == 1 match {
|
||||
case true => ScriptOk
|
||||
case false => ScriptErrorCleanStack
|
||||
}
|
||||
if (executedProgram.stack.size == 1) ScriptOk
|
||||
else ScriptErrorCleanStack
|
||||
} else if (executedProgram.stackTopIsTrue) ScriptOk
|
||||
else ScriptErrorEvalFalse
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* P2SH scripts are unique in their evaluation, first the scriptSignature must be added to the stack, next the
|
||||
* p2sh scriptPubKey must be run to make sure the serialized redeem script hashes to the value found in the p2sh
|
||||
|
@ -191,7 +187,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
if (scriptSig.asm.nonEmpty && !w.scriptPubKey.isInstanceOf[P2SHScriptPubKey]) ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessMalleated)
|
||||
else verifyWitnessProgram(witnessVersion, witness, witnessProgram, w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Verifies a segregated witness program by running it through the interpreter
|
||||
|
@ -435,17 +430,10 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the validity of a transaction in accordance to bitcoin core's CheckTransaction function
|
||||
* https://github.com/bitcoin/bitcoin/blob/f7a21dae5dbf71d5bc00485215e84e6f2b309d0a/src/main.cpp#L939
|
||||
*
|
||||
* @param transaction
|
||||
* @return
|
||||
*/
|
||||
/** Checks the validity of a transaction in accordance to bitcoin core's CheckTransaction function
|
||||
* https://github.com/bitcoin/bitcoin/blob/f7a21dae5dbf71d5bc00485215e84e6f2b309d0a/src/main.cpp#L939. */
|
||||
def checkTransaction(transaction : Transaction) : Boolean = {
|
||||
val inputOutputsNotZero = !(transaction.inputs.isEmpty || transaction.outputs.isEmpty)
|
||||
//TODO: replace 1000000 with a value that represents the max block size
|
||||
val txNotLargerThanBlock = transaction.bytes.size < Consensus.maxBlockSize
|
||||
val outputsSpendValidAmountsOfMoney = !transaction.outputs.exists(o =>
|
||||
o.value < CurrencyUnits.zero || o.value > Consensus.maxMoney)
|
||||
|
@ -467,7 +455,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
allOutputsValidMoneyRange && noDuplicateInputs && isValidScriptSigForCoinbaseTx
|
||||
}
|
||||
|
||||
|
||||
/** Determines if the given currency unit is within the valid range for the system */
|
||||
def validMoneyRange(currencyUnit : CurrencyUnit) : Boolean = {
|
||||
currencyUnit >= CurrencyUnits.zero && currencyUnit <= Consensus.maxMoney
|
||||
|
@ -481,8 +468,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
|
||||
/** Checks if the transaction contained a witness that we did not use
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1515-L1523]]
|
||||
* Return true if witness was NOT used, return false if witness was used
|
||||
* */
|
||||
* Return true if witness was NOT used, return false if witness was used. */
|
||||
private def hasUnexpectedWitness(program: ScriptProgram): Boolean = {
|
||||
val txSigComponent = program.txSignatureComponent
|
||||
txSigComponent match {
|
||||
|
|
|
@ -28,7 +28,7 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
@tailrec
|
||||
final def opCheckLockTimeVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKLOCKTIMEVERIFY,
|
||||
require(program.script.headOption.contains(OP_CHECKLOCKTIMEVERIFY),
|
||||
"Script top must be OP_CHECKLOCKTIMEVERIFY")
|
||||
val input = program.txSignatureComponent.transaction.inputs(program.txSignatureComponent.inputIndex.toInt)
|
||||
val transaction = program.txSignatureComponent.transaction
|
||||
|
@ -184,14 +184,8 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics this function inside of bitcoin core for checking the locktime of a transaction
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1160
|
||||
*
|
||||
* @param program
|
||||
* @param locktime
|
||||
* @return
|
||||
*/
|
||||
/** Mimics this function inside of bitcoin core for checking the locktime of a transaction
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1160. */
|
||||
private def checkLockTime(program : ScriptProgram, locktime : ScriptNumber) : Boolean = {
|
||||
// There are two kinds of nLockTime: lock-by-blockheight
|
||||
// and lock-by-blocktime, distinguished by whether
|
||||
|
@ -226,11 +220,7 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
} else true
|
||||
}
|
||||
|
||||
/**
|
||||
* The script number on the stack has the disable flag (1 << 31) unset
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
/** The [[ScriptNumber]] on the stack has the disable flag (1 << 31) unset. */
|
||||
def isLockTimeBitOff(s : ScriptNumber) : Boolean = (s.underlying & TransactionConstants.locktimeDisabledFlag.underlying) == 0
|
||||
|
||||
def isLockTimeBitOff(num : Int64) : Boolean = isLockTimeBitOff(ScriptNumber(num.hex))
|
||||
|
|
|
@ -12,32 +12,19 @@ import org.bitcoins.core.util.BitcoinSLogger
|
|||
*/
|
||||
trait SpliceInterpreter extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* Pushes the string length of the top element of the stack (without popping it).
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opSize(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SIZE, "Script top must be OP_SIZE")
|
||||
|
||||
program.stack.size > 0 match {
|
||||
case true =>
|
||||
if (program.stack.head == OP_0) {
|
||||
ScriptProgram(program, OP_0 :: program.stack, program.script.tail)
|
||||
} else {
|
||||
val scriptNumber = program.stack.head match {
|
||||
case ScriptNumber.zero => ScriptNumber.zero
|
||||
case x : ScriptToken => ScriptNumber(x.bytes.size)
|
||||
}
|
||||
ScriptProgram(program, scriptNumber :: program.stack, program.script.tail)
|
||||
}
|
||||
case false =>
|
||||
logger.error("Must have at least 1 element on the stack for OP_SIZE")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
/** Pushes the string length of the top element of the stack (without popping it). */
|
||||
def opSize(program: ScriptProgram): ScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_SIZE), "Script top must be OP_SIZE")
|
||||
if (program.stack.nonEmpty) {
|
||||
if (program.stack.head == OP_0) return ScriptProgram(program, OP_0 :: program.stack, program.script.tail)
|
||||
val scriptNumber = program.stack.head match {
|
||||
case ScriptNumber.zero => ScriptNumber.zero
|
||||
case x: ScriptToken => ScriptNumber(x.bytes.size)
|
||||
}
|
||||
ScriptProgram(program, scriptNumber :: program.stack, program.script.tail)
|
||||
} else {
|
||||
logger.error("Must have at least 1 element on the stack for OP_SIZE")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package org.bitcoins.core.script.stack
|
||||
|
||||
|
||||
import org.bitcoins.core.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.core.script.ScriptProgram
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
|
@ -16,15 +16,10 @@ import scala.util.{Failure, Success, Try}
|
|||
*/
|
||||
trait StackInterpreter extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* Duplicates the element on top of the stack
|
||||
* expects the first element in script to be the OP_DUP operation
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Duplicates the element on top of the stack
|
||||
* expects the first element in script to be the OP_DUP operation. */
|
||||
def opDup(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_DUP, "Top of the script stack must be OP_DUP")
|
||||
require(program.script.headOption.contains(OP_DUP), "Top of the script stack must be OP_DUP")
|
||||
program.stack match {
|
||||
case h :: t => ScriptProgram(program, h :: program.stack, program.script.tail)
|
||||
case Nil =>
|
||||
|
@ -33,101 +28,65 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the top stack value is not 0, duplicate it.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** If the top stack value is not 0, duplicate it. */
|
||||
def opIfDup(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_IFDUP, "Top of the script stack must be OP_DUP")
|
||||
program.stack.headOption.isDefined match {
|
||||
case true if (program.stack.head == ScriptNumber.zero) =>
|
||||
ScriptProgram(program,program.stack,program.script.tail)
|
||||
case true => ScriptProgram(program, program.stack.head :: program.stack,
|
||||
program.script.tail)
|
||||
case false =>
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
require(program.script.headOption.contains(OP_IFDUP), "Top of the script stack must be OP_DUP")
|
||||
if (program.stack.nonEmpty) {
|
||||
if (program.stack.head == ScriptNumber.zero) return ScriptProgram(program, program.stack, program.script.tail)
|
||||
ScriptProgram(program, program.stack.head :: program.stack, program.script.tail)
|
||||
} else {
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the number of stack items onto the stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Puts the number of stack items onto the stack. */
|
||||
def opDepth(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_DEPTH, "Top of script stack must be OP_DEPTH")
|
||||
require(program.script.size >= 1, "OP_DEPTH requires at least two elements on the script stack")
|
||||
require(program.script.headOption.contains(OP_DEPTH), "Top of script stack must be OP_DEPTH")
|
||||
val stackSize = program.stack.size
|
||||
|
||||
val numberToPush : ScriptNumber = ScriptNumber(stackSize)
|
||||
ScriptProgram(program, numberToPush :: program.stack, program.script.tail)
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the input onto the top of the alt stack. Removes it from the main stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Puts the input onto the top of the alt stack. Removes it from the main stack. */
|
||||
def opToAltStack(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_TOALTSTACK, "Top of script stack must be OP_TOALTSTACK")
|
||||
program.stack.size > 0 match {
|
||||
case true => ScriptProgram(program, program.stack.tail,
|
||||
require(program.script.headOption.contains(OP_TOALTSTACK), "Top of script stack must be OP_TOALTSTACK")
|
||||
if (program.stack.nonEmpty) {
|
||||
ScriptProgram(program, program.stack.tail,
|
||||
program.script.tail, program.stack.head :: program.altStack, ScriptProgram.AltStack)
|
||||
case false =>
|
||||
logger.error("OP_TOALTSTACK requires an element to be on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
logger.error("OP_TOALTSTACK requires an element to be on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the input onto the top of the main stack. Removes it from the alt stack.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Puts the input onto the top of the main stack. Removes it from the alt stack. */
|
||||
def opFromAltStack(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_FROMALTSTACK, "Top of script stack must be OP_FROMALTSTACK")
|
||||
program.altStack.size > 0 match {
|
||||
case true => ScriptProgram(program, program.altStack.head :: program.stack,
|
||||
require(program.script.headOption.contains(OP_FROMALTSTACK), "Top of script stack must be OP_FROMALTSTACK")
|
||||
if (program.altStack.nonEmpty) {
|
||||
ScriptProgram(program, program.altStack.head :: program.stack,
|
||||
program.script.tail, program.altStack.tail, ScriptProgram.AltStack)
|
||||
case false =>
|
||||
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
|
||||
ScriptProgram(program,ScriptErrorInvalidAltStackOperation)
|
||||
} else {
|
||||
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
|
||||
ScriptProgram(program,ScriptErrorInvalidAltStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the top stack item.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Removes the top stack item. */
|
||||
def opDrop(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_DROP, "Top of script stack must be OP_DROP")
|
||||
program.stack.size > 0 match {
|
||||
case true => ScriptProgram(program, program.stack.tail,program.script.tail)
|
||||
case false =>
|
||||
logger.error("Stack must have at least one item on it for OP_DROP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
require(program.script.headOption.contains(OP_DROP), "Top of script stack must be OP_DROP")
|
||||
if (program.stack.nonEmpty) {
|
||||
ScriptProgram(program, program.stack.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("Stack must have at least one item on it for OP_DROP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the second-to-top stack item
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Removes the second-to-top stack item. */
|
||||
def opNip(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NIP, "Top of script stack must be OP_NIP")
|
||||
require(program.script.headOption.contains(OP_NIP), "Top of script stack must be OP_NIP")
|
||||
program.stack match {
|
||||
case h :: _ :: t => ScriptProgram(program, h :: t, program.script.tail)
|
||||
case h :: t =>
|
||||
|
@ -140,14 +99,9 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies the second-to-top stack item to the top.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Copies the second-to-top stack item to the top. */
|
||||
def opOver(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_OVER, "Top of script stack must be OP_OVER")
|
||||
require(program.script.headOption.contains(OP_OVER), "Top of script stack must be OP_OVER")
|
||||
program.stack match {
|
||||
case _ :: h1 :: _ => ScriptProgram(program, h1 :: program.stack, program.script.tail)
|
||||
case h :: t => logger.error("Stack must have at least two items on it for OP_OVER")
|
||||
|
@ -158,62 +112,44 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The item n back in the stack is copied to the top.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The item n back in the stack is copied to the top. */
|
||||
def opPick(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_PICK, "Top of script stack must be OP_PICK")
|
||||
require(program.stack.size > 0,"Stack must have at least two items on it for OP_PICK")
|
||||
require(program.script.headOption.contains(OP_PICK), "Top of script stack must be OP_PICK")
|
||||
executeOpWithStackTopAsNumberArg(program, { number : ScriptNumber =>
|
||||
logger.info("Script number for OP_PICK: " + number)
|
||||
//check if n is within the bound of the script
|
||||
(number.underlying >= 0 && number.underlying < program.stack.tail.size) match {
|
||||
case true =>
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
ScriptProgram(program, newStackTop :: program.stack.tail, program.script.tail)
|
||||
case false =>
|
||||
logger.error("The index for OP_PICK would have caused an index out of bounds exception")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
if (program.stack.size < 2) ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
else if (number.underlying >= 0 && number.underlying < program.stack.tail.size) {
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
ScriptProgram(program, newStackTop :: program.stack.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("The index for OP_PICK would have caused an index out of bounds exception")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The item n back in the stack is moved to the top
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The item n back in the stack is moved to the top. */
|
||||
def opRoll(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ROLL, "Top of script stack must be OP_ROLL")
|
||||
require(program.stack.size > 0,"Stack must have at least one items on it for OP_ROLL")
|
||||
require(program.script.headOption.contains(OP_ROLL), "Top of script stack must be OP_ROLL")
|
||||
executeOpWithStackTopAsNumberArg(program, (number : ScriptNumber) =>
|
||||
(number.underlying >= 0 && number.underlying < program.stack.tail.size) match {
|
||||
case true =>
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
//removes the old instance of the stack top, appends the new index to the head
|
||||
val newStack = newStackTop :: program.stack.tail.diff(List(newStackTop))
|
||||
ScriptProgram(program,newStack,program.script.tail)
|
||||
case false =>
|
||||
logger.error("The index for OP_ROLL would have caused an index out of bounds exception")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
if (program.stack.size < 2) ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
else if (number.underlying >= 0 && number.underlying < program.stack.tail.size) {
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
//removes the old instance of the stack top, appends the new index to the head
|
||||
val newStack = newStackTop :: program.stack.tail.diff(List(newStackTop))
|
||||
ScriptProgram(program, newStack, program.script.tail)
|
||||
} else {
|
||||
logger.error("The index for OP_ROLL would have caused an index out of bounds exception")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The top three items on the stack are rotated to the left.
|
||||
* x1 x2 x3 -> x2 x3 x1
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The top three items on the stack are rotated to the left.
|
||||
* Ex: x1 x2 x3 -> x2 x3 x1 */
|
||||
def opRot(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_ROT, "Top of script stack must be OP_ROT")
|
||||
|
||||
require(program.script.headOption.contains(OP_ROT), "Top of script stack must be OP_ROT")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: t =>
|
||||
val newStack = h2 :: h :: h1 :: t
|
||||
|
@ -222,18 +158,12 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("Stack must have at least 3 items on it for OP_ROT")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The fifth and sixth items back are moved to the top of the stack.
|
||||
* x1 x2 x3 x4 x5 x6 -> x3 x4 x5 x6 x1 x2
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The fifth and sixth items back are moved to the top of the stack.
|
||||
* Ex. x1 x2 x3 x4 x5 x6 -> x3 x4 x5 x6 x1 x2 */
|
||||
def op2Rot(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2ROT, "Top of script stack must be OP_2ROT")
|
||||
require(program.script.headOption.contains(OP_2ROT), "Top of script stack must be OP_2ROT")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: h3 :: h4 :: h5 :: t =>
|
||||
val newStack = h4 :: h5 :: h :: h1 :: h2 :: h3 :: t
|
||||
|
@ -242,58 +172,36 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("OP_2ROT requires 6 elements on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the top two stack items.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Removes the top two stack items. */
|
||||
def op2Drop(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2DROP, "Top of script stack must be OP_2DROP")
|
||||
program.stack.size > 1 match {
|
||||
case true =>
|
||||
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
|
||||
case false =>
|
||||
logger.error("OP_2DROP requires two elements to be on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
require(program.script.headOption.contains(OP_2DROP), "Top of script stack must be OP_2DROP")
|
||||
if (program.stack.size > 1) {
|
||||
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("OP_2DROP requires two elements to be on the stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The top two items on the stack are swapped.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The top two items on the stack are swapped. */
|
||||
def opSwap(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_SWAP, "Top of script stack must be OP_SWAP")
|
||||
program.stack.size > 1 match {
|
||||
case true =>
|
||||
val newStack = program.stack.tail.head :: program.stack.head :: program.stack.tail.tail
|
||||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case false =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_SWAP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
require(program.script.headOption.contains(OP_SWAP), "Top of script stack must be OP_SWAP")
|
||||
if (program.stack.size > 1) {
|
||||
val newStack = program.stack.tail.head :: program.stack.head :: program.stack.tail.tail
|
||||
ScriptProgram(program, newStack, program.script.tail)
|
||||
} else {
|
||||
logger.error("Stack must have at least 2 items on it for OP_SWAP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The item at the top of the stack is copied and inserted before the second-to-top item.
|
||||
* x1 x2 -> x1 x2 x1
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** The item at the top of the stack is copied and inserted before the second-to-top item. */
|
||||
def opTuck(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_TUCK, "Top of script stack must be OP_TUCK")
|
||||
|
||||
require(program.script.headOption.contains(OP_TUCK), "Top of script stack must be OP_TUCK")
|
||||
program.stack match {
|
||||
case h :: h1 :: t =>
|
||||
val newStack = h :: h1 :: h:: t
|
||||
|
@ -302,19 +210,12 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("Stack must have at least 2 items on it for OP_TUCK")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Duplicates the top two stack items.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Duplicates the top two stack items. */
|
||||
def op2Dup(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2DUP, "Top of script stack must be OP_2DUP")
|
||||
|
||||
require(program.script.headOption.contains(OP_2DUP), "Top of script stack must be OP_2DUP")
|
||||
program.stack match {
|
||||
case h :: h1 :: t =>
|
||||
val newStack = h :: h1 :: h :: h1 :: t
|
||||
|
@ -323,17 +224,11 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("Stack must have at least 2 items on it for OP_2DUP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates the top three stack items.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Duplicates the top three stack items. */
|
||||
def op3Dup(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_3DUP, "Top of script stack must be OP_3DUP")
|
||||
require(program.script.headOption.contains(OP_3DUP), "Top of script stack must be OP_3DUP")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: t =>
|
||||
val newStack = h :: h1 :: h2 :: h :: h1 :: h2 :: t
|
||||
|
@ -342,20 +237,11 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("Stack must have at least 3 items on it for OP_3DUP")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies the pair of items two spaces back in the stack to the front.
|
||||
* x1 x2 x3 x4 -> x1 x2 x3 x4 x1 x2
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Copies the pair of items two spaces back in the stack to the front. */
|
||||
def op2Over(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2OVER, "Top of script stack must be OP_2OVER")
|
||||
|
||||
require(program.script.headOption.contains(OP_2OVER), "Top of script stack must be OP_2OVER")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: h3 :: t =>
|
||||
val newStack = h2 :: h3 :: h :: h1 :: h2 :: h3 :: t
|
||||
|
@ -364,18 +250,11 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
logger.error("Stack must have at least 4 items on it for OP_2OVER")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the top two pairs of items.
|
||||
*
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Swaps the top two pairs of items. */
|
||||
def op2Swap(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2SWAP, "Top of script stack must be OP_2SWAP")
|
||||
|
||||
require(program.script.headOption.contains(OP_2SWAP), "Top of script stack must be OP_2SWAP")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: h3 :: t =>
|
||||
val newStack = h2 :: h3 :: h :: h1 :: t
|
||||
|
@ -388,7 +267,6 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
|
||||
/**
|
||||
* Executes an operation with the stack top inside of the program as the argument
|
||||
*
|
||||
* @param program the program whose stack top is used as an argument for the operation
|
||||
* @param op the operation that is executed with the script number on the top of the stack
|
||||
* @return the program with the result of the op pushed onto to the top of the stack
|
||||
|
|
|
@ -33,7 +33,7 @@ trait RawScriptWitnessParser extends RawBitcoinSerializer[ScriptWitness] {
|
|||
loop(newRemainingBytes, stackElement +: accum, remainingStackItems - UInt64.one)
|
||||
}
|
||||
}
|
||||
//note there is no 'reversing' the accum, in bitcoin-s we assume the stop of the stack is the 'head' element in the sequence
|
||||
//note there is no 'reversing' the accum, in bitcoin-s we assume the top of the stack is the 'head' element in the sequence
|
||||
val stack = loop(stackBytes,Nil,stackSize.num)
|
||||
val witness = ScriptWitness(stack)
|
||||
witness
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package org.bitcoins.core.util
|
||||
|
||||
import org.bitcoins.core.config.{MainNet, NetworkParameters, TestNet3}
|
||||
import org.bitcoins.core.crypto.{ECPrivateKey, Sha256Hash160Digest}
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, P2PKHAddress, P2SHAddress, Address}
|
||||
import org.bitcoins.core.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.protocol.blockchain._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -17,12 +15,7 @@ trait Base58 extends BitcoinSLogger {
|
|||
val base58Characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
val base58Pairs = base58Characters.zipWithIndex.toMap
|
||||
|
||||
/**
|
||||
* Verifies a given base58 string against its checksum (last 4 decoded bytes)
|
||||
*
|
||||
* @param input base58 string
|
||||
* @return
|
||||
*/
|
||||
/** Verifies a given [[Base58Type]] string against its checksum (last 4 decoded bytes). */
|
||||
def decodeCheck(input: String) : Try[Seq[Byte]] = {
|
||||
val decoded : Seq[Byte] = decode(input)
|
||||
if (decoded.length < 4) Failure(new IllegalArgumentException("Invalid input"))
|
||||
|
@ -36,13 +29,7 @@ trait Base58 extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in sequence of bytes and returns base58 bitcoin string
|
||||
*
|
||||
* @param bytes sequence of bytes to be encoded into base58
|
||||
* @return
|
||||
*/
|
||||
//TODO: Create Base58 Type
|
||||
/** Encodes a sequence of bytes to a [[Base58Type]] string. */
|
||||
def encode(bytes : Seq[Byte]) : String = {
|
||||
val ones : String = bytes.takeWhile(_ == 0).map(_ => '1').mkString
|
||||
@tailrec
|
||||
|
@ -63,20 +50,17 @@ trait Base58 extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/** Encodes a hex string to its [[Base58Type]] representation. */
|
||||
def encode(hex : String) : String = {
|
||||
val bytes = BitcoinSUtil.decodeHex(hex)
|
||||
encode(bytes)
|
||||
}
|
||||
|
||||
/** Encodes a [[Byte]] to its [[Base58Type]] representation. */
|
||||
def encode(byte : Byte) : String = encode(Seq(byte))
|
||||
|
||||
/**
|
||||
* Takes in base58 string and returns sequence of bytes
|
||||
* https://github.com/ACINQ/bitcoin-lib/blob/master/src/main/scala/fr/acinq/bitcoin/Base58.scala
|
||||
*
|
||||
* @param input Base58 string to be decoded into a sequence of bytes
|
||||
* @return
|
||||
*/
|
||||
/** Takes in [[Base58Type]] string and returns sequence of [[Byte]]s
|
||||
* https://github.com/ACINQ/bitcoin-lib/blob/master/src/main/scala/fr/acinq/bitcoin/Base58.scala. */
|
||||
def decode(input: String) : Seq[Byte] = {
|
||||
val zeroes = input.takeWhile(_ == '1').map(_ => 0:Byte).toArray
|
||||
val trim = input.dropWhile(_ == '1').toList
|
||||
|
@ -85,37 +69,22 @@ trait Base58 extends BitcoinSLogger {
|
|||
if (trim.isEmpty) zeroes else zeroes ++ decoded.toByteArray.dropWhile(_ == 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a string is a valid Base58-encoded string.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
/** Determines if a string is a valid [[Base58Type]] string. */
|
||||
def isValid(base58 : String) : Boolean = validityChecks(base58) match {
|
||||
case Success(bool) => bool
|
||||
case Failure(exception) => false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a private key that begins with a symbol corresponding that private key to a compressed public key ('K', 'L', 'c').
|
||||
* In a Base58-encoded private key corresponding to a compressed public key, the 5th-to-last byte should be 0x01.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
/** Checks a private key that begins with a symbol corresponding that private key to a compressed public key ('K', 'L', 'c').
|
||||
* In a Base58-encoded private key corresponding to a compressed public key, the 5th-to-last byte should be 0x01. */
|
||||
private def checkCompressedPubKeyValidity(base58 : String) : Boolean = {
|
||||
val decoded = Base58.decode(base58)
|
||||
val compressedByte = decoded(decoded.length - 5)
|
||||
compressedByte == 0x01.toByte
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the string begins with an Address prefix byte/character.
|
||||
* ('1', '3', 'm', 'n', '2')
|
||||
*
|
||||
* @param byte first byte in the sequence
|
||||
* @return
|
||||
*/
|
||||
/** Checks if the string begins with an Address prefix byte/character.
|
||||
* ('1', '3', 'm', 'n', '2') */
|
||||
private def isValidAddressPreFixByte(byte : Byte) : Boolean = {
|
||||
val validAddressPreFixBytes: Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(PubKeyAddress) ++ MainNetChainParams.base58Prefixes(ScriptAddress) ++
|
||||
|
@ -123,28 +92,18 @@ trait Base58 extends BitcoinSLogger {
|
|||
validAddressPreFixBytes.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the string begins with a private key prefix byte/character.
|
||||
* ('5', '9', 'c')
|
||||
*
|
||||
* @param byte first byte in the sequence
|
||||
* @return
|
||||
*/
|
||||
/** Checks if the string begins with a private key prefix byte/character.
|
||||
* ('5', '9', 'c') */
|
||||
private def isValidSecretKeyPreFixByte(byte : Byte) : Boolean = {
|
||||
val validSecretKeyPreFixBytes : Seq[Byte] =
|
||||
MainNetChainParams.base58Prefixes(SecretKey) ++ TestNetChainParams.base58Prefixes(SecretKey)
|
||||
validSecretKeyPreFixBytes.contains(byte)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of a Base58 encoded string. A Base58 encoded string must not contain ('0', 'O', 'l', 'I').
|
||||
/** Checks the validity of a [[Base58Type]] string. A [[Base58Type]] string must not contain ('0', 'O', 'l', 'I').
|
||||
* If the string is an address: it must have a valid address prefix byte and must be between 26-35 characters in length.
|
||||
* If the string is a private key: it must have a valid private key prefix byte and must have a byte size of 32.
|
||||
* If the string is a private key corresponding to a compressed public key, the 5th-to-last byte must be 0x01.
|
||||
*
|
||||
* @param base58
|
||||
* @return
|
||||
*/
|
||||
* If the string is a private key corresponding to a compressed public key, the 5th-to-last byte must be 0x01. */
|
||||
private def validityChecks(base58: String) : Try[Boolean] = Try {
|
||||
val decoded = decode(base58)
|
||||
val firstByte = decoded.head
|
||||
|
|
|
@ -23,13 +23,8 @@ trait BinaryTree[+T] {
|
|||
case Empty => None
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a sequence with only the leaf values
|
||||
* evaluates as depth first from left to right
|
||||
* @return
|
||||
*/
|
||||
/** Creates a sequence with only the leaf values
|
||||
* evaluates as depth first from left to right. */
|
||||
def toSeqLeafValues : Seq[T] = {
|
||||
@tailrec
|
||||
def loop(tree : BinaryTree[T],accum : List[T], remainder : List[BinaryTree[T]]) : Seq[T] = tree match {
|
||||
|
@ -40,15 +35,7 @@ trait BinaryTree[+T] {
|
|||
loop(this,List(),List()).reverse
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A function to find the first occurrence of a predicate inside a binary tree
|
||||
* @param t
|
||||
* @param tree the default tree is the current instance of the binary tree that the function is being called on
|
||||
* @param f the default predicate is equality i.e. if 1 == 1
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** A function to find the first occurrence of a predicate inside a [[BinaryTree]]. */
|
||||
def findFirstDFS[T](t : T)(f : T => Boolean = (x : T) => x == t)(implicit tree : BinaryTree[T] = this) : Option[BinaryTree[T]] = {
|
||||
@tailrec
|
||||
def loop(subTree : BinaryTree[T], remainder : List[BinaryTree[T]]) : Option[BinaryTree[T]] = {
|
||||
|
@ -63,42 +50,22 @@ trait BinaryTree[+T] {
|
|||
loop(tree,List())
|
||||
}
|
||||
|
||||
/** Checks if the [[BinaryTree]] contains a certain element. */
|
||||
def contains[T](t : T)(f : T => Boolean = (x : T) => x == t)(implicit tree : BinaryTree[T] = this) : Boolean = findFirstDFS(t)(f)(tree).isDefined
|
||||
|
||||
/**
|
||||
* Checks if the binary tree contains a certain element
|
||||
* @param t
|
||||
* @param tree
|
||||
* @param f
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
def contains[T](t : T)(f : T => Boolean = (x : T) => x == t)(implicit tree : BinaryTree[T] = this) = findFirstDFS(t)(f)(tree).isDefined
|
||||
def count[T](t : T)(implicit tree : BinaryTree[T] = this) : Int = toSeq.count(_ == t)
|
||||
|
||||
|
||||
def count[T](t : T)(implicit tree : BinaryTree[T] = this) = toSeq.count(_ == t)
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a element into one of the two branches in a binary tree
|
||||
* if it cannot insert it because the branches are not empty
|
||||
* it throws a runtime exception
|
||||
* @param tree
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** Inserts an element into one of the two branches in a [[BinaryTree]].
|
||||
* If it cannot insert it because the branches are not empty,
|
||||
* it throws a [[RuntimeException]]. */
|
||||
def insert[T](t : T)(implicit tree : BinaryTree[T] = this) : BinaryTree[T] = {
|
||||
insert(Leaf(t))(tree)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a tree into one of the two branches in a binary tree
|
||||
* if it cannot insert it because the branches are not empty
|
||||
* it throws a runtime exception
|
||||
* @param subTree
|
||||
* @param parentTree
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** Inserts a tree into one of the two branches in a [[BinaryTree]]
|
||||
* If it cannot insert it because the branches are not empty,
|
||||
* it throws a [[RuntimeException]]. */
|
||||
def insert[T](subTree : BinaryTree[T])(implicit parentTree : BinaryTree[T]) : BinaryTree[T] = parentTree match {
|
||||
case n : Node[T] =>
|
||||
if (n.l == Empty) Node[T](n.v,subTree,n.r)
|
||||
|
@ -108,15 +75,7 @@ trait BinaryTree[+T] {
|
|||
case Empty => subTree
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes the subTree from the parentTree
|
||||
* @param subTree - the tree to be removed
|
||||
* @param parentTree - the tree of which the @subTree is being removed from
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** Removes the subTree from the parentTree. */
|
||||
def remove[T](subTree : BinaryTree[T])(parentTree : BinaryTree[T] = this) : BinaryTree[T] = {
|
||||
//TODO: Optimize into a tail recursive function
|
||||
parentTree match {
|
||||
|
@ -128,14 +87,7 @@ trait BinaryTree[+T] {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all instances of the original tree with the replacement tree
|
||||
* @param originalTree - the tree that needs to be replaced
|
||||
* @param replacementTree - the tree that is being put into the original tree
|
||||
* @param parentTree - the tree that is being searched for instances to replace
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
/** Replaces all instances of the original tree with the replacement tree. */
|
||||
def replace[T](originalTree : BinaryTree[T], replacementTree : BinaryTree[T])(implicit parentTree : BinaryTree[T] = this) : BinaryTree[T] = {
|
||||
//TODO: Optimize this into a tail recursive function
|
||||
parentTree match {
|
||||
|
@ -148,7 +100,6 @@ trait BinaryTree[+T] {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
def toSeq : Seq[T] = {
|
||||
@tailrec
|
||||
def loop(tree : BinaryTree[T], accum : List[T], remainder : List[BinaryTree[T]]) : List[T] = tree match {
|
||||
|
|
|
@ -15,12 +15,8 @@ trait BitcoinSUtil {
|
|||
|
||||
def encodeHex(byte : Byte) : String = encodeHex(Seq(byte))
|
||||
|
||||
/**
|
||||
* Encodes a long number to a hex string, pads it with an extra '0' char
|
||||
* if the hex string is an odd amount of characters
|
||||
* @param long
|
||||
* @return
|
||||
*/
|
||||
/** Encodes a long number to a hex string, pads it with an extra '0' char
|
||||
* if the hex string is an odd amount of characters. */
|
||||
def encodeHex(long : Long) : String = {
|
||||
val hex = long.toHexString.length % 2 match {
|
||||
case 1 => "0" + long.toHexString
|
||||
|
@ -39,52 +35,31 @@ trait BitcoinSUtil {
|
|||
|
||||
def encodeHex(bigInt : BigInt) : String = BitcoinSUtil.encodeHex(bigInt.toByteArray)
|
||||
|
||||
/**
|
||||
* Tests if a given string is a hexadecimal string
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
def isHex(str : String) = {
|
||||
/** Tests if a given string is a hexadecimal string. */
|
||||
def isHex(str : String) : Boolean = {
|
||||
//check valid characters & hex strings have to have an even number of chars
|
||||
str.matches("^[0-9a-f]+$") && (str.size % 2 == 0)
|
||||
str.matches("^[0-9a-f]+$") && (str.length % 2 == 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a two character hex string to its byte representation
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Converts a two character hex string to its byte representation. */
|
||||
def hexToByte(hex : String): Byte = {
|
||||
require(hex.size == 2)
|
||||
require(hex.length == 2)
|
||||
BitcoinSUtil.decodeHex(hex).head
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the endianness of the give hex string
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Flips the endianness of the give hex string. */
|
||||
def flipEndianness(hex : String) : String = flipEndianness(decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Flips the endianness of the given sequence of bytes
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Flips the endianness of the given sequence of bytes. */
|
||||
def flipEndianness(bytes : Seq[Byte]) : String = encodeHex(bytes.reverse)
|
||||
|
||||
|
||||
/**
|
||||
* Adds the amount padding bytes needed to fix the size of the hex string
|
||||
/** Adds the amount padding bytes needed to fix the size of the hex string
|
||||
* for instance, ints are required to be 4 bytes. If the number is just 1
|
||||
* it will only take 1 byte. We need to pad the byte with an extra 3 bytes so the result is
|
||||
* 00000001 instead of just 1
|
||||
* @param charactersNeeded
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
* 00000001 instead of just 1. */
|
||||
private def addPadding(charactersNeeded : Int, hex : String) : String = {
|
||||
val paddingNeeded = charactersNeeded - hex.size
|
||||
val paddingNeeded = charactersNeeded - hex.length
|
||||
val padding = for { i <- 0 until paddingNeeded} yield "0"
|
||||
val paddedHex = padding.mkString + hex
|
||||
paddedHex
|
||||
|
|
|
@ -24,7 +24,6 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
hex
|
||||
}
|
||||
|
||||
|
||||
/** Converts a sequence of script tokens to them to their byte values */
|
||||
def asmToBytes(asm : Seq[ScriptToken]) : Seq[Byte] = BitcoinSUtil.decodeHex(asmToHex(asm))
|
||||
|
||||
|
@ -48,10 +47,6 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
case _ : ScriptToken => false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Counts the amount of sigops in a script
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L156-L202
|
||||
|
@ -73,7 +68,6 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
checkSigCount + multiSigCount
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the number of signatures on the stack
|
||||
* This can only be called when an OP_CHECKMULTISIG operation is about to be executed
|
||||
|
@ -108,11 +102,10 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
mRequiredSignatures
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a script contains only script operations
|
||||
* This is equivalent to
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L213
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L213]]
|
||||
*/
|
||||
def isPushOnly(script : Seq[ScriptToken]) : Boolean = {
|
||||
@tailrec
|
||||
|
@ -126,11 +119,10 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
!loop(script, List()).exists(_ == false)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the token being pushed onto the stack is being pushed by the SMALLEST push operation possible
|
||||
* This is equivalent to
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L209
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L209]]
|
||||
* @param pushOp the operation that is pushing the data onto the stack
|
||||
* @param token the token that is being pushed onto the stack by the pushOp
|
||||
* @return
|
||||
|
@ -181,21 +173,14 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
else throw new IllegalArgumentException("ScriptToken is to large for pushops, size: " + scriptTokenSize)
|
||||
}
|
||||
|
||||
|
||||
def calculatePushOp(bytes: Seq[Byte]): Seq[ScriptToken] = calculatePushOp(ScriptConstant(bytes))
|
||||
|
||||
|
||||
/**
|
||||
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* Whenever a [[ScriptConstant]] is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* in the smallest encoding possible
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237 */
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237]] */
|
||||
def isShortestEncoding(constant : ScriptConstant) : Boolean = isShortestEncoding(constant.bytes)
|
||||
|
||||
/**
|
||||
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* in the smallest encoding possible
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
|
||||
*/
|
||||
def isShortestEncoding(bytes : Seq[Byte]) : Boolean = {
|
||||
// If the most-significant-byte - excluding the sign bit - is zero
|
||||
// then we're not minimal. Note how this test also rejects the
|
||||
|
@ -219,29 +204,17 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
*/
|
||||
def isShortestEncoding(hex : String) : Boolean = isShortestEncoding(BitcoinSUtil.decodeHex(hex))
|
||||
/**
|
||||
* Checks the public key encoding according to bitcoin core's function
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
|
||||
* @param key the key whose encoding we are checking
|
||||
* @param program the program whose flags which dictate the rules for the public keys encoding
|
||||
* @return if the key is encoded correctly against the rules give in the flags parameter
|
||||
*/
|
||||
* Checks the [[ECPublicKey]] encoding according to bitcoin core's function:
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202]]. */
|
||||
def checkPubKeyEncoding(key : ECPublicKey, program : ScriptProgram) : Boolean = checkPubKeyEncoding(key,program.flags)
|
||||
|
||||
/**
|
||||
* Checks the public key encoding according to bitcoin core's function
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
|
||||
* @param key the key whose encoding we are checking
|
||||
* @param flags the flags which dictate the rules for the public keys encoding
|
||||
* @return if the key is encoded correctly against the rules givein the flags parameter
|
||||
*/
|
||||
def checkPubKeyEncoding(key : ECPublicKey, flags : Seq[ScriptFlag]) : Boolean = {
|
||||
if (ScriptFlagUtil.requireStrictEncoding(flags) &&
|
||||
!isCompressedOrUncompressedPubKey(key)) false else true
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the key is compressed or uncompressed, false otherwise
|
||||
/** Returns true if the key is compressed or uncompressed, false otherwise
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L66
|
||||
* @param key the public key that is being checked
|
||||
* @return true if the key is compressed/uncompressed otherwise false
|
||||
|
@ -262,7 +235,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
// Non-canonical public key: neither compressed nor uncompressed
|
||||
return false
|
||||
}
|
||||
return true
|
||||
true
|
||||
}
|
||||
|
||||
/** Checks if the given public key is a compressed public key */
|
||||
|
@ -291,7 +264,6 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
} else None
|
||||
}
|
||||
|
||||
|
||||
/** Prepares the script we spending to be serialized for our transaction signature serialization algorithm
|
||||
* We need to check if the scriptSignature has a redeemScript
|
||||
* In that case, we need to pass the redeemScript to the TransactionSignatureChecker
|
||||
|
@ -305,8 +277,6 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
removeSignatureFromScript(signature,scriptWithSigRemoved)
|
||||
}
|
||||
|
||||
|
||||
|
||||
def calculateScriptForSigning(txSignatureComponent: TransactionSignatureComponent, script: Seq[ScriptToken]): Seq[ScriptToken] = txSignatureComponent match {
|
||||
case base: BaseTransactionSignatureComponent =>
|
||||
txSignatureComponent.scriptSignature match {
|
||||
|
@ -368,9 +338,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists
|
||||
*/
|
||||
/** Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists. */
|
||||
def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = {
|
||||
if (script.contains(ScriptConstant(signature.hex))) {
|
||||
//replicates this line in bitcoin core
|
||||
|
@ -396,19 +364,14 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
loop(sigs,script)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the OP_CODESEPARATOR in the original script according to
|
||||
* the last code separator index in the script
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
/** Removes the [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] in the original script according to
|
||||
* the last code separator index in the script. */
|
||||
def removeOpCodeSeparator(program : ExecutionInProgressScriptProgram) : Seq[ScriptToken] = {
|
||||
if (program.lastCodeSeparator.isDefined) {
|
||||
program.originalScript.slice(program.lastCodeSeparator.get+1, program.originalScript.size)
|
||||
} else program.originalScript
|
||||
}
|
||||
|
||||
|
||||
def parseScriptEither(scriptEither: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError]): Seq[ScriptToken] = scriptEither match {
|
||||
case Left((_,scriptPubKey)) =>
|
||||
logger.debug("Script pubkey asm inside calculateForSigning: " + scriptPubKey.asm)
|
||||
|
@ -417,5 +380,4 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
object BitcoinScriptUtil extends BitcoinScriptUtil
|
||||
|
|
|
@ -6,17 +6,12 @@ import org.bitcoins.core.crypto._
|
|||
import org.spongycastle.crypto.digests.RIPEMD160Digest
|
||||
|
||||
/**
|
||||
* Created by chris on 1/14/16.
|
||||
* Utility cryptographic functions
|
||||
*/
|
||||
* Created by chris on 1/14/16.
|
||||
* Utility cryptographic functions
|
||||
*/
|
||||
trait CryptoUtil {
|
||||
|
||||
/**
|
||||
* Does the following computation
|
||||
* RIPEMD160(SHA256(hex))
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Does the following computation: RIPEMD160(SHA256(hex)).*/
|
||||
def sha256Hash160(hex : String) : Sha256Hash160Digest = sha256Hash160(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
|
||||
|
@ -25,75 +20,37 @@ trait CryptoUtil {
|
|||
Sha256Hash160Digest(hash)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs sha256(sha256(hex))
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Performs sha256(sha256(hex)). */
|
||||
def doubleSHA256(hex : String) : DoubleSha256Digest = doubleSHA256(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
|
||||
/**
|
||||
* Performs sha256(sha256(bytes))
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Performs sha256(sha256(bytes)). */
|
||||
def doubleSHA256(bytes : Seq[Byte]) : DoubleSha256Digest = {
|
||||
val hash : Seq[Byte] = sha256(sha256(bytes).bytes).bytes
|
||||
DoubleSha256Digest(hash)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes sha256(hex)
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Takes sha256(hex). */
|
||||
def sha256(hex : String) : Sha256Digest = sha256(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Takes sha256(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Takes sha256(bytes). */
|
||||
def sha256(bytes : Seq[Byte]) : Sha256Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-256").digest(bytes.toArray).toList
|
||||
Sha256Digest(hash)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs SHA1(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Performs SHA1(bytes). */
|
||||
def sha1(bytes : Seq[Byte]) : Sha1Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-1").digest(bytes.toArray).toList
|
||||
Sha1Digest(hash)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Performs SHA1(hex)
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Performs SHA1(hex). */
|
||||
def sha1(hex : String) : Sha1Digest = sha1(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
|
||||
/**
|
||||
* Performs RIPEMD160(hex)
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Performs RIPEMD160(hex). */
|
||||
def ripeMd160(hex : String) : RipeMd160Digest = ripeMd160(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
|
||||
/**
|
||||
* Performs RIPEMD160(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Performs RIPEMD160(bytes). */
|
||||
def ripeMd160(bytes : Seq[Byte]) : RipeMd160Digest = {
|
||||
//from this tutorial http://rosettacode.org/wiki/RIPEMD-160#Scala
|
||||
val messageDigest = new RIPEMD160Digest
|
||||
|
@ -103,8 +60,6 @@ trait CryptoUtil {
|
|||
messageDigest.doFinal(out, 0)
|
||||
RipeMd160Digest(out.toList)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
object CryptoUtil extends CryptoUtil
|
||||
|
|
|
@ -6,31 +6,15 @@ package org.bitcoins.core.util
|
|||
*/
|
||||
trait Factory[T] extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* Creates a T out of a hex string
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Creates a T out of a hex string. */
|
||||
def fromHex(hex : String) : T = fromBytes(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Creates a T out of a sequence of bytes
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Creates a T out of a sequence of bytes. */
|
||||
def fromBytes(bytes : Seq[Byte]) : T
|
||||
|
||||
/**
|
||||
* Creates a T out of a sequence of bytes
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Creates a T out of a sequence of bytes. */
|
||||
def apply(bytes : Seq[Byte]) : T = fromBytes(bytes)
|
||||
|
||||
/**
|
||||
* Creates a T from a hex string
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Creates a T from a hex string. */
|
||||
def apply(hex : String) : T = fromHex(hex)
|
||||
}
|
||||
|
|
|
@ -15,11 +15,7 @@ trait NumberUtil extends BitcoinSLogger {
|
|||
|
||||
private def parseLong(bytes : Seq[Byte]) : Long = parseLong(bytes.toList)
|
||||
|
||||
/**
|
||||
* Takes 2^^num
|
||||
* @param exponent the exponent
|
||||
* @return
|
||||
*/
|
||||
/** Takes 2^^num. */
|
||||
def pow2(exponent : Int) : BigInt = {
|
||||
require(exponent < 64, "We cannot have anything larger than 2^64 - 1 in a long, you tried to do 2^" + exponent)
|
||||
BigInt(1) << exponent
|
||||
|
@ -45,24 +41,16 @@ trait NumberUtil extends BitcoinSLogger {
|
|||
setBits.foldLeft(BigInt(0)){_ + _}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a hex string and parses it to a [[BigInt]]
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Takes a hex string and parses it to a [[BigInt]]. */
|
||||
def toBigInt(hex : String) : BigInt = toBigInt(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Converts a sequence of bytes to twos complement signed number
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Converts a sequence of bytes to twos complement signed number. */
|
||||
def toBigInt(bytes : Seq[Byte]) : BigInt = {
|
||||
//BigInt interprets the number as an unsigned number then applies the given
|
||||
//sign in front of that number, therefore if we have a negative number we need to invert it
|
||||
//since twos complement is an inverted number representation for negative numbers
|
||||
//see https://en.wikipedia.org/wiki/Two%27s_complement
|
||||
if (bytes.length == 0) BigInt(0)
|
||||
if (bytes.isEmpty) BigInt(0)
|
||||
//check if sign bit is set
|
||||
else if ((0x80.toByte & bytes.head) !=0) {
|
||||
val invertedBytes = bytes.tail.map(b => (b ^ 0xff.toByte).toByte)
|
||||
|
@ -76,32 +64,16 @@ trait NumberUtil extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a sequence of bytes to a [[Int]]
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Converts a sequence of [[Byte]] to a [[Int]]. */
|
||||
def toInt(bytes : Seq[Byte]) : Int = toBigInt(bytes).toInt
|
||||
|
||||
/**
|
||||
* Converts a hex string to a [[Int]]
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Converts a hex string to a [[Int]]. */
|
||||
def toInt(hex : String) : Int = toInt(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
/**
|
||||
* Converts a sequence of bytes to a [[Long]]
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
/** Converts a sequence of [[Byte]] to a [[Long]]. */
|
||||
def toLong(bytes : Seq[Byte]) : Long = toBigInt(bytes).toLong
|
||||
|
||||
/**
|
||||
* Converts a hex string to a [[Long]]
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
/** Converts a hex string to a [[Long]]. */
|
||||
def toLong(hex : String): Long = toLong(BitcoinSUtil.decodeHex(hex))
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue