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

Add incentive for neo holders #1845

Merged
merged 28 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from 13 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
52 changes: 23 additions & 29 deletions src/neo/SmartContract/Native/Tokens/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public sealed class NeoToken : Nep5Token<NeoToken.NeoAccountState>
private const byte Prefix_VotersCount = 1;
private const byte Prefix_Candidate = 33;
private const byte Prefix_NextValidators = 14;
private const byte Prefix_GasPerBlock = 29;

private const byte NeoHolderRewardRatio = 10;
private const byte CommitteeRewardRatio = 5;
private const byte VoterRewardRatio = 85;

internal NeoToken()
{
Expand All @@ -50,45 +55,28 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco

private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state)
{
BigInteger gas = CalculateBonus(state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index);
BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index);
state.BalanceHeight = engine.Snapshot.PersistingBlock.Index;
GAS.Mint(engine, account, gas);
}

private BigInteger CalculateBonus(BigInteger value, uint start, uint end)
private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end)
{
if (value.IsZero || start >= end) return BigInteger.Zero;
if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value));
BigInteger amount = BigInteger.Zero;
uint ustart = start / Blockchain.DecrementInterval;
if (ustart < Blockchain.GenerationAmount.Length)
{
uint istart = start % Blockchain.DecrementInterval;
uint uend = end / Blockchain.DecrementInterval;
uint iend = end % Blockchain.DecrementInterval;
if (uend >= Blockchain.GenerationAmount.Length)
{
uend = (uint)Blockchain.GenerationAmount.Length;
iend = 0;
}
if (iend == 0)
{
uend--;
iend = Blockchain.DecrementInterval;
}
while (ustart < uend)
{
amount += (Blockchain.DecrementInterval - istart) * Blockchain.GenerationAmount[ustart];
ustart++;
istart = 0;
}
amount += (iend - istart) * Blockchain.GenerationAmount[ustart];
}
return value * amount * GAS.Factor / TotalAmount;
var gasPerBlock = GetGasPerBlock(snapshot);
return value * gasPerBlock * (end - start) * NeoHolderRewardRatio / 100 / TotalAmount;
}

internal override void Initialize(ApplicationEngine engine)
{
// Initialize economic parameters

engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_GasPerBlock), new StorageItem
{
Value = (5 * GAS.Factor).ToByteArray()
});

engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0]));
Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), TotalAmount);
}
Expand All @@ -100,13 +88,19 @@ protected override void OnPersist(ApplicationEngine engine)
storage.Value = GetValidators(engine.Snapshot).ToByteArray();
}

[ContractMethod(0_01000000, CallFlags.AllowStates)]
public BigInteger GetGasPerBlock(StoreView snapshot)
{
return new BigInteger(snapshot.Storages.TryGet(CreateStorageKey(Prefix_GasPerBlock)).Value);
}

[ContractMethod(0_03000000, CallFlags.AllowStates)]
public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end)
{
StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(account));
if (storage is null) return BigInteger.Zero;
NeoAccountState state = storage.GetInteroperable<NeoAccountState>();
return CalculateBonus(state.Balance, state.BalanceHeight, end);
return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end);
}

[ContractMethod(0_05000000, CallFlags.AllowModifyStates)]
Expand Down
37 changes: 27 additions & 10 deletions tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void Check_BalanceOfTransferAndBurn()
// Check unclaim

var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from);
unclaim.Value.Should().Be(new BigInteger(600000000000));
unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L));
unclaim.State.Should().BeTrue();

// Transfer
Expand All @@ -58,7 +58,7 @@ public void Check_BalanceOfTransferAndBurn()
NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(100000000);
NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0);

NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(30006000_00000000);
NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(30000500_00000000);
NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(0);

// Check unclaim
Expand All @@ -68,21 +68,21 @@ public void Check_BalanceOfTransferAndBurn()
unclaim.State.Should().BeTrue();

supply = NativeContract.GAS.TotalSupply(snapshot);
supply.Should().Be(30006000_00000000);
supply.Should().Be(3000050000000000);

snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas

// Transfer

keyCount = snapshot.Storages.GetChangeSet().Count();

NativeContract.GAS.Transfer(snapshot, from, to, 30006000_00000000, false).Should().BeFalse(); // Not signed
NativeContract.GAS.Transfer(snapshot, from, to, 30006000_00000001, true).Should().BeFalse(); // More than balance
NativeContract.GAS.Transfer(snapshot, from, to, 30006000_00000000, true).Should().BeTrue(); // All balance
NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, false).Should().BeFalse(); // Not signed
NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000001, true).Should().BeFalse(); // More than balance
NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, true).Should().BeTrue(); // All balance

// Balance of

NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(30006000_00000000);
NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(30000500_00000000);
NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(0);

snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // All
Expand All @@ -98,19 +98,19 @@ public void Check_BalanceOfTransferAndBurn()
// Burn more than expected

Assert.ThrowsException<InvalidOperationException>(() =>
NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(30006000_00000001)));
NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(30000500_00000001)));

// Real burn

NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1));

NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(30005999_99999999);
NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000049999999999);

keyCount.Should().Be(snapshot.Storages.GetChangeSet().Count());

// Burn all

NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(30005999_99999999));
NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000049999999999));

(keyCount - 1).Should().Be(snapshot.Storages.GetChangeSet().Count());

Expand All @@ -132,5 +132,22 @@ public void Check_BadScript()

Assert.ThrowsException<InvalidOperationException>(() => NativeContract.GAS.Invoke(engine));
}

internal static StorageKey CreateStorageKey(byte prefix, uint key)
{
return CreateStorageKey(prefix, BitConverter.GetBytes(key));
}

internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null)
{
StorageKey storageKey = new StorageKey
{
Id = NativeContract.NEO.Id,
Key = new byte[sizeof(byte) + (key?.Length ?? 0)]
};
storageKey.Key[0] = prefix;
key?.CopyTo(storageKey.Key.AsSpan(1));
return storageKey;
}
}
}
56 changes: 53 additions & 3 deletions tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public void Check_UnclaimedGas()
byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray();

var unclaim = Check_UnclaimedGas(snapshot, from);
unclaim.Value.Should().Be(new BigInteger(600000000000));
unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L));
unclaim.State.Should().BeTrue();

unclaim = Check_UnclaimedGas(snapshot, new byte[19]);
Expand Down Expand Up @@ -339,7 +339,7 @@ public void Check_Transfer()
// Check unclaim

var unclaim = Check_UnclaimedGas(snapshot, from);
unclaim.Value.Should().Be(new BigInteger(600000000000));
unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L));
unclaim.State.Should().BeTrue();

// Transfer
Expand Down Expand Up @@ -416,18 +416,35 @@ public void TestCalculateBonus()
{
var snapshot = Blockchain.Singleton.GetSnapshot();
StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray());

// Fault: balance < 0

snapshot.Storages.Add(key, new StorageItem(new NeoAccountState
{
Balance = -100
}));
Action action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0));
action.Should().Throw<ArgumentOutOfRangeException>();
snapshot.Storages.Delete(key);

// Fault range: start >= end

snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState
{
Balance = 100,
BalanceHeight = 100
}));
action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0));
snapshot.Storages.Delete(key);

// Normal 1) votee is non exist

snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState
{
Balance = 100
}));
NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 30 * Blockchain.DecrementInterval).Should().Be(new BigInteger(7000000000));
NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(0.5 * 100 * 100));
snapshot.Storages.Delete(key);
}

[TestMethod]
Expand Down Expand Up @@ -546,6 +563,16 @@ public void TestTotalSupply()
NativeContract.NEO.TotalSupply(snapshot).Should().Be(new BigInteger(100000000));
}

[TestMethod]
public void TestEconomicParameter()
{
var snapshot = Blockchain.Singleton.GetSnapshot();

(BigInteger, bool) result = Check_GetGasPerBlock(snapshot);
result.Item2.Should().BeTrue();
result.Item1.Should().Be(5 * NativeContract.GAS.Factor);
}

[TestMethod]
public void TestUnclaimedGas()
{
Expand Down Expand Up @@ -619,6 +646,29 @@ public void TestVote()
return (true, result.GetBoolean());
}

internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot)
{
var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot);

engine.LoadScript(NativeContract.NEO.Script);

var script = new ScriptBuilder();
script.EmitPush(0);
script.Emit(OpCode.PACK);
script.EmitPush("getGasPerBlock");
engine.LoadScript(script.ToArray());

if (engine.Execute() == VMState.FAULT)
{
return (BigInteger.Zero, false);
}

var result = engine.ResultStack.Pop();
result.Should().BeOfType(typeof(VM.Types.Integer));

return (((VM.Types.Integer)result).GetInteger(), true);
}

internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount)
{
var engine = ApplicationEngine.Create(TriggerType.Application,
Expand Down