Skip to content

Commit

Permalink
NeoToken: accept candidate registration via onNEP17Payment
Browse files Browse the repository at this point in the history
Solves two problems:
 * inability to estimate GAS needed for registerCandidate in a regular way
   because of its very high fee (more than what normal RPC servers allow)
 * inability to have MaxBlockSystemFee lower than the registration price
   which is very high on its own (more than practically possible to execute)

Fixes neo-project#3552.

Signed-off-by: Roman Khimov <[email protected]>
  • Loading branch information
roman-khimov committed Dec 19, 2024
1 parent 498ed91 commit a1ff32c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
30 changes: 29 additions & 1 deletion src/Neo/SmartContract/Native/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,42 @@ public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end)
return CalculateBonus(snapshot, state, end);
}

[ContractMethod(Hardfork.HF_Echidna, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)]
private async ContractTask OnNEP17Payment(ApplicationEngine engine, UInt160 from, BigInteger amount, StackItem data)
{
if (engine.CallingScriptHash != GAS.Hash)
throw new InvalidOperationException("only GAS is accepted");

if ((long)amount != GetRegisterPrice(engine.SnapshotCache))
throw new ArgumentException("incorrect GAS amount for registration");

var pubkey = ECPoint.DecodePoint(data.GetSpan(), ECCurve.Secp256r1);

if (!RegisterInternal(engine, pubkey))
throw new InvalidOperationException("failed to register candidate");

await GAS.Burn(engine, Hash, amount);
}

[ContractMethod(true, Hardfork.HF_Echidna, RequiredCallFlags = CallFlags.States)]
[ContractMethod(Hardfork.HF_Echidna, /* */ RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)]
private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey)
{
if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()))
// This check can be removed post-Echidna if compatible,
// RegisterInternal does this anyway.
var index = engine.PersistingBlock?.Index ?? Ledger.CurrentIndex(engine.SnapshotCache);
if (!engine.ProtocolSettings.IsHardforkEnabled(Hardfork.HF_Echidna, index) &&
!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()))
return false;
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
engine.AddFee(GetRegisterPrice(engine.SnapshotCache));
return RegisterInternal(engine, pubkey);
}

private bool RegisterInternal(ApplicationEngine engine, ECPoint pubkey)
{
if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()))
return false;
StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey);
StorageItem item = engine.SnapshotCache.GetAndChange(key, () => new StorageItem(new CandidateState()));
CandidateState state = item.GetInteroperable<CandidateState>();
Expand Down
Loading

0 comments on commit a1ff32c

Please sign in to comment.