Merge branch 'beta' into mergemaster

# Conflicts:
#	src/main.cpp
This commit is contained in:
jl777
2018-04-16 10:16:01 +03:00
parent 9226f69ef1
commit e73b2055c6
910 changed files with 112009 additions and 11364 deletions

139
src/cc/betprotocol.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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();
}