All files / src/validator/periods submitPeriod.js

100% Statements 41/41
100% Branches 13/13
80% Functions 4/5
100% Lines 40/40

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 106              2x 2x 2x 2x 2x   2x 9x 9x 9x 9x   9x             9x 1x   1x     8x   8x 1x 1x     7x   7x 1x       7x 2x 2x       5x           5x 1x     1x     4x 4x 4x   4x 1x       1x     3x                         3x   1x 1x     1x             3x    
/**
 * 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 { getSlotsByAddr, sendTransaction, buildCas } = require('../../utils');
const { logPeriod } = require('../../utils/debug');
const checkEnoughVotes = require('./checkEnoughVotes');
const submitPeriodVote = require('./submitPeriodVote');
const isAlreadyVoted = require('./isAlreadyVoted');
 
module.exports = async (periodProposal, bridgeState, opts = {}) => {
  const defaultResponse = { receiptPromise: Promise.resolve() };
  const { proposerSlotId, blocksRoot, prevPeriodRoot } = periodProposal;
  const { currentState } = bridgeState;
  const { slots } = currentState;
 
  logPeriod(
    '[submitPeriod] proposerSlot:%d blocksRoot:%s prevPeriodRoot:%s',
    proposerSlotId,
    blocksRoot,
    prevPeriodRoot
  );
 
  if (await bridgeState.db.getPeriodDataByBlocksRoot(blocksRoot)) {
    logPeriod('[submitPeriod] already seen onchain', blocksRoot);
 
    return { receiptPromise: Promise.resolve({ status: true }) };
  }
 
  const mySlots = getSlotsByAddr(slots, bridgeState.account.address);
 
  if (!mySlots.length) {
    logPeriod('[submitPeriod] No slots. Not a validator');
    return defaultResponse;
  }
 
  const mySlotToSubmit = mySlots.find(slot => slot.id === proposerSlotId);
 
  if (!isAlreadyVoted(blocksRoot, mySlots[0].id, periodProposal)) {
    await submitPeriodVote(blocksRoot, periodProposal, bridgeState);
  }
 
  // check if it is our turn to submit period
  if (!mySlotToSubmit) {
    logPeriod('[submitPeriod] Not a proposer. Skipping');
    return defaultResponse;
  }
 
  // check if we have enough period votes for submission
  const { result, votes, needed } = checkEnoughVotes(
    blocksRoot,
    periodProposal,
    bridgeState.currentState.slots
  );
 
  if (!result) {
    logPeriod(
      `[submitPeriod] Not enough period votes collected: ${votes}/${needed}. Waiting..`
    );
    return defaultResponse;
  }
 
  const quorumOfVotes = periodProposal.votes.slice(0, needed);
  const cas = buildCas(quorumOfVotes);
  logPeriod('[submitPeriod] CAS:%s', `0x${cas.toString(16)}`);
 
  if (periodProposal.txHash) {
    logPeriod(
      '[submitPeriod] Already submitted. txHash: %s',
      periodProposal.txHash
    );
    return defaultResponse;
  }
 
  const { receiptPromise } = await sendTransaction(
    bridgeState.web3,
    bridgeState.operatorContract.methods.submitPeriodWithCas(
      mySlotToSubmit.id,
      prevPeriodRoot,
      blocksRoot,
      `0x${cas.toString(16)}`
    ),
    bridgeState.operatorContract.options.address,
    bridgeState.account,
    opts
  );
 
  receiptPromise
    .on('transactionHash', hash => {
      logPeriod('[submitPeriod] txHash: ', hash);
      periodProposal.txHash = hash;
    })
    .then(receipt => {
      logPeriod('[submitPeriod] Receipt', receipt);
    })
    .catch(e => {
      // istanbul ignore next
      logPeriod('[submitPeriod] Error', e);
    });
 
  return { receiptPromise };
};