Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend CalculateNetworkFee with contract verification #3385

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
73 changes: 57 additions & 16 deletions src/Neo/Wallets/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using Neo.VM.Types;
using System;
using static Neo.SmartContract.Helper;

Expand Down Expand Up @@ -117,6 +118,7 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot,

if (witnessScript is null || witnessScript.Length == 0)
{
// Contract-based verification
var contract = NativeContract.ContractManagement.GetContract(snapshot, hash);
if (contract is null)
throw new ArgumentException($"The smart contract or address {hash} is not found");
Expand All @@ -126,35 +128,74 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot,
if (md.ReturnType != ContractParameterType.Boolean)
throw new ArgumentException("The verify method doesn't return boolean value.");
if (md.Parameters.Length > 0 && invocationScript is null)
throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script.");
{
var script = new ScriptBuilder();
foreach (var par in md.Parameters)
{
switch (par.Type)
{
case ContractParameterType.Any:
case ContractParameterType.Signature:
case ContractParameterType.String:
case ContractParameterType.ByteArray:
script.EmitPush(new byte[64]);
break;
case ContractParameterType.Boolean:
script.EmitPush(true);
break;
case ContractParameterType.Integer:
script.Emit(OpCode.PUSHINT256, new byte[Integer.MaxSize]);
break;
case ContractParameterType.Hash160:
script.EmitPush(new byte[UInt160.Length]);
break;
case ContractParameterType.Hash256:
script.EmitPush(new byte[UInt256.Length]);
break;
case ContractParameterType.PublicKey:
script.EmitPush(new byte[33]);
break;
case ContractParameterType.Array:
script.Emit(OpCode.NEWARRAY0);
break;
}
}
invocationScript = script.ToArray();
}

// Empty verification and non-empty invocation scripts
var invSize = invocationScript?.GetVarSize() ?? Array.Empty<byte>().GetVarSize();
size += Array.Empty<byte>().GetVarSize() + invSize;
var invSize = invocationScript?.GetVarSize() ?? System.Array.Empty<byte>().GetVarSize();
size += System.Array.Empty<byte>().GetVarSize() + invSize;

// Check verify cost
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CloneCache(), settings: settings, gas: maxExecutionCost);
engine.LoadContract(contract, md, CallFlags.ReadOnly);
if (invocationScript != null) engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None);
if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault.");
if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false.");

if (engine.Execute() == VMState.HALT)
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
// https://github.com/neo-project/neo/issues/2805
if (engine.ResultStack.Count != 1) throw new ArgumentException($"Smart contract {contract.Hash} verification fault.");
_ = engine.ResultStack.Pop().GetBoolean(); // Ensure that the result is boolean
}
maxExecutionCost -= engine.FeeConsumed;
if (maxExecutionCost <= 0) throw new InvalidOperationException("Insufficient GAS.");
networkFee += engine.FeeConsumed;
}
else if (IsSignatureContract(witnessScript))
else
{
size += 67 + witnessScript.GetVarSize();
networkFee += exec_fee_factor * SignatureContractCost();
}
else if (IsMultiSigContract(witnessScript, out int m, out int n))
{
int size_inv = 66 * m;
size += IO.Helper.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize();
networkFee += exec_fee_factor * MultiSignatureContractCost(m, n);
// Regular signature verification.
if (IsSignatureContract(witnessScript))
{
size += 67 + witnessScript.GetVarSize();
networkFee += exec_fee_factor * SignatureContractCost();
}
else if (IsMultiSigContract(witnessScript, out int m, out int n))
{
int size_inv = 66 * m;
size += IO.Helper.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize();
networkFee += exec_fee_factor * MultiSignatureContractCost(m, n);
}
}
// We can support more contract types in the future.
}
networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot);
foreach (TransactionAttribute attr in tx.Attributes)
Expand Down
Loading