This commit is contained in:
Scott Sadler
2018-05-09 16:17:28 -03:00
parent 3c7604133e
commit 20c3ac51c2
28 changed files with 884 additions and 228 deletions

View File

@@ -5,8 +5,9 @@
#include "chain.h"
#include "streams.h"
#include "script/cc.h"
#include "cc/eval.h"
#include "cc/betprotocol.h"
#include "cc/eval.h"
#include "cc/utils.h"
#include "primitives/transaction.h"

View File

@@ -1,9 +1,11 @@
#include <assert.h>
#include <cryptoconditions.h>
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/cc.h"
#include "cc/eval.h"
#include "cc/utils.h"
#include "main.h"
#include "chain.h"
#include "core_io.h"
@@ -14,9 +16,7 @@ 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_;
EvalRef eval;
bool out = eval->Dispatch(cond, tx, nIn);
assert(eval->state.IsValid() == out);
@@ -162,8 +162,7 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
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;
if (!ParseNotarisationOpReturn(notarisationTx, data)) return false;
return true;
}
@@ -176,34 +175,13 @@ bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, b
/*
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
*/
extern char ASSETCHAINS_SYMBOL[16];
bool NotarisationData::Parse(const CScript scriptPK)
bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data)
{
*this = NotarisationData();
if (tx.vout.size() < 2) return false;
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;
if (!GetOpReturnData(tx.vout[1].scriptPubKey, vdata)) return false;
bool out = E_UNMARSHAL(vdata, ss >> data);
return out;
}
@@ -240,3 +218,11 @@ uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleB
}
return hash;
}
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves)
{
bool fMutated;
std::vector<uint256> vMerkleTree;
return BuildMerkleTree(&fMutated, vLeaves, vMerkleTree);
}

View File

@@ -3,6 +3,7 @@
#include <cryptoconditions.h>
#include "cc/utils.h"
#include "chain.h"
#include "streams.h"
#include "version.h"
@@ -78,6 +79,23 @@ public:
};
extern Eval* EVAL_TEST;
/*
* Get a pointer to an Eval to use
*/
typedef std::unique_ptr<Eval,void(*)(Eval*)> EvalRef_;
class EvalRef : public EvalRef_
{
public:
EvalRef() : EvalRef_(
EVAL_TEST ? EVAL_TEST : new Eval(),
[](Eval* e){if (e!=EVAL_TEST) delete e;}) { }
};
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn);
@@ -97,24 +115,68 @@ public:
evaluate(std::vector<unsigned char> header, std::vector<unsigned char> body) = 0;
};
extern char ASSETCHAINS_SYMBOL[65];
/*
* Data from notarisation OP_RETURN
* Data from notarisation OP_RETURN from chain being notarised
*/
class NotarisationData {
class NotarisationData
{
public:
bool IsBackNotarisation = 0;
uint256 blockHash;
uint32_t height;
uint256 txHash; // Only get this guy in asset chains not in KMD
char symbol[64];
char symbol[64] = "\0";
uint256 MoM;
uint32_t MoMDepth;
uint32_t ccId;
uint256 MoMoM;
uint32_t MoMoMDepth;
bool Parse(CScript scriptPubKey);
NotarisationData(bool IsBack=0) : IsBackNotarisation(IsBack) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(blockHash);
READWRITE(height);
if (IsBackNotarisation || (!ser_action.ForRead() && !txHash.IsNull()))
READWRITE(txHash);
SerSymbol(s, ser_action);
READWRITE(MoM);
READWRITE(MoMDepth);
if (s.size() == 0) return;
READWRITE(ccId);
if (IsBackNotarisation) {
READWRITE(MoMoM);
READWRITE(MoMoMDepth);
}
}
template <typename Stream>
void SerSymbol(Stream& s, CSerActionSerialize act)
{
s.write(symbol, strlen(symbol)+1);
}
template <typename Stream>
void SerSymbol(Stream& s, CSerActionUnserialize act)
{
char *nullPos = (char*) memchr(&s[0], 0, s.size());
if (!nullPos)
throw std::ios_base::failure("couldn't parse symbol");
s.read(symbol, nullPos-&s[0]+1);
}
};
bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data);
/*
* Eval code utilities.
*/
@@ -126,31 +188,6 @@ 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;
}
/*
* Merkle stuff
*/
@@ -167,6 +204,13 @@ public:
MerkleBranch(int i, std::vector<uint256> b) : nIndex(i), branch(b) {}
uint256 Exec(uint256 hash) const { return SafeCheckMerkleBranch(hash, branch, nIndex); }
MerkleBranch& operator<<(MerkleBranch append)
{
nIndex += append.nIndex << branch.size();
branch.insert(branch.end(), append.branch.begin(), append.branch.end());
return *this;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@@ -177,4 +221,7 @@ public:
};
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves);
#endif /* CC_EVAL_H */

70
src/cc/import.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "cc/eval.h"
#include "cc/utils.h"
#include "importcoin.h"
#include "primitives/transaction.h"
/*
* CC Eval method for import coin.
*
* This method should control every parameter of the ImportCoin transaction, since it has no signature
* to protect it from malleability.
*/
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
{
if (importTx.vout.size() == 0) return Invalid("no-vouts");
// params
MomoProof proof;
CTransaction burnTx;
if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx))
return Invalid("invalid-params");
// Control all aspects of this transaction
// It must not be at all malleable
if (MakeImportCoinTransaction(proof, burnTx, importTx.vout).GetHash() != importTx.GetHash())
return Invalid("non-canonical");
// burn params
uint32_t chain; // todo
uint256 payoutsHash;
std::vector<uint8_t> burnOpret;
if (burnTx.vout.size() == 0) return Invalid("invalid-burn-outputs");
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
if (!E_UNMARSHAL(burnOpret, ss >> VARINT(chain); ss >> payoutsHash))
return Invalid("invalid-burn-params");
// check chain
if (chain != GetCurrentLedgerID())
return Invalid("importcoin-wrong-chain");
// check burn amount
{
uint64_t burnAmount = burnTx.vout[0].nValue;
if (burnAmount == 0)
return Invalid("invalid-burn-amount");
uint64_t totalOut = 0;
for (int i=0; i<importTx.vout.size(); i++)
totalOut += importTx.vout[i].nValue;
if (totalOut > burnAmount)
return Invalid("payout-too-high");
}
// Check burntx shows correct outputs hash
if (payoutsHash != SerializeHash(importTx.vout))
return Invalid("wrong-payouts");
// Check proof confirms existance of burnTx
{
NotarisationData data;
if (!GetNotarisationData(proof.notarisationHeight, data, true))
return Invalid("coudnt-load-momom");
if (data.MoMoM != proof.branch.Exec(burnTx.GetHash()))
return Invalid("momom-check-fail");
}
return Valid();
}

View File

@@ -1,178 +0,0 @@
#include "cc/importcoin.h"
#include "coins.h"
#include "hash.h"
#include "script/cc.h"
#include "primitives/transaction.h"
/*
* Generate ImportCoin transaction.
*
* Contains an empty OP_RETURN as first output; this is critical for preventing a double
* import. If it doesn't contain this it's invalid. The empty OP_RETURN will hang around
* in the UTXO set and the transaction will be detected as a duplicate.
*/
CTransaction MakeImportCoinTransaction(const MomoProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts)
{
std::vector<uint8_t> payload =
E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx);
CMutableTransaction mtx;
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload));
mtx.vout = payouts;
return CTransaction(mtx);
}
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts)
{
std::vector<uint8_t> opret = E_MARSHAL(ss << VARINT(targetChain); ss << SerializeHash(payouts));
return CTxOut(value, CScript() << OP_RETURN << opret);
}
/*
* CC Eval method for import coin.
*
* This method has to control *every* parameter of the ImportCoin transaction, so that the legal
* importTx for a valid burnTx is 1:1. There can be no two legal importTx transactions for a burnTx
* on another chain.
*/
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
{
if (importTx.vout.size() == 0) return Invalid("no-vouts");
// params
MomoProof proof;
CTransaction burnTx;
if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx))
return Invalid("invalid-params");
// Control all aspects of this transaction
// It must not be at all malleable
if (MakeImportCoinTransaction(proof, burnTx, importTx.vout).GetHash() != importTx.GetHash())
return Invalid("non-canonical");
// burn params
uint32_t chain; // todo
uint256 payoutsHash;
std::vector<uint8_t> burnOpret;
if (burnTx.vout.size() == 0) return Invalid("invalid-burn-outputs");
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
if (!E_UNMARSHAL(burnOpret, ss >> VARINT(chain); ss >> payoutsHash))
return Invalid("invalid-burn-params");
// check chain
if (chain != GetCurrentLedgerID())
return Invalid("importcoin-wrong-chain");
// check burn amount
{
uint64_t burnAmount = burnTx.vout[0].nValue;
if (burnAmount == 0)
return Invalid("invalid-burn-amount");
uint64_t totalOut = 0;
for (int i=0; i<importTx.vout.size(); i++)
totalOut += importTx.vout[i].nValue;
if (totalOut > burnAmount)
return Invalid("payout-too-high");
}
// Check burntx shows correct outputs hash
if (payoutsHash != SerializeHash(importTx.vout))
return Invalid("wrong-payouts");
// Check proof confirms existance of burnTx
{
NotarisationData data;
if (!GetNotarisationData(proof.notarisationHeight, data, true))
return Invalid("coudnt-load-momom");
if (data.MoMoM != proof.branch.Exec(burnTx.GetHash()))
return Invalid("momom-check-fail");
}
return Valid();
}
static bool UnmarshalImportTx(const CTransaction &importTx, MomoProof &proof, CTransaction &burnTx)
{
CScript scriptSig = importTx.vin[0].scriptSig;
auto pc = scriptSig.begin();
opcodetype opcode;
std::vector<uint8_t> evalScript;
int code;
bool out = false;
if (scriptSig.GetOp(pc, opcode, evalScript))
if (pc == scriptSig.end())
out = E_UNMARSHAL(evalScript, ss >> VARINT(code); ss >> proof; ss >> burnTx);
return code == EVAL_IMPORTCOIN && out;
}
/*
* Required by main
* TODO: test
*/
CAmount GetCoinImportValue(const CTransaction &tx)
{
MomoProof proof;
CTransaction burnTx;
if (UnmarshalImportTx(tx, proof, burnTx)) {
return burnTx.vout.size() ? burnTx.vout[0].nValue : 0;
}
return 0;
}
/*
* CoinImport is different enough from normal script execution that it's not worth
* making all the mods neccesary in the interpreter to do the dispatch correctly.
*/
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state)
{
auto pc = scriptSig.begin();
opcodetype opcode;
std::vector<uint8_t> evalScript;
auto f = [&] () {
if (!scriptSig.GetOp(pc, opcode, evalScript))
return false;
if (pc != scriptSig.end())
return false;
if (evalScript.size() == 0)
return false;
if (evalScript.begin()[0] != EVAL_IMPORTCOIN)
return false;
// Ok, all looks good so far...
CC *cond = CCNewEval(evalScript);
bool out = checker.CheckEvalCondition(cond);
cc_free(cond);
return out;
};
return f() ? true : state.Invalid(false, 0, "invalid-coin-import");
}
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight)
{
uint256 burnHash = importTx.vin[0].prevout.hash;
CCoinsModifier modifier = inputs.ModifyCoins(burnHash);
modifier->nHeight = nHeight;
modifier->nVersion = 1;
modifier->vout.push_back(CTxOut(0, CScript() << OP_0));
}
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs)
{
uint256 burnHash = importTx.vin[0].prevout.hash;
inputs.ModifyCoins(burnHash)->Clear();
}
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs)
{
uint256 burnHash = importTx.vin[0].prevout.hash;
return inputs.HaveCoins(burnHash);
}

View File

@@ -1,39 +0,0 @@
#ifndef CC_IMPORTCOIN_H
#define CC_IMPORTCOIN_H
#include "cc/eval.h"
#include "coins.h"
#include "primitives/transaction.h"
#include "script/interpreter.h"
#include <cryptoconditions.h>
class MomoProof
{
public:
MerkleBranch branch;
int notarisationHeight;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(branch);
READWRITE(notarisationHeight);
}
};
CAmount GetCoinImportValue(const CTransaction &tx);
CTransaction MakeImportCoinTransaction(const MomoProof proof,
const CTransaction burnTx, const std::vector<CTxOut> payouts);
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts);
bool VerifyCoinImport(const CScript& scriptSig,
TransactionSignatureChecker& checker, CValidationState &state);
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight);
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs);
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs);
#endif /* CC_IMPORTCOIN_H */

0
src/cc/utils.cpp Normal file
View File

34
src/cc/utils.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef CC_UTILS_H
#define CC_UTILS_H
#include "streams.h"
#include "version.h"
/*
* Serialisation boilerplate
*/
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());
}
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;
}
#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;})
#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;})
#endif /* CC_UTILS_H */