]> Git Repo - VerusCoin.git/blobdiff - src/crosschain.cpp
Incorporate all Zcash updates through 2.0.7-3 in addition to PBaaS, reserves, etc.
[VerusCoin.git] / src / crosschain.cpp
index 069a53f38b0c707b90964db0c9a30589c06f89c8..60840e140eb8e49b6fa456f1e6f8ea99b2227da1 100644 (file)
@@ -1,46 +1,83 @@
 #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);
         }
     }
 
@@ -49,9 +86,35 @@ end:
 }
 
 
+/*
+ * 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
@@ -60,7 +123,7 @@ std::pair<uint256,MerkleBranch> GetCrossChainProof(uint256 txid, char* targetSym
      * 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;
@@ -68,31 +131,43 @@ std::pair<uint256,MerkleBranch> GetCrossChainProof(uint256 txid, char* targetSym
         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 &nota) {
+        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++) {
@@ -101,45 +176,86 @@ cont:
             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 &nota) {
+    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;
@@ -147,33 +263,39 @@ std::pair<uint256,MerkleBranch> GetAssetchainProof(uint256 hash)
         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(&notarisedHeight, &MoM, &notarisationHash, 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 &nota) {
+            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");
     }
 
@@ -184,7 +306,7 @@ std::pair<uint256,MerkleBranch> GetAssetchainProof(uint256 hash)
         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
@@ -208,10 +330,10 @@ std::pair<uint256,MerkleBranch> GetAssetchainProof(uint256 hash)
     }
 
     // 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));
 }
This page took 0.036769 seconds and 4 git commands to generate.