Merge branch 'beta' into mergemaster
# Conflicts: # src/main.cpp
This commit is contained in:
139
src/cc/betprotocol.cpp
Normal file
139
src/cc/betprotocol.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "streams.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
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(E_MARSHAL(
|
||||
ss << disputeCode << VARINT(waitBlocks) << vmParams;
|
||||
));
|
||||
|
||||
CC *anySig = CCNewThreshold(1, PlayerConditions());
|
||||
|
||||
return CCNewThreshold(2, {disputePoker, anySig});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* spendFee is the amount assigned to each output, for the purposes of posting
|
||||
* dispute / evidence.
|
||||
*/
|
||||
CMutableTransaction BetProtocol::MakeSessionTx(CAmount spendFee)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
|
||||
CC *disputeCond = MakeDisputeCond();
|
||||
mtx.vout.push_back(CTxOut(spendFee, CCPubKey(disputeCond)));
|
||||
cc_free(disputeCond);
|
||||
|
||||
for (int i=0; i<players.size(); i++) {
|
||||
CC *cond = CCNewSecp256k1(players[i]);
|
||||
mtx.vout.push_back(CTxOut(spendFee, CCPubKey(cond)));
|
||||
cc_free(cond);
|
||||
}
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
|
||||
CC *disputeCond = MakeDisputeCond();
|
||||
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));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash,
|
||||
int playerIdx, std::vector<unsigned char> state)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
|
||||
mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript()));
|
||||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state));
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash)
|
||||
{
|
||||
// TODO: 2/3 majority
|
||||
CC* agree = CCNewThreshold(players.size(), PlayerConditions());
|
||||
|
||||
CC *import;
|
||||
{
|
||||
CC *importEval = CCNewEval(E_MARSHAL(
|
||||
ss << EVAL_IMPORTPAYOUT << signedSessionTxHash;
|
||||
));
|
||||
|
||||
CC *oneof = CCNewThreshold(1, PlayerConditions());
|
||||
|
||||
import = CCNewThreshold(2, {oneof, importEval});
|
||||
}
|
||||
|
||||
return CCNewThreshold(1, {agree, import});
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
|
||||
CC *payoutCond = MakePayoutCond(signedSessionTxHash);
|
||||
mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond)));
|
||||
cc_free(payoutCond);
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts,
|
||||
uint256 signedStakeTxHash)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
|
||||
mtx.vout = payouts;
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
|
||||
CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
|
||||
mtx.vout = payouts;
|
||||
CScript proofData;
|
||||
proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx);
|
||||
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
bool GetOpReturnHash(CScript script, uint256 &hash)
|
||||
{
|
||||
std::vector<unsigned char> vHash;
|
||||
GetOpReturnData(script, vHash);
|
||||
if (vHash.size() != 32) return false;
|
||||
hash = uint256(vHash);
|
||||
return true;
|
||||
}
|
||||
68
src/cc/betprotocol.h
Normal file
68
src/cc/betprotocol.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef BETPROTOCOL_H
|
||||
#define BETPROTOCOL_H
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "pubkey.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.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) {}
|
||||
uint256 Exec(uint256 hash) const { return CBlock::CheckMerkleBranch(hash, branch, nIndex); }
|
||||
|
||||
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 BetProtocol
|
||||
{
|
||||
protected:
|
||||
std::vector<CC*> playerConditions();
|
||||
public:
|
||||
EvalCode disputeCode;
|
||||
std::vector<CPubKey> players;
|
||||
std::vector<unsigned char> vmParams;
|
||||
uint32_t waitBlocks;
|
||||
|
||||
// Utility
|
||||
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
|
||||
CC* MakeDisputeCond();
|
||||
CMutableTransaction MakeSessionTx(CAmount spendFee);
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool GetOpReturnHash(CScript script, uint256 &hash);
|
||||
|
||||
|
||||
#endif /* BETPROTOCOL_H */
|
||||
84
src/cc/disputepayout.cpp
Normal file
84
src/cc/disputepayout.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "chain.h"
|
||||
#include "version.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that resolves a dispute of a session
|
||||
*
|
||||
* IN: vm - AppVM virtual machine to verify states
|
||||
* IN: params - condition params
|
||||
* 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 Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
||||
{
|
||||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// get payouts hash
|
||||
uint256 payoutHash;
|
||||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
||||
return Invalid("invalid-payout-hash");
|
||||
|
||||
// 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
|
||||
{
|
||||
CTransaction sessionTx;
|
||||
CBlockIndex sessionBlock;
|
||||
|
||||
// if unconformed its too soon
|
||||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
||||
return Error("couldnt-get-parent");
|
||||
|
||||
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
|
||||
return Invalid("dispute-too-soon"); // Not yet
|
||||
}
|
||||
|
||||
// get spends
|
||||
std::vector<CTransaction> spends;
|
||||
if (!GetSpendsConfirmed(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++)
|
||||
{
|
||||
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(vmParams, vmState);
|
||||
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, the first dispute wins
|
||||
else if (out.first == maxLength) {
|
||||
if (bestPayout != payoutHash) {
|
||||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
||||
bestPayout = resultHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLength == -1) return Invalid("no-evidence");
|
||||
|
||||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
||||
}
|
||||
205
src/cc/eval.cpp
Normal file
205
src/cc/eval.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include <assert.h>
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "core_io.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 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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test the validity of an Eval node
|
||||
*/
|
||||
bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return Invalid("invalid-code");
|
||||
}
|
||||
|
||||
|
||||
bool Eval::GetSpendsConfirmed(uint256 hash, std::vector<CTransaction> &spends) const
|
||||
{
|
||||
// NOT IMPLEMENTED
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Eval::GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const
|
||||
{
|
||||
bool fAllowSlow = false; // Don't allow slow
|
||||
return GetTransaction(hash, txOut, hashBlock, fAllowSlow);
|
||||
}
|
||||
|
||||
|
||||
bool Eval::GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const
|
||||
{
|
||||
uint256 hashBlock;
|
||||
if (!GetTxUnconfirmed(hash, txOut, hashBlock))
|
||||
return false;
|
||||
if (hashBlock.IsNull() || !GetBlock(hashBlock, block))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
fprintf(stderr, "CC Eval Error: Can't get block from index\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||
|
||||
|
||||
int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const
|
||||
{
|
||||
return komodo_notaries(pubkeys, height, timestamp);
|
||||
}
|
||||
|
||||
|
||||
bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||
{
|
||||
if (tx.vin.size() < 11) return false;
|
||||
|
||||
uint8_t seenNotaries[64] = {0};
|
||||
uint8_t notaries[64][33];
|
||||
int nNotaries = GetNotaries(notaries, height, timestamp);
|
||||
|
||||
BOOST_FOREACH(const CTxIn &txIn, tx.vin)
|
||||
{
|
||||
// Get notary pubkey
|
||||
CTransaction tx;
|
||||
uint256 hashBlock;
|
||||
if (!GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false;
|
||||
if (tx.vout.size() < txIn.prevout.n) return false;
|
||||
CScript spk = tx.vout[txIn.prevout.n].scriptPubKey;
|
||||
if (spk.size() != 35) return false;
|
||||
const unsigned char *pk = spk.data();
|
||||
if (pk++[0] != 33) return false;
|
||||
if (pk[33] != OP_CHECKSIG) return false;
|
||||
|
||||
// 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:;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
std::vector<unsigned char> vdata;
|
||||
if (!GetOpReturnData(scriptPK, vdata)) return false;
|
||||
|
||||
CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
try {
|
||||
ss >> blockHash;
|
||||
ss >> height;
|
||||
if (ASSETCHAINS_SYMBOL[0])
|
||||
ss >> txHash;
|
||||
|
||||
char *nullPos = (char*) memchr(&ss[0], 0, ss.size());
|
||||
if (!nullPos) return false;
|
||||
ss.read(symbol, nullPos-&ss[0]+1);
|
||||
|
||||
if (ss.size() < 36) return false;
|
||||
ss >> MoM;
|
||||
ss >> MoMDepth;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
||||
std::string EvalToStr(EvalCode c)
|
||||
{
|
||||
FOREACH_EVAL(EVAL_GENERATE_STRING);
|
||||
char s[10];
|
||||
sprintf(s, "0x%x", c);
|
||||
return std::string(s);
|
||||
}
|
||||
143
src/cc/eval.h
Normal file
143
src/cc/eval.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef CC_EVAL_H
|
||||
#define CC_EVAL_H
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
#include "consensus/validation.h"
|
||||
#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;
|
||||
|
||||
|
||||
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, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn);
|
||||
|
||||
/*
|
||||
* Test an ImportPayout CC Eval condition
|
||||
*/
|
||||
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||
|
||||
/*
|
||||
* IO functions
|
||||
*/
|
||||
virtual bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const;
|
||||
virtual bool GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const;
|
||||
virtual unsigned int GetCurrentHeight() const;
|
||||
virtual bool GetSpendsConfirmed(uint256 hash, std::vector<CTransaction> &spends) const;
|
||||
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
||||
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
||||
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) 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
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Data from notarisation OP_RETURN
|
||||
*/
|
||||
class NotarisationData {
|
||||
public:
|
||||
uint256 blockHash;
|
||||
uint32_t height;
|
||||
uint256 txHash; // Only get this guy in asset chains not in KMD
|
||||
char symbol[64];
|
||||
uint256 MoM;
|
||||
uint32_t MoMDepth;
|
||||
|
||||
bool Parse(CScript scriptPubKey);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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<uint8_t> SerializeF(const T f)
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
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 DeserializeF(const std::vector<unsigned char> vIn, T f)
|
||||
{
|
||||
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
f(ss);
|
||||
if (ss.eof()) return true;
|
||||
} catch(...) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* CC_EVAL_H */
|
||||
76
src/cc/importpayout.cpp
Normal file
76
src/cc/importpayout.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
||||
* notarised on another chain.
|
||||
*
|
||||
* IN: params - condition params
|
||||
* IN: importTx - Payout transaction on value chain (KMD)
|
||||
* IN: nIn - index of input of stake
|
||||
*
|
||||
* importTx: Spends stakeTx with payouts from asset chain
|
||||
*
|
||||
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||
* out 0: OP_RETURN MomProof, disputeTx
|
||||
* out 1-: arbitrary payouts
|
||||
*
|
||||
* disputeTx: 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 Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// load data from vout[0]
|
||||
MoMProof proof;
|
||||
CTransaction disputeTx;
|
||||
{
|
||||
std::vector<unsigned char> vopret;
|
||||
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
||||
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
||||
return Invalid("invalid-payload");
|
||||
}
|
||||
|
||||
// Check disputeTx.0 shows correct payouts
|
||||
{
|
||||
uint256 givenPayoutsHash;
|
||||
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
||||
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
||||
if (givenPayoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
}
|
||||
|
||||
// Check disputeTx spends sessionTx.0
|
||||
// condition ImportPayout params is session ID from other chain
|
||||
{
|
||||
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 (data.MoM != proof.Exec(disputeTx.GetHash()))
|
||||
return Invalid("mom-check-fail");
|
||||
}
|
||||
|
||||
return Valid();
|
||||
}
|
||||
Reference in New Issue
Block a user