Merge branch 'FSM' of https://github.com/jl777/komodo into jl777-FSM

merge
This commit is contained in:
blackjok3r
2018-11-29 23:22:32 +08:00
566 changed files with 42751 additions and 10240 deletions

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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,

View File

@@ -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(

View File

@@ -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;

View File

@@ -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
View 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
View 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

View File

@@ -12,6 +12,7 @@
#include "uint256.h"
#include "util.h"
#undef __cpuid
#include <boost/thread.hpp>
#include <boost/tuple/tuple_comparison.hpp>

View File

@@ -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
View 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
View 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

View File

@@ -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');

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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.