wip
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -120,3 +120,4 @@ src/komodo-cli
|
||||
src/komodod
|
||||
src/komodo-tx
|
||||
src/komodo-test
|
||||
src/wallet-utility
|
||||
|
||||
@@ -256,10 +256,11 @@ libbitcoin_server_a_SOURCES = \
|
||||
asyncrpcqueue.cpp \
|
||||
bloom.cpp \
|
||||
cc/eval.cpp \
|
||||
cc/importcoin.cpp \
|
||||
cc/import.cpp \
|
||||
cc/betprotocol.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
crosschain.cpp \
|
||||
deprecation.cpp \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
@@ -271,6 +272,7 @@ libbitcoin_server_a_SOURCES = \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
noui.cpp \
|
||||
notarisationdb.cpp \
|
||||
paymentdisclosure.cpp \
|
||||
paymentdisclosuredb.cpp \
|
||||
policy/fees.cpp \
|
||||
@@ -384,6 +386,7 @@ libbitcoin_common_a_SOURCES = \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
hash.cpp \
|
||||
importcoin.cpp \
|
||||
key.cpp \
|
||||
keystore.cpp \
|
||||
netbase.cpp \
|
||||
|
||||
@@ -9,7 +9,8 @@ komodo_test_SOURCES = \
|
||||
test-komodo/test_cryptoconditions.cpp \
|
||||
test-komodo/test_coinimport.cpp \
|
||||
test-komodo/test_eval_bet.cpp \
|
||||
test-komodo/test_eval_notarisation.cpp
|
||||
test-komodo/test_eval_notarisation.cpp \
|
||||
test-komodo/test_crosschain.cpp
|
||||
|
||||
komodo_test_CPPFLAGS = $(komodod_CPPFLAGS)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
105
src/cc/eval.h
105
src/cc/eval.h
@@ -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
70
src/cc/import.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
0
src/cc/utils.cpp
Normal file
0
src/cc/utils.cpp
Normal file
34
src/cc/utils.h
Normal file
34
src/cc/utils.h
Normal 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 */
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "version.h"
|
||||
#include "policy/fees.h"
|
||||
#include "komodo_defs.h"
|
||||
#include "cc/importcoin.h"
|
||||
#include "importcoin.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
217
src/crosschain.cpp
Normal file
217
src/crosschain.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "cc/eval.h"
|
||||
#include "main.h"
|
||||
#include "notarisationdb.h"
|
||||
|
||||
|
||||
/* On KMD */
|
||||
uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector<uint256> &moms, int* assetChainHeight)
|
||||
{
|
||||
/*
|
||||
* Notaries don't wait for confirmation on KMD before performing a backnotarisation,
|
||||
* but we need a determinable range that will encompass all merkle roots. Include MoMs
|
||||
* including the block height of the last notarisation until the height before the
|
||||
* previous notarisation.
|
||||
*/
|
||||
if (targetCCid <= 1)
|
||||
return uint256();
|
||||
|
||||
int seenOwnNotarisations = 0;
|
||||
|
||||
// TODO: test height out of range
|
||||
// TODO: Make sure that boundary for moms is notarisation tx not block
|
||||
|
||||
for (int i=0; i<1440; i++) {
|
||||
if (i > kmdHeight) break;
|
||||
NotarisationsInBlock notarisations;
|
||||
uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
|
||||
if (!pnotarisations->Read(blockHash, notarisations))
|
||||
continue;
|
||||
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
||||
NotarisationData& data = nota.second;
|
||||
if (data.ccId != targetCCid)
|
||||
continue;
|
||||
if (strcmp(data.symbol, symbol) == 0)
|
||||
{
|
||||
seenOwnNotarisations++;
|
||||
if (seenOwnNotarisations == 2)
|
||||
goto end;
|
||||
if (seenOwnNotarisations == 1)
|
||||
*assetChainHeight = data.height; // TODO: Needed?
|
||||
continue; // Don't include own MoMs
|
||||
}
|
||||
if (seenOwnNotarisations == 1)
|
||||
moms.push_back(data.MoM);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return GetMerkleRoot(moms);
|
||||
}
|
||||
|
||||
|
||||
/* On KMD */
|
||||
std::pair<uint256,MerkleBranch> GetCrossChainProof(uint256 txid, char* targetSymbol,
|
||||
uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof)
|
||||
{
|
||||
/*
|
||||
* Here we are given a proof generated by an assetchain A which goes from given txid to
|
||||
* an assetchain MoM. We need to go from the notarisationTxid for A to the MoMoM range of the
|
||||
* backnotarisation for B (given by kmdheight of notarisation), find the MoM within the MoMs for
|
||||
* that range, and finally extend the proof to lead to the MoMoM (proof root).
|
||||
*/
|
||||
EvalRef eval;
|
||||
uint256 MoM = assetChainProof.Exec(txid);
|
||||
|
||||
// Get a kmd height for given notarisation Txid
|
||||
int kmdHeight;
|
||||
{
|
||||
CTransaction sourceNotarisation;
|
||||
uint256 hashBlock;
|
||||
CBlockIndex blockIdx;
|
||||
if (eval->GetTxConfirmed(notarisationTxid, sourceNotarisation, blockIdx))
|
||||
kmdHeight = blockIdx.nHeight;
|
||||
else if (eval->GetTxUnconfirmed(notarisationTxid, sourceNotarisation, hashBlock))
|
||||
kmdHeight = chainActive.Tip()->nHeight;
|
||||
else
|
||||
throw std::runtime_error("Notarisation not found");
|
||||
}
|
||||
|
||||
// Get MoMs for kmd height and symbol
|
||||
std::vector<uint256> moms;
|
||||
int targetChainStartHeight;
|
||||
uint256 MoMoM = GetProofRoot(targetSymbol, targetCCid, kmdHeight, moms, &targetChainStartHeight);
|
||||
if (MoMoM.IsNull())
|
||||
throw std::runtime_error("No MoMs found");
|
||||
|
||||
// Find index of source MoM in MoMoM
|
||||
int nIndex;
|
||||
for (nIndex=0; nIndex<moms.size(); nIndex++)
|
||||
if (moms[nIndex] == MoM)
|
||||
goto cont;
|
||||
throw std::runtime_error("Couldn't find MoM within MoMoM set");
|
||||
cont:
|
||||
|
||||
// Create a branch
|
||||
std::vector<uint256> newBranch;
|
||||
{
|
||||
CBlock fakeBlock;
|
||||
for (int i=0; i<moms.size(); i++) {
|
||||
CTransaction fakeTx;
|
||||
// first value in CTransaction memory is it's hash
|
||||
memcpy((void*)&fakeTx, moms[i].begin(), 32);
|
||||
fakeBlock.vtx.push_back(fakeTx);
|
||||
}
|
||||
newBranch = fakeBlock.GetMerkleBranch(nIndex);
|
||||
}
|
||||
|
||||
// Concatenate branches
|
||||
MerkleBranch newProof = assetChainProof;
|
||||
newProof << MerkleBranch(nIndex, newBranch);
|
||||
|
||||
// Check proof
|
||||
if (newProof.Exec(txid) != MoMoM)
|
||||
throw std::runtime_error("Proof check failed");
|
||||
|
||||
return std::make_pair(uint256(), newProof);
|
||||
}
|
||||
|
||||
|
||||
/* On assetchain */
|
||||
bool ValidateCrossChainProof(uint256 txid, int notarisationHeight, MerkleBranch proof)
|
||||
{
|
||||
/*
|
||||
* Here we are given a notarisation txid, and a proof.
|
||||
* We go from the notarisation to get the backnotarisation, and verify the proof
|
||||
* against the MoMoM it contains.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip);
|
||||
|
||||
/*
|
||||
* On assetchain
|
||||
* in: txid
|
||||
* out: pair<notarisationTxHash,merkleBranch>
|
||||
*/
|
||||
std::pair<uint256,MerkleBranch> GetAssetchainProof(uint256 hash)
|
||||
{
|
||||
uint256 notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex;
|
||||
std::vector<uint256> branch;
|
||||
int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi;
|
||||
|
||||
{
|
||||
uint256 blockHash;
|
||||
CTransaction tx;
|
||||
if (!GetTransaction(hash, tx, blockHash, true))
|
||||
throw std::runtime_error("cannot find transaction");
|
||||
|
||||
blockIndex = mapBlockIndex[blockHash];
|
||||
|
||||
depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
|
||||
|
||||
if (!depth)
|
||||
throw std::runtime_error("notarisation not found");
|
||||
|
||||
// index of block in MoM leaves
|
||||
nIndex = notarisedHeight - blockIndex->nHeight;
|
||||
}
|
||||
|
||||
// build merkle chain from blocks to MoM
|
||||
{
|
||||
// since the merkle branch code is tied up in a block class
|
||||
// and we want to make a merkle branch for something that isnt transactions
|
||||
CBlock fakeBlock;
|
||||
for (int i=0; i<depth; i++) {
|
||||
uint256 mRoot = chainActive[notarisedHeight - i]->hashMerkleRoot;
|
||||
CTransaction fakeTx;
|
||||
// first value in CTransaction memory is it's hash
|
||||
memcpy((void*)&fakeTx, mRoot.begin(), 32);
|
||||
fakeBlock.vtx.push_back(fakeTx);
|
||||
}
|
||||
branch = fakeBlock.GetMerkleBranch(nIndex);
|
||||
|
||||
// Check branch
|
||||
if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex))
|
||||
throw std::runtime_error("Failed merkle block->MoM");
|
||||
}
|
||||
|
||||
// Now get the tx merkle branch
|
||||
{
|
||||
CBlock block;
|
||||
|
||||
if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
|
||||
throw std::runtime_error("Block not available (pruned data)");
|
||||
|
||||
if(!ReadBlockFromDisk(block, blockIndex,1))
|
||||
throw std::runtime_error("Can't read block from disk");
|
||||
|
||||
// Locate the transaction in the block
|
||||
int nTxIndex;
|
||||
for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
|
||||
if (block.vtx[nTxIndex].GetHash() == hash)
|
||||
break;
|
||||
|
||||
if (nTxIndex == (int)block.vtx.size())
|
||||
throw std::runtime_error("Error locating tx in block");
|
||||
|
||||
std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
|
||||
|
||||
// Check branch
|
||||
if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
|
||||
throw std::runtime_error("Failed merkle tx->block");
|
||||
|
||||
// concatenate branches
|
||||
nIndex = (nIndex << txBranch.size()) + nTxIndex;
|
||||
branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
|
||||
}
|
||||
|
||||
// Check the proof
|
||||
if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
||||
throw std::runtime_error("Failed validating MoM");
|
||||
|
||||
// All done!
|
||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||
return std::make_pair(notarisationHash, MerkleBranch(nIndex, branch));
|
||||
}
|
||||
21
src/crosschain.h
Normal file
21
src/crosschain.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CROSSCHAIN_H
|
||||
#define CROSSCHAIN_H
|
||||
|
||||
#include "cc/eval.h"
|
||||
|
||||
|
||||
/* On assetchain */
|
||||
std::pair<uint256,MerkleBranch> GetAssetchainProof(uint256 hash);
|
||||
|
||||
/* On KMD */
|
||||
uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector<uint256> &moms, int* assetChainHeight);
|
||||
|
||||
/* On KMD */
|
||||
std::pair<uint256,MerkleBranch> GetCrossChainProof(uint256 txid, char* targetSymbol,
|
||||
uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof);
|
||||
|
||||
/* On assetchain */
|
||||
bool ValidateCrossChainProof(uint256 txid, int notarisationHeight, MerkleBranch proof);
|
||||
|
||||
|
||||
#endif /* CROSSCHAIN_H */
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "cc/importcoin.h"
|
||||
#include "importcoin.h"
|
||||
#include "cc/utils.h"
|
||||
#include "coins.h"
|
||||
#include "hash.h"
|
||||
#include "script/cc.h"
|
||||
@@ -29,71 +30,6 @@ CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut>
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef CC_IMPORTCOIN_H
|
||||
#define CC_IMPORTCOIN_H
|
||||
#ifndef IMPORTCOIN_H
|
||||
#define IMPORTCOIN_H
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "coins.h"
|
||||
@@ -36,4 +36,4 @@ void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, i
|
||||
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs);
|
||||
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs);
|
||||
|
||||
#endif /* CC_IMPORTCOIN_H */
|
||||
#endif /* IMPORTCOIN_H */
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "httpserver.h"
|
||||
#include "httprpc.h"
|
||||
#include "key.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "miner.h"
|
||||
@@ -1413,11 +1414,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
delete pcoinsdbview;
|
||||
delete pcoinscatcher;
|
||||
delete pblocktree;
|
||||
delete pnotarisations;
|
||||
|
||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles);
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
pnotarisations = new NotarisationDB(100*1024*1024, false, fReindex);
|
||||
|
||||
|
||||
if (fReindex) {
|
||||
pblocktree->WriteReindexing(true);
|
||||
|
||||
12
src/main.cpp
12
src/main.cpp
@@ -10,7 +10,7 @@
|
||||
#include "addrman.h"
|
||||
#include "alert.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "cc/importcoin.h"
|
||||
#include "importcoin.h"
|
||||
#include "chainparams.h"
|
||||
#include "checkpoints.h"
|
||||
#include "checkqueue.h"
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "init.h"
|
||||
#include "merkleblock.h"
|
||||
#include "metrics.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "net.h"
|
||||
#include "pow.h"
|
||||
#include "script/interpreter.h"
|
||||
@@ -2598,6 +2599,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int64_t nTimeVerify = 0;
|
||||
static int64_t nTimeConnect = 0;
|
||||
static int64_t nTimeIndex = 0;
|
||||
@@ -2833,6 +2835,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
|
||||
// Record Notarisations
|
||||
NotarisationsInBlock notarisations = GetNotarisationsInBlock(block, pindex->nHeight);
|
||||
pnotarisations->Write(block.GetHash(), notarisations);
|
||||
WriteBackNotarisations(notarisations); // Very important to disconnect this
|
||||
// TODO: Disconnect?
|
||||
|
||||
|
||||
view.PushAnchor(tree);
|
||||
if (!fJustCheck) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "amount.h"
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "cc/importcoin.h"
|
||||
#include "importcoin.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/upgrades.h"
|
||||
#include "consensus/validation.h"
|
||||
|
||||
46
src/notarisationdb.cpp
Normal file
46
src/notarisationdb.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "leveldbwrapper.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "uint256.h"
|
||||
#include "cc/eval.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
||||
NotarisationDB *pnotarisations;
|
||||
|
||||
|
||||
NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) {
|
||||
}
|
||||
|
||||
NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight)
|
||||
{
|
||||
EvalRef eval;
|
||||
NotarisationsInBlock vNotarisations;
|
||||
bool IsBackNotarisation = ASSETCHAINS_SYMBOL[0] != 0;
|
||||
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||
CTransaction tx = block.vtx[i];
|
||||
if (eval->CheckNotaryInputs(tx, nHeight, block.nTime)) {
|
||||
NotarisationData data(IsBackNotarisation);
|
||||
if (ParseNotarisationOpReturn(tx, data))
|
||||
vNotarisations.push_back(std::make_pair(tx.GetHash(), data));
|
||||
else
|
||||
fprintf(stderr, "Warning: Couldn't parse notarisation for tx: %s at height %i\n",
|
||||
tx.GetHash().GetHex().data(), nHeight);
|
||||
}
|
||||
}
|
||||
return vNotarisations;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write an index of KMD notarisation id -> backnotarisation
|
||||
*/
|
||||
void WriteBackNotarisations(NotarisationsInBlock notarisations)
|
||||
{
|
||||
BOOST_FOREACH(Notarisation &n, notarisations)
|
||||
{
|
||||
if (n.second.IsBackNotarisation)
|
||||
pnotarisations->Write(n.second.txHash, n);
|
||||
}
|
||||
}
|
||||
26
src/notarisationdb.h
Normal file
26
src/notarisationdb.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef NOTARISATIONDB_H
|
||||
#define NOTARISATIONDB_H
|
||||
|
||||
#include "uint256.h"
|
||||
#include "leveldbwrapper.h"
|
||||
#include "cc/eval.h"
|
||||
|
||||
|
||||
class NotarisationDB : public CLevelDBWrapper
|
||||
{
|
||||
public:
|
||||
NotarisationDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||
};
|
||||
|
||||
|
||||
extern NotarisationDB *pnotarisations;
|
||||
|
||||
|
||||
typedef std::pair<uint256,NotarisationData> Notarisation;
|
||||
typedef std::vector<Notarisation> NotarisationsInBlock;
|
||||
|
||||
NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight);
|
||||
|
||||
void WriteBackNotarisations(NotarisationsInBlock notarisations);
|
||||
|
||||
#endif /* NOTARISATIONDB_H */
|
||||
@@ -15,7 +15,9 @@ uint256 CBlockHeader::GetHash() const
|
||||
return SerializeHash(*this);
|
||||
}
|
||||
|
||||
uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
|
||||
uint256 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves,
|
||||
std::vector<uint256> &vMerkleTree)
|
||||
{
|
||||
/* WARNING! If you're reading this because you're learning about crypto
|
||||
and/or designing a new system that will use merkle trees, keep in mind
|
||||
@@ -28,10 +30,10 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
transactions leading to the same merkle root. For example, these two
|
||||
trees:
|
||||
|
||||
A A
|
||||
/ \ / \
|
||||
B C B C
|
||||
/ \ | / \ / \
|
||||
A A
|
||||
/ \ / \
|
||||
B C B C
|
||||
/ \ \ / \ / \
|
||||
D E F D E F F
|
||||
/ \ / \ / \ / \ / \ / \ / \
|
||||
1 2 3 4 5 6 1 2 3 4 5 6 5 6
|
||||
@@ -52,13 +54,14 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
known ways of changing the transactions without affecting the merkle
|
||||
root.
|
||||
*/
|
||||
|
||||
vMerkleTree.clear();
|
||||
vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
|
||||
for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it)
|
||||
vMerkleTree.push_back(it->GetHash());
|
||||
vMerkleTree.reserve(leaves.size() * 2 + 16); // Safe upper bound for the number of total nodes.
|
||||
for (std::vector<uint256>::const_iterator it(leaves.begin()); it != leaves.end(); ++it)
|
||||
vMerkleTree.push_back(*it);
|
||||
int j = 0;
|
||||
bool mutated = false;
|
||||
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
||||
for (int nSize = leaves.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
||||
{
|
||||
for (int i = 0; i < nSize; i += 2)
|
||||
{
|
||||
@@ -78,6 +81,15 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
|
||||
}
|
||||
|
||||
|
||||
uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
{
|
||||
std::vector<uint256> leaves;
|
||||
for (int i=0; i<vtx.size(); i++) leaves.push_back(vtx[i].GetHash());
|
||||
return ::BuildMerkleTree(fMutated, leaves, vMerkleTree);
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
|
||||
{
|
||||
if (vMerkleTree.empty())
|
||||
|
||||
@@ -139,6 +139,10 @@ public:
|
||||
};
|
||||
|
||||
|
||||
uint256 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves,
|
||||
std::vector<uint256> &vMerkleTree);
|
||||
|
||||
|
||||
/**
|
||||
* Custom serializer for CBlockHeader that omits the nonce and solution, for use
|
||||
* as input to Equihash.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "chain.h"
|
||||
#include "chainparams.h"
|
||||
#include "checkpoints.h"
|
||||
#include "crosschain.h"
|
||||
#include "base58.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "cc/eval.h"
|
||||
@@ -934,87 +935,35 @@ UniValue txMoMproof(const UniValue& params, bool fHelp)
|
||||
int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi;
|
||||
|
||||
// parse params and get notarisation data for tx
|
||||
{
|
||||
if ( fHelp || params.size() != 1)
|
||||
throw runtime_error("txMoMproof needs a txid");
|
||||
if ( fHelp || params.size() != 1)
|
||||
throw runtime_error("txMoMproof needs a txid");
|
||||
|
||||
hash = uint256S(params[0].get_str());
|
||||
hash = uint256S(params[0].get_str());
|
||||
|
||||
uint256 blockHash;
|
||||
CTransaction tx;
|
||||
if (!GetTransaction(hash, tx, blockHash, true))
|
||||
throw runtime_error("cannot find transaction");
|
||||
|
||||
blockIndex = mapBlockIndex[blockHash];
|
||||
|
||||
depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
|
||||
|
||||
if (!depth)
|
||||
throw runtime_error("notarisation not found");
|
||||
|
||||
// index of block in MoM leaves
|
||||
nIndex = notarisedHeight - blockIndex->nHeight;
|
||||
}
|
||||
|
||||
// build merkle chain from blocks to MoM
|
||||
{
|
||||
// since the merkle branch code is tied up in a block class
|
||||
// and we want to make a merkle branch for something that isnt transactions
|
||||
CBlock fakeBlock;
|
||||
for (int i=0; i<depth; i++) {
|
||||
uint256 mRoot = chainActive[notarisedHeight - i]->hashMerkleRoot;
|
||||
CTransaction fakeTx;
|
||||
// first value in CTransaction memory is it's hash
|
||||
memcpy((void*)&fakeTx, mRoot.begin(), 32);
|
||||
fakeBlock.vtx.push_back(fakeTx);
|
||||
}
|
||||
branch = fakeBlock.GetMerkleBranch(nIndex);
|
||||
|
||||
// Check branch
|
||||
if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle block->MoM");
|
||||
}
|
||||
|
||||
// Now get the tx merkle branch
|
||||
{
|
||||
CBlock block;
|
||||
|
||||
if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)");
|
||||
|
||||
if(!ReadBlockFromDisk(block, blockIndex,1))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
||||
|
||||
// Locate the transaction in the block
|
||||
int nTxIndex;
|
||||
for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
|
||||
if (block.vtx[nTxIndex].GetHash() == hash)
|
||||
break;
|
||||
|
||||
if (nTxIndex == (int)block.vtx.size())
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error locating tx in block");
|
||||
|
||||
std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
|
||||
|
||||
// Check branch
|
||||
if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle tx->block");
|
||||
|
||||
// concatenate branches
|
||||
nIndex = (nIndex << txBranch.size()) + nTxIndex;
|
||||
branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
|
||||
}
|
||||
|
||||
// Check the proof
|
||||
if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed validating MoM");
|
||||
|
||||
// Encode and return
|
||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssProof << std::make_pair(notarisationHash, MerkleBranch(nIndex, branch));
|
||||
return HexStr(ssProof.begin(), ssProof.end());
|
||||
std::vector<uint8_t> proofData = E_MARSHAL(ss << GetAssetchainProof(hash));
|
||||
return HexStr(proofData);
|
||||
}
|
||||
|
||||
|
||||
UniValue getproofroot(const UniValue& params, bool fHelp)
|
||||
{
|
||||
std::string symbol;
|
||||
int kmdHeight;
|
||||
|
||||
|
||||
// parse params and get notarisation data for tx
|
||||
if ( fHelp || params.size() != 2)
|
||||
throw runtime_error("getproofroot needs a symbol and a kmdHeight");
|
||||
symbol = params[0].get_str();
|
||||
kmdHeight = atoi(params[0].get_str().c_str());
|
||||
if (kmdHeight <= 0)
|
||||
throw runtime_error("Invalid kmdHeight");
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
UniValue minerids(const UniValue& params, bool fHelp)
|
||||
{
|
||||
uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef SCRIPT_CC_H
|
||||
#define SCRIPT_CC_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "pubkey.h"
|
||||
#include "script/script.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.h"
|
||||
@@ -79,5 +81,4 @@ bool GetPushData(const CScript &sig, std::vector<unsigned char> &data);
|
||||
*/
|
||||
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data);
|
||||
|
||||
|
||||
#endif /* SCRIPT_CC_H */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
int main(int argc, char **argv) {
|
||||
assert(init_and_check_sodium() != -1);
|
||||
ECC_Start();
|
||||
ECCVerifyHandle handle; // Inits secp256k1 verify context
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
|
||||
CBitcoinSecret vchSecret;
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
#include <cryptoconditions.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cc/importcoin.h"
|
||||
#include "cc/eval.h"
|
||||
#include "importcoin.h"
|
||||
#include "base58.h"
|
||||
#include "core_io.h"
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "script/cc.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/cc.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "script/serverchecker.h"
|
||||
#include "txmempool.h"
|
||||
|
||||
267
src/test-komodo/test_crosschain.cpp
Normal file
267
src/test-komodo/test_crosschain.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#include <zmq.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "importcoin.h"
|
||||
#include "base58.h"
|
||||
#include "core_io.h"
|
||||
#include "crosschain.h"
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/cc.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "script/serverchecker.h"
|
||||
#include "txmempool.h"
|
||||
#include "crosschain.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
|
||||
extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
||||
|
||||
|
||||
/*
|
||||
* Tests for the whole process of creating and validating notary proofs
|
||||
* using proof roots (MoMoMs). This is to support coin imports.
|
||||
*/
|
||||
|
||||
namespace TestCrossChainProof {
|
||||
|
||||
|
||||
class TestCrossChain : public ::testing::Test, public Eval {
|
||||
public:
|
||||
bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||
{
|
||||
NotarisationData data;
|
||||
return ParseNotarisationOpReturn(tx, data);
|
||||
}
|
||||
protected:
|
||||
static void SetUpTestCase() { }
|
||||
virtual void SetUp() {
|
||||
ASSETCHAINS_CC = 1;
|
||||
EVAL_TEST = this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
TEST_F(TestCrossChain, testCreateAndValidateImportProof)
|
||||
{
|
||||
/*
|
||||
* This tests the full process of creation of a cross chain proof.
|
||||
* For the purposes of the test we will use one assetchain and a KMD chain.
|
||||
*
|
||||
* In order to do this test, we need 2 blockchains, so we'll fork and make a socket
|
||||
* for IPC.
|
||||
*/
|
||||
|
||||
int childPid = fork();
|
||||
void *ctx = zmq_ctx_new();
|
||||
void *socket = zmq_socket(ctx, ZMQ_PAIR);
|
||||
setupChain();
|
||||
std::vector<CBlock> blocks;
|
||||
blocks.resize(10);
|
||||
NotarisationData a2kmd, kmd2a(true);
|
||||
|
||||
|
||||
auto SendIPC = [&] (std::vector<uint8_t> v) {
|
||||
assert(v.size() == zmq_send(socket, v.data(), v.size(), 0));
|
||||
};
|
||||
|
||||
auto RecvIPC = [&] () {
|
||||
std::vector<uint8_t> out;
|
||||
out.resize(100000);
|
||||
int len = zmq_recv(socket, out.data(), out.size(), 0);
|
||||
assert(len != -1);
|
||||
out.resize(len);
|
||||
return out;
|
||||
};
|
||||
|
||||
auto RecordNotarisation = [&] (CTransaction inputTx, NotarisationData data) {
|
||||
CMutableTransaction mtx = spendTx(inputTx);
|
||||
mtx.vout.resize(2);
|
||||
mtx.vout[0].scriptPubKey << VCH(notaryKey.GetPubKey().begin(), 33) << OP_CHECKSIG;
|
||||
mtx.vout[1].scriptPubKey << OP_RETURN << E_MARSHAL(ss << data);
|
||||
mtx.vout[1].nValue = 0;
|
||||
mtx.vin[0].scriptSig << getSig(mtx, inputTx.vout[0].scriptPubKey);
|
||||
|
||||
acceptTxFail(CTransaction(mtx));
|
||||
printf("accept %snotarisation: %s\n", data.IsBackNotarisation ? "back" : "",
|
||||
mtx.GetHash().GetHex().data());
|
||||
return mtx.GetHash();
|
||||
};
|
||||
|
||||
auto RunTestAssetchain = [&] ()
|
||||
{
|
||||
NotarisationData back(1);
|
||||
strcpy(ASSETCHAINS_SYMBOL, "symbolA");
|
||||
strcpy(a2kmd.symbol, "symbolA");
|
||||
a2kmd.ccId = 2;
|
||||
|
||||
/*
|
||||
* Notarisation 1
|
||||
*/
|
||||
generateBlock(&blocks[1]);
|
||||
generateBlock(&blocks[2]);
|
||||
a2kmd.blockHash = blocks[2].GetHash();
|
||||
a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2);
|
||||
SendIPC(E_MARSHAL(ss << a2kmd));
|
||||
E_UNMARSHAL(RecvIPC(), ss >> back);
|
||||
RecordNotarisation(blocks[1].vtx[0], back);
|
||||
|
||||
/*
|
||||
* Notarisation 2
|
||||
*/
|
||||
generateBlock(&blocks[3]);
|
||||
generateBlock(&blocks[4]);
|
||||
a2kmd.blockHash = blocks[4].GetHash();
|
||||
a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2);
|
||||
SendIPC(E_MARSHAL(ss << a2kmd));
|
||||
E_UNMARSHAL(RecvIPC(), ss >> back);
|
||||
RecordNotarisation(blocks[3].vtx[0], back);
|
||||
|
||||
/*
|
||||
* Generate proof
|
||||
*/
|
||||
generateBlock(&blocks[5]);
|
||||
uint256 txid = blocks[3].vtx[0].GetHash();
|
||||
std::pair<uint256,MerkleBranch> assetChainProof = GetAssetchainProof(txid);
|
||||
SendIPC(E_MARSHAL(ss << txid; ss << assetChainProof));
|
||||
};
|
||||
|
||||
auto RunTestKmd = [&] ()
|
||||
{
|
||||
NotarisationData n;
|
||||
|
||||
/*
|
||||
* Notarisation 1
|
||||
*/
|
||||
E_UNMARSHAL(RecvIPC(), ss >> n);
|
||||
// Grab a coinbase input to fund notarisation
|
||||
generateBlock(&blocks[1]);
|
||||
n.txHash = RecordNotarisation(blocks[1].vtx[0], a2kmd);
|
||||
n.height = chainActive.Height();
|
||||
SendIPC(E_MARSHAL(ss << n));
|
||||
|
||||
/*
|
||||
* Notarisation 2
|
||||
*/
|
||||
E_UNMARSHAL(RecvIPC(), ss >> n);
|
||||
// Grab a coinbase input to fund notarisation
|
||||
generateBlock(&blocks[2]);
|
||||
n.txHash = RecordNotarisation(blocks[2].vtx[0], a2kmd);
|
||||
n.height = chainActive.Height();
|
||||
SendIPC(E_MARSHAL(ss << n));
|
||||
|
||||
/*
|
||||
* Extend proof
|
||||
*/
|
||||
std::pair<uint256,MerkleBranch> assetChainProof;
|
||||
uint256 txid;
|
||||
// Extend proof to MoMoM
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> kmd2a));
|
||||
std::pair<uint256,MerkleBranch> ccProof = GetCrossChainProof(txid, (char*)"symbolA",
|
||||
2, assetChainProof.first, assetChainProof.second);
|
||||
};
|
||||
|
||||
const char endpoint[] = "ipc://tmpKomodoTestCrossChainSock";
|
||||
|
||||
if (!childPid) {
|
||||
assert(0 == zmq_connect(socket, endpoint));
|
||||
usleep(20000);
|
||||
RunTestAssetchain();
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
assert(0 == zmq_bind(socket, endpoint));
|
||||
RunTestKmd();
|
||||
int returnStatus;
|
||||
waitpid(childPid, &returnStatus, 0);
|
||||
unlink("tmpKomodoTestCrossChainSock");
|
||||
ASSERT_EQ(0, returnStatus);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*
|
||||
* Assetchain notarisation 2
|
||||
*
|
||||
|
||||
ON_ASSETCHAIN {
|
||||
a2kmd.blockHash = blocks[4].GetHash();
|
||||
a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2);
|
||||
SendIPC(E_MARSHAL(ss << a2kmd));
|
||||
}
|
||||
|
||||
ON_KMD {
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> a2kmd));
|
||||
// Grab a coinbase input to fund notarisation
|
||||
RecordNotarisation(blocks[2].vtx[0], a2kmd);
|
||||
}
|
||||
|
||||
generateBlock(&blocks[5]);
|
||||
generateBlock(&blocks[6]);
|
||||
|
||||
*
|
||||
* Backnotarisation
|
||||
*
|
||||
* This is what will contain the MoMoM which allows us to prove across chains
|
||||
*
|
||||
std::vector<uint256> moms;
|
||||
int assetChainHeight;
|
||||
|
||||
ON_KMD {
|
||||
memset(kmd2a.txHash.begin(), 1, 32); // Garbage but non-null
|
||||
kmd2a.symbol[0] = 0; // KMD
|
||||
kmd2a.MoMoM = GetProofRoot((char*)"symbolA", 2, chainActive.Height(), moms, &assetChainHeight);
|
||||
kmd2a.MoMoMDepth = 0; // Needed?
|
||||
SendIPC(E_MARSHAL(ss << kmd2a));
|
||||
}
|
||||
|
||||
ON_ASSETCHAIN {
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> kmd2a));
|
||||
RecordNotarisation(blocks[1].vtx[0], kmd2a);
|
||||
}
|
||||
|
||||
|
||||
*
|
||||
* We can now prove a tx from A on A, via a merkle root backpropagated from KMD.
|
||||
*
|
||||
* The transaction that we'll try to prove is the coinbase from the 3rd block.
|
||||
* We should be able to start with only that transaction ID, and generate a merkle
|
||||
* proof.
|
||||
*
|
||||
|
||||
std::pair<uint256,MerkleBranch> assetChainProof;
|
||||
uint256 txid;
|
||||
|
||||
ON_ASSETCHAIN {
|
||||
txid = blocks[2].vtx[0].GetHash();
|
||||
|
||||
// First thing to do is get the proof from the assetchain
|
||||
assetChainProof = GetAssetchainProof(txid);
|
||||
SendIPC(E_MARSHAL(ss << txid; ss << assetChainProof));
|
||||
}
|
||||
|
||||
ON_KMD {
|
||||
// Extend proof to MoMoM
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> kmd2a));
|
||||
std::pair<uint256,MerkleBranch> ccProof = GetCrossChainProof(txid, (char*)"symbolA",
|
||||
2, assetChainProof.first, assetChainProof.second);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
} /* namespace TestCrossChainProof */
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "miner.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "random.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpcprotocol.h"
|
||||
@@ -33,13 +34,14 @@ CKey notaryKey;
|
||||
int64_t nMockTime;
|
||||
|
||||
extern uint32_t USE_EXTERNAL_PUBKEY;
|
||||
extern std::string NOTARY_PUBKEY;
|
||||
|
||||
void setupChain()
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
|
||||
// Settings to get block reward
|
||||
//NOTARY_PUBKEY = _NOTARY_PUBKEY;
|
||||
NOTARY_PUBKEY = notaryPubkey;
|
||||
USE_EXTERNAL_PUBKEY = 1;
|
||||
mapArgs["-mineraddress"] = "bogus";
|
||||
COINBASE_MATURITY = 1;
|
||||
@@ -54,6 +56,7 @@ void setupChain()
|
||||
pblocktree = new CBlockTreeDB(1 << 20, true);
|
||||
CCoinsViewDB *pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
||||
pnotarisations = new NotarisationDB(1 << 20, true);
|
||||
InitBlockIndex();
|
||||
}
|
||||
|
||||
@@ -65,15 +68,19 @@ void generateBlock(CBlock *block)
|
||||
params.push_back(1);
|
||||
uint256 blockId;
|
||||
|
||||
SetMockTime(nMockTime++); // CreateNewBlock can fail if not enough time passes
|
||||
SetMockTime(nMockTime+=100); // CreateNewBlock can fail if not enough time passes
|
||||
|
||||
char symbolPrefix = ASSETCHAINS_SYMBOL[0];
|
||||
ASSETCHAINS_SYMBOL[0] = 0; // generate block fails otherwise
|
||||
|
||||
try {
|
||||
UniValue out = generate(params, false);
|
||||
blockId.SetHex(out[0].getValStr());
|
||||
ASSETCHAINS_SYMBOL[0] = symbolPrefix;
|
||||
if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId], false));
|
||||
} catch (const UniValue& e) {
|
||||
FAIL() << "failed to create block: " << e.write().data();
|
||||
}
|
||||
if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId]));
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +98,7 @@ bool acceptTx(const CTransaction tx, CValidationState &state)
|
||||
}
|
||||
|
||||
|
||||
static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0)
|
||||
CMutableTransaction spendTx(const CTransaction &txIn, int nOut)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
@@ -103,6 +110,16 @@ static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0)
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint8_t> getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn)
|
||||
{
|
||||
uint256 hash = SignatureHash(inputPubKey, mtx, nIn, SIGHASH_ALL, 0, 0);
|
||||
std::vector<uint8_t> vchSig;
|
||||
notaryKey.Sign(hash, vchSig);
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
return vchSig;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* In order to do tests there needs to be inputs to spend.
|
||||
* This method creates a block and returns a transaction that spends the coinbase.
|
||||
|
||||
@@ -22,6 +22,8 @@ void generateBlock(CBlock *block=NULL);
|
||||
bool acceptTx(const CTransaction tx, CValidationState &state);
|
||||
void acceptTxFail(const CTransaction tx);
|
||||
void getInputTx(CScript scriptPubKey, CTransaction &txIn);
|
||||
CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0);
|
||||
std::vector<uint8_t> getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn=0);
|
||||
|
||||
|
||||
#endif /* TESTUTILS_H */
|
||||
|
||||
Reference in New Issue
Block a user