All files / src/period index.js

100% Statements 49/49
100% Branches 16/16
100% Functions 3/3
100% Lines 48/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105              1x 1x 1x   1x 7x 7x     15x 15x   15x   1x 1x 1x     14x   14x 1x 1x 1x     13x   13x 13x   13x   13x     1x 1x 1x 1x 1x     12x 1x     1x   11x 11x   11x   4x     4x             7x   7x   3x     3x 3x 4x   3x     3x           1x     1x 1x       10x   10x    
/**
 * Copyright (c) 2018-present, Leap DAO (leapdao.org)
 *
 * This source code is licensed under the Mozilla Public License Version 2.0
 * found in the LICENSE file in the root directory of this source tree.
 */
 
const submitPeriod = require('../validator/periods/submitPeriod');
const { logPeriod } = require('../utils/debug');
const getActiveSlots = require('../utils/getActiveSlots');
 
const getNextSlotToProposeFrom = (periodProposal, bridgeState) => {
  const activeSlots = getActiveSlots(bridgeState.currentState.slots);
  return (periodProposal.proposerSlotId + 1) % activeSlots.length;
};
 
module.exports = bridgeState => async (rsp, state, chainInfo) => {
  const height = chainInfo.height - 32;
 
  if (height <= 0) {
    // genesis height doesn't need check
    logPeriod('[checkBridge] Genesis period');
    rsp.status = 1;
    return;
  }
 
  const periodProposal = bridgeState.stalePeriodProposal;
 
  if (!periodProposal) {
    logPeriod('[checkBridge] No dragging period submission');
    rsp.status = 1;
    return;
  }
 
  logPeriod('[checkBridge] Pending period submission', periodProposal);
 
  rsp.status = 0;
  bridgeState.checkCallsCount += 1;
 
  const { txHash } = periodProposal;
 
  if (
    await bridgeState.db.getPeriodDataByBlocksRoot(periodProposal.blocksRoot)
  ) {
    logPeriod('[checkBridge] Found successful submission tx');
    bridgeState.stalePeriodProposal = null;
    bridgeState.db.setStalePeriodProposal(null);
    rsp.status = 1;
    return;
  }
 
  if (bridgeState.checkCallsCount > 1 && bridgeState.checkCallsCount < 10) {
    logPeriod(
      `[checkBridge] Waiting for slot ${periodProposal.proposerSlotId} to finish submission`
    );
    return;
  }
  bridgeState.checkCallsCount = 1;
  const txOpts = {};
 
  if (!txHash) {
    // proposer didn't submit period on time, assign another proposer slot
    logPeriod(
      '[checkBridge] No submission from proposer on time. Submitting from a next slot'
    );
    periodProposal.proposerSlotId = getNextSlotToProposeFrom(
      periodProposal,
      bridgeState
    );
  } else {
    // we have txHash in proposal, so submission is either failed or
    // stuck in a mempool (underpriced?)
    const receipt = await bridgeState.web3.eth.getTransactionReceipt(txHash);
 
    if (!receipt) {
      // in a mempool
      logPeriod(
        '[checkBridge] No receipt yet, probably stuck in a mempool as underpriced. Resubmitting'
      );
      const tx = await bridgeState.web3.eth.getTransaction(txHash);
      txOpts.nonce = tx.nonce;
    } else if (!receipt.status) {
      // status = 0, tx failed
      logPeriod(
        '[checkBridge] Found failed submission. Resubmitting from the next slot'
      );
      periodProposal.proposerSlotId = getNextSlotToProposeFrom(
        periodProposal,
        bridgeState
      );
    } else {
      // status = 1, tx succeed
      logPeriod(
        '[checkBridge] Found successful submission tx. Waiting for Submission event to arrive..'
      );
      rsp.status = 0;
      return;
    }
  }
 
  bridgeState.db.setStalePeriodProposal(periodProposal);
 
  await submitPeriod(periodProposal, bridgeState, txOpts);
};