From 9ef101bc21c62168b9e9b69c633047a911a013cf Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Fri, 30 Mar 2018 17:20:02 -0300 Subject: [PATCH] mock up DisputePayout --- src/Makefile.am | 1 + src/cc/disputepayout.cpp | 102 +++++++++++++++++++++++++++++++++++++++ src/cc/eval.cpp | 33 +++++++++++++ src/cc/eval.h | 31 ++++++++++++ src/cc/importpayout.cpp | 44 +++++------------ 5 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 src/cc/disputepayout.cpp diff --git a/src/Makefile.am b/src/Makefile.am index affa61fc6..5bbd26d33 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -252,6 +252,7 @@ libbitcoin_server_a_SOURCES = \ bloom.cpp \ cc/eval.cpp \ cc/importpayout.cpp \ + cc/disputepayout.cpp \ chain.cpp \ checkpoints.cpp \ deprecation.cpp \ diff --git a/src/cc/disputepayout.cpp b/src/cc/disputepayout.cpp new file mode 100644 index 000000000..36a5e1723 --- /dev/null +++ b/src/cc/disputepayout.cpp @@ -0,0 +1,102 @@ +#include "primitives/transaction.h" +#include "streams.h" +#include "chain.h" +#include "main.h" +#include "cc/eval.h" +#include "cryptoconditions/include/cryptoconditions.h" + + +bool GetSpends(uint256 hash, std::vector> &spends) +{ + // NOT IMPLEMENTED + return false; +} + + +class DisputeHeader +{ +public: + int waitBlocks; + std::vector vmHeader; + + ADD_SERIALIZE_METHODS; + + template + 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 + * + * IN: vm - AppVM virtual machine to verify states + * IN: cond - CC EVAL node + * IN: disputeTx - transaction attempting to resolve dispute + * IN: nIn - index of input of dispute tx + * + * disputeTx: attempt to resolve a dispute + * + * 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) +{ + // TODO: Error messages! + if (disputeTx->vout.size() < 2) return 0; + + // get payouts hash + std::vector vPayoutHash; + uint256 payoutHash; + if (!GetOpReturnData(disputeTx->vout[0].scriptPubKey, vPayoutHash)) return 0; + memcpy(payoutHash.begin(), vPayoutHash.data(), 32); + + // load dispute header + DisputeHeader disputeHeader; + std::vector headerData(cond->paramsBin, + cond->paramsBin+cond->paramsBinLength); + CDataStream(headerData, SER_DISK, PROTOCOL_VERSION) >> disputeHeader; + // TODO: exception? end of stream? + + // 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 + + // get spends + std::vector> spends; + if (!GetSpends(disputeTx->vin[0].prevout.hash, spends)) return 0; + + // verify result from VM + int maxLength = -1; + uint256 bestPayout; + for (int i=1; i vmBody; + if (!GetOpReturnData(spends[i]->vout[0].scriptPubKey, vmBody)) continue; + auto out = vm.evaluate(disputeHeader.vmHeader, vmBody); + uint256 resultHash = SerializeHash(out.second); + if (out.first > maxLength) { + maxLength = out.first; + bestPayout = resultHash; + } + // The below means that if for any reason there is a draw, + else if (out.first == maxLength) { + if (bestPayout != payoutHash) { + fprintf(stderr, "WARNING: VM has multiple solutions of same length\n"); + bestPayout = resultHash; + } + } + } + + return bestPayout == payoutHash; +} diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 0291d1e3f..2615d1e4e 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -18,6 +18,39 @@ bool EvalConditionValidity(const CC *cond, const CTransaction *txTo, int nIn) return CheckImportPayout(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; } + + + +bool GetPushData(const CScript &sig, std::vector &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 &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; +} + diff --git a/src/cc/eval.h b/src/cc/eval.h index 8aa797f99..aadb2e85f 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -14,5 +14,36 @@ bool EvalConditionValidity(const CC *cond, const CTransaction *tx, int nIn); */ bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn); +/* + * Virtual machine to use in the case of on-chain app evaluation + */ +class AppVM +{ +public: + /* + * in: header - paramters agreed upon by all players + * in: body - gamestate + * out: length - length of game (longest wins) + * out: payments - vector of CTxOut, always deterministically sorted. + */ + virtual std::pair> + evaluate(std::vector header, std::vector 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 + */ +bool GetPushData(const CScript &sig, std::vector &data); + +/* + * Get OP_RETURN data from a script + */ +bool GetOpReturnData(const CScript &sig, std::vector &data); + #endif /* CC_EVAL_H */ diff --git a/src/cc/importpayout.cpp b/src/cc/importpayout.cpp index d0a8c2fb0..ee8913744 100644 --- a/src/cc/importpayout.cpp +++ b/src/cc/importpayout.cpp @@ -2,30 +2,10 @@ #include "streams.h" #include "chain.h" #include "main.h" +#include "cc/eval.h" #include "cryptoconditions/include/cryptoconditions.h" -bool GetPushData(const CScript &sig, std::vector &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 &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: @@ -128,10 +108,10 @@ uint256 ExecMerkle(uint256 hash, const std::vector& vMerkleBranch, int * * in 0: Spends Stake TX and contains ImportPayout CC * out 0: OP_RETURN MomProof - * out 1: OP_RETURN serialized exportTx from other chain + * out 1: OP_RETURN serialized disputeTx from other chain * out 2-: arbitrary payouts * - * exportTx: Spends sessionTx.0 (opener on asset chain) + * disputeTx: Spends sessionTx.0 (opener on asset chain) * * in 0: spends sessionTx.0 * in 1-: anything @@ -149,30 +129,30 @@ bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn) uint256 payoutsHash = SerializeHash(payouts); std::vector vPayoutsHash(payoutsHash.begin(), payoutsHash.end()); - // load exportTx from vout[1] - CTransaction exportTx; + // load disputeTx from vout[1] + CTransaction disputeTx; { std::vector exportData; if (!GetOpReturnData(payoutTx->vout[1].scriptPubKey, exportData)) return 0; - CDataStream(exportData, SER_DISK, PROTOCOL_VERSION) >> exportTx; + CDataStream(exportData, SER_DISK, PROTOCOL_VERSION) >> disputeTx; // TODO: end of stream? exception? } - // Check exportTx.0 is vPayoutsHash + // Check disputeTx.0 is vPayoutsHash std::vector exportPayoutsHash; - if (!GetOpReturnData(exportTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0; + if (!GetOpReturnData(disputeTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0; if (exportPayoutsHash != vPayoutsHash) return 0; - // Check exportTx spends sessionTx.0 + // Check disputeTx spends sessionTx.0 // condition ImportPayout params is session ID from other chain { if (cond->paramsBinLength != 32) return 0; - COutPoint prevout = exportTx.vin[0].prevout; + COutPoint prevout = disputeTx.vin[0].prevout; if (memcmp(prevout.hash.begin(), cond->paramsBin, 32) != 0 || prevout.n != 0) return 0; } - // Check exportTx solves momproof from vout[0] + // Check disputeTx solves momproof from vout[0] { std::vector vchMomProof; if (!GetOpReturnData(payoutTx->vout[0].scriptPubKey, vchMomProof)) return 0; @@ -183,7 +163,7 @@ bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn) uint256 mom; if (!GetMoM(momProof.notaryHash, mom)) return 0; - uint256 proofResult = ExecMerkle(exportTx.GetHash(), momProof.branch, momProof.nPos); + uint256 proofResult = ExecMerkle(disputeTx.GetHash(), momProof.branch, momProof.nPos); if (proofResult != mom) return 0; }