Skip to content

Commit

Permalink
Merklized script
Browse files Browse the repository at this point in the history
  • Loading branch information
jl2012 committed Sep 11, 2017
1 parent 16e4184 commit f3f201d
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_LOW_S |
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
SCRIPT_VERIFY_MSV0;

/** For convenience, standard but not mandatory verify flags. */
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
Expand Down
4 changes: 3 additions & 1 deletion src/script/bitcoinconsensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ enum
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_MSV0 = (1U << 16), // enable MSV0 (BIP114)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG |
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY |
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS |
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_MSV0
};

/// Returns 1 if the input nIn of the serialized transaction pointed to by
Expand Down
149 changes: 148 additions & 1 deletion src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
#include "consensus/merkle.h"

typedef std::vector<unsigned char> valtype;

Expand Down Expand Up @@ -244,6 +245,12 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
}

bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
{
int nOpCount = 0;
return EvalScript(stack, script, flags, checker, sigversion, nOpCount, serror);
}

bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, int& nOpCount, ScriptError* serror)
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
Expand All @@ -263,7 +270,6 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > MAX_SCRIPT_SIZE)
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;

try
Expand Down Expand Up @@ -1351,6 +1357,79 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con
return true;
}

bool IsMSStack(std::vector<uint256>& vPath, uint32_t& nPosition, std::vector<std::vector<unsigned char> >& stack, std::vector<unsigned char>& vchKeyCode)
{
if (stack.size() < 3)
return false;

// The last stack item is script version and script. The minimum valid size is 33 bytes. Since public key size is
// 33 bytes, any scripts smaller than this are not safe. A minimum size is needed because an attacker may try to
// execute a 32-byte intermediate hash as a script.
vchKeyCode = stack.back();
if (vchKeyCode.size() < 33)
return false;
stack.pop_back();

// The second last stack item is the path. Size must be 0 to 1024, and divisible by 32
// Depth of the Merkle tree is implied by the size of path (0 to 32)
if (stack.back().size() & 0x1f || stack.back().size() > 1024)
return false;
const size_t depth = stack.back().size() >> 5;
// Path is a vector of 32-byte hashes
vPath.resize(depth);
for (unsigned int j = 0; j < depth; j++)
memcpy(vPath[j].begin(), &stack.back()[32 * j], 32);
stack.pop_back();

// The third last item encodes the position (as CScriptNum). It must be minimally encoded
try
{
const CScriptNum pos(stack.back(), true, 5);
if (pos < 0 || pos >= (1ULL << depth))
return false;
nPosition = pos.getint64();
}
catch (...)
{
return false;
}

stack.pop_back();

// Unused stack items become the stack of the following step
return true;
}

bool IsMSV0Stack(std::vector<std::vector<unsigned char> >& stack, std::vector<CScript>& vscriptWitCode)
{
if (stack.size() < 1)
return false;

// The last item is number of scriptWitCode (0 to 5). It must be minimally encoded
unsigned char nScriptWitCode = 0;
if (stack.back().size() > 0) {
nScriptWitCode = stack.back().at(0);
if (nScriptWitCode == 0 || nScriptWitCode > MAX_MSV0_SCRIPTWITCODE || stack.back().size() > 1)
return false;
}
stack.pop_back();

if (stack.size() < nScriptWitCode)
return false;
vscriptWitCode.clear();
vscriptWitCode.resize(MAX_MSV0_SCRIPTWITCODE);
for (size_t i = 0; i < nScriptWitCode; i++) {
// The first defined scriptWitCode must not be empty
if (i == nScriptWitCode - 1 && !stack.back().size())
return false;
vscriptWitCode.at(i) = CScript(stack.back().begin(), stack.back().end());
stack.pop_back();
}

// Unused stack items become the stack for script evaluation
return true;
}

static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
std::vector<std::vector<unsigned char> > stack;
Expand Down Expand Up @@ -1379,6 +1458,74 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
} else {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
}
} else if (witversion == 1 && program.size() >= 32 && (flags & SCRIPT_VERIFY_MSV0)) {
std::vector<unsigned char> vchKeyCode;
stack = witness.stack;
if (program.size() == 32) {
uint256 hashScript;
std::vector<uint256> vPath;
uint32_t nPosition;

if (!IsMSStack(vPath, nPosition, stack, vchKeyCode))
return set_error(serror, SCRIPT_ERR_INVALID_MS_STACK);

// Calculate the script hash.
CSHA256().Write(&vchKeyCode[0], vchKeyCode.size()).Finalize(hashScript.begin());

// Calculate MAST Root and compare against witness program
uint256 hashScriptRoot = ComputeMerkleRootFromBranch(hashScript, vPath, nPosition);
if (memcmp(hashScriptRoot.begin(), &program[0], 32))
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
else if (program.size() == 33) {
vchKeyCode.resize(36);
vchKeyCode[0] = 0;
vchKeyCode[1] = 0x21;
memcpy(&vchKeyCode[2], &program[0], 33);
vchKeyCode[35] = OP_CHECKSIG;
}

if (vchKeyCode.size() > 0 && vchKeyCode[0] == 0) {
std::vector<CScript> vscriptWitCode;
if (!IsMSV0Stack(stack, vscriptWitCode))
return set_error(serror, SCRIPT_ERR_INVALID_MS_STACK);

// Check the size of input stack
for (unsigned int i = 0; i < stack.size(); i++) {
if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
}

// Check script size and evaluate scripts
size_t nTotalScriptSize = 0;
int nOpCount = 0;
for (size_t i = 0; i < MAX_MSV0_SCRIPTWITCODE; i++) {
const CScript& scriptWitCode = vscriptWitCode[MAX_MSV0_SCRIPTWITCODE - i - 1];
if (scriptWitCode.size() > 0) {
nTotalScriptSize += scriptWitCode.size();
if (nTotalScriptSize > MAX_SCRIPT_SIZE)
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
if (!EvalScript(stack, scriptWitCode, flags, checker, SIGVERSION_MSV0, nOpCount, serror))
return false;
}
}

nTotalScriptSize += (vchKeyCode.size() - 1);
if (nTotalScriptSize > MAX_SCRIPT_SIZE)
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);

CScript scriptKeyCode = CScript(vchKeyCode.begin() + 1, vchKeyCode.end());
if (!EvalScript(stack, scriptKeyCode, flags, checker, SIGVERSION_MSV0, nOpCount, serror))
return false;

if (stack.size() != 1)
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
if (!CastToBool(stack.back()))
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
}
else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
return set_success(serror);
} else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
} else {
Expand Down
8 changes: 7 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ enum
//
SCRIPT_VERIFY_WITNESS = (1U << 11),

// Making v1-v16 witness program non-standard
// Making undefined witness program non-standard
//
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12),

Expand All @@ -106,6 +106,10 @@ enum
// Public keys in segregated witness scripts must be compressed
//
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),

// Support MSV0 and new opcode. Should not be used without WITNESS
//
SCRIPT_VERIFY_MSV0 = (1U << 16),
};

bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
Expand All @@ -121,6 +125,7 @@ enum SigVersion
{
SIGVERSION_BASE = 0,
SIGVERSION_WITNESS_V0 = 1,
SIGVERSION_MSV0 = 2,
};

uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
Expand Down Expand Up @@ -175,6 +180,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker
};

bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, int& nOpCount, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);

size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
Expand Down
2 changes: 2 additions & 0 deletions src/script/ismine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_WITNESS_V1_MSHASH:
case TX_WITNESS_V1_PUBKEY_V0:
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
Expand Down
8 changes: 8 additions & 0 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ static const int MAX_STACK_SIZE = 1000;
// otherwise as UNIX timestamp.
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC

// Maximum number of scriptWitCode in MSV0
static const unsigned int MAX_MSV0_SCRIPTWITCODE = 5;

template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
Expand Down Expand Up @@ -318,6 +321,11 @@ class CScriptNum
return m_value;
}

int64_t getint64() const
{
return m_value;
}

std::vector<unsigned char> getvch() const
{
return serialize(m_value);
Expand Down
2 changes: 2 additions & 0 deletions src/script/script_error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "Witness provided for non-witness script";
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
return "Using non-compressed keys in segwit";
case SCRIPT_ERR_INVALID_MS_STACK:
return "Invalid merklized script stack";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;
Expand Down
1 change: 1 addition & 0 deletions src/script/script_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_MALLEATED_P2SH,
SCRIPT_ERR_WITNESS_UNEXPECTED,
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
SCRIPT_ERR_INVALID_MS_STACK,

SCRIPT_ERR_ERROR_COUNT
} ScriptError;
Expand Down
11 changes: 11 additions & 0 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const char* GetTxnOutputType(txnouttype t)
case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_V1_MSHASH: return "witness_v1_mshash";
case TX_WITNESS_V1_PUBKEY_V0: return "witness_v1_pubkey_v0";
}
return nullptr;
}
Expand Down Expand Up @@ -75,6 +77,15 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
vSolutionsRet.push_back(witnessprogram);
return true;
}
if (witnessversion == 1 && witnessprogram.size() == 32) {
typeRet = TX_WITNESS_V1_MSHASH;
vSolutionsRet.push_back(witnessprogram);
return true;
}
if (witnessversion == 1 && witnessprogram.size() == 33) {
typeRet = TX_WITNESS_V1_PUBKEY_V0;
vSolutionsRet.push_back(witnessprogram);
}
return false;
}

Expand Down
2 changes: 2 additions & 0 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ enum txnouttype
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
TX_WITNESS_V1_MSHASH,
TX_WITNESS_V1_PUBKEY_V0,
};

class CNoDestination {
Expand Down
1 change: 1 addition & 0 deletions src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"},
{SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"},
{SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"},
{SCRIPT_ERR_INVALID_MS_STACK, "INVALID_MS_STACK"},
};

const char *FormatScriptError(ScriptError_t err)
Expand Down

0 comments on commit f3f201d

Please sign in to comment.