mirror of
https://github.com/cculianu/Fulcrum.git
synced 2026-08-18 13:09:12 +02:00
We will need this in the future to verify tx's and compose tx's The Tx class is pretty usable as-is.. just manipulates a byte stream.
500 lines
14 KiB
C++
500 lines
14 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "script.h"
|
|
|
|
#include "script_flags.h"
|
|
#include "tinyformat.h"
|
|
#include "utilstrencodings.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wold-style-cast"
|
|
#pragma clang diagnostic ignored "-Wsign-conversion"
|
|
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
|
#pragma clang diagnostic ignored "-Wunused-parameter"
|
|
#endif
|
|
|
|
namespace bitcoin {
|
|
const char *GetOpName(opcodetype opcode) {
|
|
switch (opcode) {
|
|
// push value
|
|
case OP_0:
|
|
return "0";
|
|
case OP_PUSHDATA1:
|
|
return "OP_PUSHDATA1";
|
|
case OP_PUSHDATA2:
|
|
return "OP_PUSHDATA2";
|
|
case OP_PUSHDATA4:
|
|
return "OP_PUSHDATA4";
|
|
case OP_1NEGATE:
|
|
return "-1";
|
|
case OP_RESERVED:
|
|
return "OP_RESERVED";
|
|
case OP_1:
|
|
return "1";
|
|
case OP_2:
|
|
return "2";
|
|
case OP_3:
|
|
return "3";
|
|
case OP_4:
|
|
return "4";
|
|
case OP_5:
|
|
return "5";
|
|
case OP_6:
|
|
return "6";
|
|
case OP_7:
|
|
return "7";
|
|
case OP_8:
|
|
return "8";
|
|
case OP_9:
|
|
return "9";
|
|
case OP_10:
|
|
return "10";
|
|
case OP_11:
|
|
return "11";
|
|
case OP_12:
|
|
return "12";
|
|
case OP_13:
|
|
return "13";
|
|
case OP_14:
|
|
return "14";
|
|
case OP_15:
|
|
return "15";
|
|
case OP_16:
|
|
return "16";
|
|
|
|
// control
|
|
case OP_NOP:
|
|
return "OP_NOP";
|
|
case OP_VER:
|
|
return "OP_VER";
|
|
case OP_IF:
|
|
return "OP_IF";
|
|
case OP_NOTIF:
|
|
return "OP_NOTIF";
|
|
case OP_VERIF:
|
|
return "OP_VERIF";
|
|
case OP_VERNOTIF:
|
|
return "OP_VERNOTIF";
|
|
case OP_ELSE:
|
|
return "OP_ELSE";
|
|
case OP_ENDIF:
|
|
return "OP_ENDIF";
|
|
case OP_VERIFY:
|
|
return "OP_VERIFY";
|
|
case OP_RETURN:
|
|
return "OP_RETURN";
|
|
|
|
// stack ops
|
|
case OP_TOALTSTACK:
|
|
return "OP_TOALTSTACK";
|
|
case OP_FROMALTSTACK:
|
|
return "OP_FROMALTSTACK";
|
|
case OP_2DROP:
|
|
return "OP_2DROP";
|
|
case OP_2DUP:
|
|
return "OP_2DUP";
|
|
case OP_3DUP:
|
|
return "OP_3DUP";
|
|
case OP_2OVER:
|
|
return "OP_2OVER";
|
|
case OP_2ROT:
|
|
return "OP_2ROT";
|
|
case OP_2SWAP:
|
|
return "OP_2SWAP";
|
|
case OP_IFDUP:
|
|
return "OP_IFDUP";
|
|
case OP_DEPTH:
|
|
return "OP_DEPTH";
|
|
case OP_DROP:
|
|
return "OP_DROP";
|
|
case OP_DUP:
|
|
return "OP_DUP";
|
|
case OP_NIP:
|
|
return "OP_NIP";
|
|
case OP_OVER:
|
|
return "OP_OVER";
|
|
case OP_PICK:
|
|
return "OP_PICK";
|
|
case OP_ROLL:
|
|
return "OP_ROLL";
|
|
case OP_ROT:
|
|
return "OP_ROT";
|
|
case OP_SWAP:
|
|
return "OP_SWAP";
|
|
case OP_TUCK:
|
|
return "OP_TUCK";
|
|
|
|
// splice ops
|
|
case OP_CAT:
|
|
return "OP_CAT";
|
|
case OP_SPLIT:
|
|
return "OP_SPLIT";
|
|
case OP_NUM2BIN:
|
|
return "OP_NUM2BIN";
|
|
case OP_BIN2NUM:
|
|
return "OP_BIN2NUM";
|
|
case OP_SIZE:
|
|
return "OP_SIZE";
|
|
|
|
// bit logic
|
|
case OP_INVERT:
|
|
return "OP_INVERT";
|
|
case OP_AND:
|
|
return "OP_AND";
|
|
case OP_OR:
|
|
return "OP_OR";
|
|
case OP_XOR:
|
|
return "OP_XOR";
|
|
case OP_EQUAL:
|
|
return "OP_EQUAL";
|
|
case OP_EQUALVERIFY:
|
|
return "OP_EQUALVERIFY";
|
|
case OP_RESERVED1:
|
|
return "OP_RESERVED1";
|
|
case OP_RESERVED2:
|
|
return "OP_RESERVED2";
|
|
|
|
// numeric
|
|
case OP_1ADD:
|
|
return "OP_1ADD";
|
|
case OP_1SUB:
|
|
return "OP_1SUB";
|
|
case OP_2MUL:
|
|
return "OP_2MUL";
|
|
case OP_2DIV:
|
|
return "OP_2DIV";
|
|
case OP_NEGATE:
|
|
return "OP_NEGATE";
|
|
case OP_ABS:
|
|
return "OP_ABS";
|
|
case OP_NOT:
|
|
return "OP_NOT";
|
|
case OP_0NOTEQUAL:
|
|
return "OP_0NOTEQUAL";
|
|
case OP_ADD:
|
|
return "OP_ADD";
|
|
case OP_SUB:
|
|
return "OP_SUB";
|
|
case OP_MUL:
|
|
return "OP_MUL";
|
|
case OP_DIV:
|
|
return "OP_DIV";
|
|
case OP_MOD:
|
|
return "OP_MOD";
|
|
case OP_LSHIFT:
|
|
return "OP_LSHIFT";
|
|
case OP_RSHIFT:
|
|
return "OP_RSHIFT";
|
|
case OP_BOOLAND:
|
|
return "OP_BOOLAND";
|
|
case OP_BOOLOR:
|
|
return "OP_BOOLOR";
|
|
case OP_NUMEQUAL:
|
|
return "OP_NUMEQUAL";
|
|
case OP_NUMEQUALVERIFY:
|
|
return "OP_NUMEQUALVERIFY";
|
|
case OP_NUMNOTEQUAL:
|
|
return "OP_NUMNOTEQUAL";
|
|
case OP_LESSTHAN:
|
|
return "OP_LESSTHAN";
|
|
case OP_GREATERTHAN:
|
|
return "OP_GREATERTHAN";
|
|
case OP_LESSTHANOREQUAL:
|
|
return "OP_LESSTHANOREQUAL";
|
|
case OP_GREATERTHANOREQUAL:
|
|
return "OP_GREATERTHANOREQUAL";
|
|
case OP_MIN:
|
|
return "OP_MIN";
|
|
case OP_MAX:
|
|
return "OP_MAX";
|
|
case OP_WITHIN:
|
|
return "OP_WITHIN";
|
|
|
|
// crypto
|
|
case OP_RIPEMD160:
|
|
return "OP_RIPEMD160";
|
|
case OP_SHA1:
|
|
return "OP_SHA1";
|
|
case OP_SHA256:
|
|
return "OP_SHA256";
|
|
case OP_HASH160:
|
|
return "OP_HASH160";
|
|
case OP_HASH256:
|
|
return "OP_HASH256";
|
|
case OP_CODESEPARATOR:
|
|
return "OP_CODESEPARATOR";
|
|
case OP_CHECKSIG:
|
|
return "OP_CHECKSIG";
|
|
case OP_CHECKSIGVERIFY:
|
|
return "OP_CHECKSIGVERIFY";
|
|
case OP_CHECKMULTISIG:
|
|
return "OP_CHECKMULTISIG";
|
|
case OP_CHECKMULTISIGVERIFY:
|
|
return "OP_CHECKMULTISIGVERIFY";
|
|
case OP_CHECKDATASIG:
|
|
return "OP_CHECKDATASIG";
|
|
case OP_CHECKDATASIGVERIFY:
|
|
return "OP_CHECKDATASIGVERIFY";
|
|
|
|
// expansion
|
|
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";
|
|
case OP_NOP8:
|
|
return "OP_NOP8";
|
|
case OP_NOP9:
|
|
return "OP_NOP9";
|
|
case OP_NOP10:
|
|
return "OP_NOP10";
|
|
|
|
case OP_INVALIDOPCODE:
|
|
return "OP_INVALIDOPCODE";
|
|
|
|
// Note:
|
|
// The template matching params OP_SMALLINTEGER/etc are defined in
|
|
// opcodetype enum as kind of implementation hack, they are *NOT*
|
|
// real opcodes. If found in real Script, just let the default:
|
|
// case deal with them.
|
|
|
|
default:
|
|
return "OP_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
bool CScriptNum::IsMinimallyEncoded(const std::vector<uint8_t> &vch,
|
|
const size_t nMaxNumSize) {
|
|
if (vch.size() > nMaxNumSize) {
|
|
return false;
|
|
}
|
|
|
|
if (vch.size() > 0) {
|
|
// Check that the number is encoded with the minimum possible number
|
|
// of bytes.
|
|
//
|
|
// If the most-significant-byte - excluding the sign bit - is zero
|
|
// then we're not minimal. Note how this test also rejects the
|
|
// negative-zero encoding, 0x80.
|
|
if ((vch.back() & 0x7f) == 0) {
|
|
// One exception: if there's more than one byte and the most
|
|
// significant bit of the second-most-significant-byte is set it
|
|
// would conflict with the sign bit. An example of this case is
|
|
// +-255, which encode to 0xff00 and 0xff80 respectively.
|
|
// (big-endian).
|
|
if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScriptNum::MinimallyEncode(std::vector<uint8_t> &data) {
|
|
if (data.size() == 0) {
|
|
return false;
|
|
}
|
|
|
|
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
|
|
uint8_t last = data.back();
|
|
if (last & 0x7f) {
|
|
return false;
|
|
}
|
|
|
|
// If the script is one byte long, then we have a zero, which encodes as an
|
|
// empty array.
|
|
if (data.size() == 1) {
|
|
data = {};
|
|
return true;
|
|
}
|
|
|
|
// If the next byte has it sign bit set, then we are minimaly encoded.
|
|
if (data[data.size() - 2] & 0x80) {
|
|
return false;
|
|
}
|
|
|
|
// We are not minimally encoded, we need to figure out how much to trim.
|
|
for (size_t i = data.size() - 1; i > 0; i--) {
|
|
// We found a non zero byte, time to encode.
|
|
if (data[i - 1] != 0) {
|
|
if (data[i - 1] & 0x80) {
|
|
// We found a byte with it sign bit set so we need one more
|
|
// byte.
|
|
data[i++] = last;
|
|
} else {
|
|
// the sign bit is clear, we can use it.
|
|
data[i - 1] |= last;
|
|
}
|
|
|
|
data.resize(i);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If we the whole thing is zeros, then we have a zero.
|
|
data = {};
|
|
return true;
|
|
}
|
|
|
|
uint32_t CScript::GetSigOpCount(uint32_t flags, bool fAccurate) const {
|
|
uint32_t n = 0;
|
|
const_iterator pc = begin();
|
|
opcodetype lastOpcode = OP_INVALIDOPCODE;
|
|
while (pc < end()) {
|
|
opcodetype opcode;
|
|
if (!GetOp(pc, opcode)) {
|
|
break;
|
|
}
|
|
|
|
switch (opcode) {
|
|
case OP_CHECKSIG:
|
|
case OP_CHECKSIGVERIFY:
|
|
n++;
|
|
break;
|
|
|
|
case OP_CHECKDATASIG:
|
|
case OP_CHECKDATASIGVERIFY:
|
|
if (flags & SCRIPT_ENABLE_CHECKDATASIG) {
|
|
n++;
|
|
}
|
|
break;
|
|
|
|
case OP_CHECKMULTISIG:
|
|
case OP_CHECKMULTISIGVERIFY:
|
|
if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) {
|
|
n += DecodeOP_N(lastOpcode);
|
|
} else {
|
|
n += MAX_PUBKEYS_PER_MULTISIG;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
lastOpcode = opcode;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
uint32_t CScript::GetSigOpCount(uint32_t flags,
|
|
const CScript &scriptSig) const {
|
|
if ((flags & SCRIPT_VERIFY_P2SH) == 0 || !IsPayToScriptHash()) {
|
|
return GetSigOpCount(flags, true);
|
|
}
|
|
|
|
// This is a pay-to-script-hash scriptPubKey;
|
|
// get the last item that the scriptSig
|
|
// pushes onto the stack:
|
|
const_iterator pc = scriptSig.begin();
|
|
std::vector<uint8_t> vData;
|
|
while (pc < scriptSig.end()) {
|
|
opcodetype opcode;
|
|
if (!scriptSig.GetOp(pc, opcode, vData)) {
|
|
return 0;
|
|
}
|
|
if (opcode > OP_16) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// ... and return its opcount:
|
|
CScript subscript(vData.begin(), vData.end());
|
|
return subscript.GetSigOpCount(flags, true);
|
|
}
|
|
|
|
bool CScript::IsPayToScriptHash() const {
|
|
// Extra-fast test for pay-to-script-hash CScripts:
|
|
return (this->size() == 23 && (*this)[0] == OP_HASH160 &&
|
|
(*this)[1] == 0x14 && (*this)[22] == OP_EQUAL);
|
|
}
|
|
|
|
bool CScript::IsCommitment(const std::vector<uint8_t> &data) const {
|
|
// To ensure we have an immediate push, we limit the commitment size to 64
|
|
// bytes. In addition to the data themselves, we have 2 extra bytes:
|
|
// OP_RETURN and the push opcode itself.
|
|
if (data.size() > 64 || this->size() != data.size() + 2) {
|
|
return false;
|
|
}
|
|
|
|
if ((*this)[0] != OP_RETURN || (*this)[1] != data.size()) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < data.size(); i++) {
|
|
if ((*this)[i + 2] != data[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A witness program is any valid CScript that consists of a 1-byte push opcode
|
|
// followed by a data push between 2 and 40 bytes.
|
|
bool CScript::IsWitnessProgram(int &version,
|
|
std::vector<uint8_t> &program) const {
|
|
if (this->size() < 4 || this->size() > 42) {
|
|
return false;
|
|
}
|
|
if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) {
|
|
return false;
|
|
}
|
|
if (size_t((*this)[1] + 2) == this->size()) {
|
|
version = DecodeOP_N((opcodetype)(*this)[0]);
|
|
program = std::vector<uint8_t>(this->begin() + 2, this->end());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Wrapper returning only the predicate
|
|
bool CScript::IsWitnessProgram() const {
|
|
int version;
|
|
std::vector<uint8_t> program;
|
|
return IsWitnessProgram(version, program);
|
|
}
|
|
|
|
bool CScript::IsPushOnly(const_iterator pc) const {
|
|
while (pc < end()) {
|
|
opcodetype opcode;
|
|
if (!GetOp(pc, opcode)) {
|
|
return false;
|
|
}
|
|
|
|
// Note that IsPushOnly() *does* consider OP_RESERVED to be a push-type
|
|
// opcode, however execution of OP_RESERVED fails, so it's not relevant
|
|
// to P2SH/BIP62 as the scriptSig would fail prior to the P2SH special
|
|
// validation code being executed.
|
|
if (opcode > OP_16) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CScript::IsPushOnly() const {
|
|
return this->IsPushOnly(begin());
|
|
}
|
|
|
|
} // end namespace bitcoin
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|