change Eval data structure to single code blob

This commit is contained in:
Scott Sadler
2018-04-07 18:07:52 -03:00
parent 4729632250
commit 39c9911e9c
37 changed files with 239 additions and 768 deletions

View File

@@ -18,7 +18,9 @@ std::vector<CC*> BetProtocol::PlayerConditions()
CC* BetProtocol::MakeDisputeCond()
{
CC *disputePoker = CCNewEval(disputeFunc, CheckSerialize(disputeHeader));
CC *disputePoker = CCNewEval(E_MARSHAL(
ss << disputeCode << VARINT(waitBlocks) << vmParams;
));
CC *anySig = CCNewThreshold(1, PlayerConditions());
@@ -79,8 +81,9 @@ CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash)
CC *import;
{
std::vector<unsigned char> vHash(signedSessionTxHash.begin(), signedSessionTxHash.end());
CC *importEval = CCNewEval("ImportPayout", vHash);
CC *importEval = CCNewEval(E_MARSHAL(
ss << EVAL_IMPORTPAYOUT << signedSessionTxHash;
));
CC *oneof = CCNewThreshold(1, PlayerConditions());
@@ -120,7 +123,7 @@ CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
mtx.vout = payouts;
CScript proofData;
proofData << OP_RETURN << CheckSerialize(std::make_pair(momProof, signedDisputeTx));
proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx);
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData));
return mtx;
}

View File

@@ -1,6 +1,7 @@
#ifndef BETPROTOCOL_H
#define BETPROTOCOL_H
#include "cc/eval.h"
#include "pubkey.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
@@ -29,36 +30,19 @@ public:
};
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:
EvalCode disputeCode;
std::vector<CPubKey> players;
DisputeHeader disputeHeader;
std::vector<unsigned char> vmParams;
uint32_t waitBlocks;
// Utility
BetProtocol(std::vector<CPubKey> ps, DisputeHeader dh) : players(ps), disputeHeader(dh) {}
BetProtocol(EvalCode dc, std::vector<CPubKey> ps, uint32_t wb, std::vector<uint8_t> vmp)
: disputeCode(dc), waitBlocks(wb), vmParams(vmp), players(ps) {}
std::vector<CC*> PlayerConditions();
// on PANGEA

View File

@@ -13,7 +13,7 @@
* Crypto-Condition EVAL method that resolves a dispute of a session
*
* IN: vm - AppVM virtual machine to verify states
* IN: cond - CC EVAL node
* IN: params - condition params
* IN: disputeTx - transaction attempting to resolve dispute
* IN: nIn - index of input of dispute tx
*
@@ -22,7 +22,7 @@
* in 0: Spends Session TX first output, reveals DisputeHeader
* out 0: OP_RETURN hash of payouts
*/
bool Eval::DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeTx, unsigned int nIn)
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
{
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
@@ -31,12 +31,11 @@ bool Eval::DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeT
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);
if (!CheckDeserialize(headerData, disputeHeader))
return Invalid("invalid-dispute-header");
// load params
uint16_t waitBlocks;
std::vector<uint8_t> vmParams;
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
return Invalid("malformed-params");
// ensure that enough time has passed
{
@@ -47,7 +46,7 @@ bool Eval::DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeT
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
return Error("couldnt-get-parent");
if (GetCurrentHeight() < sessionBlock.nHeight + disputeHeader.waitBlocks)
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
return Invalid("dispute-too-soon"); // Not yet
}
@@ -64,7 +63,7 @@ bool Eval::DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeT
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);
auto out = vm.evaluate(vmParams, vmState);
uint256 resultHash = SerializeHash(out.second);
if (out.first > maxLength) {
maxLength = out.first;

View File

@@ -24,8 +24,11 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
if (eval->state.IsValid()) return true;
std::string lvl = eval->state.IsInvalid() ? "Invalid" : "Error!";
fprintf(stderr, "CC Eval %s %s: %s spending tx %s\n", lvl.data(), cond->method,
eval->state.GetRejectReason().data(), tx.vin[nIn].prevout.hash.GetHex().data());
fprintf(stderr, "CC Eval %s %s: %s spending tx %s\n",
EvalToStr(cond->code[0]).data(),
lvl.data(),
eval->state.GetRejectReason().data(),
tx.vin[nIn].prevout.hash.GetHex().data());
if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data());
return false;
}
@@ -36,24 +39,17 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
*/
bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
{
if (strcmp(cond->method, "TestEval") == 0) {
bool valid = cond->paramsBinLength == 8 && memcmp(cond->paramsBin, "TestEval", 8) == 0;
return valid ? Valid() : Invalid("testing");
if (cond->codeLength == 0)
return Invalid("empty-eval");
uint8_t ecode = cond->code[0];
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
if (ecode == EVAL_IMPORTPAYOUT) {
return ImportPayout(vparams, txTo, nIn);
}
if (strcmp(cond->method, "ImportPayout") == 0) {
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);
}
}
*/
return Invalid("no-such-method");
return Invalid("invalid-code");
}
@@ -147,9 +143,26 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t
}
extern char ASSETCHAINS_SYMBOL[16];
/*
* Get MoM from a notarisation tx hash
*/
bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const
{
CTransaction notarisationTx;
CBlockIndex block;
if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false;
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false;
if (notarisationTx.vout.size() < 2) return false;
if (!data.Parse(notarisationTx.vout[1].scriptPubKey)) return false;
return true;
}
/*
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
*/
extern char ASSETCHAINS_SYMBOL[16];
bool NotarisationData::Parse(const CScript scriptPK)
{
*this = NotarisationData();
@@ -179,17 +192,14 @@ bool NotarisationData::Parse(const CScript scriptPK)
}
/*
* Get MoM from a notarisation tx hash
* Misc
*/
bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const
std::string EvalToStr(EvalCode c)
{
CTransaction notarisationTx;
CBlockIndex block;
if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false;
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false;
if (notarisationTx.vout.size() < 2) return false;
if (!data.Parse(notarisationTx.vout[1].scriptPubKey)) return false;
return true;
FOREACH_EVAL(EVAL_GENERATE_STRING);
char s[10];
sprintf(s, "0x%x", c);
return std::string(s);
}

View File

@@ -10,6 +10,22 @@
#include "primitives/transaction.h"
/*
* Eval codes
*
* Add to below macro to generate new code.
*
* If at some point a new interpretation model is introduced,
* there should be a code identifying it. For example,
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
* after the code is interpreted as a bitcoin script.
*/
#define FOREACH_EVAL(EVAL) \
EVAL(EVAL_IMPORTPAYOUT, 0xe1)
typedef uint8_t EvalCode;
class AppVM;
class NotarisationData;
@@ -31,12 +47,12 @@ public:
/*
* Dispute a payout using a VM
*/
bool DisputePayout(AppVM &vm, const CC *cond, const CTransaction &disputeTx, unsigned int nIn);
bool DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn);
/*
* Test an ImportPayout CC Eval condition
*/
bool ImportPayout(const CC *cond, const CTransaction &payoutTx, unsigned int nIn);
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
/*
* IO functions
@@ -88,23 +104,36 @@ public:
};
/*
* Eval code utilities.
*/
#define EVAL_GENERATE_DEF(L,I) const uint8_t L = I;
#define EVAL_GENERATE_STRING(L,I) if (c == I) return #L;
FOREACH_EVAL(EVAL_GENERATE_DEF);
std::string EvalToStr(EvalCode c);
/*
* Serialisation boilerplate
*/
#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;})
template <class T>
std::vector<unsigned char> CheckSerialize(const T in)
std::vector<uint8_t> SerializeF(const T f)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << in;
f(ss);
return std::vector<unsigned char>(ss.begin(), ss.end());
}
#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;})
template <class T>
bool CheckDeserialize(const std::vector<unsigned char> vIn, T &out)
bool DeserializeF(const std::vector<unsigned char> vIn, T f)
{
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
try {
ss >> out;
f(ss);
if (ss.eof()) return true;
} catch(...) {}
return false;

View File

@@ -12,7 +12,7 @@
* Crypto-Condition EVAL method that verifies a payout against a transaction
* notarised on another chain.
*
* IN: cond - CC EVAL node
* IN: params - condition params
* IN: importTx - Payout transaction on value chain (KMD)
* IN: nIn - index of input of stake
*
@@ -29,7 +29,7 @@
* out 0: OP_RETURN hash of payouts
* out 1-: anything
*/
bool Eval::ImportPayout(const CC *cond, const CTransaction &importTx, unsigned int nIn)
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
{
if (importTx.vout.size() == 0) return Invalid("no-vouts");
@@ -37,10 +37,9 @@ bool Eval::ImportPayout(const CC *cond, const CTransaction &importTx, unsigned i
MoMProof proof;
CTransaction disputeTx;
{
std::pair<MoMProof&, CTransaction&> pair(proof, disputeTx);
std::vector<unsigned char> vopret;
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
if (!CheckDeserialize(vopret, pair))
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
return Invalid("invalid-payload");
}
@@ -56,16 +55,18 @@ bool Eval::ImportPayout(const CC *cond, const CTransaction &importTx, unsigned i
// Check disputeTx spends sessionTx.0
// condition ImportPayout params is session ID from other chain
{
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 Invalid("wrong-session");
uint256 sessionHash;
if (!E_UNMARSHAL(params, ss >> sessionHash))
return Invalid("malformed-params");
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
return Invalid("wrong-session");
}
// Check disputeTx solves momproof from vout[0]
{
NotarisationData data;
if (!GetNotarisationData(proof.notarisationHash, data)) return Invalid("coudnt-load-mom");
if (!GetNotarisationData(proof.notarisationHash, data))
return Invalid("coudnt-load-mom");
if (data.MoM != proof.Exec(disputeTx.GetHash()))
return Invalid("mom-check-fail");