#include "cc/eval.h"
+#include "crosschain.h"
+#include "importcoin.h"
#include "main.h"
#include "notarisationdb.h"
+/*
+ * The crosschain workflow.
+ *
+ * 3 chains, A, B, and KMD. We would like to prove TX on B.
+ * There is a notarisation, nA0, which will include TX via an MoM.
+ * The notarisation nA0 must fall between 2 notarisations of B,
+ * ie, nB0 and nB1. An MoMoM including this range is propagated to
+ * B in notarisation receipt (backnotarisation) bnB2.
+ *
+ * A: TX bnA0
+ * \ /
+ * KMD: nB0 nA0 nB1 nB2
+ * \ \ \
+ * B: bnB0 bnB1 bnB2
+ */
+
+// XXX: There are potential crashes wherever we access chainActive without a lock,
+// because it might be disconnecting blocks at the same time.
+
+
+int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
+
/* On KMD */
-uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector<uint256> &moms, int* assetChainHeight)
+uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight,
+ std::vector<uint256> &moms, uint256 &destNotarisationTxid)
{
/*
* 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.
+ *
+ * kmdHeight notarisations-0 notarisations-1
+ * *********************|
+ * > scan backwards >
*/
- if (targetCCid <= 1)
+
+ if (targetCCid < 2)
+ return uint256();
+
+ if (kmdHeight < 0 || kmdHeight > chainActive.Height())
return uint256();
int seenOwnNotarisations = 0;
- // TODO: test height out of range
- // TODO: Make sure that boundary for moms is notarisation tx not block
+ bool txscl = IsTXSCL(symbol);
- for (int i=0; i<1440; i++) {
+ for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
if (i > kmdHeight) break;
NotarisationsInBlock notarisations;
uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
- if (!pnotarisations->Read(blockHash, notarisations))
+ if (!GetBlockNotarisations(blockHash, notarisations))
continue;
+
+ // See if we have an own notarisation in this block
BOOST_FOREACH(Notarisation& nota, notarisations) {
- NotarisationData& data = nota.second;
- if (data.ccId != targetCCid)
- continue;
- if (strcmp(data.symbol, symbol) == 0)
+ if (strcmp(nota.second.symbol, symbol) == 0)
{
seenOwnNotarisations++;
- if (seenOwnNotarisations == 2)
- goto end;
if (seenOwnNotarisations == 1)
- *assetChainHeight = data.height; // TODO: Needed?
- continue; // Don't include own MoMs
+ destNotarisationTxid = nota.first;
+ else if (seenOwnNotarisations == 2)
+ goto end;
+ break;
+ }
+ }
+
+ if (seenOwnNotarisations == 1) {
+ BOOST_FOREACH(Notarisation& nota, notarisations) {
+ if (IsTXSCL(nota.second.symbol) == txscl)
+ if (nota.second.ccId == targetCCid)
+ moms.push_back(nota.second.MoM);
}
- if (seenOwnNotarisations == 1)
- moms.push_back(data.MoM);
}
}
}
+/*
+ * Get a notarisation from a given height
+ *
+ * Will scan notarisations leveldb up to a limit
+ */
+template <typename IsTarget>
+int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found)
+{
+ int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height());
+
+ for (int h=nHeight; h<limit; h++) {
+ NotarisationsInBlock notarisations;
+
+ if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations))
+ continue;
+
+ BOOST_FOREACH(found, notarisations) {
+ if (f(found)) {
+ return h;
+ }
+ }
+ }
+ return 0;
+}
+
+
/* On KMD */
-std::pair<uint256,MerkleBranch> GetCrossChainProof(uint256 txid, char* targetSymbol,
- uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof)
+TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
+ const TxProof assetChainProof)
{
/*
* Here we are given a proof generated by an assetchain A which goes from given txid to
* that range, and finally extend the proof to lead to the MoMoM (proof root).
*/
EvalRef eval;
- uint256 MoM = assetChainProof.Exec(txid);
+ uint256 MoM = assetChainProof.second.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
+ if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx))
throw std::runtime_error("Notarisation not found");
+ kmdHeight = blockIdx.GetHeight();
}
+ // We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists
+ // at that height.
+ // If we call CalculateProofRoot with that height, it'll scan backwards, until it finds
+ // a notarisation from B, and it might not include our notarisation from A
+ // at all. So, the thing we need to do is scan forwards to find the notarisation for B,
+ // that is inclusive of A.
+ Notarisation nota;
+ auto isTarget = [&](Notarisation ¬a) {
+ return strcmp(nota.second.symbol, targetSymbol) == 0;
+ };
+ kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota);
+ if (!kmdHeight)
+ throw std::runtime_error("Cannot find notarisation for target inclusive of source");
+
// Get MoMs for kmd height and symbol
std::vector<uint256> moms;
- int targetChainStartHeight;
- uint256 MoMoM = GetProofRoot(targetSymbol, targetCCid, kmdHeight, moms, &targetChainStartHeight);
+ uint256 targetChainNotarisationTxid;
+ uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid);
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++)
+ 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;
+ std::vector<uint256> vBranch;
{
CBlock fakeBlock;
for (int i=0; i<moms.size(); i++) {
memcpy((void*)&fakeTx, moms[i].begin(), 32);
fakeBlock.vtx.push_back(fakeTx);
}
- newBranch = fakeBlock.GetMerkleBranch(nIndex);
+ vBranch = fakeBlock.GetMerkleBranch(nIndex);
}
// Concatenate branches
- MerkleBranch newProof = assetChainProof;
- newProof << MerkleBranch(nIndex, newBranch);
+ MerkleBranch newBranch = assetChainProof.second;
+ newBranch << MerkleBranch(nIndex, vBranch);
// Check proof
- if (newProof.Exec(txid) != MoMoM)
+ if (newBranch.Exec(txid) != MoMoM)
throw std::runtime_error("Proof check failed");
- return std::make_pair(uint256(), newProof);
+ return std::make_pair(targetChainNotarisationTxid,newBranch);
+}
+
+
+/*
+ * Takes an importTx that has proof leading to assetchain root
+ * and extends proof to cross chain root
+ */
+void CompleteImportTransaction(CTransaction &importTx)
+{
+ TxProof proof;
+ CTransaction burnTx;
+ std::vector<CTxOut> payouts;
+ if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
+ throw std::runtime_error("Couldn't parse importTx");
+
+ std::string targetSymbol;
+ uint32_t targetCCid;
+ uint256 payoutsHash;
+ if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash))
+ throw std::runtime_error("Couldn't parse burnTx");
+
+ proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
+
+ importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
}
+bool IsSameAssetChain(const Notarisation ¬a) {
+ return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
+};
+
+
/* On assetchain */
-bool ValidateCrossChainProof(uint256 txid, int notarisationHeight, MerkleBranch proof)
+bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out)
{
/*
- * 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.
+ * Here we are given a txid, and a proof.
+ * We go from the KMD notarisation txid to the backnotarisation,
+ * then jump to the next backnotarisation, which contains the corresponding MoMoM.
*/
-}
+ Notarisation bn;
+ if (!GetBackNotarisation(kmdNotarisationTxid, bn))
+ return false;
+ // Need to get block height of that backnotarisation
+ EvalRef eval;
+ CBlockIndex block;
+ CTransaction tx;
+ if (!eval->GetTxConfirmed(bn.first, tx, block)){
+ fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n");
+ return false;
+ }
+ return (bool) ScanNotarisationsFromHeight(block.GetHeight()+1, &IsSameAssetChain, out);
+}
-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)
+TxProof GetAssetchainProof(uint256 hash)
{
- uint256 notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex;
+ int nIndex;
+ CBlockIndex* blockIndex;
+ Notarisation nota;
std::vector<uint256> branch;
- int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi;
{
uint256 blockHash;
if (!GetTransaction(hash, tx, blockHash, true))
throw std::runtime_error("cannot find transaction");
- blockIndex = mapBlockIndex[blockHash];
+ if (blockHash.IsNull())
+ throw std::runtime_error("tx still in mempool");
- depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
-
- if (!depth)
- throw std::runtime_error("notarisation not found");
+ blockIndex = mapBlockIndex[blockHash];
+ int h = blockIndex->GetHeight();
+ // The assumption here is that the first notarisation for a height GTE than
+ // the transaction block height will contain the corresponding MoM. If there
+ // are sequence issues with the notarisations this may fail.
+ auto isTarget = [&](Notarisation ¬a) {
+ if (!IsSameAssetChain(nota)) return false;
+ return nota.second.height >= blockIndex->GetHeight();
+ };
+ if (!ScanNotarisationsFromHeight(blockIndex->GetHeight(), isTarget, nota))
+ throw std::runtime_error("backnotarisation not yet confirmed");
// index of block in MoM leaves
- nIndex = notarisedHeight - blockIndex->nHeight;
+ nIndex = nota.second.height - blockIndex->GetHeight();
}
// 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);
+ std::vector<uint256> leaves, tree;
+ for (int i=0; i<nota.second.MoMDepth; i++) {
+ uint256 mRoot = chainActive[nota.second.height - i]->hashMerkleRoot;
+ leaves.push_back(mRoot);
}
- branch = fakeBlock.GetMerkleBranch(nIndex);
+ bool fMutated;
+ BuildMerkleTree(&fMutated, leaves, tree);
+ branch = GetMerkleBranch(nIndex, leaves.size(), tree);
// Check branch
- if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex))
+ uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex);
+ if (nota.second.MoM != ourResult)
throw std::runtime_error("Failed merkle block->MoM");
}
if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
throw std::runtime_error("Block not available (pruned data)");
- if(!ReadBlockFromDisk(block, blockIndex,1))
+ if(!ReadBlockFromDisk(block, blockIndex, Params().GetConsensus(), 1))
throw std::runtime_error("Can't read block from disk");
// Locate the transaction in the block
}
// Check the proof
- if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
+ if (nota.second.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));
+ return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch));
}