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 bool txscl = IsTXSCL(symbol);
55 for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
56 if (i > kmdHeight) break;
57 NotarisationsInBlock notarisations;
58 uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
59 if (!GetBlockNotarisations(blockHash, notarisations))
62 // See if we have an own notarisation in this block
63 BOOST_FOREACH(Notarisation& nota, notarisations) {
64 if (strcmp(nota.second.symbol, symbol) == 0)
66 seenOwnNotarisations++;
67 if (seenOwnNotarisations == 1)
68 destNotarisationTxid = nota.first;
69 else if (seenOwnNotarisations == 2)
75 if (seenOwnNotarisations == 1) {
76 BOOST_FOREACH(Notarisation& nota, notarisations) {
77 if (IsTXSCL(nota.second.symbol) == txscl)
78 if (nota.second.ccId == targetCCid)
79 moms.push_back(nota.second.MoM);
85 return GetMerkleRoot(moms);
90 * Get a notarisation from a given height
92 * Will scan notarisations leveldb up to a limit
94 template <typename IsTarget>
95 int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found)
97 int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height());
99 for (int h=nHeight; h<limit; h++) {
100 NotarisationsInBlock notarisations;
102 if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations))
105 BOOST_FOREACH(found, notarisations) {
116 TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
117 const TxProof assetChainProof)
120 * Here we are given a proof generated by an assetchain A which goes from given txid to
121 * an assetchain MoM. We need to go from the notarisationTxid for A to the MoMoM range of the
122 * backnotarisation for B (given by kmdheight of notarisation), find the MoM within the MoMs for
123 * that range, and finally extend the proof to lead to the MoMoM (proof root).
126 uint256 MoM = assetChainProof.second.Exec(txid);
128 // Get a kmd height for given notarisation Txid
131 CTransaction sourceNotarisation;
133 CBlockIndex blockIdx;
134 if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx))
135 throw std::runtime_error("Notarisation not found");
136 kmdHeight = blockIdx.nHeight;
139 // We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists
141 // If we call CalculateProofRoot with that height, it'll scan backwards, until it finds
142 // a notarisation from B, and it might not include our notarisation from A
143 // at all. So, the thing we need to do is scan forwards to find the notarisation for B,
144 // that is inclusive of A.
146 auto isTarget = [&](Notarisation ¬a) {
147 return strcmp(nota.second.symbol, targetSymbol) == 0;
149 kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota);
151 throw std::runtime_error("Cannot find notarisation for target inclusive of source");
153 // Get MoMs for kmd height and symbol
154 std::vector<uint256> moms;
155 uint256 targetChainNotarisationTxid;
156 uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid);
158 throw std::runtime_error("No MoMs found");
160 // Find index of source MoM in MoMoM
162 for (nIndex=0; nIndex<moms.size(); nIndex++) {
163 if (moms[nIndex] == MoM)
166 throw std::runtime_error("Couldn't find MoM within MoMoM set");
170 std::vector<uint256> vBranch;
173 for (int i=0; i<moms.size(); i++) {
175 // first value in CTransaction memory is it's hash
176 memcpy((void*)&fakeTx, moms[i].begin(), 32);
177 fakeBlock.vtx.push_back(fakeTx);
179 vBranch = fakeBlock.GetMerkleBranch(nIndex);
182 // Concatenate branches
183 MerkleBranch newBranch = assetChainProof.second;
184 newBranch << MerkleBranch(nIndex, vBranch);
187 if (newBranch.Exec(txid) != MoMoM)
188 throw std::runtime_error("Proof check failed");
190 return std::make_pair(targetChainNotarisationTxid,newBranch);
195 * Takes an importTx that has proof leading to assetchain root
196 * and extends proof to cross chain root
198 void CompleteImportTransaction(CTransaction &importTx)
202 std::vector<CTxOut> payouts;
203 if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
204 throw std::runtime_error("Couldn't parse importTx");
206 std::string targetSymbol;
209 if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash))
210 throw std::runtime_error("Couldn't parse burnTx");
212 proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
214 importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
218 bool IsSameAssetChain(const Notarisation ¬a) {
219 return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
224 bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out)
227 * Here we are given a txid, and a proof.
228 * We go from the KMD notarisation txid to the backnotarisation,
229 * then jump to the next backnotarisation, which contains the corresponding MoMoM.
232 if (!GetBackNotarisation(kmdNotarisationTxid, bn))
235 // Need to get block height of that backnotarisation
239 if (!eval->GetTxConfirmed(bn.first, tx, block)){
240 fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n");
244 return (bool) ScanNotarisationsFromHeight(block.nHeight+1, &IsSameAssetChain, out);
251 * out: pair<notarisationTxHash,merkleBranch>
253 TxProof GetAssetchainProof(uint256 hash)
256 CBlockIndex* blockIndex;
258 std::vector<uint256> branch;
263 if (!GetTransaction(hash, tx, blockHash, true))
264 throw std::runtime_error("cannot find transaction");
266 if (blockHash.IsNull())
267 throw std::runtime_error("tx still in mempool");
269 blockIndex = mapBlockIndex[blockHash];
270 int h = blockIndex->nHeight;
271 // The assumption here is that the first notarisation for a height GTE than
272 // the transaction block height will contain the corresponding MoM. If there
273 // are sequence issues with the notarisations this may fail.
274 auto isTarget = [&](Notarisation ¬a) {
275 if (!IsSameAssetChain(nota)) return false;
276 return nota.second.height >= blockIndex->nHeight;
278 if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota))
279 throw std::runtime_error("backnotarisation not yet confirmed");
281 // index of block in MoM leaves
282 nIndex = nota.second.height - blockIndex->nHeight;
285 // build merkle chain from blocks to MoM
287 std::vector<uint256> leaves, tree;
288 for (int i=0; i<nota.second.MoMDepth; i++) {
289 uint256 mRoot = chainActive[nota.second.height - i]->hashMerkleRoot;
290 leaves.push_back(mRoot);
293 BuildMerkleTree(&fMutated, leaves, tree);
294 branch = GetMerkleBranch(nIndex, leaves.size(), tree);
297 uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex);
298 if (nota.second.MoM != ourResult)
299 throw std::runtime_error("Failed merkle block->MoM");
302 // Now get the tx merkle branch
306 if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
307 throw std::runtime_error("Block not available (pruned data)");
309 if(!ReadBlockFromDisk(block, blockIndex,1))
310 throw std::runtime_error("Can't read block from disk");
312 // Locate the transaction in the block
314 for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
315 if (block.vtx[nTxIndex].GetHash() == hash)
318 if (nTxIndex == (int)block.vtx.size())
319 throw std::runtime_error("Error locating tx in block");
321 std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
324 if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
325 throw std::runtime_error("Failed merkle tx->block");
327 // concatenate branches
328 nIndex = (nIndex << txBranch.size()) + nTxIndex;
329 branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
333 if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
334 throw std::runtime_error("Failed validating MoM");
337 CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
338 return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch));