From e679ec969c8b22c676ebb10bea1038f6c8f13b33 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 3 Oct 2011 13:05:43 -0400 Subject: OP_EVAL implementation OP_EVAL is a new opcode that evaluates an item on the stack as a script. It enables a new type of bitcoin address that needs an arbitrarily complex script to redeem. --- src/test/script_op_eval_tests.cpp | 203 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 src/test/script_op_eval_tests.cpp (limited to 'src/test/script_op_eval_tests.cpp') diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp new file mode 100644 index 000000000..857d04bc6 --- /dev/null +++ b/src/test/script_op_eval_tests.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include + +#include "../main.h" +#include "../script.h" +#include "../wallet.h" + +using namespace std; + +// Test routines internal to script.cpp: +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType); + +BOOST_AUTO_TEST_SUITE(script_op_eval_tests) + +BOOST_AUTO_TEST_CASE(script_op_eval1) +{ + // OP_EVAL looks like this: + // scriptSig: + // scriptPubKey: DUP HASH160 EQUALVERIFY EVAL + + // Test SignSignature() (and therefore the version of Solver() that signs transactions) + CBasicKeyStore keystore; + CKey key[4]; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(); + keystore.AddKey(key[i]); + } + + // 8 Scripts: checking all combinations of + // different keys, straight/EVAL, pubkey/pubkeyhash + CScript standardScripts[4]; + standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG; + standardScripts[1].SetBitcoinAddress(key[1].GetPubKey()); + standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG; + standardScripts[3].SetBitcoinAddress(key[2].GetPubKey()); + CScript evalScripts[4]; + uint160 sigScriptHashes[4]; + for (int i = 0; i < 4; i++) + { + sigScriptHashes[i] = Hash160(standardScripts[i]); + keystore.AddCScript(sigScriptHashes[i], standardScripts[i]); + evalScripts[i] << OP_DUP << OP_HASH160 << sigScriptHashes[i] << OP_EQUALVERIFY << OP_EVAL; + } + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(8); + for (int i = 0; i < 4; i++) + { + txFrom.vout[i].scriptPubKey = evalScripts[i]; + txFrom.vout[i+4].scriptPubKey = standardScripts[i]; + } + BOOST_CHECK(txFrom.IsStandard()); + + CTransaction txTo[8]; // Spending transactions + for (int i = 0; i < 8; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); + } + for (int i = 0; i < 8; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + } + // All of the above should be OK, and the txTos have valid signatures + // Check to make sure signature verification fails if we use the wrong ScriptSig: + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) + { + CScript sigSave = txTo[i].vin[0].scriptSig; + txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; + int nUnused = 0; + bool sigOK = VerifySignature(txFrom, txTo[i], 0, nUnused); + if (i == j) + BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); + else + BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j)); + txTo[i].vin[0].scriptSig = sigSave; + } +} + +BOOST_AUTO_TEST_CASE(script_op_eval2) +{ + // Test OP_EVAL edge cases + + CScript recurse; + recurse << OP_DUP << OP_EVAL; + + uint160 recurseHash = Hash160(recurse); + + CScript fund; + fund << OP_DUP << OP_HASH160 << recurseHash << OP_EQUALVERIFY << OP_EVAL; + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = fund; + + BOOST_CHECK(txFrom.IsStandard()); // Looks like a standard transaction until you try to spend it + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = CScript() << static_cast >(recurse); + txTo.vout[0].nValue = 1; + + int nUnused = 0; + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); +} + +BOOST_AUTO_TEST_CASE(script_op_eval3) +{ + // Test the CScript::Set* methods + CBasicKeyStore keystore; + CKey key[4]; + std::vector keys; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(); + keystore.AddKey(key[i]); + keys.push_back(key[i]); + } + + CScript inner[4]; + inner[0].SetBitcoinAddress(key[0].GetPubKey()); + inner[1].SetMultisig(2, std::vector(keys.begin(), keys.begin()+2)); + inner[2].SetMultisig(1, std::vector(keys.begin(), keys.begin()+2)); + inner[3].SetMultisig(2, std::vector(keys.begin(), keys.begin()+3)); + + CScript outer[4]; + for (int i = 0; i < 4; i++) + { + outer[i].SetEval(inner[i]); + keystore.AddCScript(Hash160(inner[i]), inner[i]); + } + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(4); + for (int i = 0; i < 4; i++) + { + txFrom.vout[i].scriptPubKey = outer[i]; + } + BOOST_CHECK(txFrom.IsStandard()); + + CTransaction txTo[4]; // Spending transactions + for (int i = 0; i < 4; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + txTo[i].vout[0].scriptPubKey = inner[i]; + BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); + } + for (int i = 0; i < 4; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i)); + } +} + +BOOST_AUTO_TEST_CASE(script_op_eval_backcompat) +{ + // Check backwards-incompatibility-testing code + CScript returnsEleven; + returnsEleven << OP_11; + + // This will validate on new clients, but will + // be invalid on old clients (that interpret OP_EVAL as a no-op) + CScript fund; + fund << OP_EVAL << OP_11 << OP_EQUAL; + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = fund; + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = CScript() << static_cast >(returnsEleven); + txTo.vout[0].nValue = 1; + + int nUnused = 0; + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); +} + + +BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From a0871afb2b1d6d358c833fd08bca2f13c840fd4d Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Fri, 21 Oct 2011 13:12:05 -0400 Subject: Interpret OP_EVAL as OP_NOP until Feb 1, 2012 --- src/test/script_op_eval_tests.cpp | 54 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'src/test/script_op_eval_tests.cpp') diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp index 857d04bc6..6c683b572 100644 --- a/src/test/script_op_eval_tests.cpp +++ b/src/test/script_op_eval_tests.cpp @@ -15,7 +15,22 @@ using namespace std; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType); -BOOST_AUTO_TEST_SUITE(script_op_eval_tests) +static const int64 nEvalSwitchover = 1328054400; + +struct CEvalFixture { + CEvalFixture() + { + // Set mock time to AFTER OP_EVAL deployed + SetMockTime(nEvalSwitchover+1); + } + ~CEvalFixture() + { + // Reset back to use-real-time + SetMockTime(0); + } +}; + +BOOST_FIXTURE_TEST_SUITE(script_op_eval_tests, CEvalFixture) BOOST_AUTO_TEST_CASE(script_op_eval1) { @@ -171,14 +186,16 @@ BOOST_AUTO_TEST_CASE(script_op_eval3) } } -BOOST_AUTO_TEST_CASE(script_op_eval_backcompat) +BOOST_AUTO_TEST_CASE(script_op_eval_backcompat1) { // Check backwards-incompatibility-testing code CScript returnsEleven; returnsEleven << OP_11; - // This will validate on new clients, but will + // This should validate on new clients, but will // be invalid on old clients (that interpret OP_EVAL as a no-op) + // ... except there's a special rule that makes new clients reject + // it. CScript fund; fund << OP_EVAL << OP_11 << OP_EQUAL; @@ -199,5 +216,36 @@ BOOST_AUTO_TEST_CASE(script_op_eval_backcompat) BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); } +BOOST_AUTO_TEST_CASE(script_op_eval_switchover) +{ + // Use SetMockTime to test OP_EVAL switchover code + CScript notValid; + notValid << OP_11 << OP_12 << OP_EQUALVERIFY; + + // This will be valid under old rules, invalid under new: + CScript fund; + fund << OP_EVAL; + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = fund; + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = CScript() << static_cast >(notValid); + txTo.vout[0].nValue = 1; + + SetMockTime(nEvalSwitchover-1); + + int nUnused = 0; + BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + + // After eval switchover time, it should validate: + SetMockTime(nEvalSwitchover); + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); +} BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From 2a45a494b0bec6a0f1fc6ab7f26c260b85e7ff3e Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Tue, 8 Nov 2011 13:20:29 -0500 Subject: Use block times for 'hard' OP_EVAL switchover, and refactored EvalScript so it takes a flag for how to interpret OP_EVAL. Also increased IsStandard size of scriptSigs to 500 bytes, so a 3-of-3 multisig transaction IsStandard. --- src/test/script_op_eval_tests.cpp | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) (limited to 'src/test/script_op_eval_tests.cpp') diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp index 6c683b572..c44642c6e 100644 --- a/src/test/script_op_eval_tests.cpp +++ b/src/test/script_op_eval_tests.cpp @@ -13,24 +13,10 @@ using namespace std; // Test routines internal to script.cpp: extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, + int nHashType, bool fStrictOpEval); -static const int64 nEvalSwitchover = 1328054400; - -struct CEvalFixture { - CEvalFixture() - { - // Set mock time to AFTER OP_EVAL deployed - SetMockTime(nEvalSwitchover+1); - } - ~CEvalFixture() - { - // Reset back to use-real-time - SetMockTime(0); - } -}; - -BOOST_FIXTURE_TEST_SUITE(script_op_eval_tests, CEvalFixture) +BOOST_AUTO_TEST_SUITE(script_op_eval_tests) BOOST_AUTO_TEST_CASE(script_op_eval1) { @@ -130,8 +116,8 @@ BOOST_AUTO_TEST_CASE(script_op_eval2) txTo.vout[0].nValue = 1; int nUnused = 0; - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); - BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); } BOOST_AUTO_TEST_CASE(script_op_eval3) @@ -212,13 +198,13 @@ BOOST_AUTO_TEST_CASE(script_op_eval_backcompat1) txTo.vout[0].nValue = 1; int nUnused = 0; - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); - BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); } BOOST_AUTO_TEST_CASE(script_op_eval_switchover) { - // Use SetMockTime to test OP_EVAL switchover code + // Test OP_EVAL switchover code CScript notValid; notValid << OP_11 << OP_12 << OP_EQUALVERIFY; @@ -238,14 +224,11 @@ BOOST_AUTO_TEST_CASE(script_op_eval_switchover) txTo.vin[0].scriptSig = CScript() << static_cast >(notValid); txTo.vout[0].nValue = 1; - SetMockTime(nEvalSwitchover-1); - int nUnused = 0; - BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, false)); - // After eval switchover time, it should validate: - SetMockTime(nEvalSwitchover); - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + // Under strict op_eval switchover, it should be considered invalid: + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); } BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3