]>
Commit | Line | Data |
---|---|---|
20c3ac51 | 1 | #include "cc/eval.h" |
7f3cc8a2 | 2 | #include "crosschain.h" |
0b485d3c | 3 | #include "importcoin.h" |
20c3ac51 SS |
4 | #include "main.h" |
5 | #include "notarisationdb.h" | |
7f3cc8a2 | 6 | |
87d08c5a SS |
7 | /* |
8 | * The crosschain workflow. | |
9 | * | |
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. | |
15 | * | |
16 | * A: TX bnA0 | |
17 | * \ / | |
18 | * KMD: nB0 nA0 nB1 nB2 | |
19 | * \ \ \ | |
20 | * B: bnB0 bnB1 bnB2 | |
21 | */ | |
7f3cc8a2 | 22 | |
87d08c5a SS |
23 | // XXX: There are potential crashes wherever we access chainActive without a lock, |
24 | // because it might be disconnecting blocks at the same time. | |
20c3ac51 SS |
25 | |
26 | ||
87d08c5a SS |
27 | int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440; |
28 | ||
0b485d3c | 29 | |
20c3ac51 | 30 | /* On KMD */ |
e4f943d8 SS |
31 | uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, |
32 | std::vector<uint256> &moms, uint256 &destNotarisationTxid) | |
20c3ac51 SS |
33 | { |
34 | /* | |
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. | |
e4f943d8 SS |
39 | * |
40 | * kmdHeight notarisations-0 notarisations-1 | |
469bd637 | 41 | * *********************| |
e4f943d8 | 42 | * > scan backwards > |
20c3ac51 | 43 | */ |
06c960d2 | 44 | |
20c3ac51 SS |
45 | if (targetCCid <= 1) |
46 | return uint256(); | |
47 | ||
e4f943d8 SS |
48 | if (kmdHeight < 0 || kmdHeight > chainActive.Height()) |
49 | return uint256(); | |
20c3ac51 | 50 | |
e4f943d8 | 51 | int seenOwnNotarisations = 0; |
20c3ac51 | 52 | |
7f3cc8a2 | 53 | for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) { |
20c3ac51 SS |
54 | if (i > kmdHeight) break; |
55 | NotarisationsInBlock notarisations; | |
56 | uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock; | |
e4f943d8 | 57 | if (!GetBlockNotarisations(blockHash, notarisations)) |
20c3ac51 | 58 | continue; |
469bd637 SS |
59 | |
60 | // See if we have an own notarisation in this block | |
20c3ac51 | 61 | BOOST_FOREACH(Notarisation& nota, notarisations) { |
469bd637 | 62 | if (strcmp(nota.second.symbol, symbol) == 0) |
20c3ac51 SS |
63 | { |
64 | seenOwnNotarisations++; | |
20c3ac51 | 65 | if (seenOwnNotarisations == 1) |
e4f943d8 | 66 | destNotarisationTxid = nota.first; |
469bd637 SS |
67 | else if (seenOwnNotarisations == 2) |
68 | goto end; | |
69 | break; | |
70 | } | |
71 | } | |
72 | ||
73 | if (seenOwnNotarisations == 1) { | |
74 | BOOST_FOREACH(Notarisation& nota, notarisations) { | |
75 | if (nota.second.ccId == targetCCid) | |
76 | moms.push_back(nota.second.MoM); | |
20c3ac51 | 77 | } |
20c3ac51 SS |
78 | } |
79 | } | |
80 | ||
81 | end: | |
82 | return GetMerkleRoot(moms); | |
83 | } | |
84 | ||
85 | ||
7f3cc8a2 SS |
86 | /* |
87 | * Get a notarisation from a given height | |
88 | * | |
89 | * Will scan notarisations leveldb up to a limit | |
90 | */ | |
91 | template <typename IsTarget> | |
92 | int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found) | |
93 | { | |
94 | int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height()); | |
95 | ||
96 | for (int h=nHeight; h<limit; h++) { | |
97 | NotarisationsInBlock notarisations; | |
84964112 SS |
98 | |
99 | if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations)) | |
7f3cc8a2 SS |
100 | continue; |
101 | ||
102 | BOOST_FOREACH(found, notarisations) { | |
103 | if (f(found)) { | |
104 | return h; | |
105 | } | |
106 | } | |
107 | } | |
108 | return 0; | |
109 | } | |
110 | ||
111 | ||
20c3ac51 | 112 | /* On KMD */ |
e4f943d8 SS |
113 | TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid, |
114 | const TxProof assetChainProof) | |
20c3ac51 SS |
115 | { |
116 | /* | |
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). | |
121 | */ | |
122 | EvalRef eval; | |
e4f943d8 | 123 | uint256 MoM = assetChainProof.second.Exec(txid); |
20c3ac51 SS |
124 | |
125 | // Get a kmd height for given notarisation Txid | |
126 | int kmdHeight; | |
127 | { | |
128 | CTransaction sourceNotarisation; | |
129 | uint256 hashBlock; | |
130 | CBlockIndex blockIdx; | |
469bd637 | 131 | if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx)) |
20c3ac51 | 132 | throw std::runtime_error("Notarisation not found"); |
469bd637 | 133 | kmdHeight = blockIdx.nHeight; |
20c3ac51 SS |
134 | } |
135 | ||
469bd637 SS |
136 | // We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists |
137 | // at that height. | |
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. | |
142 | Notarisation nota; | |
143 | auto isTarget = [&](Notarisation ¬a) { | |
144 | return strcmp(nota.second.symbol, targetSymbol) == 0; | |
145 | }; | |
146 | kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota); | |
147 | if (!kmdHeight) | |
148 | throw std::runtime_error("Cannot find notarisation for target inclusive of source"); | |
149 | ||
20c3ac51 SS |
150 | // Get MoMs for kmd height and symbol |
151 | std::vector<uint256> moms; | |
e4f943d8 SS |
152 | uint256 targetChainNotarisationTxid; |
153 | uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid); | |
20c3ac51 SS |
154 | if (MoMoM.IsNull()) |
155 | throw std::runtime_error("No MoMs found"); | |
156 | ||
157 | // Find index of source MoM in MoMoM | |
158 | int nIndex; | |
06c960d2 | 159 | for (nIndex=0; nIndex<moms.size(); nIndex++) { |
20c3ac51 SS |
160 | if (moms[nIndex] == MoM) |
161 | goto cont; | |
06c960d2 | 162 | } |
20c3ac51 SS |
163 | throw std::runtime_error("Couldn't find MoM within MoMoM set"); |
164 | cont: | |
165 | ||
166 | // Create a branch | |
e4f943d8 | 167 | std::vector<uint256> vBranch; |
20c3ac51 SS |
168 | { |
169 | CBlock fakeBlock; | |
170 | for (int i=0; i<moms.size(); i++) { | |
171 | CTransaction fakeTx; | |
172 | // first value in CTransaction memory is it's hash | |
173 | memcpy((void*)&fakeTx, moms[i].begin(), 32); | |
174 | fakeBlock.vtx.push_back(fakeTx); | |
175 | } | |
e4f943d8 | 176 | vBranch = fakeBlock.GetMerkleBranch(nIndex); |
20c3ac51 SS |
177 | } |
178 | ||
179 | // Concatenate branches | |
e4f943d8 SS |
180 | MerkleBranch newBranch = assetChainProof.second; |
181 | newBranch << MerkleBranch(nIndex, vBranch); | |
20c3ac51 SS |
182 | |
183 | // Check proof | |
e4f943d8 | 184 | if (newBranch.Exec(txid) != MoMoM) |
20c3ac51 SS |
185 | throw std::runtime_error("Proof check failed"); |
186 | ||
e4f943d8 | 187 | return std::make_pair(targetChainNotarisationTxid,newBranch); |
20c3ac51 SS |
188 | } |
189 | ||
190 | ||
0b485d3c SS |
191 | /* |
192 | * Takes an importTx that has proof leading to assetchain root | |
193 | * and extends proof to cross chain root | |
194 | */ | |
195 | void CompleteImportTransaction(CTransaction &importTx) | |
196 | { | |
197 | TxProof proof; | |
198 | CTransaction burnTx; | |
199 | std::vector<CTxOut> payouts; | |
200 | if (!UnmarshalImportTx(importTx, proof, burnTx, payouts)) | |
201 | throw std::runtime_error("Couldn't parse importTx"); | |
202 | ||
203 | std::string targetSymbol; | |
204 | uint32_t targetCCid; | |
205 | uint256 payoutsHash; | |
206 | if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash)) | |
207 | throw std::runtime_error("Couldn't parse burnTx"); | |
208 | ||
209 | proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof); | |
210 | ||
89cfc427 | 211 | importTx = MakeImportCoinTransaction(proof, burnTx, payouts); |
0b485d3c SS |
212 | } |
213 | ||
214 | ||
7f3cc8a2 SS |
215 | bool IsSameAssetChain(const Notarisation ¬a) { |
216 | return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0; | |
217 | }; | |
e4f943d8 SS |
218 | |
219 | ||
20c3ac51 | 220 | /* On assetchain */ |
7f3cc8a2 | 221 | bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out) |
20c3ac51 SS |
222 | { |
223 | /* | |
e4f943d8 SS |
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. | |
20c3ac51 | 227 | */ |
e4f943d8 SS |
228 | Notarisation bn; |
229 | if (!GetBackNotarisation(kmdNotarisationTxid, bn)) | |
230 | return false; | |
20c3ac51 | 231 | |
36f8002f SS |
232 | // Need to get block height of that backnotarisation |
233 | EvalRef eval; | |
234 | CBlockIndex block; | |
235 | CTransaction tx; | |
236 | if (!eval->GetTxConfirmed(bn.first, tx, block)){ | |
237 | fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n"); | |
238 | return false; | |
239 | } | |
240 | ||
241 | return (bool) ScanNotarisationsFromHeight(block.nHeight+1, &IsSameAssetChain, out); | |
e4f943d8 | 242 | } |
20c3ac51 SS |
243 | |
244 | ||
20c3ac51 SS |
245 | /* |
246 | * On assetchain | |
247 | * in: txid | |
248 | * out: pair<notarisationTxHash,merkleBranch> | |
249 | */ | |
e4f943d8 | 250 | TxProof GetAssetchainProof(uint256 hash) |
20c3ac51 | 251 | { |
7f3cc8a2 | 252 | int nIndex; |
06c960d2 | 253 | CBlockIndex* blockIndex; |
7f3cc8a2 | 254 | Notarisation nota; |
20c3ac51 | 255 | std::vector<uint256> branch; |
20c3ac51 SS |
256 | |
257 | { | |
258 | uint256 blockHash; | |
259 | CTransaction tx; | |
260 | if (!GetTransaction(hash, tx, blockHash, true)) | |
261 | throw std::runtime_error("cannot find transaction"); | |
262 | ||
91d92922 SS |
263 | if (blockHash.IsNull()) |
264 | throw std::runtime_error("tx still in mempool"); | |
265 | ||
20c3ac51 | 266 | blockIndex = mapBlockIndex[blockHash]; |
469bd637 SS |
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; | |
274 | }; | |
275 | if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota)) | |
87d08c5a | 276 | throw std::runtime_error("backnotarisation not yet confirmed"); |
20c3ac51 SS |
277 | |
278 | // index of block in MoM leaves | |
7f3cc8a2 | 279 | nIndex = nota.second.height - blockIndex->nHeight; |
20c3ac51 SS |
280 | } |
281 | ||
282 | // build merkle chain from blocks to MoM | |
283 | { | |
c7bcf05d | 284 | std::vector<uint256> leaves, tree; |
7f3cc8a2 SS |
285 | for (int i=0; i<nota.second.MoMDepth; i++) { |
286 | uint256 mRoot = chainActive[nota.second.height - i]->hashMerkleRoot; | |
c7bcf05d | 287 | leaves.push_back(mRoot); |
20c3ac51 | 288 | } |
c7bcf05d SS |
289 | bool fMutated; |
290 | BuildMerkleTree(&fMutated, leaves, tree); | |
291 | branch = GetMerkleBranch(nIndex, leaves.size(), tree); | |
20c3ac51 SS |
292 | |
293 | // Check branch | |
c7bcf05d | 294 | uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex); |
7f3cc8a2 | 295 | if (nota.second.MoM != ourResult) |
20c3ac51 SS |
296 | throw std::runtime_error("Failed merkle block->MoM"); |
297 | } | |
298 | ||
299 | // Now get the tx merkle branch | |
300 | { | |
301 | CBlock block; | |
302 | ||
303 | if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0) | |
304 | throw std::runtime_error("Block not available (pruned data)"); | |
305 | ||
306 | if(!ReadBlockFromDisk(block, blockIndex,1)) | |
307 | throw std::runtime_error("Can't read block from disk"); | |
308 | ||
309 | // Locate the transaction in the block | |
310 | int nTxIndex; | |
311 | for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++) | |
312 | if (block.vtx[nTxIndex].GetHash() == hash) | |
313 | break; | |
314 | ||
315 | if (nTxIndex == (int)block.vtx.size()) | |
316 | throw std::runtime_error("Error locating tx in block"); | |
317 | ||
318 | std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex); | |
319 | ||
320 | // Check branch | |
321 | if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex)) | |
322 | throw std::runtime_error("Failed merkle tx->block"); | |
323 | ||
324 | // concatenate branches | |
325 | nIndex = (nIndex << txBranch.size()) + nTxIndex; | |
326 | branch.insert(branch.begin(), txBranch.begin(), txBranch.end()); | |
327 | } | |
328 | ||
329 | // Check the proof | |
7f3cc8a2 | 330 | if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) |
20c3ac51 SS |
331 | throw std::runtime_error("Failed validating MoM"); |
332 | ||
333 | // All done! | |
334 | CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); | |
7f3cc8a2 | 335 | return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch)); |
20c3ac51 | 336 | } |