Skip to content
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

Set attribute fee #2916

Merged
merged 7 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Neo/Network/P2P/Payloads/Conflicts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,10 @@ public override bool Verify(DataCache snapshot, Transaction tx)
// on-chain transaction.
return !NativeContract.Ledger.ContainsTransaction(snapshot, Hash);
}

public override long CalculateNetworkFee(DataCache snapshot, Transaction tx)
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
return tx.Signers.Length * base.CalculateNetworkFee(snapshot, tx);
}
}
}
5 changes: 4 additions & 1 deletion src/Neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,13 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data
if (NativeContract.Policy.IsBlocked(snapshot, hash))
return VerifyResult.PolicyFail;
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;
long net_fee = NetworkFee - Size * NativeContract.Policy.GetFeePerByte(snapshot);
else
attributesFee += attribute.CalculateNetworkFee(snapshot, this);
long net_fee = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee;
if (net_fee < 0) return VerifyResult.InsufficientFunds;

if (net_fee > MaxVerificationGas) net_fee = MaxVerificationGas;
Expand Down
7 changes: 5 additions & 2 deletions src/Neo/Network/P2P/Payloads/TransactionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;
using System.IO;
using Neo.IO;
using Neo.IO.Caching;
using Neo.Json;
using Neo.Persistence;
using System;
using System.IO;
using Neo.SmartContract.Native;

namespace Neo.Network.P2P.Payloads
{
Expand Down Expand Up @@ -92,5 +93,7 @@ public void Serialize(BinaryWriter writer)
/// <param name="tx">The <see cref="Transaction"/> that contains the attribute.</param>
/// <returns><see langword="true"/> if the verification passes; otherwise, <see langword="false"/>.</returns>
public virtual bool Verify(DataCache snapshot, Transaction tx) => true;

public virtual long CalculateNetworkFee(DataCache snapshot, Transaction tx) => NativeContract.Policy.GetAttributeFee(snapshot, (byte)Type);
}
}
40 changes: 39 additions & 1 deletion src/Neo/SmartContract/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

#pragma warning disable IDE0051

using Neo.Persistence;
using System;
using System.Numerics;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;

namespace Neo.SmartContract.Native
{
Expand All @@ -36,11 +37,21 @@ public sealed class PolicyContract : NativeContract
/// </summary>
public const uint DefaultFeePerByte = 1000;

/// <summary>
/// The default fee for attribute.
/// </summary>
public const uint DefaultAttributeFee = 0;

/// <summary>
/// The maximum execution fee factor that the committee can set.
/// </summary>
public const uint MaxExecFeeFactor = 100;

/// <summary>
/// The maximum fee for attribute that the committee can set.
/// </summary>
public const uint MaxAttributeFee = 10_0000_0000;

/// <summary>
/// The maximum storage price that the committee can set.
/// </summary>
Expand All @@ -50,6 +61,7 @@ public sealed class PolicyContract : NativeContract
private const byte Prefix_FeePerByte = 10;
private const byte Prefix_ExecFeeFactor = 18;
private const byte Prefix_StoragePrice = 19;
private const byte Prefix_AttributeFee = 20;

internal PolicyContract()
{
Expand Down Expand Up @@ -96,6 +108,22 @@ public uint GetStoragePrice(DataCache snapshot)
return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_StoragePrice)];
}

/// <summary>
/// Gets the fee for attribute.
/// </summary>
/// <param name="snapshot">The snapshot used to read data.</param>
/// <param name="attributeType">Attribute type</param>
/// <returns>The fee for attribute.</returns>
[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)]
public uint GetAttributeFee(DataCache snapshot, byte attributeType)
{
if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException();
StorageItem entry = snapshot.TryGet(CreateStorageKey(Prefix_AttributeFee).Add(attributeType));
if (entry == null) return DefaultAttributeFee;

return (uint)(BigInteger)entry;
}

/// <summary>
/// Determines whether the specified account is blocked.
/// </summary>
Expand All @@ -108,6 +136,16 @@ 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)
{
if (!Enum.IsDefined(typeof(TransactionAttributeType), attributeType)) throw new InvalidOperationException();
if (value > MaxAttributeFee) throw new ArgumentOutOfRangeException(nameof(value));
if (!CheckCommittee(engine)) throw new InvalidOperationException();

engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_AttributeFee).Add(attributeType), () => new StorageItem(DefaultAttributeFee)).Set(value);
}

[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)]
private void SetFeePerByte(ApplicationEngine engine, long value)
{
Expand Down
18 changes: 11 additions & 7 deletions src/Neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Neo.Cryptography;
using Neo.IO;
using Neo.Network.P2P.Payloads;
Expand All @@ -17,13 +24,6 @@
using Neo.VM;
using Neo.Wallets.NEP6;
using Org.BouncyCastle.Crypto.Generators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using static Neo.SmartContract.Helper;
using static Neo.Wallets.Helper;
using ECPoint = Neo.Cryptography.ECC.ECPoint;
Expand Down Expand Up @@ -661,6 +661,10 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx, long maxExec
// We can support more contract types in the future.
}
networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot);
foreach (TransactionAttribute attr in tx.Attributes)
{
networkFee += attr.CalculateNetworkFee(snapshot, tx);
}
return networkFee;
}

Expand Down
70 changes: 68 additions & 2 deletions tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System;
using System.Linq;
using System.Numerics;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.IO;
Expand All @@ -6,8 +9,6 @@
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.UnitTests.Extensions;
using System;
using System.Linq;

namespace Neo.UnitTests.SmartContract.Native
{
Expand All @@ -33,6 +34,71 @@ public void Check_Default()
var ret = NativeContract.Policy.Call(snapshot, "getFeePerByte");
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(1000);

ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)(byte)TransactionAttributeType.Conflicts });
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(PolicyContract.DefaultAttributeFee);

Assert.ThrowsException<InvalidOperationException>(() => NativeContract.Policy.Call(snapshot, "getAttributeFee", new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)byte.MaxValue }));
}

[TestMethod]
public void Check_SetAttributeFee()
{
var snapshot = _snapshot.CreateSnapshot();

// Fake blockchain
Block block = new()
{
Header = new Header
{
Index = 1000,
PrevHash = UInt256.Zero
}
};

var attr = new ContractParameter(ContractParameterType.Integer) { Value = (BigInteger)(byte)TransactionAttributeType.Conflicts };

// Without signature
Assert.ThrowsException<InvalidOperationException>(() =>
{
NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block,
"setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 100500 });
});

var ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr);
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(0);

// With signature, wrong value
UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot);
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block,
"setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 11_0000_0000 });
});

ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr);
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(0);

// Proper set
ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block,
"setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 300300 });
ret.IsNull.Should().BeTrue();

ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr);
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(300300);

// Set to zero
ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block,
"setAttributeFee", attr, new ContractParameter(ContractParameterType.Integer) { Value = 0 });
ret.IsNull.Should().BeTrue();

ret = NativeContract.Policy.Call(snapshot, "getAttributeFee", attr);
ret.Should().BeOfType<VM.Types.Integer>();
ret.GetInteger().Should().Be(0);
}

[TestMethod]
Expand Down