Add CScript::IsWithdrawLock() (which is standard)

This commit is contained in:
Matt Corallo 2016-01-18 20:35:04 -08:00 committed by Gregory Sanders
parent c22932429e
commit 8c121ccd5c
4 changed files with 169 additions and 13 deletions

View file

@ -75,17 +75,6 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys (remember the 520 byte limit on redeemScript size). That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard.
if (txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
@ -129,6 +118,19 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
if (prev.scriptPubKey.IsWithdrawLock())
continue;
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys. (remember the 520 byte limit on redeemScript size) That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard)
if (tx.vin[i].scriptSig.size() > 1650)
return false;
std::vector<std::vector<unsigned char> > vSolutions;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:

View file

@ -426,8 +426,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
break;
}
case OP_NOP1: case OP_NOP4: case OP_NOP5:
case OP_NOP1: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
case OP_WITHDRAWPROOFVERIFY:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);

View file

@ -5,8 +5,14 @@
#include "script.h"
#include <list>
#include "hash.h"
#include "primitives/transaction.h"
#include "streams.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "version.h"
using namespace std;
@ -133,7 +139,6 @@ const char* GetOpName(opcodetype opcode)
case OP_NOP1 : return "OP_NOP1";
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY";
case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY";
case OP_NOP4 : return "OP_NOP4";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
@ -141,6 +146,9 @@ const char* GetOpName(opcodetype opcode)
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";
// sidechains/withdraw-proofs
case OP_WITHDRAWPROOFVERIFY : return "OP_WITHDRAWPROOFVERIFY";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
// Note:
@ -201,6 +209,133 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
return subscript.GetSigOpCount(true);
}
bool CScript::IsWithdrawProof() const
{
// Format is <contract> <merkleBlock>xN <N> <locking tx>xM <M> <output index>
// for at least 6 total pushes
// Here we simply check that the script is push-only and has at least 6 pushes.
// The output must be P2SH to the appropriate script
const_iterator pc = begin();
opcodetype opcode;
uint32_t push_count = 0;
while (pc < end())
{
if (!GetOp(pc, opcode))
return false;
if (opcode > OP_16 || opcode == OP_RESERVED)
return false;
push_count++;
}
return push_count >= 6;
}
bool CScript::IsWithdrawLock() const
{
// Locks look like [<chaindest> OP_DROP] <genesishash> OP_WITHDRAWPROOFVERIFY
// This function must return true for an OP_WITHDRAWPROOFVERIFY opcode to execute.
// We require all pushes be in their minimal form, to make inspection of
// withdraw locks a purely byte-matching affair.
const_iterator pc = begin();
vector<unsigned char> data;
opcodetype opcode;
if (!GetOp(pc, opcode, data))
return false;
if (opcode == 24 && data.size() == 24) { // 4 byte type + 20 byte destination is suggested
if (!GetOp(pc, opcode, data) || opcode != OP_DROP || data.size() != 0)
return false;
if (!GetOp(pc, opcode, data))
return false;
}
if (opcode != 32 || data.size() != 32)
return false;
if (!GetOp(pc, opcode, data) || opcode != OP_WITHDRAWPROOFVERIFY || data.size() != 0)
return false;
if (GetOp(pc, opcode))
return false;
return true;
}
uint256 CScript::GetWithdrawLockGenesisHash() const
{
assert(IsWithdrawLock());
const_iterator pc = begin();
opcodetype opcode;
vector<unsigned char> vchgenesishash;
assert(GetOp(pc, opcode, vchgenesishash));
if (vchgenesishash.size() != 32) {
assert(GetOp(pc, opcode) && opcode == OP_DROP);
assert(GetOp(pc, opcode, vchgenesishash));
}
assert(vchgenesishash.size() == 32);
return uint256(vchgenesishash);
}
static bool PopWithdrawPush(vector<vector<unsigned char> >& pushes, vector<unsigned char> *read=NULL) {
if (pushes.empty())
return false;
int pushCount = CScriptNum(pushes.back(), false).getint();
pushes.pop_back();
if (pushCount < 0 || pushCount > 2000 || pushes.size() < size_t(pushCount))
return false;
for (int i = pushCount; i > 0; i--) {
if (i != 1 && pushes[pushes.size() - i].size() != 520)
return false;
if (read != NULL) {
const vector<unsigned char> &push = pushes[pushes.size() - i];
read->insert(read->end(), push.begin(), push.end());
}
}
for (int i = 0; i < pushCount; i++)
pushes.pop_back();
return true;
}
COutPoint CScript::GetWithdrawSpent() const
{
assert(IsWithdrawProof());
try {
const_iterator pc = begin();
opcodetype opcode;
// We have to read the script from back-to-front, so we stack-ize it
vector<vector<unsigned char> > pushes;
pushes.reserve(6);
while (pc < end()) {
pushes.push_back(vector<unsigned char>());
assert(GetOp(pc, opcode, pushes.back()));
if (opcode <= OP_16 && opcode >= OP_1)
pushes.back().push_back(opcode - OP_1 + 1);
else if (opcode == OP_1NEGATE)
pushes.back().push_back(0x81);
}
int ntxOut = CScriptNum(pushes.back(), false).getint();
pushes.pop_back();
vector<unsigned char> vTx;
if (!PopWithdrawPush(pushes, &vTx))
return COutPoint();
CTransactionRef tx;
CDataStream(vTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX) >> tx;
if (ntxOut < 0 || (unsigned int)ntxOut >= tx->vout.size())
return COutPoint();
return COutPoint(tx->GetHash(), ntxOut);
} catch (std::exception& e) {
return COutPoint();
}
}
bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:

View file

@ -29,6 +29,8 @@ static const int MAX_PUBKEYS_PER_MULTISIG = 20;
// Maximum script length in bytes
static const int MAX_SCRIPT_SIZE = 10000;
class uint256;
class COutPoint;
// Threshold for nLockTime: below this value it is interpreted as block number,
// otherwise as UNIX timestamp.
@ -170,6 +172,7 @@ enum opcodetype
OP_CHECKSEQUENCEVERIFY = 0xb2,
OP_NOP3 = OP_CHECKSEQUENCEVERIFY,
OP_NOP4 = 0xb3,
OP_WITHDRAWPROOFVERIFY = OP_NOP4,
OP_NOP5 = 0xb4,
OP_NOP6 = 0xb5,
OP_NOP7 = 0xb6,
@ -622,6 +625,21 @@ public:
bool IsPayToScriptHash() const;
bool IsPayToWitnessScriptHash() const;
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;
/**
* Returns true if this is a withdraw-lock scriptPubKey.
* Note that a withdraw-lock could be a plan [re-]lock output *or* a
* x-chain transfer lock.
*/
bool IsWithdrawLock() const;
/** Returns true if this is a proof-of-withdraw, spending an IsWithdrawLock */
bool IsWithdrawProof() const;
/** Get the withdraw output spent, asserting IsWithdrawProof first */
COutPoint GetWithdrawSpent() const;
/** Get the genesis hash locked to, asserting IsWithdrawLock first */
uint256 GetWithdrawLockGenesisHash() const;
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
bool IsPushOnly(const_iterator pc) const;