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();