Merge branch 'FSM' of https://github.com/jl777/komodo into jl777-FSM
merge
This commit is contained in:
@@ -86,6 +86,14 @@ CScript CCSig(const CC *cond)
|
||||
return CScript() << ffill;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> CCSigVec(const CC *cond)
|
||||
{
|
||||
unsigned char buf[10000];
|
||||
size_t len = cc_fulfillmentBinary(cond, buf, 10000);
|
||||
auto ffill = std::vector<unsigned char>(buf, buf+len);
|
||||
ffill.push_back(1); // SIGHASH_ALL
|
||||
return ffill;
|
||||
}
|
||||
|
||||
std::string CCShowStructure(CC *cond)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,12 @@ CScript CCPubKey(const CC *cond);
|
||||
*/
|
||||
CScript CCSig(const CC *cond);
|
||||
|
||||
/*
|
||||
* Turn a condition into a scriptSig
|
||||
*
|
||||
* Note: This will fail in undefined ways if the condition is missing signatures
|
||||
*/
|
||||
std::vector<unsigned char> CCSigVec(const CC *cond);
|
||||
|
||||
/*
|
||||
* Produces a string showing the structure of a CC condition
|
||||
|
||||
@@ -191,7 +191,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||
if (vchSig.size() == 0) {
|
||||
@@ -843,6 +843,10 @@ bool EvalScript(
|
||||
}
|
||||
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, script, consensusBranchId);
|
||||
|
||||
// comment below when not debugging
|
||||
//printf("OP_CHECKSIG: scriptSig.%s\nscriptPubKey.%s\nbranchid.%x, success: %s\n",
|
||||
// CScript(vchSig).ToString().c_str(), CScript(vchPubKey).ToString().c_str(), consensusBranchId, (fSuccess ? "true" : "false"));
|
||||
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
stack.push_back(fSuccess ? vchTrue : vchFalse);
|
||||
@@ -952,7 +956,7 @@ bool EvalScript(
|
||||
|
||||
if (stack.size() < 2)
|
||||
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
|
||||
//fprintf(stderr,"check cryptocondition\n");
|
||||
//fprintf(stderr,"check cryptocondition\n");
|
||||
int fResult = checker.CheckCryptoCondition(stacktop(-1), stacktop(-2), script, consensusBranchId);
|
||||
if (fResult == -1) {
|
||||
return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT);
|
||||
@@ -1018,7 +1022,7 @@ public:
|
||||
|
||||
/** Serialize the passed scriptCode */
|
||||
template<typename S>
|
||||
void SerializeScriptCode(S &s, int nType, int nVersion) const {
|
||||
void SerializeScriptCode(S &s) const {
|
||||
auto size = scriptCode.size();
|
||||
::WriteCompactSize(s, size);
|
||||
s.write((char*)&scriptCode.begin()[0], size);
|
||||
@@ -1026,54 +1030,54 @@ public:
|
||||
|
||||
/** Serialize an input of txTo */
|
||||
template<typename S>
|
||||
void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const {
|
||||
void SerializeInput(S &s, unsigned int nInput) const {
|
||||
// In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
|
||||
if (fAnyoneCanPay)
|
||||
nInput = nIn;
|
||||
// Serialize the prevout
|
||||
::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
|
||||
::Serialize(s, txTo.vin[nInput].prevout);
|
||||
// Serialize the script
|
||||
assert(nInput != NOT_AN_INPUT);
|
||||
if (nInput != nIn)
|
||||
// Blank out other inputs' signatures
|
||||
::Serialize(s, CScript(), nType, nVersion);
|
||||
::Serialize(s, CScriptBase());
|
||||
else
|
||||
SerializeScriptCode(s, nType, nVersion);
|
||||
SerializeScriptCode(s);
|
||||
// Serialize the nSequence
|
||||
if (nInput != nIn && (fHashSingle || fHashNone))
|
||||
// let the others update at will
|
||||
::Serialize(s, (int)0, nType, nVersion);
|
||||
::Serialize(s, (int)0);
|
||||
else
|
||||
::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion);
|
||||
::Serialize(s, txTo.vin[nInput].nSequence);
|
||||
}
|
||||
|
||||
/** Serialize an output of txTo */
|
||||
template<typename S>
|
||||
void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const {
|
||||
void SerializeOutput(S &s, unsigned int nOutput) const {
|
||||
if (fHashSingle && nOutput != nIn)
|
||||
// Do not lock-in the txout payee at other indices as txin
|
||||
::Serialize(s, CTxOut(), nType, nVersion);
|
||||
::Serialize(s, CTxOut());
|
||||
else
|
||||
::Serialize(s, txTo.vout[nOutput], nType, nVersion);
|
||||
::Serialize(s, txTo.vout[nOutput]);
|
||||
}
|
||||
|
||||
/** Serialize txTo */
|
||||
template<typename S>
|
||||
void Serialize(S &s, int nType, int nVersion) const {
|
||||
void Serialize(S &s) const {
|
||||
// Serialize nVersion
|
||||
::Serialize(s, txTo.nVersion, nType, nVersion);
|
||||
::Serialize(s, txTo.nVersion);
|
||||
// Serialize vin
|
||||
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
|
||||
::WriteCompactSize(s, nInputs);
|
||||
for (unsigned int nInput = 0; nInput < nInputs; nInput++)
|
||||
SerializeInput(s, nInput, nType, nVersion);
|
||||
SerializeInput(s, nInput);
|
||||
// Serialize vout
|
||||
unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
|
||||
::WriteCompactSize(s, nOutputs);
|
||||
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
|
||||
SerializeOutput(s, nOutput, nType, nVersion);
|
||||
SerializeOutput(s, nOutput);
|
||||
// Serialize nLockTime
|
||||
::Serialize(s, txTo.nLockTime, nType, nVersion);
|
||||
::Serialize(s, txTo.nLockTime);
|
||||
|
||||
// Serialize vjoinsplit
|
||||
if (txTo.nVersion >= 2) {
|
||||
@@ -1083,12 +1087,12 @@ public:
|
||||
// keeps the JoinSplit cryptographically bound
|
||||
// to the transaction.
|
||||
//
|
||||
::Serialize(s, txTo.vjoinsplit, nType, nVersion);
|
||||
::Serialize(s, txTo.vjoinsplit);
|
||||
if (txTo.vjoinsplit.size() > 0) {
|
||||
::Serialize(s, txTo.joinSplitPubKey, nType, nVersion);
|
||||
::Serialize(s, txTo.joinSplitPubKey);
|
||||
|
||||
CTransaction::joinsplit_sig_t nullSig = {};
|
||||
::Serialize(s, nullSig, nType, nVersion);
|
||||
::Serialize(s, nullSig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1102,6 +1106,10 @@ const unsigned char ZCASH_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2
|
||||
{'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'};
|
||||
const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'};
|
||||
const unsigned char ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z','c','a','s','h','S','S','p','e','n','d','s','H','a','s','h'};
|
||||
const unsigned char ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z','c','a','s','h','S','O','u','t','p','u','t','H','a','s','h'};
|
||||
|
||||
uint256 GetPrevoutHash(const CTransaction& txTo) {
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION);
|
||||
@@ -1128,7 +1136,7 @@ uint256 GetOutputsHash(const CTransaction& txTo) {
|
||||
}
|
||||
|
||||
uint256 GetJoinSplitsHash(const CTransaction& txTo) {
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_JOINSPLITS_HASH_PERSONALIZATION);
|
||||
CBLAKE2bWriter ss(SER_GETHASH, static_cast<int>(txTo.GetHeader()), ZCASH_JOINSPLITS_HASH_PERSONALIZATION);
|
||||
for (unsigned int n = 0; n < txTo.vjoinsplit.size(); n++) {
|
||||
ss << txTo.vjoinsplit[n];
|
||||
}
|
||||
@@ -1136,6 +1144,26 @@ uint256 GetJoinSplitsHash(const CTransaction& txTo) {
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
uint256 GetShieldedSpendsHash(const CTransaction& txTo) {
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION);
|
||||
for (unsigned int n = 0; n < txTo.vShieldedSpend.size(); n++) {
|
||||
ss << txTo.vShieldedSpend[n].cv;
|
||||
ss << txTo.vShieldedSpend[n].anchor;
|
||||
ss << txTo.vShieldedSpend[n].nullifier;
|
||||
ss << txTo.vShieldedSpend[n].rk;
|
||||
ss << txTo.vShieldedSpend[n].zkproof;
|
||||
}
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
uint256 GetShieldedOutputsHash(const CTransaction& txTo) {
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION);
|
||||
for (unsigned int n = 0; n < txTo.vShieldedOutput.size(); n++) {
|
||||
ss << txTo.vShieldedOutput[n];
|
||||
}
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||
@@ -1144,12 +1172,18 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||
hashSequence = GetSequenceHash(txTo);
|
||||
hashOutputs = GetOutputsHash(txTo);
|
||||
hashJoinSplits = GetJoinSplitsHash(txTo);
|
||||
hashShieldedSpends = GetShieldedSpendsHash(txTo);
|
||||
hashShieldedOutputs = GetShieldedOutputsHash(txTo);
|
||||
}
|
||||
|
||||
SigVersion SignatureHashVersion(const CTransaction& txTo)
|
||||
{
|
||||
if (txTo.fOverwintered) {
|
||||
return SIGVERSION_OVERWINTER;
|
||||
if (txTo.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
||||
return SIGVERSION_SAPLING;
|
||||
} else {
|
||||
return SIGVERSION_OVERWINTER;
|
||||
}
|
||||
} else {
|
||||
return SIGVERSION_SPROUT;
|
||||
}
|
||||
@@ -1171,11 +1205,13 @@ uint256 SignatureHash(
|
||||
|
||||
auto sigversion = SignatureHashVersion(txTo);
|
||||
|
||||
if (sigversion == SIGVERSION_OVERWINTER) {
|
||||
if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {
|
||||
uint256 hashPrevouts;
|
||||
uint256 hashSequence;
|
||||
uint256 hashOutputs;
|
||||
uint256 hashJoinSplits;
|
||||
uint256 hashShieldedSpends;
|
||||
uint256 hashShieldedOutputs;
|
||||
|
||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
|
||||
@@ -1197,6 +1233,14 @@ uint256 SignatureHash(
|
||||
hashJoinSplits = cache ? cache->hashJoinSplits : GetJoinSplitsHash(txTo);
|
||||
}
|
||||
|
||||
if (!txTo.vShieldedSpend.empty()) {
|
||||
hashShieldedSpends = cache ? cache->hashShieldedSpends : GetShieldedSpendsHash(txTo);
|
||||
}
|
||||
|
||||
if (!txTo.vShieldedOutput.empty()) {
|
||||
hashShieldedOutputs = cache ? cache->hashShieldedOutputs : GetShieldedOutputsHash(txTo);
|
||||
}
|
||||
|
||||
uint32_t leConsensusBranchId = htole32(consensusBranchId);
|
||||
unsigned char personalization[16] = {};
|
||||
memcpy(personalization, "ZcashSigHash", 12);
|
||||
@@ -1214,10 +1258,20 @@ uint256 SignatureHash(
|
||||
ss << hashOutputs;
|
||||
// JoinSplits
|
||||
ss << hashJoinSplits;
|
||||
if (sigversion == SIGVERSION_SAPLING) {
|
||||
// Spend descriptions
|
||||
ss << hashShieldedSpends;
|
||||
// Output descriptions
|
||||
ss << hashShieldedOutputs;
|
||||
}
|
||||
// Locktime
|
||||
ss << txTo.nLockTime;
|
||||
// Expiry height
|
||||
ss << txTo.nExpiryHeight;
|
||||
if (sigversion == SIGVERSION_SAPLING) {
|
||||
// Sapling value balance
|
||||
ss << txTo.valueBalance;
|
||||
}
|
||||
// Sighash type
|
||||
ss << nHashType;
|
||||
|
||||
@@ -1228,7 +1282,7 @@ uint256 SignatureHash(
|
||||
// The prevout may already be contained in hashPrevout, and the nSequence
|
||||
// may already be contained in hashSequence.
|
||||
ss << txTo.vin[nIn].prevout;
|
||||
ss << scriptCode;
|
||||
ss << static_cast<const CScriptBase&>(scriptCode);
|
||||
ss << amount;
|
||||
ss << txTo.vin[nIn].nSequence;
|
||||
}
|
||||
@@ -1300,8 +1354,9 @@ int TransactionSignatureChecker::CheckCryptoCondition(
|
||||
if (ffillBin.empty())
|
||||
return false;
|
||||
|
||||
CC *cond = cc_readFulfillmentBinary((unsigned char*)ffillBin.data(), ffillBin.size()-1);
|
||||
if (!cond) return -1;
|
||||
CC *cond;
|
||||
int error = cc_readFulfillmentBinaryExt((unsigned char*)ffillBin.data(), ffillBin.size()-1, &cond);
|
||||
if (error || !cond) return -1;
|
||||
|
||||
if (!IsSupportedCryptoCondition(cond)) return 0;
|
||||
if (!IsSignedCryptoCondition(cond)) return 0;
|
||||
@@ -1309,7 +1364,7 @@ int TransactionSignatureChecker::CheckCryptoCondition(
|
||||
uint256 sighash;
|
||||
int nHashType = ffillBin.back();
|
||||
try {
|
||||
sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata);
|
||||
sighash = SignatureHash(CCPubKey(cond), *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata);
|
||||
} catch (logic_error ex) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1337,8 +1392,8 @@ int TransactionSignatureChecker::CheckCryptoCondition(
|
||||
|
||||
int TransactionSignatureChecker::CheckEvalCondition(const CC *cond) const
|
||||
{
|
||||
fprintf(stderr, "Cannot check crypto-condition Eval outside of server\n");
|
||||
return 0;
|
||||
//fprintf(stderr, "Cannot check crypto-condition Eval outside of server, returning true in pre-checks\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1445,9 +1500,15 @@ bool VerifyScript(
|
||||
// serror is set
|
||||
return false;
|
||||
if (stack.empty())
|
||||
{
|
||||
//printf("interpreter stack is empty, comment this debugging message\nscriptSig: %s\nscriptPubKey: %s\n",scriptSig.ToString().c_str(),scriptPubKey.ToString().c_str());
|
||||
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
|
||||
}
|
||||
if (CastToBool(stack.back()) == false)
|
||||
{
|
||||
//printf("false return value, comment this debugging message\nscriptSig: %s\nscriptPubKey: %s\n",scriptSig.ToString().c_str(),scriptPubKey.ToString().c_str());
|
||||
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
|
||||
}
|
||||
|
||||
// Additional validation for spend-to-script-hash transactions:
|
||||
if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash())
|
||||
@@ -1472,9 +1533,15 @@ bool VerifyScript(
|
||||
// serror is set
|
||||
return false;
|
||||
if (stack.empty())
|
||||
{
|
||||
//printf("interpreter stack is empty #2, comment this debugging message\nscriptSig: %s\nscriptPubKey: %s\n",scriptSig.ToString().c_str(),scriptPubKey.ToString().c_str());
|
||||
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
|
||||
}
|
||||
if (!CastToBool(stack.back()))
|
||||
{
|
||||
//printf("false return value #2, comment this debugging message\nscriptSig: %s\nscriptPubKey: %s\n",scriptSig.ToString().c_str(),scriptPubKey.ToString().c_str());
|
||||
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// The CLEANSTACK check is only performed after potential P2SH evaluation,
|
||||
|
||||
@@ -89,9 +89,11 @@ enum
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
||||
};
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits;
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits, hashShieldedSpends, hashShieldedOutputs;
|
||||
|
||||
PrecomputedTransactionData(const CTransaction& tx);
|
||||
};
|
||||
@@ -100,6 +102,7 @@ enum SigVersion
|
||||
{
|
||||
SIGVERSION_SPROUT = 0,
|
||||
SIGVERSION_OVERWINTER = 1,
|
||||
SIGVERSION_SAPLING = 2,
|
||||
};
|
||||
|
||||
uint256 SignatureHash(
|
||||
|
||||
@@ -11,19 +11,18 @@
|
||||
#include "cc/eval.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
if (vch.size() <= 4)
|
||||
return strprintf("%d", CScriptNum(vch, false).getint());
|
||||
else
|
||||
return HexStr(vch);
|
||||
}
|
||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
if (vch.size() <= 4)
|
||||
return strprintf("%d", CScriptNum(vch, false).getint());
|
||||
else
|
||||
return HexStr(vch);
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char* GetOpName(opcodetype opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
@@ -241,14 +240,114 @@ bool CScript::IsPayToScriptHash() const
|
||||
{
|
||||
// Extra-fast test for pay-to-script-hash CScripts:
|
||||
return (this->size() == 23 &&
|
||||
this->at(0) == OP_HASH160 &&
|
||||
this->at(1) == 0x14 &&
|
||||
this->at(22) == OP_EQUAL);
|
||||
(*this)[0] == OP_HASH160 &&
|
||||
(*this)[1] == 0x14 &&
|
||||
(*this)[22] == OP_EQUAL);
|
||||
}
|
||||
|
||||
bool CScript::IsPayToCryptoCondition() const
|
||||
// this returns true if either there is nothing left and pc points at the end, or
|
||||
// all instructions from the pc to the end of the script are balanced pushes and pops
|
||||
// if there is data, it also returns all the values as byte vectors in a list of vectors
|
||||
bool CScript::GetBalancedData(const_iterator& pc, std::vector<std::vector<unsigned char>>& vSolutions) const
|
||||
{
|
||||
const_iterator pc = this->begin();
|
||||
int netPushes = 0;
|
||||
vSolutions.clear();
|
||||
|
||||
while (pc < end())
|
||||
{
|
||||
vector<unsigned char> data;
|
||||
opcodetype opcode;
|
||||
if (this->GetOp(pc, opcode, data))
|
||||
{
|
||||
if (opcode == OP_DROP)
|
||||
{
|
||||
// this should never pop what it hasn't pushed (like a success code)
|
||||
if (--netPushes < 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// push or fail
|
||||
netPushes++;
|
||||
if (opcode == OP_0)
|
||||
{
|
||||
data.resize(1);
|
||||
data[0] = 0;
|
||||
vSolutions.push_back(data);
|
||||
}
|
||||
else if (opcode >= OP_1 && opcode <= OP_16)
|
||||
{
|
||||
data.resize(1);
|
||||
data[0] = (opcode - OP_1) + 1;
|
||||
vSolutions.push_back(data);
|
||||
}
|
||||
else if (opcode > 0 && opcode <= OP_PUSHDATA4 && data.size() > 0)
|
||||
{
|
||||
vSolutions.push_back(data);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return netPushes == 0;
|
||||
}
|
||||
|
||||
// this returns true if either there is nothing left and pc points at the end
|
||||
// if there is data, it also returns all the values as byte vectors in a list of vectors
|
||||
bool CScript::GetPushedData(CScript::const_iterator pc, std::vector<std::vector<unsigned char>>& vData) const
|
||||
{
|
||||
vector<unsigned char> data;
|
||||
opcodetype opcode;
|
||||
std::vector<unsigned char> vch1 = std::vector<unsigned char>(1);
|
||||
|
||||
vData.clear();
|
||||
|
||||
while (pc < end())
|
||||
{
|
||||
if (GetOp(pc, opcode, data))
|
||||
{
|
||||
if (opcode == OP_0)
|
||||
{
|
||||
vch1[0] = 0;
|
||||
vData.push_back(vch1);
|
||||
}
|
||||
else if (opcode >= OP_1 && opcode <= OP_16)
|
||||
{
|
||||
vch1[0] = (opcode - OP_1) + 1;
|
||||
vData.push_back(vch1);
|
||||
}
|
||||
else if (opcode > 0 && opcode <= OP_PUSHDATA4 && data.size() > 0)
|
||||
{
|
||||
vData.push_back(data);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return vData.size() != 0;
|
||||
}
|
||||
|
||||
// this returns true if either there is nothing left and pc points at the end
|
||||
// if there is data, it also returns all the values as byte vectors in a list of vectors
|
||||
bool CScript::GetOpretData(std::vector<std::vector<unsigned char>>& vData) const
|
||||
{
|
||||
vector<unsigned char> data;
|
||||
opcodetype opcode;
|
||||
CScript::const_iterator pc = this->begin();
|
||||
|
||||
if (GetOp(pc, opcode, data) && opcode == OP_RETURN)
|
||||
{
|
||||
return GetPushedData(pc, vData);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector<std::vector<unsigned char>>& vParams) const
|
||||
{
|
||||
const_iterator pc = begin();
|
||||
vector<unsigned char> data;
|
||||
opcodetype opcode;
|
||||
if (this->GetOp(pc, opcode, data))
|
||||
@@ -256,9 +355,27 @@ bool CScript::IsPayToCryptoCondition() const
|
||||
if (opcode > OP_0 && opcode < OP_PUSHDATA1)
|
||||
if (this->GetOp(pc, opcode, data))
|
||||
if (opcode == OP_CHECKCRYPTOCONDITION)
|
||||
if (pc == this->end())
|
||||
return 1;
|
||||
return 0;
|
||||
{
|
||||
const_iterator pcCCEnd = pc;
|
||||
if (GetBalancedData(pc, vParams))
|
||||
{
|
||||
if (pCCSubScript)
|
||||
*pCCSubScript = CScript(begin(),pcCCEnd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript) const
|
||||
{
|
||||
std::vector<std::vector<unsigned char>> vParams;
|
||||
return IsPayToCryptoCondition(pCCSubScript, vParams);
|
||||
}
|
||||
|
||||
bool CScript::IsPayToCryptoCondition() const
|
||||
{
|
||||
return IsPayToCryptoCondition(NULL);
|
||||
}
|
||||
|
||||
bool CScript::MayAcceptCryptoCondition() const
|
||||
@@ -305,6 +422,37 @@ bool CScript::IsPushOnly() const
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the front of the script has check lock time verify. this is a fairly simple check.
|
||||
// accepts NULL as parameter if unlockTime is not needed.
|
||||
bool CScript::IsCheckLockTimeVerify(int64_t *unlockTime) const
|
||||
{
|
||||
opcodetype op;
|
||||
std::vector<unsigned char> unlockTimeParam = std::vector<unsigned char>();
|
||||
CScript::const_iterator it = this->begin();
|
||||
|
||||
if (this->GetOp2(it, op, &unlockTimeParam))
|
||||
{
|
||||
if (unlockTimeParam.size() >= 0 && unlockTimeParam.size() < 6 &&
|
||||
(*this)[unlockTimeParam.size() + 1] == OP_CHECKLOCKTIMEVERIFY)
|
||||
{
|
||||
int i = unlockTimeParam.size() - 1;
|
||||
for (*unlockTime = 0; i >= 0; i--)
|
||||
{
|
||||
*unlockTime <<= 8;
|
||||
*unlockTime |= *((unsigned char *)unlockTimeParam.data() + i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScript::IsCheckLockTimeVerify() const
|
||||
{
|
||||
int64_t ult;
|
||||
return this->IsCheckLockTimeVerify(&ult);
|
||||
}
|
||||
|
||||
std::string CScript::ToString() const
|
||||
{
|
||||
std::string str;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BITCOIN_SCRIPT_SCRIPT_H
|
||||
|
||||
#include "crypto/common.h"
|
||||
#include "prevector.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <climits>
|
||||
@@ -17,6 +18,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define OPRETTYPE_TIMELOCK 1
|
||||
#define OPRETTYPE_STAKEPARAMS 2
|
||||
#define OPRETTYPE_STAKECHEAT 3
|
||||
|
||||
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
|
||||
|
||||
// Max size of pushdata in a CC sig in bytes
|
||||
@@ -356,8 +361,10 @@ private:
|
||||
int64_t m_value;
|
||||
};
|
||||
|
||||
typedef prevector<28, unsigned char> CScriptBase;
|
||||
|
||||
/** Serialized script, used inside transaction inputs and outputs */
|
||||
class CScript : public std::vector<unsigned char>
|
||||
class CScript : public CScriptBase
|
||||
{
|
||||
protected:
|
||||
CScript& push_int64(int64_t n)
|
||||
@@ -376,11 +383,13 @@ protected:
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
bool GetBalancedData(const_iterator& pc, std::vector<std::vector<unsigned char>>& vSolutions) const;
|
||||
public:
|
||||
CScript() { }
|
||||
CScript(const CScript& b) : std::vector<unsigned char>(b.begin(), b.end()) { }
|
||||
CScript(const_iterator pbegin, const_iterator pend) : std::vector<unsigned char>(pbegin, pend) { }
|
||||
CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector<unsigned char>(pbegin, pend) { }
|
||||
CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { }
|
||||
CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { }
|
||||
CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { }
|
||||
CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { }
|
||||
|
||||
CScript& operator+=(const CScript& b)
|
||||
{
|
||||
@@ -396,12 +405,10 @@ public:
|
||||
}
|
||||
|
||||
CScript(int64_t b) { operator<<(b); }
|
||||
|
||||
explicit CScript(opcodetype b) { operator<<(b); }
|
||||
explicit CScript(const CScriptNum& b) { operator<<(b); }
|
||||
explicit CScript(const std::vector<unsigned char>& b) { operator<<(b); }
|
||||
|
||||
|
||||
CScript& operator<<(int64_t b) { return push_int64(b); }
|
||||
|
||||
CScript& operator<<(opcodetype opcode)
|
||||
@@ -570,6 +577,12 @@ public:
|
||||
bool IsPayToPublicKey() const;
|
||||
|
||||
bool IsPayToScriptHash() const;
|
||||
bool GetPushedData(CScript::const_iterator pc, std::vector<std::vector<unsigned char>>& vData) const;
|
||||
bool IsOpReturn() const { return size() > 0 && (*this)[0] == OP_RETURN; }
|
||||
bool GetOpretData(std::vector<std::vector<unsigned char>>& vData) const;
|
||||
|
||||
bool IsPayToCryptoCondition(CScript *ccSubScript, std::vector<std::vector<unsigned char>>& vSolutions) const;
|
||||
bool IsPayToCryptoCondition(CScript *ccSubScript) const;
|
||||
bool IsPayToCryptoCondition() const;
|
||||
bool IsCoinImport() const;
|
||||
bool MayAcceptCryptoCondition() const;
|
||||
@@ -577,6 +590,13 @@ public:
|
||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||
bool IsPushOnly() const;
|
||||
|
||||
/** if the front of the script has check lock time verify. this is a fairly simple check.
|
||||
* accepts NULL as parameter if unlockTime is not needed.
|
||||
*/
|
||||
bool IsCheckLockTimeVerify(int64_t *unlockTime) const;
|
||||
|
||||
bool IsCheckLockTimeVerify() const;
|
||||
|
||||
/**
|
||||
* Returns whether the script is guaranteed to fail at execution,
|
||||
* regardless of the initial stack. This allows outputs to be pruned
|
||||
@@ -588,10 +608,11 @@ public:
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
void clear()
|
||||
{
|
||||
// The default std::vector::clear() does not release memory.
|
||||
std::vector<unsigned char>().swap(*this);
|
||||
CScriptBase().swap(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
121
src/script/script_ext.cpp
Normal file
121
src/script/script_ext.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||||
// Copyright (c) 2018 The Verus developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "script_ext.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool CScriptExt::IsPayToScriptHash(CScriptID *scriptID) const
|
||||
{
|
||||
if (((CScript *)this)->IsPayToScriptHash())
|
||||
{
|
||||
*scriptID = CScriptID(uint160(std::vector<unsigned char>(this->begin() + 2, this->end() - 1)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// P2PKH script, adds to whatever is already in the script (for example CLTV)
|
||||
const CScriptExt &CScriptExt::AddPayToPubKeyHash(const CKeyID &key) const
|
||||
{
|
||||
*((CScript *)this) << OP_DUP;
|
||||
*((CScript *)this) << OP_HASH160;
|
||||
*((CScript *)this) << ToByteVector(key);
|
||||
*((CScript *)this) << OP_EQUALVERIFY;
|
||||
*((CScript *)this) << OP_CHECKSIG;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// push data into an op_return script with an opret type indicator, fails if the op_return is too large
|
||||
const CScriptExt &CScriptExt::OpReturnScript(const vector<unsigned char> &data, unsigned char opretType) const
|
||||
{
|
||||
((CScript *)this)->clear();
|
||||
if (data.size() < MAX_SCRIPT_ELEMENT_SIZE)
|
||||
{
|
||||
vector<unsigned char> scratch = vector<unsigned char>(data);
|
||||
scratch.insert(scratch.begin(), opretType);
|
||||
*((CScript *)this) << OP_RETURN;
|
||||
*((CScript *)this) << scratch;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// push data into an op_return script with an opret type indicator, fails if the op_return is too large
|
||||
const CScriptExt &CScriptExt::OpReturnScript(const CScript &src, unsigned char opretType) const
|
||||
{
|
||||
vector<unsigned char> vch = vector<unsigned char>(src.begin(), src.end());
|
||||
return OpReturnScript(vch, opretType);
|
||||
}
|
||||
|
||||
// P2SH script, adds to whatever is already in the script (for example CLTV)
|
||||
const CScriptExt &CScriptExt::PayToScriptHash(const CScriptID &scriptID) const
|
||||
{
|
||||
((CScript *)this)->clear();
|
||||
*((CScript *)this) << OP_HASH160;
|
||||
*((CScript *)this) << ToByteVector(scriptID);
|
||||
*((CScript *)this) << OP_EQUAL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// P2SH script, adds to whatever is already in the script (for example CLTV)
|
||||
const CScriptExt &CScriptExt::AddCheckLockTimeVerify(int64_t unlocktime) const
|
||||
{
|
||||
if (unlocktime > 0)
|
||||
{
|
||||
*((CScript *)this) << CScriptNum::serialize(unlocktime);
|
||||
*((CScript *)this) << OP_CHECKLOCKTIMEVERIFY;
|
||||
*((CScript *)this) << OP_DROP;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
// combined CLTV script and P2PKH
|
||||
const CScriptExt &CScriptExt::TimeLockSpend(const CKeyID &key, int64_t unlocktime) const
|
||||
{
|
||||
((CScript *)this)->clear();
|
||||
this->AddCheckLockTimeVerify(unlocktime);
|
||||
this->AddPayToPubKeyHash(key);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* provide destination extraction for non-standard, timelocked coinbase transactions
|
||||
* as well as other transactions
|
||||
*/
|
||||
bool CScriptExt::ExtractVoutDestination(const CTransaction& tx, int32_t voutNum, CTxDestination& addressRet)
|
||||
{
|
||||
if (tx.vout.size() <= voutNum)
|
||||
return false;
|
||||
|
||||
CScriptID scriptHash;
|
||||
CScriptExt spk = tx.vout[voutNum].scriptPubKey;
|
||||
|
||||
// if this is a timelocked transaction, get the destination behind the time lock
|
||||
if (tx.IsCoinBase() && tx.vout.size() == 2 && voutNum == 0 &&
|
||||
spk.IsPayToScriptHash(&scriptHash) &&
|
||||
tx.vout[1].scriptPubKey.IsOpReturn())
|
||||
{
|
||||
opcodetype op;
|
||||
std::vector<uint8_t> opretData = std::vector<uint8_t>();
|
||||
CScript::const_iterator it = tx.vout[1].scriptPubKey.begin() + 1;
|
||||
if (tx.vout[1].scriptPubKey.GetOp2(it, op, &opretData))
|
||||
{
|
||||
if (opretData.size() > 0 && opretData[0] == OPRETTYPE_TIMELOCK)
|
||||
{
|
||||
int64_t unlocktime;
|
||||
CScriptExt se = CScriptExt(&opretData[1], &opretData[opretData.size()]);
|
||||
|
||||
if (CScriptID(se) == scriptHash &&
|
||||
se.IsCheckLockTimeVerify(&unlocktime))
|
||||
{
|
||||
spk = se;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExtractDestination(spk, addressRet);
|
||||
}
|
||||
|
||||
50
src/script/script_ext.h
Normal file
50
src/script/script_ext.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||||
// Copyright (c) 2018 The Verus developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_SCRIPT_SCRIPT_EXT_H
|
||||
#define BITCOIN_SCRIPT_SCRIPT_EXT_H
|
||||
|
||||
#include "script.h"
|
||||
#include "standard.h"
|
||||
#include "pubkey.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CScriptExt : public CScript
|
||||
{
|
||||
public:
|
||||
CScriptExt() { }
|
||||
CScriptExt(const CScript& b) : CScript(b) { }
|
||||
CScriptExt(const_iterator pbegin, const_iterator pend) : CScript(pbegin, pend) { }
|
||||
CScriptExt(const unsigned char* pbegin, const unsigned char* pend) : CScript(pbegin, pend) { }
|
||||
|
||||
// overload to return the hash of the referenced script
|
||||
bool IsPayToScriptHash(CScriptID *scriptID) const;
|
||||
|
||||
// P2PKH script, adds to whatever is already in the script (for example CLTV)
|
||||
const CScriptExt &AddPayToPubKeyHash(const CKeyID &key) const;
|
||||
|
||||
// push data into an op_return script with an opret type indicator, fails if the op_return is too large
|
||||
const CScriptExt &OpReturnScript(const std::vector<unsigned char> &data, unsigned char opretType) const;
|
||||
|
||||
// push data into an op_return script with an opret type indicator, fails if the op_return is too large
|
||||
const CScriptExt &OpReturnScript(const CScript &src, unsigned char opretType) const;
|
||||
|
||||
// P2SH script
|
||||
const CScriptExt &PayToScriptHash(const CScriptID &scriptID) const;
|
||||
|
||||
// P2SH script, adds to whatever is already in the script (for example CLTV)
|
||||
const CScriptExt &AddCheckLockTimeVerify(int64_t unlocktime) const;
|
||||
|
||||
// combined CLTV script and P2PKH
|
||||
const CScriptExt &TimeLockSpend(const CKeyID &key, int64_t unlocktime) const;
|
||||
|
||||
// lookup for destinations that includes non-standard destinations for time locked coinbases
|
||||
static bool ExtractVoutDestination(const CTransaction& tx, int32_t voutNum, CTxDestination& addressRet);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef __cpuid
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/tuple/tuple_comparison.hpp>
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ private:
|
||||
bool store;
|
||||
|
||||
public:
|
||||
ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nIn, amount, txdataIn), store(storeIn) {}
|
||||
ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn, const PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nIn, amount, txdataIn), store(storeIn) {}
|
||||
ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nIn, amount), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
int CheckEvalCondition(const CC *cond) const;
|
||||
|
||||
92
src/script/sigcache.cpp
Normal file
92
src/script/sigcache.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2014 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 "sigcache.h"
|
||||
|
||||
#include "pubkey.h"
|
||||
#include "random.h"
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
#ifdef _WIN32
|
||||
#undef __cpuid
|
||||
#endif
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/tuple/tuple_comparison.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Valid signature cache, to avoid doing expensive ECDSA signature checking
|
||||
* twice for every transaction (once when accepted into memory pool, and
|
||||
* again when accepted into the block chain)
|
||||
*/
|
||||
class CSignatureCache
|
||||
{
|
||||
private:
|
||||
//! sigdata_type is (signature hash, signature, public key):
|
||||
typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type;
|
||||
std::set< sigdata_type> setValid;
|
||||
boost::shared_mutex cs_sigcache;
|
||||
|
||||
public:
|
||||
bool
|
||||
Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
|
||||
|
||||
sigdata_type k(hash, vchSig, pubKey);
|
||||
std::set<sigdata_type>::iterator mi = setValid.find(k);
|
||||
if (mi != setValid.end())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
|
||||
{
|
||||
// DoS prevention: limit cache size to less than 10MB
|
||||
// (~200 bytes per cache entry times 50,000 entries)
|
||||
// Since there can be no more than 20,000 signature operations per block
|
||||
// 50,000 is a reasonable default.
|
||||
int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000);
|
||||
if (nMaxCacheSize <= 0) return;
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
|
||||
|
||||
while (static_cast<int64_t>(setValid.size()) > nMaxCacheSize)
|
||||
{
|
||||
// Evict a random entry. Random because that helps
|
||||
// foil would-be DoS attackers who might try to pre-generate
|
||||
// and re-use a set of valid signatures just-slightly-greater
|
||||
// than our cache size.
|
||||
uint256 randomHash = GetRandHash();
|
||||
std::vector<unsigned char> unused;
|
||||
std::set<sigdata_type>::iterator it =
|
||||
setValid.lower_bound(sigdata_type(randomHash, unused, unused));
|
||||
if (it == setValid.end())
|
||||
it = setValid.begin();
|
||||
setValid.erase(*it);
|
||||
}
|
||||
|
||||
sigdata_type k(hash, vchSig, pubKey);
|
||||
setValid.insert(k);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
static CSignatureCache signatureCache;
|
||||
|
||||
if (signatureCache.Get(sighash, vchSig, pubkey))
|
||||
return true;
|
||||
|
||||
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
|
||||
return false;
|
||||
|
||||
if (store)
|
||||
signatureCache.Set(sighash, vchSig, pubkey);
|
||||
return true;
|
||||
}
|
||||
26
src/script/sigcache.h
Normal file
26
src/script/sigcache.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_SCRIPT_SIGCACHE_H
|
||||
#define BITCOIN_SCRIPT_SIGCACHE_H
|
||||
|
||||
#include "script/interpreter.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CPubKey;
|
||||
|
||||
class CachingTransactionSignatureChecker : public TransactionSignatureChecker
|
||||
{
|
||||
private:
|
||||
bool store;
|
||||
|
||||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_SCRIPT_SIGCACHE_H
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "keystore.h"
|
||||
#include "script/standard.h"
|
||||
#include "uint256.h"
|
||||
#include "cc/CCinclude.h"
|
||||
#include "cc/eval.h"
|
||||
#include "key_io.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -20,10 +23,12 @@ extern uint8_t ASSETCHAINS_TXPOW;
|
||||
|
||||
TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
|
||||
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId) const
|
||||
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId, CKey *pprivKey, void *extraData) const
|
||||
{
|
||||
CKey key;
|
||||
if (!keystore->GetKey(address, key))
|
||||
if (pprivKey)
|
||||
key = *pprivKey;
|
||||
else if (!keystore || !keystore->GetKey(address, key))
|
||||
return false;
|
||||
|
||||
uint256 hash;
|
||||
@@ -33,17 +38,31 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig,
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ASSETCHAINS_TXPOW == 0 )
|
||||
if (scriptCode.IsPayToCryptoCondition())
|
||||
{
|
||||
if (!key.Sign(hash, vchSig))
|
||||
CC *cc = (CC *)extraData;
|
||||
// assume either 1of1 or 1of2. if the condition created by the
|
||||
if (!cc || cc_signTreeSecp256k1Msg32(cc, key.begin(), hash.begin()) == 0)
|
||||
return false;
|
||||
vchSig = CCSigVec(cc);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!key.Sign(hash, vchSig, rand()))
|
||||
return false;
|
||||
if ( ASSETCHAINS_TXPOW == 0 )
|
||||
{
|
||||
if (!key.Sign(hash, vchSig))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!key.Sign(hash, vchSig, rand()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vchSig.push_back((unsigned char)nHashType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,6 +89,204 @@ static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreato
|
||||
return nSigned==nRequired;
|
||||
}
|
||||
|
||||
CC *CCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk1));
|
||||
pks.push_back(CCNewSecp256k1(pk2));
|
||||
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
|
||||
CC *Sig = CCNewThreshold(1, pks);
|
||||
return CCNewThreshold(2, {condCC, Sig});
|
||||
}
|
||||
|
||||
CC *CCcond1(uint8_t evalcode,CPubKey pk)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk));
|
||||
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
|
||||
CC *Sig = CCNewThreshold(1, pks);
|
||||
return CCNewThreshold(2, {condCC, Sig});
|
||||
}
|
||||
|
||||
std::vector<CCcontract_info> &GetCryptoConditions()
|
||||
{
|
||||
static bool initialized = false;
|
||||
static std::vector<CCcontract_info> vCC = std::vector<CCcontract_info>();
|
||||
CCcontract_info C;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
// this should initialize any desired auto-signed crypto-conditions
|
||||
}
|
||||
return vCC;
|
||||
}
|
||||
|
||||
bool GetCCByUnspendableAddress(struct CCcontract_info *cp, char *addrstr)
|
||||
{
|
||||
std::vector<CCcontract_info> &vCC = GetCryptoConditions();
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < vCC.size(); i++)
|
||||
{
|
||||
if (strcmp(addrstr, vCC[i].unspendableCCaddr) == 0)
|
||||
{
|
||||
found = true;
|
||||
*cp = vCC[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode)
|
||||
{
|
||||
std::vector<CCcontract_info> &vCC = GetCryptoConditions();
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < vCC.size(); i++)
|
||||
{
|
||||
if (vCC[i].evalcode == evalcode)
|
||||
{
|
||||
found = true;
|
||||
*cp = vCC[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool _Getscriptaddress(char *destaddr, const CScript &scriptPubKey)
|
||||
{
|
||||
CTxDestination address;
|
||||
txnouttype whichType;
|
||||
std::vector<std::vector<unsigned char>> vvch = std::vector<std::vector<unsigned char>>();
|
||||
if (Solver(scriptPubKey, whichType, vvch) && vvch[0].size() == 20)
|
||||
{
|
||||
address = CKeyID(uint160(vvch[0]));
|
||||
strcpy(destaddr,(char *)CBitcoinAddress(address).ToString().c_str());
|
||||
return(true);
|
||||
}
|
||||
fprintf(stderr,"Solver for scriptPubKey failed\n%s\n", scriptPubKey.ToString().c_str());
|
||||
return(false);
|
||||
}
|
||||
|
||||
CScript _CCPubKey(const CC *cond)
|
||||
{
|
||||
unsigned char buf[1000];
|
||||
size_t len = cc_conditionBinary(cond, buf);
|
||||
return CScript() << std::vector<unsigned char>(buf, buf+len) << OP_CHECKCRYPTOCONDITION;
|
||||
}
|
||||
|
||||
static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scriptPubKey, vector<valtype> &vSolutions,
|
||||
vector<valtype>& ret, uint32_t consensusBranchId)
|
||||
{
|
||||
CScript subScript;
|
||||
vector<CPubKey> vPK;
|
||||
vector<valtype> vParams = vector<valtype>();
|
||||
COptCCParams p;
|
||||
|
||||
// get information to sign with
|
||||
CCcontract_info C;
|
||||
|
||||
scriptPubKey.IsPayToCryptoCondition(&subScript, vParams);
|
||||
if (vParams.empty())
|
||||
{
|
||||
// get the keyID address of the cc and if it is an unspendable cc address, use its pubkey
|
||||
// we have nothing else
|
||||
char addr[64];
|
||||
if (_Getscriptaddress(addr, subScript) && GetCCByUnspendableAddress(&C, addr))
|
||||
{
|
||||
vPK.push_back(CPubKey(ParseHex(C.CChexstr)));
|
||||
p = COptCCParams(p.VERSION, C.evalcode, 1, 1, vPK, vParams);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = COptCCParams(vParams[0]);
|
||||
}
|
||||
|
||||
if (p.IsValid() && p.vKeys.size() >= p.n)
|
||||
{
|
||||
bool is1of2 = (p.m == 1 && p.n == 2);
|
||||
CKey privKey;
|
||||
|
||||
// must be a valid cc eval code
|
||||
if (CCinitLite(&C, p.evalCode))
|
||||
{
|
||||
// pay to cc address is a valid tx
|
||||
if (!is1of2)
|
||||
{
|
||||
bool havePriv = creator.KeyStore().GetKey(p.vKeys[0].GetID(), privKey);
|
||||
|
||||
// if we don't have the private key, it must be the unspendable address
|
||||
if (!havePriv && (p.vKeys[0] == CPubKey(ParseHex(C.CChexstr))))
|
||||
{
|
||||
privKey = CKey();
|
||||
std::vector<unsigned char> vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv));
|
||||
privKey.Set(vch.begin(), vch.end(), false);
|
||||
}
|
||||
|
||||
CC *cc = CCcond1(p.evalCode, p.vKeys[0]);
|
||||
|
||||
if (cc)
|
||||
{
|
||||
vector<unsigned char> vch;
|
||||
if (creator.CreateSig(vch, p.vKeys[0].GetID(), _CCPubKey(cc), consensusBranchId, &privKey, (void *)cc))
|
||||
{
|
||||
ret.push_back(vch);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", p.vKeys[0].GetID().ToString().c_str());
|
||||
}
|
||||
|
||||
cc_free(cc);
|
||||
return ret.size() != 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// first of priv key in our key store or contract address is what we sign with
|
||||
for (auto pk : p.vKeys)
|
||||
{
|
||||
if (creator.IsKeystoreValid() && creator.KeyStore().GetKey(pk.GetID(), privKey) && privKey.IsValid())
|
||||
break;
|
||||
|
||||
if (pk == CPubKey(ParseHex(C.CChexstr)))
|
||||
{
|
||||
privKey = CKey();
|
||||
std::vector<unsigned char> vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv));
|
||||
privKey.Set(vch.begin(), vch.end(), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!privKey.IsValid())
|
||||
return false;
|
||||
|
||||
CC *cc = CCcond1of2(p.evalCode, p.vKeys[0], p.vKeys[1]);
|
||||
|
||||
if (cc)
|
||||
{
|
||||
vector<unsigned char> vch;
|
||||
if (creator.CreateSig(vch, p.vKeys[0].GetID(), _CCPubKey(cc), consensusBranchId, &privKey, (void *)cc))
|
||||
{
|
||||
ret.push_back(vch);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"vin has 1of2 CC signing error with addresses.(%s)\n(%s)\n", p.vKeys[0].GetID().ToString().c_str(), p.vKeys[1].GetID().ToString().c_str());
|
||||
}
|
||||
|
||||
cc_free(cc);
|
||||
return ret.size() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign scriptPubKey using signature made with creator.
|
||||
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
|
||||
@@ -84,10 +301,28 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
||||
ret.clear();
|
||||
|
||||
vector<valtype> vSolutions;
|
||||
|
||||
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
|
||||
return false;
|
||||
{
|
||||
// if this is a CLTV script, solve for the destination after CLTV
|
||||
if (scriptPubKey.IsCheckLockTimeVerify())
|
||||
{
|
||||
uint8_t pushOp = scriptPubKey[0];
|
||||
uint32_t scriptStart = pushOp + 3;
|
||||
|
||||
// check post CLTV script
|
||||
CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end());
|
||||
|
||||
// check again with only postfix subscript
|
||||
if (!Solver(postfix, whichTypeRet, vSolutions))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
CKeyID keyID;
|
||||
|
||||
switch (whichTypeRet)
|
||||
{
|
||||
case TX_NONSTANDARD:
|
||||
@@ -113,6 +348,9 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case TX_CRYPTOCONDITION:
|
||||
return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId);
|
||||
|
||||
case TX_MULTISIG:
|
||||
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
|
||||
@@ -302,6 +540,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
|
||||
return sigs2;
|
||||
case TX_PUBKEY:
|
||||
case TX_PUBKEYHASH:
|
||||
case TX_CRYPTOCONDITION:
|
||||
// Signatures are bigger than placeholders or empty scripts:
|
||||
if (sigs1.script.empty() || sigs1.script[0].empty())
|
||||
return sigs2;
|
||||
@@ -376,7 +615,9 @@ bool DummySignatureCreator::CreateSig(
|
||||
std::vector<unsigned char>& vchSig,
|
||||
const CKeyID& keyid,
|
||||
const CScript& scriptCode,
|
||||
uint32_t consensusBranchId) const
|
||||
uint32_t consensusBranchId,
|
||||
CKey *key,
|
||||
void *extraData) const
|
||||
{
|
||||
// Create a dummy signature that is a valid DER-encoding
|
||||
vchSig.assign(72, '\000');
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "script/interpreter.h"
|
||||
|
||||
class CKey;
|
||||
class CKeyID;
|
||||
class CKeyStore;
|
||||
class CScript;
|
||||
@@ -22,12 +23,18 @@ protected:
|
||||
|
||||
public:
|
||||
BaseSignatureCreator(const CKeyStore* keystoreIn) : keystore(keystoreIn) {}
|
||||
const CKeyStore& KeyStore() const { return *keystore; };
|
||||
const bool IsKeystoreValid() const { return keystore != NULL; }
|
||||
const CKeyStore& KeyStore() const { return *keystore; }
|
||||
virtual ~BaseSignatureCreator() {}
|
||||
virtual const BaseSignatureChecker& Checker() const =0;
|
||||
|
||||
/** Create a singular (non-script) signature. */
|
||||
virtual bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const =0;
|
||||
virtual bool CreateSig(std::vector<unsigned char>& vchSig,
|
||||
const CKeyID& keyid,
|
||||
const CScript& scriptCode,
|
||||
uint32_t consensusBranchId,
|
||||
CKey *key = NULL,
|
||||
void *extraData = NULL) const = 0;
|
||||
};
|
||||
|
||||
/** A signature creator for transactions. */
|
||||
@@ -41,7 +48,7 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
|
||||
public:
|
||||
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
|
||||
const BaseSignatureChecker& Checker() const { return checker; }
|
||||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const;
|
||||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId, CKey *key = NULL, void *extraData = NULL) const;
|
||||
};
|
||||
|
||||
class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
|
||||
@@ -56,7 +63,7 @@ class DummySignatureCreator : public BaseSignatureCreator {
|
||||
public:
|
||||
DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {}
|
||||
const BaseSignatureChecker& Checker() const;
|
||||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const;
|
||||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId, CKey *key = NULL, void *extraData = NULL) const;
|
||||
};
|
||||
|
||||
struct SignatureData {
|
||||
|
||||
@@ -19,6 +19,106 @@ typedef vector<unsigned char> valtype;
|
||||
|
||||
unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
|
||||
|
||||
COptCCParams::COptCCParams(std::vector<unsigned char> &vch)
|
||||
{
|
||||
CScript inScr = CScript(vch.begin(), vch.end());
|
||||
if (inScr.size() > 1)
|
||||
{
|
||||
CScript::const_iterator pc = inScr.begin();
|
||||
opcodetype opcode;
|
||||
std::vector<std::vector<unsigned char>> data;
|
||||
std::vector<unsigned char> param;
|
||||
bool valid = true;
|
||||
|
||||
while (pc < inScr.end())
|
||||
{
|
||||
param.clear();
|
||||
if (inScr.GetOp(pc, opcode, param))
|
||||
{
|
||||
if (opcode == OP_0)
|
||||
{
|
||||
param.resize(1);
|
||||
param[0] = 0;
|
||||
data.push_back(param);
|
||||
}
|
||||
else if (opcode >= OP_1 && opcode <= OP_16)
|
||||
{
|
||||
param.resize(1);
|
||||
param[0] = (opcode - OP_1) + 1;
|
||||
data.push_back(param);
|
||||
}
|
||||
else if (opcode > 0 && opcode <= OP_PUSHDATA4 && param.size() > 0)
|
||||
{
|
||||
data.push_back(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid && pc == inScr.end() && data.size() > 0)
|
||||
{
|
||||
version = 0;
|
||||
param = data[0];
|
||||
if (param.size() == 4)
|
||||
{
|
||||
version = param[0];
|
||||
evalCode = param[1];
|
||||
m = param[2];
|
||||
n = param[3];
|
||||
if (version != VERSION || m != 1 || (n != 1 && n != 2) || data.size() <= n)
|
||||
{
|
||||
// we only support one version, and 1 of 1 or 1 of 2 now, so set invalid
|
||||
version = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// load keys and data
|
||||
vKeys.clear();
|
||||
vData.clear();
|
||||
int i;
|
||||
for (i = 1; i <= n; i++)
|
||||
{
|
||||
vKeys.push_back(CPubKey(data[i]));
|
||||
if (!vKeys[vKeys.size() - 1].IsValid())
|
||||
{
|
||||
version = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (version != 0)
|
||||
{
|
||||
// get the rest of the data
|
||||
for ( ; i < data.size(); i++)
|
||||
{
|
||||
vData.push_back(data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned char> COptCCParams::AsVector()
|
||||
{
|
||||
CScript cData = CScript();
|
||||
|
||||
cData << std::vector<unsigned char>({version, evalCode, n, m});
|
||||
for (auto k : vKeys)
|
||||
{
|
||||
cData << std::vector<unsigned char>(k.begin(), k.end());
|
||||
}
|
||||
for (auto d : vData)
|
||||
{
|
||||
cData << std::vector<unsigned char>(d);
|
||||
}
|
||||
return std::vector<unsigned char>(cData.begin(), cData.end());
|
||||
}
|
||||
|
||||
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||
|
||||
const char* GetTxnOutputType(txnouttype t)
|
||||
@@ -73,19 +173,32 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
|
||||
|
||||
if (IsCryptoConditionsEnabled()) {
|
||||
// Shortcut for pay-to-crypto-condition
|
||||
if (scriptPubKey.IsPayToCryptoCondition())
|
||||
CScript ccSubScript = CScript();
|
||||
std::vector<std::vector<unsigned char>> vParams;
|
||||
if (scriptPubKey.IsPayToCryptoCondition(&ccSubScript, vParams))
|
||||
{
|
||||
if (scriptPubKey.MayAcceptCryptoCondition())
|
||||
{
|
||||
typeRet = TX_CRYPTOCONDITION;
|
||||
vector<unsigned char> hashBytes; uint160 x; int32_t i; uint8_t hash20[20],*ptr;;
|
||||
x = Hash160(scriptPubKey);
|
||||
x = Hash160(ccSubScript);
|
||||
memcpy(hash20,&x,20);
|
||||
hashBytes.resize(20);
|
||||
ptr = hashBytes.data();
|
||||
for (i=0; i<20; i++)
|
||||
ptr[i] = hash20[i];
|
||||
vSolutionsRet.push_back(hashBytes);
|
||||
if (vParams.size())
|
||||
{
|
||||
COptCCParams cp = COptCCParams(vParams[0]);
|
||||
if (cp.IsValid())
|
||||
{
|
||||
for (auto k : cp.vKeys)
|
||||
{
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>(k.begin(), k.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -234,10 +347,22 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
|
||||
return whichType != TX_NONSTANDARD;
|
||||
}
|
||||
|
||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
||||
bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet, bool returnPubKey)
|
||||
{
|
||||
vector<valtype> vSolutions;
|
||||
txnouttype whichType;
|
||||
CScript scriptPubKey = _scriptPubKey;
|
||||
|
||||
// if this is a CLTV script, get the destination after CLTV
|
||||
if (scriptPubKey.IsCheckLockTimeVerify())
|
||||
{
|
||||
uint8_t pushOp = scriptPubKey[0];
|
||||
uint32_t scriptStart = pushOp + 3;
|
||||
|
||||
// check post CLTV script
|
||||
scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end());
|
||||
}
|
||||
|
||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
@@ -250,9 +375,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
||||
return false;
|
||||
}
|
||||
|
||||
addressRet = pubKey.GetID();
|
||||
if (returnPubKey)
|
||||
addressRet = pubKey;
|
||||
else
|
||||
addressRet = pubKey.GetID();
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (whichType == TX_PUBKEYHASH)
|
||||
{
|
||||
addressRet = CKeyID(uint160(vSolutions[0]));
|
||||
@@ -266,7 +395,16 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
||||
|
||||
else if (IsCryptoConditionsEnabled() != 0 && whichType == TX_CRYPTOCONDITION)
|
||||
{
|
||||
addressRet = CKeyID(uint160(vSolutions[0]));
|
||||
if (vSolutions.size() > 1)
|
||||
{
|
||||
CPubKey pk = CPubKey((vSolutions[1]));
|
||||
addressRet = pk;
|
||||
return pk.IsValid();
|
||||
}
|
||||
else
|
||||
{
|
||||
addressRet = CKeyID(uint160(vSolutions[0]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Multisig txns have more than one address...
|
||||
@@ -278,6 +416,20 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
|
||||
addressRet.clear();
|
||||
typeRet = TX_NONSTANDARD;
|
||||
vector<valtype> vSolutions;
|
||||
|
||||
// if this is a CLTV script, get the destinations after CLTV
|
||||
if (scriptPubKey.IsCheckLockTimeVerify())
|
||||
{
|
||||
uint8_t pushOp = scriptPubKey[0];
|
||||
uint32_t scriptStart = pushOp + 3;
|
||||
|
||||
// check post CLTV script
|
||||
CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end());
|
||||
|
||||
// check again with only postfix subscript
|
||||
return(ExtractDestinations(postfix, typeRet, addressRet, nRequiredRet));
|
||||
}
|
||||
|
||||
if (!Solver(scriptPubKey, typeRet, vSolutions))
|
||||
return false;
|
||||
if (typeRet == TX_NULL_DATA){
|
||||
@@ -301,6 +453,26 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
|
||||
if (addressRet.empty())
|
||||
return false;
|
||||
}
|
||||
else if (IsCryptoConditionsEnabled() != 0 && typeRet == TX_CRYPTOCONDITION)
|
||||
{
|
||||
nRequiredRet = vSolutions.front()[0];
|
||||
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
|
||||
{
|
||||
CTxDestination address;
|
||||
if (vSolutions[i].size() == 20)
|
||||
{
|
||||
address = CKeyID(uint160(vSolutions[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
address = CPubKey(vSolutions[i]);
|
||||
}
|
||||
addressRet.push_back(address);
|
||||
}
|
||||
|
||||
if (addressRet.empty())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nRequiredRet = 1;
|
||||
@@ -329,6 +501,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const CPubKey &key) const {
|
||||
script->clear();
|
||||
*script << ToByteVector(key) << OP_CHECKSIG;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const CKeyID &keyID) const {
|
||||
script->clear();
|
||||
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
@@ -361,3 +539,7 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
|
||||
script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
|
||||
return script;
|
||||
}
|
||||
|
||||
bool IsValidDestination(const CTxDestination& dest) {
|
||||
return dest.which() != 0;
|
||||
}
|
||||
|
||||
@@ -79,16 +79,81 @@ public:
|
||||
* * CNoDestination: no destination set
|
||||
* * CKeyID: TX_PUBKEYHASH destination
|
||||
* * CScriptID: TX_SCRIPTHASH destination
|
||||
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
|
||||
typedef boost::variant<CNoDestination, CPubKey, CKeyID, CScriptID> CTxDestination;
|
||||
|
||||
class COptCCParams
|
||||
{
|
||||
public:
|
||||
static const uint8_t VERSION = 1;
|
||||
|
||||
uint8_t version;
|
||||
uint8_t evalCode;
|
||||
uint8_t m, n; // for m of n sigs required, n pub keys for sigs will follow
|
||||
std::vector<CPubKey> vKeys; // n public keys to aid in signing
|
||||
std::vector<std::vector<unsigned char>> vData; // extra parameters
|
||||
|
||||
COptCCParams() : version(0), evalCode(0), n(0), m(0) {}
|
||||
|
||||
COptCCParams(uint8_t ver, uint8_t code, uint8_t _n, uint8_t _m, std::vector<CPubKey> &vkeys, std::vector<std::vector<unsigned char>> &vdata) :
|
||||
version(ver), evalCode(code), n(_n), m(_m), vKeys(vkeys), vData(vdata) {}
|
||||
|
||||
COptCCParams(std::vector<unsigned char> &vch);
|
||||
|
||||
bool IsValid() { return version != 0; }
|
||||
|
||||
std::vector<unsigned char> AsVector();
|
||||
};
|
||||
|
||||
class CStakeParams
|
||||
{
|
||||
public:
|
||||
static const uint32_t STAKE_MINPARAMS = 4;
|
||||
static const uint32_t STAKE_MAXPARAMS = 5;
|
||||
|
||||
uint32_t srcHeight;
|
||||
uint32_t blkHeight;
|
||||
uint256 prevHash;
|
||||
CPubKey pk;
|
||||
|
||||
CStakeParams() : srcHeight(0), blkHeight(0), prevHash(), pk() {}
|
||||
|
||||
CStakeParams(const std::vector<std::vector<unsigned char>> &vData);
|
||||
|
||||
CStakeParams(uint32_t _srcHeight, uint32_t _blkHeight, const uint256 &_prevHash, const CPubKey &_pk) :
|
||||
srcHeight(_srcHeight), blkHeight(_blkHeight), prevHash(_prevHash), pk(_pk) {}
|
||||
|
||||
std::vector<unsigned char> AsVector()
|
||||
{
|
||||
std::vector<unsigned char> ret;
|
||||
CScript scr = CScript();
|
||||
scr << OPRETTYPE_STAKEPARAMS;
|
||||
scr << srcHeight;
|
||||
scr << blkHeight;
|
||||
scr << std::vector<unsigned char>(prevHash.begin(), prevHash.end());
|
||||
|
||||
if (pk.IsValid())
|
||||
{
|
||||
scr << std::vector<unsigned char>(pk.begin(), pk.end());
|
||||
}
|
||||
|
||||
ret = std::vector<unsigned char>(scr.begin(), scr.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsValid() { return srcHeight != 0; }
|
||||
};
|
||||
|
||||
/** Check whether a CTxDestination is a CNoDestination. */
|
||||
bool IsValidDestination(const CTxDestination& dest);
|
||||
|
||||
const char* GetTxnOutputType(txnouttype t);
|
||||
|
||||
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
|
||||
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
|
||||
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
|
||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet, bool returnPubKey=false);
|
||||
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
|
||||
|
||||
CScript GetScriptForDestination(const CTxDestination& dest);
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
m_remaining(txToLen)
|
||||
{}
|
||||
|
||||
TxInputStream& read(char* pch, size_t nSize)
|
||||
void read(char* pch, size_t nSize)
|
||||
{
|
||||
if (nSize > m_remaining)
|
||||
throw std::ios_base::failure(std::string(__func__) + ": end of data");
|
||||
@@ -38,16 +38,17 @@ public:
|
||||
memcpy(pch, m_data, nSize);
|
||||
m_remaining -= nSize;
|
||||
m_data += nSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TxInputStream& operator>>(T& obj)
|
||||
{
|
||||
::Unserialize(*this, obj, m_type, m_version);
|
||||
::Unserialize(*this, obj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int GetVersion() const { return m_version; }
|
||||
int GetType() const { return m_type; }
|
||||
private:
|
||||
const int m_type;
|
||||
const int m_version;
|
||||
@@ -80,7 +81,7 @@ int zcashconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int
|
||||
stream >> tx;
|
||||
if (nIn >= tx.vin.size())
|
||||
return set_error(err, zcashconsensus_ERR_TX_INDEX);
|
||||
if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
||||
return set_error(err, zcashconsensus_ERR_TX_SIZE_MISMATCH);
|
||||
|
||||
// Regardless of the verification result, the tx did not error.
|
||||
|
||||
Reference in New Issue
Block a user