diff --git a/src/Neo/Network/P2P/Payloads/Transaction.cs b/src/Neo/Network/P2P/Payloads/Transaction.cs index b922674d91..8fc771ce7c 100644 --- a/src/Neo/Network/P2P/Payloads/Transaction.cs +++ b/src/Neo/Network/P2P/Payloads/Transaction.cs @@ -370,10 +370,13 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data if (!(context?.CheckTransaction(this, conflictsList, snapshot) ?? true)) return VerifyResult.InsufficientFunds; long attributesFee = 0; foreach (TransactionAttribute attribute in Attributes) + { if (!attribute.Verify(snapshot, this)) return VerifyResult.InvalidAttribute; - else - attributesFee += attribute.CalculateNetworkFee(snapshot, this); + if (attribute.Type == TransactionAttributeType.NotaryAssisted && !settings.IsHardforkEnabled(Hardfork.HF_Domovoi, height)) + return VerifyResult.InvalidAttribute; + attributesFee += attribute.CalculateNetworkFee(snapshot, this); + } long netFeeDatoshi = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee; if (netFeeDatoshi < 0) return VerifyResult.InsufficientFunds; diff --git a/src/Neo/SmartContract/Native/PolicyContract.cs b/src/Neo/SmartContract/Native/PolicyContract.cs index adeb2f1c19..7dcabe2771 100644 --- a/src/Neo/SmartContract/Native/PolicyContract.cs +++ b/src/Neo/SmartContract/Native/PolicyContract.cs @@ -79,6 +79,9 @@ internal override ContractTask InitializeAsync(ApplicationEngine engine, Hardfor engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte)); engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); + } + if (hardfork == Hardfork.HF_Domovoi) + { engine.Snapshot.Add(CreateStorageKey(Prefix_AttributeFee).Add((byte)TransactionAttributeType.NotaryAssisted), new StorageItem(DefaultNotaryAssistedAttributeFee)); } return ContractTask.CompletedTask; @@ -118,15 +121,41 @@ public uint GetStoragePrice(DataCache snapshot) } /// - /// Gets the fee for attribute. + /// Gets the fee for attribute before Domovoi hardfork. + /// + /// The snapshot used to read data. + /// Attribute type excluding + /// The fee for attribute. + [ContractMethod(Hardfork.HF_Domovoi, CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates, Name = "getAttributeFee")] + public uint GetAttributeFeeV0(DataCache snapshot, byte attributeType) + { + return GetAttributeFee(snapshot, attributeType, false); + } + + /// + /// Gets the fee for attribute after Domovoi hardfork. /// /// The snapshot used to read data. /// Attribute type /// The fee for attribute. - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + [ContractMethod(true, Hardfork.HF_Domovoi, CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint GetAttributeFee(DataCache snapshot, byte attributeType) { - if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException(); + return GetAttributeFee(snapshot, attributeType, true); + } + + /// + /// Generic handler for GetAttributeFeeV0 and GetAttributeFeeV1 that + /// gets the fee for attribute. + /// + /// The snapshot used to read data. + /// Attribute type + /// Whether to support attribute type. + /// The fee for attribute. + private uint GetAttributeFee(DataCache snapshot, byte attributeType, bool allowNotaryAssisted) + { + if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType) || (!allowNotaryAssisted && attributeType == (byte)(TransactionAttributeType.NotaryAssisted))) + throw new InvalidOperationException(); StorageItem entry = snapshot.TryGet(CreateStorageKey(Prefix_AttributeFee).Add(attributeType)); if (entry == null) return DefaultAttributeFee; @@ -145,10 +174,45 @@ public bool IsBlocked(DataCache snapshot, UInt160 account) return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] - private void SetAttributeFee(ApplicationEngine engine, byte attributeType, uint value) + /// + /// Sets the fee for attribute before Domovoi hardfork. + /// + /// The engine used to check committee witness and read data. + /// Attribute type excluding + /// Attribute fee value + /// The fee for attribute. + [ContractMethod(Hardfork.HF_Domovoi, CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States, Name = "setAttributeFee")] + private void SetAttributeFeeV0(ApplicationEngine engine, byte attributeType, uint value) + { + SetAttributeFee(engine, attributeType, value, false); + } + + /// + /// Sets the fee for attribute after Domovoi hardfork. + /// + /// The engine used to check committee witness and read data. + /// Attribute type excluding + /// Attribute fee value + /// The fee for attribute. + [ContractMethod(true, Hardfork.HF_Domovoi, CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States, Name = "setAttributeFee")] + private void SetAttributeFeeV1(ApplicationEngine engine, byte attributeType, uint value) + { + SetAttributeFee(engine, attributeType, value, true); + } + + /// + /// Generic handler for SetAttributeFeeV0 and SetAttributeFeeV1 that + /// gets the fee for attribute. + /// + /// The engine used to check committee witness and read data. + /// Attribute type + /// Attribute fee value + /// Whether to support attribute type. + /// The fee for attribute. + private void SetAttributeFee(ApplicationEngine engine, byte attributeType, uint value, bool allowNotaryAssisted) { - if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException(); + if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType) || (!allowNotaryAssisted && attributeType == (byte)(TransactionAttributeType.NotaryAssisted))) + throw new InvalidOperationException(); if (value > MaxAttributeFee) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException();