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:
Chris Stewart 2016-12-15 20:06:54 -06:00 committed by GitHub
commit 8183d3764c
29 changed files with 490 additions and 1463 deletions

View file

@ -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 blocks 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
}

View file

@ -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 = {

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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))
}

View file

@ -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
}

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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"

View file

@ -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 {

View file

@ -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))

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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))
}