Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion docs/native-contracts-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ When calling a native contract method by transaction script, there are several t
|--------|---------|------------|--------------|---------|-------------|------------|----------|
| getFeePerByte | Gets the network fee per transaction byte. | -- | Int64 | 1<<15 | 0 | ReadStates | -- |
| getExecFeeFactor | Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions. | -- | UInt32 | 1<<15 | 0 | ReadStates | -- |
| getExecPicoFeeFactor | Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions. | -- | BigInteger | 1<<15 | 0 | ReadStates | HF_Faun |
| getStoragePrice | Gets the storage price. | -- | UInt32 | 1<<15 | 0 | ReadStates | -- |
| getMillisecondsPerBlock | Gets the block generation time in milliseconds. | -- | UInt32 | 1<<15 | 0 | ReadStates | HF_Echidna |
| getMaxValidUntilBlockIncrement | Gets the upper increment size of blockchain height (in blocks) exceeding that a transaction should fail validation. | -- | UInt32 | 1<<15 | 0 | ReadStates | HF_Echidna |
Expand All @@ -161,7 +162,7 @@ When calling a native contract method by transaction script, there are several t
| setAttributeFee | Sets the fee for attribute before Echidna hardfork. NotaryAssisted attribute type not supported. | Byte(*attributeType*), UInt32(*value*) | Void | 1<<15 | 0 | States | Deprecated in HF_Echidna |
| setAttributeFee | Sets the fee for attribute after Echidna hardfork. NotaryAssisted attribute type supported. | Byte(*attributeType*), UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
| setFeePerByte | -- | Int64(*value*) | Void | 1<<15 | 0 | States | -- |
| setExecFeeFactor | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | -- |
| setExecFeeFactor | -- | UInt64(*value*) | Void | 1<<15 | 0 | States | -- |
| setStoragePrice | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | -- |
| setMaxValidUntilBlockIncrement | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
| setMaxTraceableBlocks | Sets the length of the chain accessible to smart contracts. | UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
Expand Down
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 @@ -387,11 +387,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 @@ -322,7 +322,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 @@ -447,7 +447,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
63 changes: 45 additions & 18 deletions src/Neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,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 @@ -131,19 +135,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 @@ -205,17 +219,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 @@ -296,13 +324,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 @@ -448,11 +474,12 @@ protected override void ContextUnloaded(ExecutionContext context)
/// <returns>The engine instance created.</returns>
public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null)
{
settings ??= ProtocolSettings.Default;
var index = persistingBlock?.Index ?? (snapshot == null ? 0 : NativeContract.Ledger.CurrentIndex(snapshot));

// Adjust jump table according persistingBlock

var jumpTable = settings == null || settings.IsHardforkEnabled(Hardfork.HF_Echidna, index) ? DefaultJumpTable : NotEchidnaJumpTable;
var jumpTable = settings.IsHardforkEnabled(Hardfork.HF_Echidna, index) ? DefaultJumpTable : NotEchidnaJumpTable;
var engine = Provider?.Create(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable)
?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable);

Expand Down Expand Up @@ -657,7 +684,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 +698,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 @@ -435,8 +435,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
Loading