diff --git a/src/Neo/Wallets/Helper.cs b/src/Neo/Wallets/Helper.cs index 32638f6c6c..15a88890a4 100644 --- a/src/Neo/Wallets/Helper.cs +++ b/src/Neo/Wallets/Helper.cs @@ -9,9 +9,10 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +#nullable enable + using Neo.Cryptography; using Neo.Extensions; -using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -63,7 +64,7 @@ public static string ToAddress(this UInt160 scriptHash, byte version) /// The converted script hash. public static UInt160 ToScriptHash(this string address, byte version) { - byte[] data = address.Base58CheckDecode(); + var data = address.Base58CheckDecode(); if (data.Length != 21) throw new FormatException(); if (data[0] != version) @@ -74,12 +75,28 @@ public static UInt160 ToScriptHash(this string address, byte version) internal static byte[] XOR(byte[] x, byte[] y) { if (x.Length != y.Length) throw new ArgumentException(); - byte[] r = new byte[x.Length]; - for (int i = 0; i < r.Length; i++) + var r = new byte[x.Length]; + for (var i = 0; i < r.Length; i++) r[i] = (byte)(x[i] ^ y[i]); return r; } + /// + /// Calculates the network fee for the specified transaction. + /// In the unit of datoshi, 1 datoshi = 1e-8 GAS + /// + /// The transaction to calculate. + /// The snapshot used to read data. + /// Thr protocol settings to use. + /// User wallet. + /// The maximum cost that can be spent when a contract is executed. + /// The network fee of the transaction. + public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, ProtocolSettings settings, Wallet? wallet = null, long maxExecutionCost = ApplicationEngine.TestModeGas) + { + Func? accountScript = wallet != null ? (scriptHash) => wallet.GetAccount(scriptHash)?.Contract?.Script : null; + return CalculateNetworkFee(tx, snapshot, settings, accountScript, maxExecutionCost); + } + /// /// Calculates the network fee for the specified transaction. /// In the unit of datoshi, 1 datoshi = 1e-8 GAS @@ -90,24 +107,24 @@ internal static byte[] XOR(byte[] x, byte[] y) /// Function to retrive the script's account from a hash. /// The maximum cost that can be spent when a contract is executed. /// The network fee of the transaction. - public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, ProtocolSettings settings, Func accountScript, long maxExecutionCost = ApplicationEngine.TestModeGas) + public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, ProtocolSettings settings, Func? accountScript, long maxExecutionCost = ApplicationEngine.TestModeGas) { - UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); + var hashes = tx.GetScriptHashesForVerifying(snapshot); // base size for transaction: includes const_header + signers + attributes + script + hashes int size = Transaction.HeaderSize + tx.Signers.GetVarSize() + tx.Attributes.GetVarSize() + tx.Script.GetVarSize() + UnsafeData.GetVarSize(hashes.Length), index = -1; - uint exec_fee_factor = NativeContract.Policy.GetExecFeeFactor(snapshot); + var exec_fee_factor = NativeContract.Policy.GetExecFeeFactor(snapshot); long networkFee = 0; - foreach (UInt160 hash in hashes) + foreach (var hash in hashes) { index++; - byte[] witnessScript = accountScript(hash); - byte[] invocationScript = null; + var witnessScript = accountScript != null ? accountScript(hash) : null; + byte[]? invocationScript = null; if (tx.Witnesses != null && witnessScript is null) { // Try to find the script in the witnesses - Witness witness = tx.Witnesses[index]; + var witness = tx.Witnesses[index]; witnessScript = witness?.VerificationScript.ToArray(); if (witnessScript is null || witnessScript.Length == 0) @@ -193,14 +210,14 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, } else if (IsMultiSigContract(witnessScript, out int m, out int n)) { - int size_inv = 66 * m; + var size_inv = 66 * m; size += UnsafeData.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize(); networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); } } } networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); - foreach (TransactionAttribute attr in tx.Attributes) + foreach (var attr in tx.Attributes) { networkFee += attr.CalculateNetworkFee(snapshot, tx); } diff --git a/src/Neo/Wallets/Wallet.cs b/src/Neo/Wallets/Wallet.cs index 40254f1b4e..e73e8a09e9 100644 --- a/src/Neo/Wallets/Wallet.cs +++ b/src/Neo/Wallets/Wallet.cs @@ -584,7 +584,7 @@ private Transaction MakeTransaction(DataCache snapshot, ReadOnlyMemory scr tx.SystemFee = engine.FeeConsumed; } - tx.NetworkFee = tx.CalculateNetworkFee(snapshot, ProtocolSettings, (a) => GetAccount(a)?.Contract?.Script, maxGas); + tx.NetworkFee = tx.CalculateNetworkFee(snapshot, ProtocolSettings, this, maxGas); if (value >= tx.SystemFee + tx.NetworkFee) return tx; } throw new InvalidOperationException("Insufficient GAS"); diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 56232e765c..086bb3c9d5 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -177,9 +177,7 @@ protected internal virtual JToken CalculateNetworkFee(JArray _params) var tx = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid tx: {_params[0]}")); ; JObject account = new(); - var networkfee = Wallets.Helper.CalculateNetworkFee( - tx.AsSerializable(), system.StoreView, system.Settings, - wallet is not null ? a => wallet.GetAccount(a).Contract.Script : _ => null); + var networkfee = Wallets.Helper.CalculateNetworkFee(tx.AsSerializable(), system.StoreView, system.Settings, wallet); account["networkfee"] = networkfee.ToString(); return account; }