]> Git Repo - VerusCoin.git/blame - src/crosschain.cpp
Fix exception
[VerusCoin.git] / src / crosschain.cpp
CommitLineData
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
27int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
28
0b485d3c 29
20c3ac51 30/* On KMD */
e4f943d8
SS
31uint256 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
81end:
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 */
91template <typename IsTarget>
92int 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
113TxProof 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 &nota) {
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");
164cont:
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 */
195void 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
215bool IsSameAssetChain(const Notarisation &nota) {
216 return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
217};
e4f943d8
SS
218
219
20c3ac51 220/* On assetchain */
7f3cc8a2 221bool 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 250TxProof 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 &nota) {
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}
This page took 0.071648 seconds and 4 git commands to generate.