2 #include "crosschain.h"
3 #include "importcoin.h"
5 #include "notarisationdb.h"
8 * The crosschain workflow.
10 * 3 chains, A, B, and KMD. We would like to prove TX on B.
11 * There is a notarisation, nA0, which will include TX via an MoM.
12 * The notarisation nA0 must fall between 2 notarisations of B,
13 * ie, nB0 and nB1. An MoMoM including this range is propagated to
14 * B in notarisation receipt (backnotarisation) bnB2.
18 * KMD: nB0 nA0 nB1 nB2
23 // XXX: There are potential crashes wherever we access chainActive without a lock,
24 // because it might be disconnecting blocks at the same time.
27 int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
31 uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight,
32 std::vector<uint256> &moms, uint256 &destNotarisationTxid)
35 * Notaries don't wait for confirmation on KMD before performing a backnotarisation,
36 * but we need a determinable range that will encompass all merkle roots. Include MoMs
37 * including the block height of the last notarisation until the height before the
38 * previous notarisation.
40 * kmdHeight notarisations-0 notarisations-1
41 * *********************|
48 if (kmdHeight < 0 || kmdHeight > chainActive.Height())
51 int seenOwnNotarisations = 0;
53 for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
54 if (i > kmdHeight) break;
55 NotarisationsInBlock notarisations;
56 uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
57 if (!GetBlockNotarisations(blockHash, notarisations))
60 // See if we have an own notarisation in this block
61 BOOST_FOREACH(Notarisation& nota, notarisations) {
62 if (strcmp(nota.second.symbol, symbol) == 0)
64 seenOwnNotarisations++;
65 if (seenOwnNotarisations == 1)
66 destNotarisationTxid = nota.first;
67 else if (seenOwnNotarisations == 2)
73 if (seenOwnNotarisations == 1) {
74 BOOST_FOREACH(Notarisation& nota, notarisations) {
75 if (nota.second.ccId == targetCCid)
76 moms.push_back(nota.second.MoM);
82 return GetMerkleRoot(moms);
87 * Get a notarisation from a given height
89 * Will scan notarisations leveldb up to a limit
91 template <typename IsTarget>
92 int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found)
94 int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height());
96 for (int h=nHeight; h<limit; h++) {
97 NotarisationsInBlock notarisations;
99 if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations))
102 BOOST_FOREACH(found, notarisations) {
113 TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
114 const TxProof assetChainProof)
117 * Here we are given a proof generated by an assetchain A which goes from given txid to
118 * an assetchain MoM. We need to go from the notarisationTxid for A to the MoMoM range of the
119 * backnotarisation for B (given by kmdheight of notarisation), find the MoM within the MoMs for
120 * that range, and finally extend the proof to lead to the MoMoM (proof root).
123 uint256 MoM = assetChainProof.second.Exec(txid);
125 // Get a kmd height for given notarisation Txid
128 CTransaction sourceNotarisation;
130 CBlockIndex blockIdx;
131 if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx))
132 throw std::runtime_error("Notarisation not found");
133 kmdHeight = blockIdx.nHeight;
136 // We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists
138 // If we call CalculateProofRoot with that height, it'll scan backwards, until it finds
139 // a notarisation from B, and it might not include our notarisation from A
140 // at all. So, the thing we need to do is scan forwards to find the notarisation for B,
141 // that is inclusive of A.
143 auto isTarget = [&](Notarisation ¬a) {
144 return strcmp(nota.second.symbol, targetSymbol) == 0;
146 kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota);
148 throw std::runtime_error("Cannot find notarisation for target inclusive of source");
150 // Get MoMs for kmd height and symbol
151 std::vector<uint256> moms;
152 uint256 targetChainNotarisationTxid;
153 uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid);
155 throw std::runtime_error("No MoMs found");
157 // Find index of source MoM in MoMoM
159 for (nIndex=0; nIndex<moms.size(); nIndex++) {
160 if (moms[nIndex] == MoM)
163 throw std::runtime_error("Couldn't find MoM within MoMoM set");
167 std::vector<uint256> vBranch;
170 for (int i=0; i<moms.size(); i++) {
172 // first value in CTransaction memory is it's hash
173 memcpy((void*)&fakeTx, moms[i].begin(), 32);
174 fakeBlock.vtx.push_back(fakeTx);
176 vBranch = fakeBlock.GetMerkleBranch(nIndex);
179 // Concatenate branches
180 MerkleBranch newBranch = assetChainProof.second;
181 newBranch << MerkleBranch(nIndex, vBranch);
184 if (newBranch.Exec(txid) != MoMoM)
185 throw std::runtime_error("Proof check failed");
187 return std::make_pair(targetChainNotarisationTxid,newBranch);
192 * Takes an importTx that has proof leading to assetchain root
193 * and extends proof to cross chain root
195 void CompleteImportTransaction(CTransaction &importTx)
199 std::vector<CTxOut> payouts;
200 if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
201 throw std::runtime_error("Couldn't parse importTx");
203 std::string targetSymbol;
206 if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash))
207 throw std::runtime_error("Couldn't parse burnTx");
209 proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
211 importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
215 bool IsSameAssetChain(const Notarisation ¬a) {
216 return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
221 bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out)
224 * Here we are given a txid, and a proof.
225 * We go from the KMD notarisation txid to the backnotarisation,
226 * then jump to the next backnotarisation, which contains the corresponding MoMoM.
229 if (!GetBackNotarisation(kmdNotarisationTxid, bn))
232 // Need to get block height of that backnotarisation
236 if (!eval->GetTxConfirmed(bn.first, tx, block)){
237 fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n");
241 return (bool) ScanNotarisationsFromHeight(block.nHeight+1, &IsSameAssetChain, out);
248 * out: pair<notarisationTxHash,merkleBranch>
250 TxProof GetAssetchainProof(uint256 hash)
253 CBlockIndex* blockIndex;
255 std::vector<uint256> branch;
260 if (!GetTransaction(hash, tx, blockHash, true))
261 throw std::runtime_error("cannot find transaction");
263 if (blockHash.IsNull())
264 throw std::runtime_error("tx still in mempool");
266 blockIndex = mapBlockIndex[blockHash];
267 int h = blockIndex->nHeight;
268 // The assumption here is that the first notarisation for a height GTE than
269 // the transaction block height will contain the corresponding MoM. If there
270 // are sequence issues with the notarisations this may fail.
271 auto isTarget = [&](Notarisation ¬a) {
272 if (!IsSameAssetChain(nota)) return false;
273 return nota.second.height >= blockIndex->nHeight;
275 if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota))
276 throw std::runtime_error("backnotarisation not yet confirmed");
278 // index of block in MoM leaves
279 nIndex = nota.second.height - blockIndex->nHeight;
282 // build merkle chain from blocks to MoM
284 std::vector<uint256> leaves, tree;
285 for (int i=0; i<nota.second.MoMDepth; i++) {
286 uint256 mRoot = chainActive[nota.second.height - i]->hashMerkleRoot;
287 leaves.push_back(mRoot);
290 BuildMerkleTree(&fMutated, leaves, tree);
291 branch = GetMerkleBranch(nIndex, leaves.size(), tree);
294 uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex);
295 if (nota.second.MoM != ourResult)
296 throw std::runtime_error("Failed merkle block->MoM");
299 // Now get the tx merkle branch
303 if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
304 throw std::runtime_error("Block not available (pruned data)");
306 if(!ReadBlockFromDisk(block, blockIndex,1))
307 throw std::runtime_error("Can't read block from disk");
309 // Locate the transaction in the block
311 for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
312 if (block.vtx[nTxIndex].GetHash() == hash)
315 if (nTxIndex == (int)block.vtx.size())
316 throw std::runtime_error("Error locating tx in block");
318 std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
321 if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
322 throw std::runtime_error("Failed merkle tx->block");
324 // concatenate branches
325 nIndex = (nIndex << txBranch.size()) + nTxIndex;
326 branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
330 if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
331 throw std::runtime_error("Failed validating MoM");
334 CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
335 return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch));