mock up DisputePayout
This commit is contained in:
@@ -252,6 +252,7 @@ libbitcoin_server_a_SOURCES = \
|
|||||||
bloom.cpp \
|
bloom.cpp \
|
||||||
cc/eval.cpp \
|
cc/eval.cpp \
|
||||||
cc/importpayout.cpp \
|
cc/importpayout.cpp \
|
||||||
|
cc/disputepayout.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
deprecation.cpp \
|
deprecation.cpp \
|
||||||
|
|||||||
102
src/cc/disputepayout.cpp
Normal file
102
src/cc/disputepayout.cpp
Normal file
@@ -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<boost::optional<CTransaction>> &spends)
|
||||||
|
{
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* 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<unsigned char> 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<unsigned char> 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<boost::optional<CTransaction>> 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<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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -18,6 +18,39 @@ bool EvalConditionValidity(const CC *cond, const CTransaction *txTo, int nIn)
|
|||||||
return CheckImportPayout(cond, txTo, 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);
|
fprintf(stderr, "no defined behaviour for method: %s\n", cond->method);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,36 @@ bool EvalConditionValidity(const CC *cond, const CTransaction *tx, int nIn);
|
|||||||
*/
|
*/
|
||||||
bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, 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<int,std::vector<CTxOut>>
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
bool GetPushData(const CScript &sig, std::vector<unsigned char> &data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get OP_RETURN data from a script
|
||||||
|
*/
|
||||||
|
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data);
|
||||||
|
|
||||||
|
|
||||||
#endif /* CC_EVAL_H */
|
#endif /* CC_EVAL_H */
|
||||||
|
|||||||
@@ -2,30 +2,10 @@
|
|||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "chain.h"
|
#include "chain.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.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
|
class MomProof
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -128,10 +108,10 @@ uint256 ExecMerkle(uint256 hash, const std::vector<uint256>& vMerkleBranch, int
|
|||||||
*
|
*
|
||||||
* in 0: Spends Stake TX and contains ImportPayout CC
|
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||||
* out 0: OP_RETURN MomProof
|
* 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
|
* 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 0: spends sessionTx.0
|
||||||
* in 1-: anything
|
* in 1-: anything
|
||||||
@@ -149,30 +129,30 @@ bool CheckImportPayout(const CC *cond, const CTransaction *payoutTx, int nIn)
|
|||||||
uint256 payoutsHash = SerializeHash(payouts);
|
uint256 payoutsHash = SerializeHash(payouts);
|
||||||
std::vector<unsigned char> vPayoutsHash(payoutsHash.begin(), payoutsHash.end());
|
std::vector<unsigned char> vPayoutsHash(payoutsHash.begin(), payoutsHash.end());
|
||||||
|
|
||||||
// load exportTx from vout[1]
|
// load disputeTx from vout[1]
|
||||||
CTransaction exportTx;
|
CTransaction disputeTx;
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> exportData;
|
std::vector<unsigned char> exportData;
|
||||||
if (!GetOpReturnData(payoutTx->vout[1].scriptPubKey, exportData)) return 0;
|
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?
|
// TODO: end of stream? exception?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check exportTx.0 is vPayoutsHash
|
// Check disputeTx.0 is vPayoutsHash
|
||||||
std::vector<unsigned char> exportPayoutsHash;
|
std::vector<unsigned char> exportPayoutsHash;
|
||||||
if (!GetOpReturnData(exportTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0;
|
if (!GetOpReturnData(disputeTx.vout[0].scriptPubKey, exportPayoutsHash)) return 0;
|
||||||
if (exportPayoutsHash != vPayoutsHash) 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
|
// condition ImportPayout params is session ID from other chain
|
||||||
{
|
{
|
||||||
if (cond->paramsBinLength != 32) return 0;
|
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 ||
|
if (memcmp(prevout.hash.begin(), cond->paramsBin, 32) != 0 ||
|
||||||
prevout.n != 0) return 0;
|
prevout.n != 0) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check exportTx solves momproof from vout[0]
|
// Check disputeTx solves momproof from vout[0]
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> vchMomProof;
|
std::vector<unsigned char> vchMomProof;
|
||||||
if (!GetOpReturnData(payoutTx->vout[0].scriptPubKey, vchMomProof)) return 0;
|
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;
|
uint256 mom;
|
||||||
if (!GetMoM(momProof.notaryHash, mom)) return 0;
|
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;
|
if (proofResult != mom) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user