Commit 0e96b4a6 authored by Cédric Moreau's avatar Cédric Moreau
Browse files

[fix] #1242 Set the depth limit for a chain of transactions to 5

parent 080f32dd
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ test/integration/documents-currency.d.ts
test/integration/forwarding.js
test/integration/branches_switch.js
test/integration/branches2.js
test/integration/transactions-chaining.js
test/fast/modules/crawler/block_pulling.js*
test/fast/modules/crawler/block_pulling.d.ts
test/fast/fork*.js*
@@ -72,3 +73,6 @@ test/fast/modules/common/grammar.d.ts
test/fast/prover/pow-1-cluster.d.ts
test/fast/prover/pow-1-cluster.js
test/fast/prover/pow-1-cluster.js.map
test/fast/protocol-local-rule-chained-tx-depth.js
test/fast/protocol-local-rule-chained-tx-depth.js.map
test/fast/protocol-local-rule-chained-tx-depth.d.ts
+2 −0
Original line number Diff line number Diff line
@@ -285,6 +285,8 @@ export const CommonConstants = {
    BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"),
    SPECIAL_BLOCK
  },

  BLOCK_MAX_TX_CHAINING_DEPTH: 5
}

function exact (regexpContent:string) {
+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ export const ALIAS = {
    await LOCAL_RULES_FUNCTIONS.checkTxRecipients(block);
    await LOCAL_RULES_FUNCTIONS.checkTxAmounts(block);
    await LOCAL_RULES_FUNCTIONS.checkTxSignature(block);
    await LOCAL_RULES_FUNCTIONS.checkMaxTransactionChainingDepth(block, conf, index);
  },

  ALL_LOCAL_BUT_POW_AND_SIGNATURE: async (block:BlockDTO, conf:ConfDTO, index:IndexEntry[]) => {
@@ -60,6 +61,7 @@ export const ALIAS = {
    await LOCAL_RULES_FUNCTIONS.checkTxRecipients(block);
    await LOCAL_RULES_FUNCTIONS.checkTxAmounts(block);
    await LOCAL_RULES_FUNCTIONS.checkTxSignature(block);
    await LOCAL_RULES_FUNCTIONS.checkMaxTransactionChainingDepth(block, conf, index);
  }
}

+62 −16
Original line number Diff line number Diff line
@@ -379,17 +379,61 @@ export const LOCAL_RULES_FUNCTIONS = {
      }
    }
    return true;
  },

  checkMaxTransactionChainingDepth: async (block:BlockDTO, conf:ConfDTO, index:IndexEntry[]) => {
    const sindex = Indexer.sindex(index)
    const max = getMaxTransactionDepth(sindex)
    //
    const allowedMax = block.medianTime > 1517443200 ? CommonConstants.BLOCK_MAX_TX_CHAINING_DEPTH : 1
    if (max > allowedMax) {
      throw "The maximum transaction chaining length per block is " + CommonConstants.BLOCK_MAX_TX_CHAINING_DEPTH
    }
    return true
  }
}

export interface SindexShortEntry {
  op:string,
  identifier:string,
  pos:number,
  tx:string|null
}

function getMaxTransactionDepth(sindex:SindexShortEntry[]) {
  const ids = _.uniq(_.pluck(sindex, 'tx'))
  let maxTxChainingDepth = 0
  for (let id of ids) {
    maxTxChainingDepth = Math.max(maxTxChainingDepth, getTransactionDepth(id, sindex, 0))
  }
  return maxTxChainingDepth
}

function getTransactionDepth(txHash:string, sindex:SindexShortEntry[], localDepth = 0) {
  const inputs = _.filter(sindex, (s:SindexShortEntry) => s.op === 'UPDATE' && s.tx === txHash)
  let depth = localDepth
  for (let input of inputs) {
    const consumedOutput = _.findWhere(sindex, { op: 'CREATE', identifier: input.identifier, pos: input.pos })
    if (consumedOutput) {
      if (localDepth < 5) {
        const subTxDepth = getTransactionDepth(consumedOutput.tx, sindex, localDepth + 1)
        depth = Math.max(depth, subTxDepth)
      } else {
        depth++
      }
    }
  }
  return depth
}

function checkSingleMembershipSignature(ms:any) {
  return verify(ms.getRaw(), ms.signature, ms.issuer);
}

function checkBunchOfTransactions(transactions:TransactionDTO[], done:any = undefined){
  const block:any = { transactions };
function checkBunchOfTransactions(transactions:TransactionDTO[], conf:ConfDTO, options?:{ dontCareAboutChaining?:boolean }){
  const block:any = { transactions, identities: [], joiners: [], actives: [], leavers: [], revoked: [], excluded: [], certifications: [] };
  const index = Indexer.localIndex(block, conf)
  return (async () => {
    try {
    let local_rule = LOCAL_RULES_FUNCTIONS;
    await local_rule.checkTxLen(block);
    await local_rule.checkTxIssuers(block);
@@ -397,10 +441,8 @@ function checkBunchOfTransactions(transactions:TransactionDTO[], done:any = unde
    await local_rule.checkTxRecipients(block);
    await local_rule.checkTxAmounts(block);
    await local_rule.checkTxSignature(block);
      done && done();
    } catch (err) {
      if (done) return done(err);
      throw err;
    if (!options || !options.dontCareAboutChaining) {
      await local_rule.checkMaxTransactionChainingDepth(block, conf, index);
    }
  })()
}
@@ -411,9 +453,13 @@ export const LOCAL_RULES_HELPERS = {

  checkSingleMembershipSignature: checkSingleMembershipSignature,

  checkBunchOfTransactions: checkBunchOfTransactions,
  checkBunchOfTransactions,

  getTransactionDepth,

  getMaxTransactionDepth,

  checkSingleTransactionLocally: (tx:any, done:any = undefined) => checkBunchOfTransactions([tx], done),
  checkSingleTransactionLocally: (tx:any, conf:ConfDTO) => checkBunchOfTransactions([tx], conf),

  checkTxAmountsValidity: (tx:TransactionDTO) => {
    const inputs = tx.inputsAsObjects()
+5 −9
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ export class BlockGenerator {
    const wereExcludeds = await this.dal.getRevokedPubkeys();
    const newCertsFromWoT = await generator.findNewCertsFromWoT(current);
    const newcomersLeavers = await this.findNewcomersAndLeavers(current, (joinersData:any) => generator.filterJoiners(joinersData));
    const transactions = await this.findTransactions(current);
    const transactions = await this.findTransactions(current, manualValues);
    const joinData = newcomersLeavers[2];
    const leaveData = newcomersLeavers[3];
    const newCertsFromNewcomers = newcomersLeavers[4];
@@ -104,7 +104,8 @@ export class BlockGenerator {
    return [cur, newWoTMembers, finalJoinData, leavers, updates];
  }

  private async findTransactions(current:DBBlock) {
  private async findTransactions(current:DBBlock, options:{ dontCareAboutChaining?:boolean }) {
    const ALSO_CHECK_PENDING_TXS = true
    const versionMin = current ? Math.min(CommonConstants.LAST_VERSION_FOR_TX, current.version) : CommonConstants.DOCUMENTS_VERSION;
    const txs = await this.dal.getTransactionsPending(versionMin);
    const transactions = [];
@@ -113,14 +114,9 @@ export class BlockGenerator {
      obj.currency = this.conf.currency
      const tx = TransactionDTO.fromJSONObject(obj);
      try {
        await new Promise((resolve, reject) => {
          LOCAL_RULES_HELPERS.checkBunchOfTransactions(passingTxs.concat(tx), (err:any, res:any) => {
            if (err) return reject(err)
            return resolve(res)
          })
        })
        await LOCAL_RULES_HELPERS.checkBunchOfTransactions(passingTxs.concat(tx), this.conf, options)
        const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
        await GLOBAL_RULES_HELPERS.checkSingleTransaction(tx, nextBlockWithFakeTimeVariation, this.conf, this.dal);
        await GLOBAL_RULES_HELPERS.checkSingleTransaction(tx, nextBlockWithFakeTimeVariation, this.conf, this.dal, ALSO_CHECK_PENDING_TXS);
        await GLOBAL_RULES_HELPERS.checkTxBlockStamp(tx, this.dal);
        transactions.push(tx);
        passingTxs.push(tx);
Loading