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:
exec_fee_factor to define CheckMultisig price:
|
AddGas(CheckSigPrice * n * exec_fee_factor); |
exec_fee_factor to define syscall price:
|
AddGas(descriptor.FixedPrice * exec_fee_factor); |
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:
- 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:
Describe the bug
ApplicationEngineclass has two fields to define ExecFeeFactor and StoragePrice -exec_fee_factorandStoragePricecorrespondingly:neo/src/neo/SmartContract/ApplicationEngine.cs
Lines 61 to 62 in 41055be
Once a new instance of
ApplicationEngineis created, the initial values of these fields are stored and being used to calculate syscall, opcodes and interops prices:neo/src/neo/SmartContract/ApplicationEngine.cs
Lines 156 to 157 in 41055be
Consider the case of block processing: for each transaction in block the new instance of
ApplicationEngineis created withexec_fee_factorandStoragePricegot from the cloned snapshot:neo/src/neo/Ledger/Blockchain.cs
Line 413 in 41055be
However, within a single transaction processing, we can see that these cached values are used to define syscall prices:
neo/src/neo/SmartContract/ApplicationEngine.cs
Line 482 in 41055be
and at the same time the fresh value stored in Policy contract is used to define price for native call:
neo/src/neo/SmartContract/Native/NativeContract.cs
Line 201 in 41055be
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_factorandStoragePricevalues from ApplicationEngine:exec_fee_factorto define CheckMultisig price:neo/src/neo/SmartContract/ApplicationEngine.Crypto.cs
Line 68 in 41055be
exec_fee_factorto define syscall price:neo/src/neo/SmartContract/ApplicationEngine.cs
Line 482 in 41055be
exec_fee_factorto define VM opcode price:neo/src/neo/SmartContract/ApplicationEngine.cs
Line 496 in 41055be
An example of usage of fresh
ExecFeeFactorandStoragePricevalues provided by Policy native contract:neo/src/neo/SmartContract/Native/NativeContract.cs
Line 201 in 41055be
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: