Creating LockTimeScriptPubKey type to generecize different locktime types, refactoring EscrowTimeout to accept LockTimeScriptPubKeys instead of only CSVScriptPubKeys

This commit is contained in:
Chris Stewart 2017-04-02 14:10:29 -05:00
parent 365c0a698b
commit 13c8960a04
7 changed files with 99 additions and 98 deletions

View file

@ -305,16 +305,9 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
} }
/** sealed trait LockTimeScriptPubKey extends ScriptPubKey {
* Represents a scriptPubKey that contains OP_CHECKLOCKTIMEVERIFY. /** Determines the nested ScriptPubKey inside the LockTimeScriptPubKey */
* Adds an absolute/defined locktime condition to any scriptPubKey. def nestedScriptPubKey : ScriptPubKey = {
* [[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki]]
* Format: <locktime> OP_CLTV OP_DROP <scriptPubKey>
*/
sealed trait CLTVScriptPubKey extends ScriptPubKey {
/** Determines the nested ScriptPubKey inside the CLTVScriptPubKey */
def scriptPubKeyAfterCLTV : ScriptPubKey = {
val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation] val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
bool match { bool match {
case true => ScriptPubKey(asm.slice(3, asm.length)) case true => ScriptPubKey(asm.slice(3, asm.length))
@ -322,17 +315,43 @@ sealed trait CLTVScriptPubKey extends ScriptPubKey {
} }
} }
/** The absolute CLTV-LockTime value (i.e. the output will remain unspendable until this timestamp or block height */ /** The relative locktime value (i.e. the amount of time the output should remain unspendable) */
def locktime : ScriptNumber = { def locktime : ScriptNumber = {
asm.head match { asm.head match {
case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.underlying) case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.underlying)
case pushBytes : BytesToPushOntoStack => ScriptNumber(asm(1).hex) case _: BytesToPushOntoStack => ScriptNumber(asm(1).hex)
case x @ (_ : ScriptConstant | _ : ScriptOperation) => throw new IllegalArgumentException("In a CLTVScriptPubKey, " + case x @ (_ : ScriptConstant | _ : ScriptOperation) => throw new IllegalArgumentException("In a LockTimeScriptPubKey, " +
"the first asm must be either a ScriptNumberOperation (i.e. OP_5), or the BytesToPushOntoStack for the proceeding ScriptConstant.") "the first asm must be either a ScriptNumberOperation (i.e. OP_5), or the BytesToPushOntoStack for the proceeding ScriptConstant.")
} }
} }
} }
object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
override def fromBytes(bytes: Seq[Byte]): LockTimeScriptPubKey = {
val asm = RawScriptPubKeyParser.read(bytes).asm
fromAsm(asm)
}
def fromAsm(asm: Seq[ScriptToken]): LockTimeScriptPubKey = {
require(isValidLockTimeScriptPubKey(asm))
if (asm.contains(OP_CHECKLOCKTIMEVERIFY)) CLTVScriptPubKey(asm)
else if (asm.contains(OP_CHECKSEQUENCEVERIFY)) CSVScriptPubKey(asm)
else throw new IllegalArgumentException("Given asm was not a LockTimeScriptPubKey, got: " + asm)
}
def isValidLockTimeScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
CLTVScriptPubKey.isCLTVScriptPubKey(asm) || CSVScriptPubKey.isCSVScriptPubKey(asm)
}
}
/**
* Represents a scriptPubKey that contains OP_CHECKLOCKTIMEVERIFY.
* Adds an absolute/defined locktime condition to any scriptPubKey.
* [[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki]]
* Format: <locktime> OP_CLTV OP_DROP <scriptPubKey>
*/
sealed trait CLTVScriptPubKey extends LockTimeScriptPubKey
object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] { object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
private case class CLTVScriptPubKeyImpl(hex : String) extends CLTVScriptPubKey private case class CLTVScriptPubKeyImpl(hex : String) extends CLTVScriptPubKey
@ -402,28 +421,7 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki * https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
* Format: <locktime> OP_CSV OP_DROP <scriptPubKey> * Format: <locktime> OP_CSV OP_DROP <scriptPubKey>
*/ */
sealed trait CSVScriptPubKey extends ScriptPubKey { sealed trait CSVScriptPubKey extends LockTimeScriptPubKey
/** Determines the nested ScriptPubKey inside the CSVScriptPubKey */
def scriptPubKeyAfterCSV : ScriptPubKey = {
val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
bool match {
case true => ScriptPubKey(asm.slice(3, asm.length))
case false => ScriptPubKey(asm.slice(4, asm.length))
}
}
/** The relative CSV-LockTime value (i.e. the amount of time the output should remain unspendable) */
def locktime : ScriptNumber = {
asm.head match {
case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.underlying)
case pushBytes : BytesToPushOntoStack => ScriptNumber(asm(1).hex)
case x @ (_ : ScriptConstant | _ : ScriptOperation) => throw new IllegalArgumentException("In a CSVScriptPubKey, " +
"the first asm must be either a ScriptNumberOperation (i.e. OP_5), or the BytesToPushOntoStack for the proceeding ScriptConstant.")
}
}
}
object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] { object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
private case class CSVScriptPubKeyImpl(hex : String) extends CSVScriptPubKey private case class CSVScriptPubKeyImpl(hex : String) extends CSVScriptPubKey
@ -515,7 +513,7 @@ object ScriptPubKey extends Factory[ScriptPubKey] with BitcoinSLogger {
case _ if CSVScriptPubKey.isCSVScriptPubKey(asm) => CSVScriptPubKey(asm) case _ if CSVScriptPubKey.isCSVScriptPubKey(asm) => CSVScriptPubKey(asm)
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => WitnessScriptPubKey(asm).get case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => WitnessScriptPubKey(asm).get
case _ if WitnessCommitment.isWitnessCommitment(asm) => WitnessCommitment(asm) case _ if WitnessCommitment.isWitnessCommitment(asm) => WitnessCommitment(asm)
case _ if CSVEscrowTimeoutScriptPubKey.isValidCSVEscrowTimeout(asm) => CSVEscrowTimeoutScriptPubKey.fromAsm(asm) case _ if EscrowTimeoutScriptPubKey.isValidEscrowTimeout(asm) => EscrowTimeoutScriptPubKey.fromAsm(asm)
case _ => NonStandardScriptPubKey(asm) case _ => NonStandardScriptPubKey(asm)
} }
@ -670,9 +668,9 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
/** Represents a [[ScriptPubKey]] that either times out allowing Alice to spend from the scriptpubkey /** Represents a [[ScriptPubKey]] that either times out allowing Alice to spend from the scriptpubkey
* or allows a federation to spend from the escrow * or allows a federation to spend from the escrow
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki#contracts-with-expiration-deadlines]] * [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki#contracts-with-expiration-deadlines]]
* Format: OP_IF <multsig scriptpubkey> OP_ELSE <csv scriptPubKey> OP_ENDIF * Format: OP_IF <multsig scriptpubkey> OP_ELSE <locktime scriptPubKey> OP_ENDIF
*/ */
sealed trait CSVEscrowTimeoutScriptPubKey extends ScriptPubKey { sealed trait EscrowTimeoutScriptPubKey extends ScriptPubKey {
/** The [[MultiSignatureScriptPubKey]] that can be used to spend funds */ /** The [[MultiSignatureScriptPubKey]] that can be used to spend funds */
def escrow: MultiSignatureScriptPubKey = { def escrow: MultiSignatureScriptPubKey = {
val escrowAsm = asm.slice(1,opElseIndex) val escrowAsm = asm.slice(1,opElseIndex)
@ -680,9 +678,9 @@ sealed trait CSVEscrowTimeoutScriptPubKey extends ScriptPubKey {
} }
/** The [[CSVScriptPubKey]] that you can spend funds from after a certain timeout */ /** The [[CSVScriptPubKey]] that you can spend funds from after a certain timeout */
def timeout: CSVScriptPubKey = { def timeout: LockTimeScriptPubKey = {
val timeoutAsm = asm.slice(opElseIndex+1, asm.length-1) val timeoutAsm = asm.slice(opElseIndex+1, asm.length-1)
CSVScriptPubKey(timeoutAsm) LockTimeScriptPubKey.fromAsm(timeoutAsm)
} }
private def opElseIndex: Int = { private def opElseIndex: Int = {
@ -692,19 +690,19 @@ sealed trait CSVEscrowTimeoutScriptPubKey extends ScriptPubKey {
} }
} }
object CSVEscrowTimeoutScriptPubKey extends ScriptFactory[CSVEscrowTimeoutScriptPubKey] { object EscrowTimeoutScriptPubKey extends ScriptFactory[EscrowTimeoutScriptPubKey] {
private case class CSVEscrowTimeoutScriptPubKeyImpl(hex: String) extends CSVEscrowTimeoutScriptPubKey private case class EscrowTimeoutScriptPubKeyImpl(hex: String) extends EscrowTimeoutScriptPubKey
override def fromBytes(bytes: Seq[Byte]): CSVEscrowTimeoutScriptPubKey = { override def fromBytes(bytes: Seq[Byte]): EscrowTimeoutScriptPubKey = {
val asm = RawScriptPubKeyParser.read(bytes).asm val asm = RawScriptPubKeyParser.read(bytes).asm
fromAsm(asm) fromAsm(asm)
} }
override def fromAsm(asm: Seq[ScriptToken]): CSVEscrowTimeoutScriptPubKey = { override def fromAsm(asm: Seq[ScriptToken]): EscrowTimeoutScriptPubKey = {
buildScript(asm, CSVEscrowTimeoutScriptPubKeyImpl(_), isValidCSVEscrowTimeout(_), "Given asm was not a valid CSVEscrowTimeout, got: " + asm) buildScript(asm, EscrowTimeoutScriptPubKeyImpl(_), isValidEscrowTimeout(_), "Given asm was not a valid CSVEscrowTimeout, got: " + asm)
} }
def isValidCSVEscrowTimeout(asm: Seq[ScriptToken]): Boolean = { def isValidEscrowTimeout(asm: Seq[ScriptToken]): Boolean = {
val opElseIndex = asm.indexOf(OP_ELSE) val opElseIndex = asm.indexOf(OP_ELSE)
val correctControlStructure = asm.headOption.contains(OP_IF) && asm.last == OP_ENDIF && opElseIndex != -1 val correctControlStructure = asm.headOption.contains(OP_IF) && asm.last == OP_ENDIF && opElseIndex != -1
if (correctControlStructure) { if (correctControlStructure) {
@ -716,7 +714,7 @@ object CSVEscrowTimeoutScriptPubKey extends ScriptFactory[CSVEscrowTimeoutScript
} else false } else false
} }
def apply(escrow: MultiSignatureScriptPubKey, timeout: CSVScriptPubKey): CSVEscrowTimeoutScriptPubKey = { def apply(escrow: MultiSignatureScriptPubKey, timeout: LockTimeScriptPubKey): EscrowTimeoutScriptPubKey = {
fromAsm(Seq(OP_IF) ++ escrow.asm ++ Seq(OP_ELSE) ++ timeout.asm ++ Seq(OP_ENDIF)) fromAsm(Seq(OP_IF) ++ escrow.asm ++ Seq(OP_ELSE) ++ timeout.asm ++ Seq(OP_ENDIF))
} }
} }

View file

@ -214,16 +214,12 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
redeemScriptTry match { redeemScriptTry match {
case Success(redeemScript) => case Success(redeemScript) =>
redeemScript match { redeemScript match {
case x : P2PKHScriptPubKey => true case _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey
case x : MultiSignatureScriptPubKey => true | _: P2SHScriptPubKey | _: P2PKScriptPubKey
case x : P2SHScriptPubKey => true | _: CLTVScriptPubKey | _: CSVScriptPubKey
case x : P2PKScriptPubKey => true | _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey
case x : CLTVScriptPubKey => true | _: EscrowTimeoutScriptPubKey => true
case x : CSVScriptPubKey => true case _: NonStandardScriptPubKey | _: WitnessCommitment => false
case x : WitnessScriptPubKeyV0 => true
case x : UnassignedWitnessScriptPubKey => true
case x : NonStandardScriptPubKey => false
case x : WitnessCommitment => false
case EmptyScriptPubKey => false case EmptyScriptPubKey => false
} }
case Failure(_) => false case Failure(_) => false
@ -376,8 +372,8 @@ object CLTVScriptSignature extends Factory[CLTVScriptSignature] {
case p2pkScriptPubKey : P2PKScriptPubKey => CLTVScriptSignature(P2PKScriptSignature(sigs.head)) case p2pkScriptPubKey : P2PKScriptPubKey => CLTVScriptSignature(P2PKScriptSignature(sigs.head))
case p2pkhScriptPubKey : P2PKHScriptPubKey => CLTVScriptSignature(P2PKHScriptSignature(sigs.head, pubKeys.head)) case p2pkhScriptPubKey : P2PKHScriptPubKey => CLTVScriptSignature(P2PKHScriptSignature(sigs.head, pubKeys.head))
case multiSigScriptPubKey : MultiSignatureScriptPubKey => CLTVScriptSignature(MultiSignatureScriptSignature(sigs)) case multiSigScriptPubKey : MultiSignatureScriptPubKey => CLTVScriptSignature(MultiSignatureScriptSignature(sigs))
case cltvScriptPubKey : CLTVScriptPubKey => apply(cltvScriptPubKey.scriptPubKeyAfterCLTV, sigs, pubKeys) case cltvScriptPubKey : CLTVScriptPubKey => apply(cltvScriptPubKey.nestedScriptPubKey, sigs, pubKeys)
case csvScriptPubKey : CSVScriptPubKey => apply(csvScriptPubKey.scriptPubKeyAfterCSV, sigs, pubKeys) case csvScriptPubKey : CSVScriptPubKey => apply(csvScriptPubKey.nestedScriptPubKey, sigs, pubKeys)
case EmptyScriptPubKey => CLTVScriptSignature(EmptyScriptSignature) case EmptyScriptPubKey => CLTVScriptSignature(EmptyScriptSignature)
case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey => case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey =>
//bare segwit always has an empty script sig, see BIP141 //bare segwit always has an empty script sig, see BIP141
@ -421,10 +417,10 @@ object CSVScriptSignature extends Factory[CSVScriptSignature] {
case _: P2PKScriptPubKey => CSVScriptSignature(P2PKScriptSignature(sigs.head)) case _: P2PKScriptPubKey => CSVScriptSignature(P2PKScriptSignature(sigs.head))
case _: P2PKHScriptPubKey => CSVScriptSignature(P2PKHScriptSignature(sigs.head, pubKeys.head)) case _: P2PKHScriptPubKey => CSVScriptSignature(P2PKHScriptSignature(sigs.head, pubKeys.head))
case _: MultiSignatureScriptPubKey => CSVScriptSignature(MultiSignatureScriptSignature(sigs)) case _: MultiSignatureScriptPubKey => CSVScriptSignature(MultiSignatureScriptSignature(sigs))
case cltvScriptPubKey : CLTVScriptPubKey => CSVScriptSignature(cltvScriptPubKey.scriptPubKeyAfterCLTV, sigs, pubKeys) case cltvScriptPubKey : CLTVScriptPubKey => CSVScriptSignature(cltvScriptPubKey.nestedScriptPubKey, sigs, pubKeys)
case csvScriptPubKey : CSVScriptPubKey => CSVScriptSignature(csvScriptPubKey.scriptPubKeyAfterCSV, sigs, pubKeys) case csvScriptPubKey : CSVScriptPubKey => CSVScriptSignature(csvScriptPubKey.nestedScriptPubKey, sigs, pubKeys)
case csvEscrowTimeout: CSVEscrowTimeoutScriptPubKey => case csvEscrowTimeout: EscrowTimeoutScriptPubKey =>
CSVScriptSignature(csvEscrowTimeout.timeout.scriptPubKeyAfterCSV,sigs,pubKeys) CSVScriptSignature(csvEscrowTimeout.timeout.nestedScriptPubKey,sigs,pubKeys)
case EmptyScriptPubKey => CSVScriptSignature(EmptyScriptSignature) case EmptyScriptPubKey => CSVScriptSignature(EmptyScriptSignature)
case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey => case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey =>
//bare segwit always has an empty script sig, see BIP141 //bare segwit always has an empty script sig, see BIP141
@ -477,10 +473,10 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
case _: NonStandardScriptPubKey => Try(NonStandardScriptSignature.fromAsm(tokens)) case _: NonStandardScriptPubKey => Try(NonStandardScriptSignature.fromAsm(tokens))
case s : CLTVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCLTV) case s : CLTVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCLTV)
case s : CSVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCSV) case s : CSVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCSV)
case escrowWithTimeout : CSVEscrowTimeoutScriptPubKey => case escrowWithTimeout : EscrowTimeoutScriptPubKey =>
val isMultiSig = BitcoinScriptUtil.castToBool(tokens.head) val isMultSig = BitcoinScriptUtil.castToBool(tokens.head)
if (isMultiSig) Try(MultiSignatureScriptSignature.fromAsm(tokens.tail)) if (isMultSig) Try(MultiSignatureScriptSignature.fromAsm(tokens.tail))
else Try(CSVEscrowTimeoutScriptSignature.fromAsm(tokens,escrowWithTimeout)) else Try(EscrowTimeoutScriptSignature.fromAsm(tokens,escrowWithTimeout))
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey => Success(EmptyScriptSignature) case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey => Success(EmptyScriptSignature)
case EmptyScriptPubKey => case EmptyScriptPubKey =>
if (tokens.isEmpty) Success(EmptyScriptSignature) else Try(NonStandardScriptSignature.fromAsm(tokens)) if (tokens.isEmpty) Success(EmptyScriptSignature) else Try(NonStandardScriptSignature.fromAsm(tokens))
@ -492,41 +488,41 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
} }
} }
/** [[ScriptSignature]] that spends a [[CSVEscrowTimeoutScriptPubKey]], the underlying script signature can be /** [[ScriptSignature]] that spends a [[EscrowTimeoutScriptPubKey]], the underlying script signature can be
* a [[MultiSignatureScriptSignature]] or a [[CSVScriptSignature]] as those are te two underlying scripts * a [[MultiSignatureScriptSignature]] or a [[CSVScriptSignature]] as those are te two underlying scripts
* of a [[CSVEscrowTimeoutScriptPubKey]] * of a [[EscrowTimeoutScriptPubKey]]
* *
* If the last element of the [[asm]] evaluates to true, it is a scriptsig that attempts to spend the escrow * If the last element of the [[asm]] evaluates to true, it is a scriptsig that attempts to spend the escrow
* if the last element of the [[asm]] evaluates to false, it is a scriptsig that attempts to spend the timeout * if the last element of the [[asm]] evaluates to false, it is a scriptsig that attempts to spend the timeout
* */ * */
sealed trait CSVEscrowTimeoutScriptSignature extends ScriptSignature { sealed trait EscrowTimeoutScriptSignature extends ScriptSignature {
def scriptSig: ScriptSignature = ScriptSignature(hex) def scriptSig: ScriptSignature = ScriptSignature(hex)
override def signatures = scriptSig.signatures override def signatures = scriptSig.signatures
override def toString = "CSVEscrowWithTimeoutScriptSignature(" + scriptSig + ")" override def toString = "CSVEscrowWithTimeoutScriptSignature(" + scriptSig + ")"
/** Checks if the given asm fulfills the timeout or escrow of the [[CSVEscrowTimeoutScriptPubKey]] */ /** Checks if the given asm fulfills the timeout or escrow of the [[EscrowTimeoutScriptPubKey]] */
def isEscrow: Boolean = BitcoinScriptUtil.castToBool(asm.last) def isEscrow: Boolean = BitcoinScriptUtil.castToBool(asm.last)
def isTimeout: Boolean = !isEscrow def isTimeout: Boolean = !isEscrow
} }
object CSVEscrowTimeoutScriptSignature extends Factory[CSVEscrowTimeoutScriptSignature] { object EscrowTimeoutScriptSignature extends Factory[EscrowTimeoutScriptSignature] {
private case class CSVEscrowTimeoutScriptSignatureImpl(hex: String) extends CSVEscrowTimeoutScriptSignature private case class EscrowTimeoutScriptSignatureImpl(hex: String) extends EscrowTimeoutScriptSignature
override def fromBytes(bytes: Seq[Byte]): CSVEscrowTimeoutScriptSignature = { override def fromBytes(bytes: Seq[Byte]): EscrowTimeoutScriptSignature = {
CSVEscrowTimeoutScriptSignatureImpl(BitcoinSUtil.encodeHex(bytes)) EscrowTimeoutScriptSignatureImpl(BitcoinSUtil.encodeHex(bytes))
} }
def fromAsm(asm: Seq[ScriptToken], scriptPubKey: CSVEscrowTimeoutScriptPubKey): CSVEscrowTimeoutScriptSignature = { def fromAsm(asm: Seq[ScriptToken], scriptPubKey: EscrowTimeoutScriptPubKey): EscrowTimeoutScriptSignature = {
require(isValidCSVEscrowTimeoutScriptSig(asm,scriptPubKey), "Given asm was not a CSVEscrowWithTimeoutScriptSignature, got: " + asm) require(isValidEscrowTimeoutScriptSig(asm,scriptPubKey), "Given asm was not a CSVEscrowWithTimeoutScriptSignature, got: " + asm)
val asmHex = asm.map(_.hex).mkString val asmHex = asm.map(_.hex).mkString
val c = CompactSizeUInt.calculateCompactSizeUInt(asmHex) val c = CompactSizeUInt.calculateCompactSizeUInt(asmHex)
val fullHex = c.hex + asmHex val fullHex = c.hex + asmHex
fromHex(fullHex) fromHex(fullHex)
} }
def fromAsm(asm: Seq[ScriptToken]): CSVEscrowTimeoutScriptSignature = { def fromAsm(asm: Seq[ScriptToken]): EscrowTimeoutScriptSignature = {
require(asm.nonEmpty) require(asm.nonEmpty)
val nested = asm.take(asm.length - 1) val nested = asm.take(asm.length - 1)
val nestedScriptSig = if (BitcoinScriptUtil.castToBool(asm.last)) { val nestedScriptSig = if (BitcoinScriptUtil.castToBool(asm.last)) {
@ -541,26 +537,26 @@ object CSVEscrowTimeoutScriptSignature extends Factory[CSVEscrowTimeoutScriptSig
fromBytes(fullBytes) fromBytes(fullBytes)
} }
def isValidCSVEscrowTimeoutScriptSig(asm: Seq[ScriptToken], def isValidEscrowTimeoutScriptSig(asm: Seq[ScriptToken],
scriptPubKey: CSVEscrowTimeoutScriptPubKey): Boolean = { scriptPubKey: EscrowTimeoutScriptPubKey): Boolean = {
val nested = asm.take(asm.length - 1) val nested = asm.take(asm.length - 1)
val isMultiSig = BitcoinScriptUtil.castToBool(asm.last) val isMultiSig = BitcoinScriptUtil.castToBool(asm.last)
if (isMultiSig) { if (isMultiSig) {
MultiSignatureScriptSignature.isMultiSignatureScriptSignature(nested) MultiSignatureScriptSignature.isMultiSignatureScriptSignature(nested)
} else { } else {
val locktimeScript = scriptPubKey.timeout.scriptPubKeyAfterCSV val locktimeScript = scriptPubKey.timeout.nestedScriptPubKey
Try(ScriptSignature(nested,locktimeScript)).isSuccess Try(ScriptSignature(nested,locktimeScript)).isSuccess
} }
} }
def apply(scriptSig: ScriptSignature): CSVEscrowTimeoutScriptSignature = fromBytes(scriptSig.bytes) def apply(scriptSig: ScriptSignature): EscrowTimeoutScriptSignature = fromBytes(scriptSig.bytes)
def apply(multiSigScriptSig: MultiSignatureScriptSignature): CSVEscrowTimeoutScriptSignature = { def apply(multiSigScriptSig: MultiSignatureScriptSignature): EscrowTimeoutScriptSignature = {
val asm = multiSigScriptSig.asm ++ Seq(OP_1) val asm = multiSigScriptSig.asm ++ Seq(OP_1)
fromAsm(asm) fromAsm(asm)
} }
def apply(csv: CSVScriptSignature): CSVEscrowTimeoutScriptSignature = { def apply(csv: CSVScriptSignature): EscrowTimeoutScriptSignature = {
val asm = csv.asm ++ Seq(OP_0) val asm = csv.asm ++ Seq(OP_0)
fromAsm(asm) fromAsm(asm)
} }

View file

@ -78,8 +78,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
else scriptPubKeyExecutedProgram else scriptPubKeyExecutedProgram
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey case _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey
| _: CLTVScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment | _: CLTVScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment
| _: CSVEscrowTimeoutScriptPubKey | EmptyScriptPubKey => | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey =>
logger.info("")
scriptPubKeyExecutedProgram scriptPubKeyExecutedProgram
} }
} }
@ -165,7 +164,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
} }
case s @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey case s @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey
| _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | _ : WitnessCommitment | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | _ : WitnessCommitment
| _: CSVEscrowTimeoutScriptPubKey | EmptyScriptPubKey) => | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey) =>
logger.debug("redeemScript: " + s.asm) logger.debug("redeemScript: " + s.asm)
run(scriptPubKeyExecutedProgram,stack,s) run(scriptPubKeyExecutedProgram,stack,s)
} }
@ -520,7 +519,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
p2shScriptSig.redeemScript.isInstanceOf[WitnessScriptPubKey] p2shScriptSig.redeemScript.isInstanceOf[WitnessScriptPubKey]
case _: CLTVScriptPubKey | _: CSVScriptPubKey | _: MultiSignatureScriptPubKey | _: NonStandardScriptPubKey case _: CLTVScriptPubKey | _: CSVScriptPubKey | _: MultiSignatureScriptPubKey | _: NonStandardScriptPubKey
| _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: WitnessCommitment | _: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: WitnessCommitment
| _: CSVEscrowTimeoutScriptPubKey | EmptyScriptPubKey => | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey =>
w.witness.stack.isEmpty w.witness.stack.isEmpty
} }
!witnessedUsed !witnessedUsed

View file

@ -308,7 +308,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
} }
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey case _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey
| _: NonStandardScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey | _: NonStandardScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey
| _: WitnessCommitment | _: CSVEscrowTimeoutScriptPubKey | EmptyScriptPubKey => script | _: WitnessCommitment | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => script
} }
/** Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists. */ /** Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists. */
@ -354,7 +354,7 @@ trait BitcoinScriptUtil extends BitcoinSLogger {
case Right(_) => Nil //error case Right(_) => Nil //error
} }
/** Casts the given script token to a boolean value /** Casts the given script token to a boolean value
* Mimics this function inside of Bitcoin Core * Mimics this function inside of Bitcoin Core
* [[https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L38]] * [[https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L38]]
* All bytes in the byte vector must be zero, unless it is the last byte, which can be 0 or 0x80 (negative zero) * All bytes in the byte vector must be zero, unless it is the last byte, which can be 0 or 0x80 (negative zero)

View file

@ -139,10 +139,18 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
Seq(ScriptErrorPushSize, ScriptOk).contains(result) Seq(ScriptErrorPushSize, ScriptOk).contains(result)
} }
property("generate a valid signature for a csv escrow timeout transaction") = property("generate a valid signature for a csv escrow timeout transaction") =
Prop.forAll(TransactionGenerators.spendableCSVEscrowTimeoutTransaction) { txSigComponent: TransactionSignatureComponent => Prop.forAll(TransactionGenerators.spendableEscrowTimeoutTransaction) { txSigComponent: TransactionSignatureComponent =>
val program = ScriptProgram(txSigComponent) val program = ScriptProgram(txSigComponent)
val result = ScriptInterpreter.run(program) val result = ScriptInterpreter.run(program)
result == ScriptOk result == ScriptOk
} }
property("fail to evaluate a csv escrow timeout transaction due to invalid csv timeout values") = {
Prop.forAll(TransactionGenerators.unspendableEscrowTimeoutTransaction) { txSigComponent: TransactionSignatureComponent =>
val program = ScriptProgram(txSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptErrorUnsatisfiedLocktime
}
}
} }

View file

@ -33,9 +33,9 @@ class CLTVScriptPubKeyTest extends FlatSpec with MustMatchers {
val pubKey = ECPrivateKey().publicKey val pubKey = ECPrivateKey().publicKey
val p2pkh = P2PKHScriptPubKey(pubKey) val p2pkh = P2PKHScriptPubKey(pubKey)
CLTVScriptPubKey(scriptNum17, p2pkh).scriptPubKeyAfterCLTV must be (p2pkh) CLTVScriptPubKey(scriptNum17, p2pkh).nestedScriptPubKey must be (p2pkh)
CLTVScriptPubKey(scriptNum5, p2pkh).scriptPubKeyAfterCLTV must be (p2pkh) CLTVScriptPubKey(scriptNum5, p2pkh).nestedScriptPubKey must be (p2pkh)
CLTVScriptPubKey(negativeOne, p2pkh).scriptPubKeyAfterCLTV must be (p2pkh) CLTVScriptPubKey(negativeOne, p2pkh).nestedScriptPubKey must be (p2pkh)
CLTVScriptPubKey(scriptNum17, p2pkh).locktime must be (scriptNum17) CLTVScriptPubKey(scriptNum17, p2pkh).locktime must be (scriptNum17)
CLTVScriptPubKey(scriptNum5, p2pkh).locktime must be (scriptNum5) CLTVScriptPubKey(scriptNum5, p2pkh).locktime must be (scriptNum5)

View file

@ -33,9 +33,9 @@ class CSVScriptPubKeyTest extends FlatSpec with MustMatchers {
val pubKey = ECPrivateKey().publicKey val pubKey = ECPrivateKey().publicKey
val p2pkh = P2PKHScriptPubKey(pubKey) val p2pkh = P2PKHScriptPubKey(pubKey)
CSVScriptPubKey(scriptNum17, p2pkh).scriptPubKeyAfterCSV must be(p2pkh) CSVScriptPubKey(scriptNum17, p2pkh).nestedScriptPubKey must be(p2pkh)
CSVScriptPubKey(scriptNum5, p2pkh).scriptPubKeyAfterCSV must be(p2pkh) CSVScriptPubKey(scriptNum5, p2pkh).nestedScriptPubKey must be(p2pkh)
CSVScriptPubKey(negativeOne, p2pkh).scriptPubKeyAfterCSV must be(p2pkh) CSVScriptPubKey(negativeOne, p2pkh).nestedScriptPubKey must be(p2pkh)
CSVScriptPubKey(scriptNum17, p2pkh).locktime must be(scriptNum17) CSVScriptPubKey(scriptNum17, p2pkh).locktime must be(scriptNum17)
CSVScriptPubKey(scriptNum5, p2pkh).locktime must be(scriptNum5) CSVScriptPubKey(scriptNum5, p2pkh).locktime must be(scriptNum5)