Beginning of N@S solution using CoinbaseGuard CC

This commit is contained in:
miketout
2018-10-02 19:49:54 -07:00
parent c68ca1a225
commit 8a727a26a7
49 changed files with 832 additions and 62 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

@@ -952,7 +952,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);

View File

@@ -245,9 +245,64 @@ bool CScript::IsPayToScriptHash() const
(*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;
}
if (opcode < 1 || opcode > OP_PUSHDATA4)
return false;
netPushes++;
vSolutions.push_back(data);
}
else
return false;
}
return netPushes == 0;
}
// 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::GetOpretData(std::vector<std::vector<unsigned char>>& vData) const
{
vector<unsigned char> data;
opcodetype opcode;
const_iterator pc = begin();
vData.clear();
if (GetOp(pc, opcode, data) && opcode == OP_RETURN)
{
while (pc < end())
{
if (GetOp(pc, opcode, data))
{
vData.push_back(data);
}
}
return vData.size() != 0;
}
}
bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector<std::vector<unsigned char>>& vSolutions) const
{
const_iterator pc = begin();
vector<unsigned char> data;
opcodetype opcode;
if (this->GetOp(pc, opcode, data))
@@ -255,9 +310,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())
{
const_iterator pcCCEnd = pc;
if (GetBalancedData(pc, vSolutions))
{
if (pCCSubScript)
*pCCSubScript = CScript(begin(),pc);
return 1;
return 0;
}
}
return false;
}
bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript) const
{
std::vector<std::vector<unsigned char>> vSolutions;
return IsPayToCryptoCondition(pCCSubScript, vSolutions);
}
bool CScript::IsPayToCryptoCondition() const
{
return IsPayToCryptoCondition(NULL);
}
bool CScript::MayAcceptCryptoCondition() const

View File

@@ -19,6 +19,7 @@
#include <vector>
#define OPRETTYPE_TIMELOCK 1
#define OPRETTYPE_STAKEPARAMS 2
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
@@ -381,6 +382,7 @@ protected:
}
return *this;
}
bool GetBalancedData(const_iterator& pc, std::vector<std::vector<unsigned char>>& vSolutions) const;
public:
CScript() { }
CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { }
@@ -574,6 +576,11 @@ public:
bool IsPayToPublicKey() const;
bool IsPayToScriptHash() 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;

View File

@@ -10,6 +10,8 @@
#include "keystore.h"
#include "script/standard.h"
#include "uint256.h"
#include "cc/CCinclude.h"
#include "cc/eval.h"
#include <boost/foreach.hpp>
@@ -19,10 +21,12 @@ typedef std::vector<unsigned char> valtype;
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->GetKey(address, key))
return false;
uint256 hash;
@@ -32,8 +36,19 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig,
return false;
}
if (!key.Sign(hash, vchSig))
return false;
if (scriptCode.IsPayToCryptoCondition())
{
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);
}
else
{
if (!key.Sign(hash, vchSig))
return false;
}
vchSig.push_back((unsigned char)nHashType);
return true;
}
@@ -61,6 +76,201 @@ 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});
}
bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode)
{
cp->evalcode = evalcode;
switch ( evalcode )
{
case EVAL_COINBASEGUARD:
uint8_t privKey[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 };
strcpy(cp->unspendableCCaddr,"RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6");
strcpy(cp->normaladdr,"RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u");
strcpy(cp->CChexstr,"02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702");
memcpy(cp->CCpriv, privKey,32);
return true;
}
return false;
}
static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scriptPubKey, vector<valtype> &vSolutions,
vector<valtype>& ret, uint32_t consensusBranchId)
{
CScript subScript;
vector<CPubKey> vPK;
vector<CKeyID> vKeyID = vector<CKeyID>();
vector<valtype> vParams = vector<valtype>();
COptCCParams p;
scriptPubKey.IsPayToCryptoCondition(&subScript, vParams);
if (vParams.size() > 1 && (p = COptCCParams(vParams[0])).IsValid())
{
bool is1of2 = (p.n == 1 && p.m == 1);
uint32_t extraAddrs = p.m;
CKey privKey;
// get information to sign with
CCcontract_info C;
// must be a valid cc eval code
if (CCinitLite(&C, p.evalCode))
{
// pay to cc address is a valid tx
if (!is1of2)
{
try
{
if (!extraAddrs)
{
vKeyID.push_back(CKeyID(uint160(vSolutions[0])));
// if this isn't our main CC address, we can't do anything with it
if (strcmp(C.unspendableCCaddr, CBitcoinAddress(CTxDestination(vKeyID[0])).ToString().c_str()) != 0)
return false;
// otherwise, push back the corresponding pub key
vPK.push_back(CPubKey(ParseHex(C.CChexstr)));
}
else if (vParams.size() >= (extraAddrs + 1))
{
bool havePriv;
vKeyID.push_back(CKeyID(uint160(vParams[1])));
// if this isn't the normal CC address and we also don't have it in our keystore, fail
CBitcoinAddress addr = CBitcoinAddress(CTxDestination(vKeyID[0]));
if (strcmp(C.normaladdr, addr.ToString().c_str()) == 0 &&
!(havePriv = creator.KeyStore().GetKey(vKeyID[0], privKey)))
return false;
vPK.push_back(CPubKey());
// if we don't have the private key, it is the unspendable address
if (!havePriv)
{
vPK[0] = CPubKey(ParseHex(C.CChexstr));
privKey = CKey();
CPrivKey vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv));
privKey.SetPrivKey(vch, false);
}
else if (!creator.KeyStore().GetPubKey(vKeyID[0], vPK[0]))
return false;
}
} catch (...)
{
fprintf(stderr,"exception calculating 1of1 spend\n");
return false;
}
CC *cc = CCcond1(p.evalCode, vPK[0]);
if (cc)
{
vector<unsigned char> vch;
if (creator.CreateSig(vch, vKeyID[0], scriptPubKey, consensusBranchId, &privKey, (void *)cc))
{
ret.push_back(vch);
}
else
{
fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", vKeyID[0].ToString().c_str());
}
cc_free(cc);
return ret.size() != 0;
}
}
else if (extraAddrs > 1 && vParams.size() >= (extraAddrs + 1))
{
// we need to get 2 addresses, and we will need the private key for one
// to spend
bool pkValid = false;
for (int i = 0; i < extraAddrs; i++)
{
// loop through in order and choose the first key we have a priv key to for signing
try
{
bool isCCAddr = false;
CPubKey pk;
vKeyID.push_back(CKeyID(uint160(vParams[i + 1])));
// if this isn't the CC address and we also don't have the pubkey in our keystore, fail, because we won't
// be able to make the condition to fulfill
if (!(isCCAddr = (strcmp(C.normaladdr, CBitcoinAddress(CTxDestination(vKeyID[0])).ToString().c_str()) == 0)) &&
!creator.KeyStore().GetPubKey(vKeyID[0], pk))
return false;
if (isCCAddr)
{
pk = CPubKey(ParseHex(C.CChexstr));
// only set the private key to this address if we don't have one yet
if (!pkValid)
{
privKey = CKey();
CPrivKey vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv));
privKey.SetPrivKey(vch, false);
pkValid = true;
}
}
else
{
if (!pkValid)
{
if (creator.KeyStore().GetKey(vKeyID[0], privKey))
pkValid = true;
}
}
vPK.push_back(pk);
} catch (...)
{
fprintf(stderr,"exception calculating 1of2 spend\n");
return false;
}
}
if (!pkValid)
return false;
CC *cc = CCcond1of2(p.evalCode, vPK[0], vPK[1]);
if (cc)
{
vector<unsigned char> vch;
if (creator.CreateSig(vch, vKeyID[0], scriptPubKey, consensusBranchId, &privKey, (void *)cc))
{
ret.push_back(vch);
}
else
{
fprintf(stderr,"vin has 1of2 CC signing error with address.(%s)\n", vKeyID[0].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),
@@ -96,6 +306,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
}
CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
@@ -121,6 +332,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
@@ -384,7 +598,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;
@@ -27,7 +28,12 @@ public:
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 +47,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 +62,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

@@ -73,19 +73,37 @@ 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() && vParams.size() > cp.m)
{
// all addresses that should be there must be 20 byte keyIDs
for (int i = 1; i <= cp.m; i++)
{
if (vParams[i].size() != 20)
{
// we accept no errors
return false;
}
}
}
}
return true;
}
return false;
@@ -331,6 +349,18 @@ 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 = CKeyID(uint160(vSolutions[i]));
addressRet.push_back(address);
}
if (addressRet.empty())
return false;
}
else
{
nRequiredRet = 1;

View File

@@ -83,6 +83,44 @@ public:
*/
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 n, m; // for n of m sigs required, m addresses for sigs will follow
COptCCParams() : version(0), evalCode(0), n(0), m(0) {}
COptCCParams(uint8_t ver, uint8_t code, uint8_t _n, uint8_t _m) : version(ver), evalCode(code), n(_n), m(_m) {}
COptCCParams(std::vector<unsigned char> &vch)
{
version = 0;
if (vch.size() == 4)
{
version = vch[0];
evalCode = vch[1];
n = vch[2];
m = vch[3];
if (version != VERSION && n == 1 && (m == 1 || m == 2))
{
// we only support one version, and 1 of 1 or 1 of 2 now, so set invalid
version = 0;
}
}
}
bool IsValid() { return version != 0; }
std::vector<unsigned char> AsVector()
{
std::vector<unsigned char> vch = std::vector<unsigned char>({version, evalCode, n, m});
}
};
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);