-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Conversation
CalculateNetworkFee
with contract verification
src/Neo/Wallets/Helper.cs
Outdated
script.Emit(OpCode.NEWMAP); | ||
break; | ||
default: | ||
throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It differs slightly from NeoGo behaviour, we need to discuss it: NeoGo doesn't return error on unknown type; instead it tries to do the best and emit as much parameters as possible, skipping the others. Then "verify" method is processed in any case, and an error is returned only if "verify" doesn't return boolean. I think that this approach is good because it tries to do as much as possible without extra user's data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you need to support contract
as signer with verify
function with custom parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing that is missing is Map and InteropInterface currently. For maps some zero value can be added (empty map), but for interop interfaces it's harder to say, maybe NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing that is missing is Map and InteropInterface currently. For maps some zero value can be added (empty map), but for interop interfaces it's harder to say, maybe NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, but then throwing an error is not the best way to handle this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@roman-khimov done
src/Neo/Wallets/Helper.cs
Outdated
else | ||
{ | ||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a nice time to support custom verification contracts, it's a part of the issue. So we need to perform witness verification by executing scripts and record GAS consumed not only for contract-based witnesses, but also for unknown witness types. For example, Koblitz-based verification scripts (#3209) could be perfectly handled by running this custom verification script (with user-defined invocation script).
So e.g. in NeoGo we removed this part with signature/multisignature scripts fee calculation and use unified approach with witness script invocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently should work as the same, this optimization (if it's faster) can come in a different PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not faster, but it allows to remove some code and reduce cognitive overhead for developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and BTW, running non-contract scripts (let's put default sig/multisig aside for a moment) is an important part of the deal. Koblitz scripts are the best check for this --- if you can handle them without any specific code you're good.
src/Neo/Wallets/Helper.cs
Outdated
script.Emit(OpCode.NEWMAP); | ||
break; | ||
default: | ||
throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you need to support contract
as signer with verify
function with custom parameters.
src/Neo/Wallets/Helper.cs
Outdated
|
||
// Check verify cost | ||
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot(), 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."); | ||
_ = engine.Execute(); // https://github.com/neo-project/neo/issues/2805 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_ = engine.Execute(); // https://github.com/neo-project/neo/issues/2805 | |
if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); | |
_ = engine.ResultStack.Pop().GetBoolean(); // The result must be convertible to boolean |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's right, but here we should add one more check that there's no extra items on stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly like a standard verification code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but here we should add one more check that there's no extra items on stack.
FYI. That's a good point for users. But it's not the case the real verification process doing.
I'm OK with both versions. It's not a bad thing to have a strict check before submitting to the blockchain.
neo/src/Neo/SmartContract/Helper.cs
Lines 361 to 363 in 3351533
if (engine.Execute() == VMState.FAULT) return false; | |
if (!engine.ResultStack.Peek().GetBoolean()) return false; | |
fee = engine.FeeConsumed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can reuse the VerifyWitness from neo/src/Neo/SmartContract/Helper.cs, it would be much better. If not, ignore me and let's keep going on.
@neo-project/core anything else? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It mostly looks OK, but more tests are needed to ensure it works correctly and the old code works is not broken somehow.
src/Neo/Wallets/Helper.cs
Outdated
script.Emit(OpCode.NEWMAP); | ||
break; | ||
default: | ||
throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing that is missing is Map and InteropInterface currently. For maps some zero value can be added (empty map), but for interop interfaces it's harder to say, maybe NULL.
You mean without a contract? |
Yes. Like in #3209. |
It's done in neo-go? i can't find it |
Take a look at https://github.com/nspcc-dev/neo-go/blob/79e78980c4679e8929ceb509ed56359c5b017279/pkg/services/rpcsrv/server.go#L1001, we take signer's witness, infer invocation script (if needed) and don't change verification script. And hence, verification script is executed by |
Any following work? |
https://github.com/neo-project/neo/pull/3434/files#r1725538241 |
Anyway you can do this #3481 (comment) |
in a different pr please, this is for a different thing |
@shargon you have conflict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd really like to see some tests added along with it, but code-wise it's correct for the purpose and we have #3539 for extension. Let's get this in and move on.
256e1e2
Description
Close #2805
Type of change
How Has This Been Tested?
Test Configuration:
Checklist: