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 15 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
6 changes: 6 additions & 0 deletions src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ protected NativeContract()
contractsHashDictionary.Add(Hash, this);
}

protected bool CheckCommittee(ApplicationEngine engine)
{
UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.Snapshot);
return engine.CheckWitnessInternal(committeeMultiSigAddr);
}

private protected KeyBuilder CreateStorageKey(byte prefix)
{
return new KeyBuilder(Id, prefix);
Expand Down
18 changes: 6 additions & 12 deletions src/neo/SmartContract/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ public PolicyContract()
Manifest.Features = ContractFeatures.HasStorage;
}

private bool CheckCommittees(ApplicationEngine engine)
{
UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.Snapshot);
return engine.CheckWitnessInternal(committeeMultiSigAddr);
}

[ContractMethod(0_01000000, CallFlags.AllowStates)]
public uint GetMaxTransactionsPerBlock(StoreView snapshot)
{
Expand Down Expand Up @@ -93,7 +87,7 @@ public bool IsAnyAccountBlocked(StoreView snapshot, params UInt160[] hashes)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool SetMaxBlockSize(ApplicationEngine engine, uint value)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
if (Network.P2P.Message.PayloadMaxSize <= value) return false;
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem());
storage.Set(value);
Expand All @@ -103,7 +97,7 @@ private bool SetMaxBlockSize(ApplicationEngine engine, uint value)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem());
storage.Set(value);
return true;
Expand All @@ -112,7 +106,7 @@ private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool SetMaxBlockSystemFee(ApplicationEngine engine, long value)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
if (value <= 4007600) return false;
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee), () => new StorageItem());
storage.Set(value);
Expand All @@ -122,7 +116,7 @@ private bool SetMaxBlockSystemFee(ApplicationEngine engine, long value)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool SetFeePerByte(ApplicationEngine engine, long value)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem());
storage.Set(value);
return true;
Expand All @@ -131,7 +125,7 @@ private bool SetFeePerByte(ApplicationEngine engine, long value)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool BlockAccount(ApplicationEngine engine, UInt160 account)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
StorageKey key = CreateStorageKey(Prefix_BlockedAccounts);
StorageItem storage = engine.Snapshot.Storages.GetOrAdd(key, () => new StorageItem(new byte[1]));
List<UInt160> accounts = storage.GetSerializableList<UInt160>();
Expand All @@ -145,7 +139,7 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account)
[ContractMethod(0_03000000, CallFlags.AllowModifyStates)]
private bool UnblockAccount(ApplicationEngine engine, UInt160 account)
{
if (!CheckCommittees(engine)) return false;
if (!CheckCommittee(engine)) return false;
StorageKey key = CreateStorageKey(Prefix_BlockedAccounts);
StorageItem storage = engine.Snapshot.Storages.TryGet(key);
if (storage is null) return false;
Expand Down
75 changes: 52 additions & 23 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,48 @@ 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)

StorageKey keyLeft = CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue - end);
StorageKey keyRight = CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue);
var enumerator = snapshot.Storages.FindRange(keyLeft, keyRight).GetEnumerator();
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
BigInteger sum = 0;
uint right = end;
while (enumerator.MoveNext())
{
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)
var gasPerBlock = new BigInteger(enumerator.Current.Value.Value);
var index = uint.MaxValue - BitConverter.ToUInt32(enumerator.Current.Key.Key.Skip(1).ToArray()) - 1;
if (index <= start)
{
uend--;
iend = Blockchain.DecrementInterval;
sum += gasPerBlock * (right - start);
break;
}
while (ustart < uend)
else
{
amount += (Blockchain.DecrementInterval - istart) * Blockchain.GenerationAmount[ustart];
ustart++;
istart = 0;
sum += gasPerBlock * (right - index);
right = index;
}
amount += (iend - istart) * Blockchain.GenerationAmount[ustart];
}
return value * amount * GAS.Factor / TotalAmount;
return value * sum * NeoHolderRewardRatio / 100 / TotalAmount;
}

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

engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue - 1), 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 +108,34 @@ protected override void OnPersist(ApplicationEngine engine)
storage.Value = GetValidators(engine.Snapshot).ToByteArray();
}

[ContractMethod(0_05000000, CallFlags.AllowModifyStates)]
private bool SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock)
{
if (gasPerBlock < 0 || gasPerBlock > 10 * GAS.Factor) return false;
if (!CheckCommittee(engine)) return false;
StorageKey key = CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue - engine.Snapshot.PersistingBlock.Index - 1);
StorageItem item = engine.Snapshot.Storages.GetAndChange(key, () => new StorageItem { Value = BigInteger.Zero.ToByteArray() });
item.Value = gasPerBlock.ToByteArray();
return true;
}

[ContractMethod(0_01000000, CallFlags.AllowStates)]
public BigInteger GetGasPerBlock(StoreView snapshot)
{
StorageKey keyLeft = CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue - snapshot.PersistingBlock.Index - 1);
StorageKey keyRight = CreateStorageKey(Prefix_GasPerBlock).Add(uint.MaxValue);
var enumerator = snapshot.Storages.FindRange(keyLeft, keyRight).GetEnumerator();
enumerator.MoveNext();
return new BigInteger(enumerator.Current.Value.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;
}
}
}
Loading