LiskArchive / lisk-sdk

🔩 Lisk software development kit
https://lisk.com
Apache License 2.0
2.72k stars 454 forks source link

Add utility function getMinFee to lisk-transactions library #5673

Closed shuse2 closed 3 years ago

shuse2 commented 4 years ago

Description

Add utility function calculateMinFee and getMinFee to the lisk-transactions library, which can be used by UI/Service to calculate the minimum fee required to be shown to the end-users.

Motivation

Currently, lisk-transactions has no utility function to calculate minimum fee for a given transaction with fee set to 0.

Acceptance Criteria

janhack commented 4 years ago

One utility function could be a function that enables computing the required minimum fee for a transaction where the fee property is not set. The challenge here is that the minimum required fee of a transaction depends on the transaction size, which again depends on the value of the fee property of the transaction. Therefore, it is not straightforward to precompute the exact minimum required fee.

Current Approach

We overestimate the size of the fee property and use this approximation to find a sufficient minimum fee, which can be slightly above the minimum required transaction fee. More precisely, we do the following:

  1. Let trs be the transaction for which the minimum fee has to be computed.
  2. Let k be the number of signatures required by the transaction (k=1 for non-multisignature accounts and k=numberOfSignaturesfor multisignature account). Then set the signatures property of trs to an array of k bytes of size 64 (e.g., zero bytes).
  3. Set the fee property of trs to 2^64-1 (max unsigned int64)
  4. Compute trs.size()*minFeePerByte+trs.getNameFee() where trs.size() is the size of the transaction with the changed fee and signatures property.

Main shortcomings

Technical Details

Alternative approach

We compute the exact minimum required fee. One approach is the following:

Do 1. and 2. as above

  1. Call the function findRequiredMinFee on the transaction with trs.fee initialized with 0.
computeMinFee(trs):
    return trs.size()*minFeePerByte+trs.getNameFee()

findRequiredMinFee(trs):
    minFee=trs.computeMinFee()
    while minFee > trs.getFee():
        trs.setFee(minFee)
        minFee=trs.computeMinFee()

The idea is to use an iteration that iteratively computes the transaction size and then sets the fee of the transaction using that size. Note that both the transaction size and minimum fee are monotonically increasing in the function. Observe the following:

ManuGowda commented 3 years ago
getMinFee(
  trx: Record<string, unknown>, 
  assetSchema: Schema, 
  { minFeePerByte: number, baseFees: [{moduleId: number, assetId: number, baseFee: string}]}
): bigint