Give ScriptInterpreter functions to verify a transaction or single input (#1223)

* Add TxSigCompenent constuctor

* Add verifyInputScript to ScriptInterpreter

* Add tests
This commit is contained in:
Ben Carman 2020-03-19 07:58:56 -05:00 committed by GitHub
parent 45e1a90fba
commit f6515c6779
3 changed files with 93 additions and 0 deletions

View File

@ -8,6 +8,7 @@ import org.bitcoins.core.crypto.{
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionOutput,
WitnessTransaction
}
@ -108,4 +109,31 @@ class ScriptInterpreterTest extends BitcoinSUnitTest {
}
}
}
it must "evaluate a p2sh witness transaction as valid" in {
val p2shWitTx = Transaction(
"02000000000101c29e1b0bc7d7eef558cab27731e68526c94a8ee30b5a8795674b39b25430dacd0000000017160014471b19074d141d11042cba016cd60d89caee0150feffffff02ee2916000000000017a91401d95721e723fb9d02fa7ae0d5a330925fa6f90187f0e864da0100000017a9149829ca82bf4d71daeffb14a6b96da4e621cccb31870247304402205406cf034993af5fc0dd9fcd96eafe04acedeb9d4d2a012aa539561cd50b6ce30220680079485226ee184c678da475e291e81ca8b83b5b973d0a8513a5b4a7e24c52012103c6350aad80873966c5a778bc3e019eef399d242ef386e21267652ca1f3e3a6ae087a1900")
val prevOut = TransactionOutput(
"48677bda0100000017a914885437151ad21f21c5c714ddffdf67e56fcd63ea87")
ScriptInterpreter.verifyTransaction(p2shWitTx, Vector(prevOut))
}
it must "evaluate a witness transaction as valid" in {
val wtx = Transaction(
"02000000000101b62a936a1389f6f28a71f88be2703800e267bd0501e4fcd7c803a2eb2ca9d96f0000000000feffffff011027000000000000160014bb7e25b0e93a3d378b813e97d5d61efcfa69863e040047304402204e0401cf6bd54c3551a7534f9c04e30701da735693670028a5ccd5c99934df1a02200249b701467f461015bf6643bed692f911b6a43a05f1e18301e61ace743ba788014730440220385f39b54755df27ddc353b24dd83fd236e6ba0c39fefe2a41d2f0145498612b022036d0992f3245d4da79e0cc4562b8b40fb5325d8bc794cfa92d2bd36263fedcef014752210325815e1ecf3cdc683c16be773798b07ce3ca2af6969bb03975c5e0e0c06f7f0521031d8bcc3a6a7e93d904a89c20104f44b0bf6c157b6e761e20f2751381e8ba382452ae1f6d1900")
val prevOut = TransactionOutput(
"b82a0000000000002200202d501d0b2df21825d3dca7dc2bdaeea2c484c552b4ccbf687ffa887ae47e42c2")
ScriptInterpreter.verifyTransaction(wtx, Vector(prevOut))
}
it must "evaluate a base transaction as valid" in {
val tx = Transaction(
"01000000010289c14b991dde643aca30b9010c9b37b8347f3984fa5c31e78adceeea4eefab000000006a47304402206d1ae877e2339ed74a1b203aacdb8e4dc6202f873a228d681d3075f7cd8ef95c022067383abdf0bc1839db0c26a0d77df097d815b4ef086da3872b4a928468b667f30121032fb875942a07a7606933e383c85d688b5fd3482c29035d4289638987582c3f2bffffffff02500e40a10b0000001976a9142301071cf4f2550a705c6348f33e6b33544b768488ac2752fb02000000001976a914741423cca440e7dc81d3468b832433e4db3c924288ac00000000")
val prevOut = TransactionOutput(
"37733ba40b0000001976a914741423cca440e7dc81d3468b832433e4db3c924288ac")
ScriptInterpreter.verifyTransaction(tx, Vector(prevOut))
}
}

View File

@ -42,6 +42,43 @@ sealed abstract class TxSigComponent {
def sigVersion: SignatureVersion
}
object TxSigComponent {
def apply(
transaction: Transaction,
inputIndex: UInt32,
output: TransactionOutput,
flags: Seq[ScriptFlag]): TxSigComponent = {
val scriptSig = transaction.inputs(inputIndex.toInt).scriptSignature
output.scriptPubKey match {
case _: WitnessScriptPubKey =>
transaction match {
case _: BaseTransaction =>
throw new IllegalArgumentException(
s"Cannot spend from segwit output ($output) with a base transaction ($transaction)")
case wtx: WitnessTransaction =>
WitnessTxSigComponent(wtx, inputIndex, output, flags)
}
case _: P2SHScriptPubKey =>
val p2shScriptSig = scriptSig.asInstanceOf[P2SHScriptSignature]
if (WitnessScriptPubKey.isWitnessScriptPubKey(
p2shScriptSig.redeemScript.asm)) {
transaction match {
case _: BaseTransaction =>
throw new IllegalArgumentException(
s"Cannot spend from segwit output ($output) with a base transaction ($transaction)")
case wtx: WitnessTransaction =>
WitnessTxSigComponentP2SH(wtx, inputIndex, output, flags)
}
} else {
P2SHTxSigComponent(transaction, inputIndex, output, flags)
}
case _: RawScriptPubKey =>
BaseTxSigComponent(transaction, inputIndex, output, flags)
}
}
}
/**
* The [[org.bitcoins.core.crypto.TxSigComponent TxSigComponent]]
* used to evaluate the the original Satoshi transaction digest algorithm.

View File

@ -3,6 +3,8 @@ package org.bitcoins.core.script.interpreter
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
@ -177,6 +179,32 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
!programs.exists(p => ScriptInterpreter.run(p) != ScriptOk)
}
def verifyInputScript(
transaction: Transaction,
inputIndex: Long,
prevOut: TransactionOutput): Boolean = {
val sigComponent = TxSigComponent(
transaction,
UInt32(inputIndex),
prevOut,
Policy.standardFlags
)
ScriptInterpreter.runVerify(PreExecutionScriptProgram(sigComponent))
}
def verifyTransaction(
transaction: Transaction,
prevOuts: Vector[TransactionOutput]): Boolean = {
require(
transaction.inputs.size == prevOuts.size,
s"There must be a prevOut for every input in the transaction, got ${prevOuts.size}")
prevOuts.zipWithIndex.forall {
case (prevOut, index) =>
verifyInputScript(transaction, index, prevOut)
}
}
/**
* 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