Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,11 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data
return VerifyResult.InvalidAttribute;
attributesFee += attribute.CalculateNetworkFee(snapshot, this);
}
long netFeeDatoshi = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee;
var netFeeDatoshi = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee;
if (netFeeDatoshi < 0) return VerifyResult.InsufficientFunds;

if (netFeeDatoshi > MaxVerificationGas) netFeeDatoshi = MaxVerificationGas;
uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot);
var execFeeFactor = NativeContract.Policy.GetExecFeeFactor(settings, snapshot, height);
for (int i = 0; i < hashes.Length; i++)
{
if (IsSignatureContract(Witnesses[i].VerificationScript.Span) && IsSingleSignatureInvocationScript(Witnesses[i].InvocationScript, out var _))
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/ApplicationEngine.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ internal protected UInt160 CreateStandardAccount(ECPoint pubKey)
long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone)
? CheckSigPrice
: 1 << 8;
AddFee(fee * ExecFeeFactor);
AddFee(fee * _execFeeFactor);
return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash();
}

Expand All @@ -141,7 +141,7 @@ internal protected UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys)
long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone)
? CheckSigPrice * pubKeys.Length
: 1 << 8;
AddFee(fee * ExecFeeFactor);
AddFee(fee * _execFeeFactor);
return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/ApplicationEngine.Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures)
if (n == 0) throw new ArgumentException("pubkeys array cannot be empty.");
if (m == 0) throw new ArgumentException("signatures array cannot be empty.");
if (m > n) throw new ArgumentException($"signatures count ({m}) cannot be greater than pubkeys count ({n}).");
AddFee(CheckSigPrice * n * ExecFeeFactor);
AddFee(CheckSigPrice * n * _execFeeFactor);
try
{
for (int i = 0, j = 0; i < m && j < n;)
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/ApplicationEngine.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ protected internal BigInteger GetRandom()
buffer = nonceData = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network);
price = 1 << 4;
}
AddFee(price * ExecFeeFactor);
AddFee(price * _execFeeFactor);
return new BigInteger(buffer, isUnsigned: true);
}

Expand Down Expand Up @@ -449,7 +449,7 @@ protected internal void BurnGas(long datoshi)
{
if (datoshi <= 0)
throw new InvalidOperationException("GAS must be positive.");
AddFee(datoshi);
AddFee(datoshi * FeeFactor);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/ApplicationEngine.Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ protected internal void Put(StorageContext context, byte[] key, byte[] value)
else
newDataSize = (item.Value.Length - 1) / 4 + 1 + value.Length - item.Value.Length;
}
AddFee(newDataSize * StoragePrice);
AddFee(newDataSize * StoragePrice * FeeFactor);

item.Value = value;
}
Expand Down
60 changes: 43 additions & 17 deletions src/Neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,19 @@ public partial class ApplicationEngine : ExecutionEngine

private static Dictionary<uint, InteropDescriptor>? services;
// Total amount of GAS spent to execute.
// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
private readonly long _feeAmount;
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
private readonly BigInteger _feeAmount;
private BigInteger _feeConsumed;
// Decimals for fee calculation
public const uint FeeFactor = 10000;
private Dictionary<Type, object>? states;
private readonly DataCache originalSnapshotCache;
private List<NotifyEventArgs>? notifications;
private List<IDisposable>? disposables;
private readonly Dictionary<UInt160, int> invocationCounter = new();
private readonly Dictionary<ExecutionContext, ContractTaskAwaiter> contractTasks = new();
internal readonly uint ExecFeeFactor;
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
private readonly BigInteger _execFeeFactor;
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
internal readonly uint StoragePrice;
private byte[] nonceData;
Expand Down Expand Up @@ -132,19 +136,29 @@ public partial class ApplicationEngine : ExecutionEngine
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
[Obsolete("This property is deprecated. Use FeeConsumed instead.")]
public long GasConsumed { get; protected set; } = 0;
public long GasConsumed => FeeConsumed;

/// <summary>
/// Exec Fee Factor. In the unit of datoshi, 1 datoshi = 1e-8 GAS
/// </summary>
internal long ExecFeeFactor => (long)_execFeeFactor.DivideCeiling(FeeFactor);

/// <summary>
/// GAS spent to execute.
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
public long FeeConsumed { get; protected set; } = 0;
public long FeeConsumed => (long)_feeConsumed.DivideCeiling(FeeFactor);

/// <summary>
/// Exec Fee Factor. In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
/// </summary>
internal BigInteger ExecFeePicoFactor => _execFeeFactor;

/// <summary>
/// The remaining GAS that can be spent in order to complete the execution.
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
public long GasLeft => _feeAmount - FeeConsumed;
public long GasLeft => (long)(_feeAmount - _feeConsumed).DivideCeiling(FeeFactor);

/// <summary>
/// The exception that caused the execution to terminate abnormally. This field could be <see langword="null"/> if no exception is thrown.
Expand Down Expand Up @@ -206,17 +220,31 @@ protected ApplicationEngine(
originalSnapshotCache = snapshotCache;
PersistingBlock = persistingBlock;
ProtocolSettings = settings;
_feeAmount = gas;
_feeAmount = gas * FeeFactor; // PicoGAS
Diagnostic = diagnostic;
nonceData = container is Transaction tx ? tx.Hash.ToArray()[..16] : new byte[16];
if (snapshotCache is null || persistingBlock?.Index == 0)
{
ExecFeeFactor = PolicyContract.DefaultExecFeeFactor;
_execFeeFactor = PolicyContract.DefaultExecFeeFactor * FeeFactor; // Add fee decimals
StoragePrice = PolicyContract.DefaultStoragePrice;
}
else
{
ExecFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshotCache);
var persistingIndex = persistingBlock?.Index ?? NativeContract.Ledger.CurrentIndex(snapshotCache);

if (settings == null || !settings.IsHardforkEnabled(Hardfork.HF_Faun, persistingIndex))
{
// The values doesn't have the decimals stored
_execFeeFactor = NativeContract.Policy.GetExecFeeFactor(this) * FeeFactor;
}
else
{
// The values have the decimals stored starting from OnPersist of Faun's block.
_execFeeFactor = NativeContract.Policy.GetExecPicoFeeFactor(this);
if (trigger == TriggerType.OnPersist && persistingIndex > 0 && !settings.IsHardforkEnabled(Hardfork.HF_Faun, persistingIndex - 1))
_execFeeFactor *= FeeFactor;
}

StoragePrice = NativeContract.Policy.GetStoragePrice(snapshotCache);
}

Expand Down Expand Up @@ -297,13 +325,11 @@ protected static void OnSysCall(ExecutionEngine engine, Instruction instruction)
/// <summary>
/// Adds GAS to <see cref="FeeConsumed"/> and checks if it has exceeded the maximum limit.
/// </summary>
/// <param name="datoshi">The amount of GAS, in the unit of datoshi, 1 datoshi = 1e-8 GAS, to be added.</param>
protected internal void AddFee(long datoshi)
/// <param name="picoGas">The amount of GAS, in the unit of picoGAS, 1 picoGAS = 1e-12 GAS, to be added.</param>
protected internal void AddFee(BigInteger picoGas)
{
#pragma warning disable CS0618 // Type or member is obsolete
FeeConsumed = GasConsumed = checked(FeeConsumed + datoshi);
#pragma warning restore CS0618 // Type or member is obsolete
if (FeeConsumed > _feeAmount)
_feeConsumed = _feeConsumed + picoGas;
if (_feeConsumed > _feeAmount)
throw new InvalidOperationException("Insufficient GAS.");
}

Expand Down Expand Up @@ -657,7 +683,7 @@ internal protected void ValidateCallFlags(CallFlags requiredCallFlags)
protected virtual void OnSysCall(InteropDescriptor descriptor)
{
ValidateCallFlags(descriptor.RequiredCallFlags);
AddFee(descriptor.FixedPrice * ExecFeeFactor);
AddFee(descriptor.FixedPrice * _execFeeFactor);

object?[] parameters = new object?[descriptor.Parameters.Count];
for (int i = 0; i < parameters.Length; i++)
Expand All @@ -671,7 +697,7 @@ protected virtual void OnSysCall(InteropDescriptor descriptor)
protected override void PreExecuteInstruction(Instruction instruction)
{
Diagnostic?.PreExecuteInstruction(instruction);
AddFee(ExecFeeFactor * OpCodePriceTable[(byte)instruction.OpCode]);
AddFee(_execFeeFactor * OpCodePriceTable[(byte)instruction.OpCode]);
}

protected override void PostExecuteInstruction(Instruction instruction)
Expand Down
10 changes: 5 additions & 5 deletions src/Neo/SmartContract/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,10 @@ private async ContractTask<ContractState> Deploy(ApplicationEngine engine, byte[
if (manifest.Length == 0)
throw new ArgumentException($"Manifest length cannot be zero.");

engine.AddFee(Math.Max(
engine.StoragePrice * (nefFile.Length + manifest.Length),
GetMinimumDeploymentFee(engine.SnapshotCache)
));
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(BigInteger.Max(engine.StoragePrice * (nefFile.Length + manifest.Length),
GetMinimumDeploymentFee(engine.SnapshotCache))
* ApplicationEngine.FeeFactor);

NefFile nef = nefFile.AsSerializable<NefFile>();
ContractManifest parsedManifest = ContractManifest.Parse(manifest);
Expand Down Expand Up @@ -336,7 +336,7 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man
if (nefFile is null && manifest is null)
throw new ArgumentException("NEF file and manifest cannot both be null.");

engine.AddFee(engine.StoragePrice * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0)));
engine.AddFee(engine.StoragePrice * ApplicationEngine.FeeFactor * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0)));

var contractState = engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_Contract, engine.CallingScriptHash!))
?? throw new InvalidOperationException($"Updating Contract Does Not Exist: {engine.CallingScriptHash}");
Expand Down
6 changes: 4 additions & 2 deletions src/Neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,10 @@ internal async void Invoke(ApplicationEngine engine, byte version)
var state = context.GetState<ExecutionContextState>();
if (!state.CallFlags.HasFlag(method.RequiredCallFlags))
throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}.");
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
engine.AddFee(method.CpuFee * engine.ExecFeeFactor + method.StorageFee * engine.StoragePrice);
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(
(method.CpuFee * engine.ExecFeePicoFactor) +
(method.StorageFee * engine.StoragePrice * ApplicationEngine.FeeFactor));
List<object?> parameters = new();
if (method.NeedApplicationEngine) parameters.Add(engine);
if (method.NeedSnapshot) parameters.Add(engine.SnapshotCache);
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/Native/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,8 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey)
if (!engine.IsHardforkEnabled(Hardfork.HF_Echidna) &&
!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()))
return false;
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
engine.AddFee(GetRegisterPrice(engine.SnapshotCache));
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(GetRegisterPrice(engine.SnapshotCache) * ApplicationEngine.FeeFactor);
return RegisterInternal(engine, pubkey);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/Native/OracleContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,10 @@ private async ContractTask Request(ApplicationEngine engine, string url, string?
if (gasForResponse < 0_10000000)
throw new ArgumentException($"gasForResponse {gasForResponse} must be at least 0.1 datoshi.");

engine.AddFee(GetPrice(engine.SnapshotCache));
engine.AddFee(GetPrice(engine.SnapshotCache) * ApplicationEngine.FeeFactor);

//Mint gas for the response
engine.AddFee(gasForResponse);
engine.AddFee(gasForResponse * ApplicationEngine.FeeFactor);
await GAS.Mint(engine, Hash, gasForResponse, false);

//Increase the request id
Expand Down
51 changes: 43 additions & 8 deletions src/Neo/SmartContract/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public sealed class PolicyContract : NativeContract
/// <summary>
/// The maximum execution fee factor that the committee can set.
/// </summary>
public const uint MaxExecFeeFactor = 100;
public const ulong MaxExecFeeFactor = 100;

/// <summary>
/// The maximum fee for attribute that the committee can set.
Expand Down Expand Up @@ -132,6 +132,16 @@ internal override ContractTask InitializeAsync(ApplicationEngine engine, Hardfor
engine.SnapshotCache.Add(_maxValidUntilBlockIncrement, new StorageItem(engine.ProtocolSettings.MaxValidUntilBlockIncrement));
engine.SnapshotCache.Add(_maxTraceableBlocks, new StorageItem(engine.ProtocolSettings.MaxTraceableBlocks));
}

// After Faun Hardfork the unit it's pico-gas, before it was datoshi

if (hardfork == Hardfork.HF_Faun)
{
// Add decimals to exec fee factor
var item = engine.SnapshotCache.TryGet(_execFeeFactor) ??
throw new InvalidOperationException("Policy was not initialized");
item.Set((uint)(BigInteger)item * ApplicationEngine.FeeFactor);
}
return ContractTask.CompletedTask;
}

Expand All @@ -149,12 +159,34 @@ public long GetFeePerByte(IReadOnlyStore snapshot)
/// <summary>
/// Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions.
/// </summary>
/// <param name="snapshot">The snapshot used to read data.</param>
/// <param name="engine">The execution engine.</param>
/// <returns>The execution fee factor.</returns>
[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)]
public uint GetExecFeeFactor(IReadOnlyStore snapshot)
public uint GetExecFeeFactor(ApplicationEngine engine)
{
if (engine.IsHardforkEnabled(Hardfork.HF_Faun))
return (uint)((BigInteger)engine.SnapshotCache[_execFeeFactor] / ApplicationEngine.FeeFactor);

return (uint)(BigInteger)engine.SnapshotCache[_execFeeFactor];
}

public long GetExecFeeFactor(ProtocolSettings settings, IReadOnlyStore snapshot, uint index)
{
if (settings.IsHardforkEnabled(Hardfork.HF_Faun, index))
return (long)((BigInteger)snapshot[_execFeeFactor] / ApplicationEngine.FeeFactor);

return (long)(BigInteger)snapshot[_execFeeFactor];
}

/// <summary>
/// Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions.
/// </summary>
/// <param name="engine">The execution engine.</param>
/// <returns>The execution fee factor in the unit of pico Gas. 1 picoGAS = 1e-12 GAS</returns>
[ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)]
public BigInteger GetExecPicoFeeFactor(ApplicationEngine engine)
{
return (uint)(BigInteger)snapshot[_execFeeFactor];
return (BigInteger)engine.SnapshotCache[_execFeeFactor];
}

/// <summary>
Expand Down Expand Up @@ -341,12 +373,15 @@ private void SetFeePerByte(ApplicationEngine engine, long value)
}

[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)]
private void SetExecFeeFactor(ApplicationEngine engine, uint value)
private void SetExecFeeFactor(ApplicationEngine engine, ulong value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not we add a SetExecFeeFactorV2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, if there is a v2 value, we will ignore the v1 value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems simple if we don't have an extra method, just change the factor

{
if (value == 0 || value > MaxExecFeeFactor)
throw new ArgumentOutOfRangeException(nameof(value), $"ExecFeeFactor must be between [1, {MaxExecFeeFactor}], got {value}");
AssertCommittee(engine);
// After FAUN hardfork, the max exec fee factor is with decimals defined in ApplicationEngine.FeeFactor
var maxValue = engine.IsHardforkEnabled(Hardfork.HF_Faun) ? ApplicationEngine.FeeFactor * MaxExecFeeFactor : MaxExecFeeFactor;

if (value == 0 || value > maxValue)
throw new ArgumentOutOfRangeException(nameof(value), $"ExecFeeFactor must be between [1, {maxValue}], got {value}");

AssertCommittee(engine);
engine.SnapshotCache.GetAndChange(_execFeeFactor)!.Set(value);
}

Expand Down
7 changes: 4 additions & 3 deletions src/Neo/Wallets/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Neo.VM;
using Neo.VM.Types;
using System;
using System.Numerics;
using static Neo.SmartContract.Helper;
using Array = System.Array;

Expand Down Expand Up @@ -116,8 +117,8 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot,
int size = Transaction.HeaderSize + tx.Signers.GetVarSize() + tx.Attributes.GetVarSize()
+ tx.Script.GetVarSize() + hashes.Length.GetVarSize();
int index = -1;
var execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot);
long networkFee = 0;
var execFeeFactor = NativeContract.Policy.GetExecFeeFactor(settings, snapshot, NativeContract.Ledger.CurrentIndex(snapshot) + 1);
BigInteger networkFee = 0;
foreach (var hash in hashes)
{
index++;
Expand Down Expand Up @@ -226,7 +227,7 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot,
{
networkFee += attr.CalculateNetworkFee(snapshot, tx);
}
return networkFee;
return (long)networkFee;
}
}
}
Loading