From 2a40f67d013ee6ede423f55b35eebdb0d7b1edb4 Mon Sep 17 00:00:00 2001 From: Wi1l-B0t <201105916+Wi1l-B0t@users.noreply.github.com> Date: Sun, 26 Oct 2025 21:27:45 +0800 Subject: [PATCH 1/2] Add: assert committe info --- .../Native/ContractManagement.cs | 2 +- .../SmartContract/Native/NativeContract.cs | 6 ++++ src/Neo/SmartContract/Native/NeoToken.cs | 5 +-- src/Neo/SmartContract/Native/Notary.cs | 5 ++- .../SmartContract/Native/OracleContract.cs | 6 ++-- .../SmartContract/Native/PolicyContract.cs | 32 ++++++++++++------- .../SmartContract/Native/RoleManagement.cs | 7 ++-- 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs index 051e9305fc..cf62b3cbf6 100644 --- a/src/Neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -140,7 +140,7 @@ private long GetMinimumDeploymentFee(IReadOnlyStore snapshot) private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value/* In the unit of datoshi, 1 datoshi = 1e-8 GAS*/) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "cannot be negative"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_MinimumDeploymentFee)).Set(value); } diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index e2e3d640db..610e6d13a1 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -354,6 +354,12 @@ protected static bool CheckCommittee(ApplicationEngine engine) return engine.CheckWitnessInternal(committeeMultiSigAddr); } + protected static void AssertCommittee(ApplicationEngine engine) + { + if (!CheckCommittee(engine)) + throw new InvalidOperationException("Invalid committee signature. It should be a multisig(len(committee) - (len(committee) - 1) / 2))."); + } + #region Storage keys [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Neo/SmartContract/Native/NeoToken.cs b/src/Neo/SmartContract/Native/NeoToken.cs index ac597a2eb1..2612da731f 100644 --- a/src/Neo/SmartContract/Native/NeoToken.cs +++ b/src/Neo/SmartContract/Native/NeoToken.cs @@ -286,7 +286,7 @@ private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) { if (gasPerBlock < 0 || gasPerBlock > 10 * GAS.Factor) throw new ArgumentOutOfRangeException(nameof(gasPerBlock), $"GasPerBlock must be between [0, {10 * GAS.Factor}]"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); var index = engine.PersistingBlock.Index + 1; var entry = engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_GasPerBlock, index), () => new StorageItem(gasPerBlock)); @@ -314,7 +314,8 @@ private void SetRegisterPrice(ApplicationEngine engine, long registerPrice) { if (registerPrice <= 0) throw new ArgumentOutOfRangeException(nameof(registerPrice), "RegisterPrice must be positive"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_registerPrice).Set(registerPrice); } diff --git a/src/Neo/SmartContract/Native/Notary.cs b/src/Neo/SmartContract/Native/Notary.cs index 7c3c89dc2c..fe41178f74 100644 --- a/src/Neo/SmartContract/Native/Notary.cs +++ b/src/Neo/SmartContract/Native/Notary.cs @@ -257,9 +257,12 @@ private void SetMaxNotValidBeforeDelta(ApplicationEngine engine, uint value) { var maxVUBIncrement = engine.SnapshotCache.GetMaxValidUntilBlockIncrement(engine.ProtocolSettings); if (value > maxVUBIncrement / 2 || value < ProtocolSettings.Default.ValidatorsCount) + { throw new FormatException(string.Format("MaxNotValidBeforeDelta cannot be more than {0} or less than {1}", maxVUBIncrement / 2, ProtocolSettings.Default.ValidatorsCount)); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + } + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_MaxNotValidBeforeDelta))!.Set(value); } diff --git a/src/Neo/SmartContract/Native/OracleContract.cs b/src/Neo/SmartContract/Native/OracleContract.cs index 0f6c15221d..7ab60f2595 100644 --- a/src/Neo/SmartContract/Native/OracleContract.cs +++ b/src/Neo/SmartContract/Native/OracleContract.cs @@ -59,9 +59,9 @@ internal OracleContract() : base() { } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetPrice(ApplicationEngine engine, long price) { - if (price <= 0) - throw new ArgumentOutOfRangeException(nameof(price), "Price must be positive"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + if (price <= 0) throw new ArgumentOutOfRangeException(nameof(price), "Price must be positive"); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_Price)).Set(price); } diff --git a/src/Neo/SmartContract/Native/PolicyContract.cs b/src/Neo/SmartContract/Native/PolicyContract.cs index b9bcc1e22d..bddcb70e73 100644 --- a/src/Neo/SmartContract/Native/PolicyContract.cs +++ b/src/Neo/SmartContract/Native/PolicyContract.cs @@ -269,8 +269,8 @@ public bool IsBlocked(IReadOnlyStore snapshot, UInt160 account) public void SetMillisecondsPerBlock(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxMillisecondsPerBlock) - throw new ArgumentOutOfRangeException(nameof(value), $"MillisecondsPerBlock must be between 1 and {MaxMillisecondsPerBlock}, got {value}"); - if (!CheckCommittee(engine)) throw new InvalidOperationException("Invalid committee signature"); + throw new ArgumentOutOfRangeException(nameof(value), $"MillisecondsPerBlock must be between [1, {MaxMillisecondsPerBlock}], got {value}"); + AssertCommittee(engine); var oldTime = GetMillisecondsPerBlock(engine.SnapshotCache); engine.SnapshotCache.GetAndChange(_millisecondsPerBlock).Set(value); @@ -325,7 +325,7 @@ private void SetAttributeFee(ApplicationEngine engine, byte attributeType, uint if (value > MaxAttributeFee) throw new ArgumentOutOfRangeException(nameof(value), $"AttributeFee must be less than {MaxAttributeFee}"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_AttributeFee, attributeType), () => new StorageItem(DefaultAttributeFee)).Set(value); } @@ -335,7 +335,8 @@ private void SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value), $"FeePerByte must be between [0, 100000000], got {value}"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_feePerByte).Set(value); } @@ -344,7 +345,8 @@ private void SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value), $"ExecFeeFactor must be between [1, {MaxExecFeeFactor}], got {value}"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_execFeeFactor).Set(value); } @@ -353,7 +355,8 @@ private void SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value), $"StoragePrice must be between [1, {MaxStoragePrice}], got {value}"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_storagePrice).Set(value); } @@ -365,7 +368,8 @@ private void SetMaxValidUntilBlockIncrement(ApplicationEngine engine, uint value var mtb = GetMaxTraceableBlocks(engine.SnapshotCache); if (value >= mtb) throw new InvalidOperationException($"MaxValidUntilBlockIncrement must be lower than MaxTraceableBlocks ({value} vs {mtb})"); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_maxValidUntilBlockIncrement).Set(value); } @@ -379,26 +383,31 @@ private void SetMaxTraceableBlocks(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxMaxTraceableBlocks) throw new ArgumentOutOfRangeException(nameof(value), $"MaxTraceableBlocks must be between [1, {MaxMaxTraceableBlocks}], got {value}"); + var oldVal = GetMaxTraceableBlocks(engine.SnapshotCache); if (value > oldVal) throw new InvalidOperationException($"MaxTraceableBlocks can not be increased (old {oldVal}, new {value})"); + var mVUBIncrement = GetMaxValidUntilBlockIncrement(engine.SnapshotCache); if (value <= mVUBIncrement) throw new InvalidOperationException($"MaxTraceableBlocks must be larger than MaxValidUntilBlockIncrement ({value} vs {mVUBIncrement})"); - if (!CheckCommittee(engine)) throw new InvalidOperationException("Invalid committee signature"); + + AssertCommittee(engine); + engine.SnapshotCache.GetAndChange(_maxTraceableBlocks).Set(value); } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private bool BlockAccount(ApplicationEngine engine, UInt160 account) { - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + return BlockAccount(engine.SnapshotCache, account); } internal bool BlockAccount(DataCache snapshot, UInt160 account) { - if (IsNative(account)) throw new InvalidOperationException("It's impossible to block a native contract."); + if (IsNative(account)) throw new InvalidOperationException("Cannot block a native contract."); var key = CreateStorageKey(Prefix_BlockedAccount, account); if (snapshot.Contains(key)) return false; @@ -410,7 +419,8 @@ internal bool BlockAccount(DataCache snapshot, UInt160 account) [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { - if (!CheckCommittee(engine)) throw new InvalidOperationException(); + AssertCommittee(engine); + var key = CreateStorageKey(Prefix_BlockedAccount, account); if (!engine.SnapshotCache.Contains(key)) return false; diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index c045b5c237..25afa552f5 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -68,14 +68,15 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node throw new ArgumentException($"Nodes count {nodes.Length} must be between 1 and 32", nameof(nodes)); if (!Enum.IsDefined(typeof(Role), role)) throw new ArgumentOutOfRangeException(nameof(role), $"Role {role} is not valid"); - if (!CheckCommittee(engine)) - throw new InvalidOperationException("Invalid committee signature"); + AssertCommittee(engine); + if (engine.PersistingBlock is null) throw new InvalidOperationException("Persisting block is null"); var index = engine.PersistingBlock.Index + 1; var key = CreateStorageKey((byte)role, index); if (engine.SnapshotCache.Contains(key)) - throw new InvalidOperationException(); + throw new InvalidOperationException("Role already designated"); + NodeList list = new(); list.AddRange(nodes); list.Sort(); From 2882823b8755bbe8dcf56f968bfc60a60e64e2df Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 28 Oct 2025 11:28:41 +0100 Subject: [PATCH 2/2] Use AggressiveInlining for CheckCommittee and AssertCommittee --- src/Neo/SmartContract/Native/NativeContract.cs | 4 +++- src/Neo/SmartContract/Native/Notary.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index 610e6d13a1..d6af3b7c19 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -348,12 +348,14 @@ public bool IsActive(ProtocolSettings settings, uint blockHeight) /// /// The that is executing the contract. /// if the committee has witnessed the current transaction; otherwise, . + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static bool CheckCommittee(ApplicationEngine engine) { - UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.SnapshotCache); + var committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.SnapshotCache); return engine.CheckWitnessInternal(committeeMultiSigAddr); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static void AssertCommittee(ApplicationEngine engine) { if (!CheckCommittee(engine)) diff --git a/src/Neo/SmartContract/Native/Notary.cs b/src/Neo/SmartContract/Native/Notary.cs index fe41178f74..76e7a6050a 100644 --- a/src/Neo/SmartContract/Native/Notary.cs +++ b/src/Neo/SmartContract/Native/Notary.cs @@ -259,7 +259,7 @@ private void SetMaxNotValidBeforeDelta(ApplicationEngine engine, uint value) if (value > maxVUBIncrement / 2 || value < ProtocolSettings.Default.ValidatorsCount) { throw new FormatException(string.Format("MaxNotValidBeforeDelta cannot be more than {0} or less than {1}", - maxVUBIncrement / 2, ProtocolSettings.Default.ValidatorsCount)); + maxVUBIncrement / 2, ProtocolSettings.Default.ValidatorsCount)); } AssertCommittee(engine);