ImportPayout cc eval code and alot of general cc polish. tests to write

This commit is contained in:
Scott Sadler
2018-03-30 15:46:41 -03:00
parent 991c422a9d
commit 2c8d8268dd
25 changed files with 389 additions and 116 deletions

191
src/cc/importpayout.cpp Normal file
View File

@@ -0,0 +1,191 @@
#include "primitives/transaction.h"
#include "streams.h"
#include "chain.h"
#include "main.h"
#include "cryptoconditions/include/cryptoconditions.h"
bool GetPushData(const CScript &sig, std::vector<unsigned char> &data)
{
opcodetype opcode;
auto pc = sig.begin();
if (sig.GetOp(pc, opcode, data)) return opcode > OP_0 && opcode <= OP_PUSHDATA4;
return false;
}
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data)
{
auto pc = sig.begin();
opcodetype opcode;
if (sig.GetOp2(pc, opcode, NULL))
if (opcode == OP_RETURN)
if (sig.GetOp(pc, opcode, data))
return opcode > OP_0 && opcode <= OP_PUSHDATA4;
return false;
}
class MomProof
{
public:
uint256 notaryHash;
int nPos; // Position of imported tx in MoM
std::vector<uint256> branch;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(notaryHash);
READWRITE(VARINT(nPos));
READWRITE(branch);
}
};
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
bool DerefNotaryPubkey(const COutPoint &prevout, char *pk33)
{
CTransaction tx;
uint256 hashBlock;
if (!GetTransaction(prevout.hash, tx, hashBlock, false)) return false;
if (tx.vout.size() < prevout.n) return false;
const unsigned char *script = tx.vout[prevout.n].scriptPubKey.data();
if (script[0] != 33) return false;
memcpy(pk33, script+1, 33);
return true;
}
bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp)
{
if (tx.vin.size() < 11) return false;
uint8_t notaries[64][33];
uint8_t seenNotaries[64];
int nNotaries = komodo_notaries(notaries, height, timestamp);
char *pk;
BOOST_FOREACH(const CTxIn &txIn, tx.vin)
{
if (!DerefNotaryPubkey(txIn.prevout, pk)) return false;
for (int i=0; i<nNotaries; i++) {
if (!seenNotaries[i]) {
if (memcmp(pk, notaries[i], 33) == 0) {
seenNotaries[i] = 1;
goto found;
}
}
}
return false;
found:;
}
}
/*
* Get MoM from a notarisation tx hash
*/
bool GetMoM(const uint256 notaryHash, uint256 &mom)
{
CTransaction notarisationTx;
uint256 notarisationBlock;
if (!GetTransaction(notaryHash, notarisationTx, notarisationBlock, true)) return 0;
CBlockIndex* blockindex = mapBlockIndex[notarisationBlock];
if (!CheckNotaryInputs(notarisationTx, blockindex->nHeight, blockindex->nTime)) {
return false;
}
if (!notarisationTx.vout.size() < 1) return 0;
std::vector<unsigned char> opret;
if (!GetOpReturnData(notarisationTx.vout[0].scriptPubKey, opret)) return 0;
if (opret.size() < 36) return 0; // In reality it is more than 36, but at the moment I
// only know where it is relative to the end, and this
// is enough to prevent a memory fault. In the case that
// the assumption about the presence of a MoM at this
// offset fails, we will return random other data that is
// not more likely to generate a false positive.
memcpy(mom.begin(), opret.data()+opret.size()-36, 32);
return 1;
}
uint256 ExecMerkle(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
{
return CBlock::CheckMerkleBranch(hash, vMerkleBranch, nIndex);
}
/*
* Crypto-Condition EVAL method that verifies a payout against a transaction
* notarised on another chain.
*
* IN: cond - CC EVAL node
* IN: payoutTx - Payout transaction on value chain (KMD)
* IN: nIn - index of input of stake
*
* payoutTx: Spends stakeTx with payouts from asset chain
*
* in 0: Spends Stake TX and contains ImportPayout CC
* out 0: OP_RETURN MomProof
* out 1: OP_RETURN serialized exportTx from other chain
* out 2-: arbitrary payouts
*
* exportTx: Spends sessionTx.0 (opener on asset chain)
*
* in 0: spends sessionTx.0
* in 1-: anything
* out 0: OP_RETURN hash of payouts
* out 1-: anything
*/
bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn)
{
// TODO: Error messages!
if (payoutTx->vin.size() != 1) return 0;
if (payoutTx->vout.size() < 2) return 0;
// Get hash of payouts
std::vector<CTxOut> payouts(payoutTx->vout.begin() + 2, payoutTx->vout.end());
uint256 payoutsHash = SerializeHash(payouts);
std::vector<unsigned char> vPayoutsHash(payoutsHash.begin(), payoutsHash.end());
// load exportTx from vout[1]
CTransaction exportTx;
{
std::vector<unsigned char> exportData;
if (!GetOpReturnData(payoutTx->vout[1].scriptPubKey, exportData)) return 0;
CDataStream(exportData, SER_DISK, PROTOCOL_VERSION) >> exportTx;
// TODO: end of stream? exception?
}
// Check exportTx.0 is vPayoutsHash
std::vector<unsigned char> exportPayoutsHash;
if (!GetOpReturnData(exportTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0;
if (exportPayoutsHash != vPayoutsHash) return 0;
// Check exportTx spends sessionTx.0
// condition ImportPayout params is session ID from other chain
{
if (cond->paramsBinLength != 32) return 0;
COutPoint prevout = exportTx.vin[0].prevout;
if (memcmp(prevout.hash.begin(), cond->paramsBin, 32) != 0 ||
prevout.n != 0) return 0;
}
// Check exportTx solves momproof from vout[0]
{
std::vector<unsigned char> vchMomProof;
if (!GetOpReturnData(payoutTx->vout[0].scriptPubKey, vchMomProof)) return 0;
MomProof momProof;
CDataStream(vchMomProof, SER_DISK, PROTOCOL_VERSION) >> momProof;
uint256 mom;
if (!GetMoM(momProof.notaryHash, mom)) return 0;
uint256 proofResult = ExecMerkle(exportTx.GetHash(), momProof.branch, momProof.nPos);
if (proofResult != mom) return 0;
}
return 1;
}