Changeset View
Changeset View
Standalone View
Standalone View
src/script/standard.cpp
Show All 29 Lines | switch (t) { | ||||
case TX_MULTISIG: | case TX_MULTISIG: | ||||
return "multisig"; | return "multisig"; | ||||
case TX_NULL_DATA: | case TX_NULL_DATA: | ||||
return "nulldata"; | return "nulldata"; | ||||
} | } | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, | static bool MatchPayToPubkey(const CScript &script, valtype &pubkey) { | ||||
std::vector<std::vector<uint8_t>> &vSolutionsRet) { | if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && | ||||
// Templates | script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { | ||||
static std::multimap<txnouttype, CScript> mTemplates; | pubkey = valtype(script.begin() + 1, | ||||
if (mTemplates.empty()) { | script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1); | ||||
// Standard tx, sender provides pubkey, receiver adds signature | return CPubKey::ValidSize(pubkey); | ||||
mTemplates.insert( | } | ||||
std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); | if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && | ||||
script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && | |||||
// Bitcoin address tx, sender provides hash of pubkey, receiver provides | script.back() == OP_CHECKSIG) { | ||||
// signature and pubkey | pubkey = | ||||
mTemplates.insert(std::make_pair( | valtype(script.begin() + 1, | ||||
TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH | script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1); | ||||
<< OP_EQUALVERIFY << OP_CHECKSIG)); | return CPubKey::ValidSize(pubkey); | ||||
} | |||||
// Sender provides N pubkeys, receivers provides M signatures | return false; | ||||
mTemplates.insert(std::make_pair( | } | ||||
TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS | |||||
<< OP_SMALLINTEGER << OP_CHECKMULTISIG)); | static bool MatchPayToPubkeyHash(const CScript &script, valtype &pubkeyhash) { | ||||
if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && | |||||
script[2] == 20 && script[23] == OP_EQUALVERIFY && | |||||
script[24] == OP_CHECKSIG) { | |||||
pubkeyhash = valtype(script.begin() + 3, script.begin() + 23); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */ | |||||
static constexpr bool IsSmallInteger(opcodetype opcode) { | |||||
return opcode >= OP_1 && opcode <= OP_16; | |||||
} | |||||
static bool MatchMultisig(const CScript &script, unsigned int &required, | |||||
std::vector<valtype> &pubkeys) { | |||||
opcodetype opcode; | |||||
valtype data; | |||||
CScript::const_iterator it = script.begin(); | |||||
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) { | |||||
return false; | |||||
} | |||||
if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) { | |||||
return false; | |||||
} | |||||
required = CScript::DecodeOP_N(opcode); | |||||
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { | |||||
pubkeys.emplace_back(std::move(data)); | |||||
} | |||||
if (!IsSmallInteger(opcode)) { | |||||
return false; | |||||
} | |||||
unsigned int keys = CScript::DecodeOP_N(opcode); | |||||
if (pubkeys.size() != keys || keys < required) { | |||||
return false; | |||||
} | |||||
return (it + 1 == script.end()); | |||||
} | } | ||||
bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, | |||||
std::vector<std::vector<uint8_t>> &vSolutionsRet) { | |||||
vSolutionsRet.clear(); | vSolutionsRet.clear(); | ||||
// Shortcut for pay-to-script-hash, which are more constrained than the | // Shortcut for pay-to-script-hash, which are more constrained than the | ||||
// other types: | // other types: | ||||
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL | // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL | ||||
if (scriptPubKey.IsPayToScriptHash()) { | if (scriptPubKey.IsPayToScriptHash()) { | ||||
typeRet = TX_SCRIPTHASH; | typeRet = TX_SCRIPTHASH; | ||||
std::vector<uint8_t> hashBytes(scriptPubKey.begin() + 2, | std::vector<uint8_t> hashBytes(scriptPubKey.begin() + 2, | ||||
scriptPubKey.begin() + 22); | scriptPubKey.begin() + 22); | ||||
vSolutionsRet.push_back(hashBytes); | vSolutionsRet.push_back(hashBytes); | ||||
return true; | return true; | ||||
} | } | ||||
// Provably prunable, data-carrying output | // Provably prunable, data-carrying output | ||||
// | // | ||||
// So long as script passes the IsUnspendable() test and all but the first | // So long as script passes the IsUnspendable() test and all but the first | ||||
// byte passes the IsPushOnly() test we don't care what exactly is in the | // byte passes the IsPushOnly() test we don't care what exactly is in the | ||||
// script. | // script. | ||||
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && | if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && | ||||
scriptPubKey.IsPushOnly(scriptPubKey.begin() + 1)) { | scriptPubKey.IsPushOnly(scriptPubKey.begin() + 1)) { | ||||
typeRet = TX_NULL_DATA; | typeRet = TX_NULL_DATA; | ||||
return true; | return true; | ||||
} | } | ||||
// Scan templates | std::vector<uint8_t> data; | ||||
const CScript &script1 = scriptPubKey; | if (MatchPayToPubkey(scriptPubKey, data)) { | ||||
for (const std::pair<const txnouttype, CScript> &tplate : mTemplates) { | typeRet = TX_PUBKEY; | ||||
const CScript &script2 = tplate.second; | vSolutionsRet.push_back(std::move(data)); | ||||
vSolutionsRet.clear(); | |||||
opcodetype opcode1, opcode2; | |||||
std::vector<uint8_t> vch1, vch2; | |||||
// Compare | |||||
CScript::const_iterator pc1 = script1.begin(); | |||||
CScript::const_iterator pc2 = script2.begin(); | |||||
while (true) { | |||||
if (pc1 == script1.end() && pc2 == script2.end()) { | |||||
// Found a match | |||||
typeRet = tplate.first; | |||||
if (typeRet == TX_MULTISIG) { | |||||
// Additional checks for TX_MULTISIG: | |||||
uint8_t m = vSolutionsRet.front()[0]; | |||||
uint8_t n = vSolutionsRet.back()[0]; | |||||
if (m < 1 || n < 1 || m > n || | |||||
vSolutionsRet.size() - 2 != n) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
if (!script1.GetOp(pc1, opcode1, vch1)) { | |||||
break; | |||||
} | |||||
if (!script2.GetOp(pc2, opcode2, vch2)) { | |||||
break; | |||||
} | |||||
// Template matching opcodes: | if (MatchPayToPubkeyHash(scriptPubKey, data)) { | ||||
if (opcode2 == OP_PUBKEYS) { | typeRet = TX_PUBKEYHASH; | ||||
while (CPubKey::ValidSize(vch1)) { | vSolutionsRet.push_back(std::move(data)); | ||||
vSolutionsRet.push_back(vch1); | return true; | ||||
if (!script1.GetOp(pc1, opcode1, vch1)) { | |||||
break; | |||||
} | |||||
} | |||||
if (!script2.GetOp(pc2, opcode2, vch2)) { | |||||
break; | |||||
} | |||||
// Normal situation is to fall through to other if/else | |||||
// statements | |||||
} | } | ||||
if (opcode2 == OP_PUBKEY) { | unsigned int required; | ||||
if (!CPubKey::ValidSize(vch1)) { | std::vector<std::vector<uint8_t>> keys; | ||||
break; | if (MatchMultisig(scriptPubKey, required, keys)) { | ||||
} | typeRet = TX_MULTISIG; | ||||
vSolutionsRet.push_back(vch1); | // safe as required is in range 1..16 | ||||
} else if (opcode2 == OP_PUBKEYHASH) { | vSolutionsRet.push_back({static_cast<uint8_t>(required)}); | ||||
if (vch1.size() != sizeof(uint160)) { | vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); | ||||
break; | // safe as size is in range 1..16 | ||||
} | vSolutionsRet.push_back({static_cast<uint8_t>(keys.size())}); | ||||
vSolutionsRet.push_back(vch1); | return true; | ||||
} else if (opcode2 == OP_SMALLINTEGER) { | |||||
// Single-byte small integer pushed onto vSolutions | |||||
if (opcode1 == OP_0 || (opcode1 >= OP_1 && opcode1 <= OP_16)) { | |||||
char n = (char)CScript::DecodeOP_N(opcode1); | |||||
vSolutionsRet.push_back(valtype(1, n)); | |||||
} else { | |||||
break; | |||||
} | |||||
} else if (opcode1 != opcode2 || vch1 != vch2) { | |||||
// Others must match exactly | |||||
break; | |||||
} | |||||
} | |||||
} | } | ||||
vSolutionsRet.clear(); | vSolutionsRet.clear(); | ||||
typeRet = TX_NONSTANDARD; | typeRet = TX_NONSTANDARD; | ||||
return false; | return false; | ||||
} | } | ||||
bool ExtractDestination(const CScript &scriptPubKey, | bool ExtractDestination(const CScript &scriptPubKey, | ||||
▲ Show 20 Lines • Show All 123 Lines • Show Last 20 Lines |