Skip to content

ExecFeeFactor and StoragePrice inconsistent usages #2690

@AnnaShaleva

Description

@AnnaShaleva

Describe the bug

ApplicationEngine class has two fields to define ExecFeeFactor and StoragePrice - exec_fee_factor and StoragePrice correspondingly:

private readonly uint exec_fee_factor;
internal readonly uint StoragePrice;

Once a new instance of ApplicationEngine is created, the initial values of these fields are stored and being used to calculate syscall, opcodes and interops prices:
this.exec_fee_factor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot);
this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot);

Consider the case of block processing: for each transaction in block the new instance of ApplicationEngine is created with exec_fee_factor and StoragePrice got from the cloned snapshot:

using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee);

However, within a single transaction processing, we can see that these cached values are used to define syscall prices:
AddGas(descriptor.FixedPrice * exec_fee_factor);

and at the same time the fresh value stored in Policy contract is used to define price for native call:
engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot));

It's OK for those transactions that do not change BaseExecFee or StoragePrice values within the transaction invocation. However, in case if a single transaction firstly changes BaseExecFee/StoragePrice and after that performs additional actions, the further prices are inconsistently calculated (some of them are calculated using the old cached value provided by ApplicationEngine, and some of them use the newly set value right from the Policy contract).

To Reproduce
An example of usages of cached exec_fee_factor and StoragePrice values from ApplicationEngine:

  1. exec_fee_factor to define CheckMultisig price:
    AddGas(CheckSigPrice * n * exec_fee_factor);
  2. exec_fee_factor to define syscall price:
    AddGas(descriptor.FixedPrice * exec_fee_factor);
  3. exec_fee_factor to define VM opcode price:
    AddGas(exec_fee_factor * OpCodePrices[CurrentContext.CurrentInstruction.OpCode]);

An example of usage of fresh ExecFeeFactor and StoragePrice values provided by Policy native contract:

  1. Price for native contract call:
    engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot));

Expected behavior

Usages of ExecFeeFactor and StoragePrice should be unified within a single transaction invocation.
I.e. we should use either values that were cached before the transaction invocation within the whole invocation process
or we should use the freshest values from native Policy contract within a single transaction invocation, not both.

Platform:

  • Version: neo v3.2.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions