tests for bet protocol done; verify notarisation still to test

This commit is contained in:
Scott Sadler
2018-04-05 05:06:22 -03:00
parent 92df780015
commit 561f3e18c1
23 changed files with 1195 additions and 443 deletions

View File

@@ -1,117 +1,79 @@
#include <cryptoconditions.h>
#include "streams.h"
#include "komodo_cc.h"
#include "cc/eval.h"
#include "cc/betprotocol.h"
#include "primitives/transaction.h"
class DisputeHeader
{
public:
int waitBlocks;
std::vector<unsigned char> vmParams;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(waitBlocks));
READWRITE(vmParams);
}
};
static 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 CScript CCSig(const CC *cond)
{
unsigned char buf[1000];
size_t len = cc_fulfillmentBinary(cond, buf, 1000);
auto ffill = std::vector<unsigned char>(buf, buf+len);
ffill.push_back(SIGHASH_ALL);
return CScript() << ffill;
}
static unsigned char* CopyPubKey(CPubKey pkIn)
{
auto *pk = malloc(33);
unsigned char* pk = (unsigned char*) malloc(33);
memcpy(pk, pkIn.begin(), 33); // TODO: compressed?
return pk;
}
class PokerProtocol
CC* CCNewThreshold(int t, std::vector<CC*> v)
{
public:
CAmount MINFEE = 1;
std::vector<CPubKey> players;
DisputeHeader disputeHeader;
// on PANGEA
CC* MakeDisputeCond();
CMutableTransaction MakeSessionTx(CTxIn dealerInput);
CMutableTransaction MakeDisputeTx(uint256 signedSessionTxHash);
CMutableTransaction MakePostEvidenceTx(uint256 signedSessionTxHash,
CPubKey playerKey, std::vector<unsigned char> state);
// on KMD
CC* MakePayoutCond();
CMutableTransaction MakeStakeTx(CAmount totalPayout, std::vector<CTxIn> playerInputs,
uint256 signedSessionTx);
CMutableTransaction MakeAgreePayoutTx(std::vector<CTxOut> payouts,
CC *signedPayoutCond, uint256 signedStakeTxHash);
CMutableTransaction MakeImportPayoutTx(std::vector<CTxOut> payouts,
CC *signedPayoutCond, CTransaction signedDisputeTx);
}
CC* PokerProtocol::MakeDisputeCond()
{
CC *disputePoker = cond->subconditions[0] = cc_new(CC_Eval);
char err[1000];
std::vector<unsigned char> headerData;
disputeHeader >> CDataStream(headerData, SER_DISK, PROTOCOL_VERSION);
disputePoker->paramsBin = malloc(headerData.size());
memcpy(disputePoker->paramsBin, headerData.data());
disputePoker.paramsBinLength = headerData.size();
CC *spendSig = cond->subconditions[1] = cc_new(CC_Threshold);
spendSig->threshold = 1;
spendSig->size = players.size() + 1;
spendSig->subconditions = malloc(spendSig->size, sizeof(CC*));
for (int i=0; i<players.size()+1; i++) {
CC *sub = spendSig->subconditions[i] = cc_new(CC_Secp256k1);
sub->publicKey = CopyPubKey(players[i]);
}
CC *cond = cc_new(CC_Threshold);
cond->threshold = 2;
cond->size = 2;
cond->subconditions = calloc(2, sizeof(CC*));
cond->subconditions[0] = disputePoker;
cond->subconditions[1] = spendSig;
cond->threshold = t;
cond->size = v.size();
cond->subconditions = (CC**) calloc(v.size(), sizeof(CC*));
memcpy(cond->subconditions, v.data(), v.size() * sizeof(CC*));
return cond;
}
CMutableTransaction PokerProtocol::MakeSessionTx(CTxIn dealerInput)
CC* CCNewSecp256k1(CPubKey k)
{
CC *cond = cc_new(CC_Secp256k1);
cond->publicKey = CopyPubKey(k);
return cond;
}
CC* CCNewEval(std::string method, std::vector<unsigned char> paramsBin)
{
CC *cond = cc_new(CC_Eval);
strcpy(cond->method, method.data());
cond->paramsBin = (unsigned char*) malloc(paramsBin.size());
memcpy(cond->paramsBin, paramsBin.data(), paramsBin.size());
cond->paramsBinLength = paramsBin.size();
return cond;
}
std::vector<CC*> BetProtocol::PlayerConditions()
{
std::vector<CC*> subs;
for (int i=0; i<players.size(); i++)
subs.push_back(CCNewSecp256k1(players[i]));
return subs;
}
CC* BetProtocol::MakeDisputeCond()
{
CC *disputePoker = CCNewEval(disputeFunc, CheckSerialize(disputeHeader));
CC *anySig = CCNewThreshold(1, PlayerConditions());
return CCNewThreshold(2, {disputePoker, anySig});
}
CMutableTransaction BetProtocol::MakeSessionTx()
{
CMutableTransaction mtx;
mtx.vin.push_back(dealerInput);
CC *disputeCond = MakeDisputeCond(players, disputeHeader);
CC *disputeCond = MakeDisputeCond();
mtx.vout.push_back(CTxOut(MINFEE, CCPubKey(disputeCond)));
cc_free(disputeCond);
for (int i=0; i<players.size(); i++) {
CC *cond = cc_new(CC_Secp256k1);
cond->publicKey = CopyPubKey(players[i]);
CC *cond = CCNewSecp256k1(players[i]);
mtx.vout.push_back(CTxOut(MINFEE, CCPubKey(cond)));
cc_free(cond);
}
@@ -119,12 +81,12 @@ CMutableTransaction PokerProtocol::MakeSessionTx(CTxIn dealerInput)
}
CMutableTransaction PokerProtocol::MakeDisputeTx(uint256 signedSessionTxHash, CC *signedDisputeCond, uint256 vmResultHash)
CMutableTransaction BetProtocol::MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash)
{
CMutableTransaction mtx;
CC *disputeCond = MakeDisputeCond();
mtx.vin.push_back(CTxIn(signedSessionTxHash, 0, CCSig(signedDisputeCond)));
mtx.vin.push_back(CTxIn(signedSessionTxHash, 0, CScript()));
std::vector<unsigned char> result(vmResultHash.begin(), vmResultHash.begin()+32);
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << result));
@@ -132,69 +94,29 @@ CMutableTransaction PokerProtocol::MakeDisputeTx(uint256 signedSessionTxHash, CC
}
CMutableTransaction PokerProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash,
CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash,
int playerIdx, std::vector<unsigned char> state)
{
CMutableTransaction mtx;
CC *cond = cc_new(CC_Secp256k1);
cond->publicKey = CopyPubKey(players[i]);
mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CCSig(cond)));
mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript()));
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state));
return mtx;
}
CC* CCNewThreshold(int t, std::vector<CC*> v)
CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash)
{
CC *cond = cc_new(CC_Threshold);
cond->threshold = t;
cond->size = v.size();
cond->subconditions = calloc(1, sizeof(CC*));
memcpy(cond->subconditions, v.data(), v.size() * sizeof(CC*));
return cond;
}
CCNewSecp256k1(CPubKey &k)
{
cond = cc_new(CC_Secp256k1);
cond->publicKey = CopyPubKey(players[i]);
return cond;
}
CCNewEval(char *method, unsigned char* paramsBin, size_t len)
{
CC *cond = cc_new(CC_Eval);
strcpy(cond->method, method);
cond->paramsBin = malloc(32);
memcpy(cond->paramsBin, bin, len);
cond->paramsBinLength = len;
return cond;
}
CC* MakePayoutCond(uint256 signedSessionTx)
{
CC* agree;
{
// TODO: 2/3 majority
std::vector<CC*> subs;
for (int i=0; i<players.size(); i++)
subs[i] = CCNewSecp256k1(players[i]);
agree = CCNewThreshold(players.size(), subs);
}
// TODO: 2/3 majority
CC* agree = CCNewThreshold(players.size(), PlayerConditions());
CC *import;
{
CC *importEval = CCNewEval("ImportPayout", signedSessionTx.begin(), 32);
std::vector<unsigned char> vHash(signedSessionTxHash.begin(), signedSessionTxHash.end());
CC *importEval = CCNewEval("ImportPayout", vHash);
std::vector<CC*> subs;
for (int i=0; i<players.size(); i++)
subs[i] = CCNewSecp256k1(players[i]);
CC *oneof = CCNewThreshold(1, subs);
CC *oneof = CCNewThreshold(1, PlayerConditions());
import = CCNewThreshold(2, {oneof, importEval});
}
@@ -203,13 +125,11 @@ CC* MakePayoutCond(uint256 signedSessionTx)
}
CMutableTransaction PokerProtocol::MakeStakeTx(CAmount totalPayout, std::vector<CTxIn> playerInputs,
uint256 signedSessionTx)
CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash)
{
CMutableTransaction mtx;
mtx.vin = playerInputs;
CC *payoutCond = MakePayoutCond(signedSessionTx);push
CC *payoutCond = MakePayoutCond(signedSessionTxHash);
mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond)));
cc_free(payoutCond);
@@ -217,24 +137,23 @@ CMutableTransaction PokerProtocol::MakeStakeTx(CAmount totalPayout, std::vector<
}
CMutableTransaction PokerProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts,
CC *signedPayoutCond, uint256 signedStakeTxHash)
CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts,
uint256 signedStakeTxHash)
{
CMutableTransaction mtx;
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CCSig(signedPayoutCond)));
mtx.vouts = payouts;
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
mtx.vout = payouts;
return mtx;
}
CMutableTransaction PokerProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
CC *signedPayoutCond, CTransaction signedDisputeTx, uint256 signedStakeTxHash)
CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof)
{
std::vector<unsigned char> vDisputeTx;
signedDisputeTx >> CDataStream(vDisputeTx, SER_DISK, PROTOCOL_VERSION);
CMutableTransaction mtx;
mtx.vin.push_back(CTxInput(signedStakeTxHash, 0, CCSig(signedPayoutCond)));
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
mtx.vout = payouts;
CMutableTransaction.vout.insert(0, CTxOutput(0, CScript() << OP_RETURN << "PROOF HERE"));
CMutableTransaction.vout.insert(1, CTxOutput(0, CScript() << OP_RETURN << vDisputeTx));
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << CheckSerialize(momProof)));
mtx.vout.insert(mtx.vout.begin()+1, CTxOut(0, CScript() << OP_RETURN << CheckSerialize(signedDisputeTx)));
return mtx;
}

85
src/cc/betprotocol.h Normal file
View File

@@ -0,0 +1,85 @@
#ifndef BETPROTOCOL_H
#define BETPROTOCOL_H
#include "pubkey.h"
#include "primitives/transaction.h"
#include "cryptoconditions/include/cryptoconditions.h"
#define ExecMerkle CBlock::CheckMerkleBranch
class MoMProof
{
public:
int nIndex;
std::vector<uint256> branch;
uint256 notarisationHash;
MoMProof() {}
MoMProof(int i, std::vector<uint256> b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(nIndex));
READWRITE(branch);
READWRITE(notarisationHash);
}
};
class DisputeHeader
{
public:
int waitBlocks;
std::vector<unsigned char> vmParams;
DisputeHeader() {}
DisputeHeader(int w, std::vector<unsigned char> vmp) : waitBlocks(w), vmParams(vmp) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(waitBlocks));
READWRITE(vmParams);
}
};
class BetProtocol
{
protected:
char* disputeFunc = (char*) "DisputeBet";
std::vector<CC*> playerConditions();
public:
CAmount MINFEE = 1;
std::vector<CPubKey> players;
DisputeHeader disputeHeader;
// Utility
BetProtocol(std::vector<CPubKey> ps, DisputeHeader dh) : players(ps), disputeHeader(dh) {}
std::vector<CC*> PlayerConditions();
// on PANGEA
CC* MakeDisputeCond();
CMutableTransaction MakeSessionTx();
CMutableTransaction MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash);
CMutableTransaction MakePostEvidenceTx(uint256 signedSessionTxHash,
int playerIndex, std::vector<unsigned char> state);
// on KMD
CC* MakePayoutCond(uint256 signedSessionTxHash);
CMutableTransaction MakeStakeTx(CAmount totalPayout, uint256 signedSessionTx);
CMutableTransaction MakeAgreePayoutTx(std::vector<CTxOut> payouts, uint256 signedStakeTxHash);
CMutableTransaction MakeImportPayoutTx(std::vector<CTxOut> payouts,
CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof);
};
CC* CCNewSecp256k1(CPubKey k);
#endif /* BETPROTOCOL_H */

View File

@@ -1,34 +1,27 @@
#include "primitives/transaction.h"
#include "streams.h"
#include <cryptoconditions.h>
#include "hash.h"
#include "chain.h"
#include "main.h"
#include "version.h"
#include "komodo_cc.h"
#include "cc/eval.h"
#include "cryptoconditions/include/cryptoconditions.h"
#include "cc/betprotocol.h"
#include "primitives/transaction.h"
bool GetSpends(uint256 hash, std::vector<boost::optional<CTransaction>> &spends)
class DisputeHeader;
static bool GetOpReturnHash(CScript script, uint256 &hash)
{
// NOT IMPLEMENTED
return false;
std::vector<unsigned char> vHash;
GetOpReturnData(script, vHash);
if (vHash.size() != 32) return false;
memcpy(hash.begin(), vHash.data(), 32);
return true;
}
class DisputeHeader
{
public:
int waitBlocks;
std::vector<unsigned char> vmHeader;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(waitBlocks));
READWRITE(vmHeader);
}
};
/*
* Crypto-Condition EVAL method that resolves a dispute of a session
*
@@ -42,48 +35,51 @@ public:
* in 0: Spends Session TX first output, reveals DisputeHeader
* out 0: OP_RETURN hash of payouts
*/
bool DisputePayout(AppVM &vm, const CC *cond, const CTransaction *disputeTx, int nIn)
bool Eval::DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeTx, unsigned int nIn)
{
// TODO: Error messages!
if (disputeTx->vout.size() < 2) return 0;
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
// get payouts hash
std::vector<unsigned char> vPayoutHash;
uint256 payoutHash;
if (!GetOpReturnData(disputeTx->vout[0].scriptPubKey, vPayoutHash)) return 0;
memcpy(payoutHash.begin(), vPayoutHash.data(), 32);
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
return Invalid("invalid-payout-hash");
// load dispute header
DisputeHeader disputeHeader;
std::vector<unsigned char> headerData(cond->paramsBin,
cond->paramsBin+cond->paramsBinLength);
CDataStream(headerData, SER_DISK, PROTOCOL_VERSION) >> disputeHeader;
// TODO: exception? end of stream?
if (!CheckDeserialize(headerData, disputeHeader))
return Invalid("invalid-dispute-header");
// ensure that enough time has passed
CTransaction sessionTx;
uint256 sessionBlockHash;
if (!GetTransaction(disputeTx->vin[0].prevout.hash, sessionTx, sessionBlockHash, false))
return false; // wth? TODO: log TODO: MUST be upsteam of disputeTx, how to ensure?
// below does this by looking up block in blockindex
// what if GetTransaction returns from mempool, maybe theres no block?
CBlockIndex* sessionBlockIndex = mapBlockIndex[sessionBlockHash];
if (chainActive.Height() < sessionBlockIndex->nHeight + disputeHeader.waitBlocks)
return false; // Not yet
CBlockIndex sessionBlock;
if (!GetTx(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlockHash, false))
return Error("couldnt-get-parent");
// TODO: This may not be an error, if both txs are to go into the same block...
// Probably change it to Invalid
if (!GetBlock(sessionBlockHash, sessionBlock))
return Error("couldnt-get-block");
if (GetCurrentHeight() < sessionBlock.nHeight + disputeHeader.waitBlocks)
return Invalid("dispute-too-soon"); // Not yet
// get spends
std::vector<boost::optional<CTransaction>> spends;
if (!GetSpends(disputeTx->vin[0].prevout.hash, spends)) return 0;
std::vector<CTransaction> spends;
if (!GetSpends(disputeTx.vin[0].prevout.hash, spends))
return Error("couldnt-get-spends");
// verify result from VM
int maxLength = -1;
uint256 bestPayout;
for (int i=1; i<spends.size(); i++)
{
if (!spends[i]) continue;
std::vector<unsigned char> vmBody;
if (!GetOpReturnData(spends[i]->vout[0].scriptPubKey, vmBody)) continue;
auto out = vm.evaluate(disputeHeader.vmHeader, vmBody);
std::vector<unsigned char> vmState;
if (!spends[i].vout.size() > 0) continue;
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
auto out = vm.evaluate(disputeHeader.vmParams, vmState);
uint256 resultHash = SerializeHash(out.second);
if (out.first > maxLength) {
maxLength = out.first;
@@ -98,5 +94,7 @@ bool DisputePayout(AppVM &vm, const CC *cond, const CTransaction *disputeTx, int
}
}
return bestPayout == payoutHash;
if (maxLength == -1) return Invalid("no-evidence");
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
}

View File

@@ -1,56 +1,151 @@
#include <assert.h>
#include <cryptoconditions.h>
#include "primitives/transaction.h"
#include "komodo_cc.h"
#include "cc/eval.h"
#include <cryptoconditions.h>
#include "main.h"
#include "chain.h"
Eval* EVAL_TEST = 0;
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
{
Eval eval_;
Eval *eval = EVAL_TEST;
if (!eval) eval = &eval_;
bool out = eval->Dispatch(cond, tx, nIn);
assert(eval->state.IsValid() == out);
if (eval->state.IsValid()) return true;
std::string lvl = eval->state.IsInvalid() ? "Invalid" : "Error!";
fprintf(stderr, "CC Eval %s %s: %s in tx %s\n", lvl.data(), cond->method,
eval->state.GetRejectReason().data(), tx.GetHash().GetHex().data());
return false;
}
/*
* Test the validity of an Eval node
*/
bool EvalConditionValidity(const CC *cond, const CTransaction *txTo, int nIn)
bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
{
if (strcmp(cond->method, "TestEval") == 0) {
return cond->paramsBinLength == 8 &&
memcmp(cond->paramsBin, "TestEval", 8) == 0;
bool valid = cond->paramsBinLength == 8 && memcmp(cond->paramsBin, "TestEval", 8) == 0;
return valid ? Valid() : Invalid("testing");
}
if (strcmp(cond->method, "ImportPayout") == 0) {
return CheckImportPayout(cond, txTo, nIn);
return ImportPayout(cond, txTo, nIn);
}
/* Example of how you might call DisputePayout
if (strcmp(ASSETCHAINS_SYMBOL, "PANGEA") == 0) {
if (strcmp(cond->method, "DisputePoker") == 0) {
return DisputePayout(PokerVM(), cond, txTo, nIn);
}
}
*/
fprintf(stderr, "no defined behaviour for method: %s\n", cond->method);
return 0;
return Invalid("no-such-method");
}
bool GetPushData(const CScript &sig, std::vector<unsigned char> &data)
bool Eval::GetSpends(uint256 hash, std::vector<CTransaction> &spends) const
{
opcodetype opcode;
auto pc = sig.begin();
if (sig.GetOp(pc, opcode, data)) return opcode > OP_0 && opcode <= OP_PUSHDATA4;
// NOT IMPLEMENTED
return false;
}
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data)
bool Eval::GetTx(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) const
{
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 GetTransaction(hash, txOut, hashBlock, fAllowSlow);
}
unsigned int Eval::GetCurrentHeight() const
{
return chainActive.Height();
}
bool Eval::GetBlock(uint256 hash, CBlockIndex& blockIdx) const
{
auto r = mapBlockIndex.find(hash);
if (r != mapBlockIndex.end()) {
blockIdx = *r->second;
return true;
}
return false;
}
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
{
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[33];
BOOST_FOREACH(const CTxIn &txIn, tx.vin)
{
// Get notary pubkey
CTransaction tx;
uint256 hashBlock;
if (!GetTx(txIn.prevout.hash, tx, hashBlock, false)) return false;
if (tx.vout.size() < txIn.prevout.n) return false;
const unsigned char *script = tx.vout[txIn.prevout.n].scriptPubKey.data();
if (script[0] != 33) return false;
memcpy(pk, script+1, 33);
return true;
// Check it's a notary
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 Eval::GetMoM(const uint256 notaryHash, uint256 &mom) const
{
CTransaction notarisationTx;
uint256 notarisationBlock;
if (!GetTx(notaryHash, notarisationTx, notarisationBlock, true)) return 0;
CBlockIndex block;
if (!GetBlock(notarisationBlock, block)) return 0;
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.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;
}

View File

@@ -1,18 +1,56 @@
#ifndef CC_EVAL_H
#define CC_EVAL_H
#include "cryptoconditions/include/cryptoconditions.h"
#include <cryptoconditions.h>
#include "chain.h"
#include "streams.h"
#include "version.h"
#include "consensus/validation.h"
#include "primitives/transaction.h"
/*
* Test validity of a CC_Eval node
*/
bool EvalConditionValidity(const CC *cond, const CTransaction *tx, int nIn);
/*
* Test an ImportPayout CC Eval condition
*/
bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn);
class AppVM;
class Eval
{
public:
CValidationState state;
bool Invalid(std::string s) { return state.Invalid(false, 0, s); }
bool Error(std::string s) { return state.Error(s); }
bool Valid() { return true; }
/*
* Test validity of a CC_Eval node
*/
virtual bool Dispatch(const CC *cond, const CTransaction &tx, unsigned int nIn);
/*
* Dispute a payout using a VM
*/
bool DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeTx, unsigned int nIn);
/*
* Test an ImportPayout CC Eval condition
*/
bool ImportPayout(const CC *cond, const CTransaction &payoutTx, unsigned int nIn);
/*
* IO functions
*/
virtual bool GetTx(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) const;
virtual unsigned int GetCurrentHeight() const;
virtual bool GetSpends(uint256 hash, std::vector<CTransaction> &spends) const;
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
virtual bool GetMoM(uint256 notarisationHash, uint256& MoM) const;
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
};
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn);
/*
* Virtual machine to use in the case of on-chain app evaluation
@@ -30,20 +68,36 @@ public:
evaluate(std::vector<unsigned char> header, std::vector<unsigned char> body) = 0;
};
/*
* Test a DisputePayout CC Eval condition, using a provided AppVM
*/
bool DisputePayout(AppVM &vm, const CC *cond, const CTransaction *disputeTx, int nIn);
/*
* Get PUSHDATA from a script
* Serialisation boilerplate
*/
bool GetPushData(const CScript &sig, std::vector<unsigned char> &data);
template <class T>
std::vector<unsigned char> CheckSerialize(T &in);
template <class T>
bool CheckDeserialize(std::vector<unsigned char> vIn, T &out);
/*
* Get OP_RETURN data from a script
*/
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data);
template <class T>
std::vector<unsigned char> CheckSerialize(T &in)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << in;
return std::vector<unsigned char>(ss.begin(), ss.end());
}
template <class T>
bool CheckDeserialize(std::vector<unsigned char> vIn, T &out)
{
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
try {
ss >> out;
if (ss.eof()) return true;
} catch(...) {}
return false;
}
#endif /* CC_EVAL_H */

View File

@@ -1,78 +1,11 @@
#include "primitives/transaction.h"
#include "streams.h"
#include "chain.h"
#include <cryptoconditions.h>
#include "main.h"
#include "chain.h"
#include "streams.h"
#include "cc/eval.h"
#include "cc/importpayout.h"
#include "cryptoconditions/include/cryptoconditions.h"
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;
}
#define ExecMerkle CBlock::CheckMerkleBranch
#include "cc/betprotocol.h"
#include "primitives/transaction.h"
/*
@@ -97,54 +30,51 @@ bool GetMoM(const uint256 notaryHash, uint256 &mom)
* out 0: OP_RETURN hash of payouts
* out 1-: anything
*/
bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn)
bool Eval::ImportPayout(const CC *cond, const CTransaction &payoutTx, unsigned 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());
if (payoutTx.vout.size() < 2) return Invalid("need-2-vouts");
// load disputeTx from vout[1]
CTransaction disputeTx;
{
std::vector<unsigned char> exportData;
if (!GetOpReturnData(payoutTx->vout[1].scriptPubKey, exportData)) return 0;
CDataStream(exportData, SER_DISK, PROTOCOL_VERSION) >> disputeTx;
// TODO: end of stream? exception?
}
std::vector<unsigned char> exportData;
GetOpReturnData(payoutTx.vout[1].scriptPubKey, exportData);
if (!CheckDeserialize(exportData, disputeTx))
return Invalid("invalid-dispute-tx");
// Check disputeTx.0 is vPayoutsHash
std::vector<unsigned char> exportPayoutsHash;
if (!GetOpReturnData(disputeTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0;
if (exportPayoutsHash != vPayoutsHash) return 0;
// Check disputeTx.0 shows correct 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());
if (disputeTx.vout[0].scriptPubKey != CScript() << OP_RETURN << vPayoutsHash)
return Invalid("wrong-payouts");
}
// Check disputeTx spends sessionTx.0
// condition ImportPayout params is session ID from other chain
{
if (cond->paramsBinLength != 32) return 0;
if (cond->paramsBinLength != 32) return Invalid("malformed-params");
COutPoint prevout = disputeTx.vin[0].prevout;
if (memcmp(prevout.hash.begin(), cond->paramsBin, 32) != 0 ||
prevout.n != 0) return 0;
prevout.n != 0) return Invalid("wrong-session");
}
// Check disputeTx solves momproof from vout[0]
{
std::vector<unsigned char> vchMomProof;
if (!GetOpReturnData(payoutTx->vout[0].scriptPubKey, vchMomProof)) return 0;
std::vector<unsigned char> vProof;
GetOpReturnData(payoutTx.vout[0].scriptPubKey, vProof);
MoMProof proof;
if (!CheckDeserialize(vProof, proof))
return Invalid("invalid-mom-proof-payload");
MoMProof momProof;
CDataStream(vchMomProof, SER_DISK, PROTOCOL_VERSION) >> momProof;
uint256 mom;
if (!GetMoM(momProof.notarisationHash, mom)) return 0;
uint256 MoM;
if (!GetMoM(proof.notarisationHash, MoM)) return Invalid("coudnt-load-mom");
uint256 proofResult = ExecMerkle(disputeTx.GetHash(), momProof.branch, momProof.nIndex);
if (proofResult != mom) return 0;
if (MoM != ExecMerkle(disputeTx.GetHash(), proof.branch, proof.nIndex))
return Invalid("mom-check-fail");
}
return 1;
return Valid();
}

View File

@@ -1,26 +0,0 @@
#ifndef KOMODO_TXPROOF_H
#define KOMODO_TXPROOF_H
class MoMProof
{
public:
int nIndex;
std::vector<uint256> branch;
uint256 notarisationHash;
MoMProof() {}
MoMProof(int i, std::vector<uint256> b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(nIndex));
READWRITE(branch);
READWRITE(notarisationHash);
}
};
#endif /* KOMODO_TXPROOF_H */