Skip to content

Commit

Permalink
Merge branch 'master' into asynchronous-oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored Aug 21, 2020
2 parents dfe8904 + 35eea42 commit cb7a327
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 38 deletions.
2 changes: 0 additions & 2 deletions src/neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ internal class PreverifyCompleted { public Transaction Transaction; public Verif
public class RelayResult { public IInventory Inventory; public VerifyResult Result; }

public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock;
public const uint DecrementInterval = 2000000;
public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock);
public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray();
public static readonly ECPoint[] StandbyValidators = StandbyCommittee[0..ProtocolSettings.Default.ValidatorsCount];
Expand Down
8 changes: 8 additions & 0 deletions src/neo/SmartContract/Native/KeyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ unsafe public KeyBuilder Add<T>(T key) where T : unmanaged
return Add(new ReadOnlySpan<byte>(&key, sizeof(T)));
}

unsafe public KeyBuilder AddBigEndian<T>(T key) where T : unmanaged
{
ReadOnlySpan<byte> buffer = new ReadOnlySpan<byte>(&key, sizeof(T));
for (int i = buffer.Length - 1; i >= 0; i--)
stream.WriteByte(buffer[i]);
return this;
}

public byte[] ToArray()
{
using (stream)
Expand Down
97 changes: 74 additions & 23 deletions src/neo/SmartContract/Native/Tokens/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Array = Neo.VM.Types.Array;

namespace Neo.SmartContract.Native.Tokens
{
Expand All @@ -26,6 +27,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 +56,45 @@ 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)

GasRecord gasRecord = snapshot.Storages[CreateStorageKey(Prefix_GasPerBlock)].GetInteroperable<GasRecord>();
BigInteger sum = 0;
for (var i = gasRecord.Count - 1; i >= 0; i--)
{
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 currentIndex = gasRecord[i].Index;
if (currentIndex >= end) continue;
if (currentIndex > start)
{
uend--;
iend = Blockchain.DecrementInterval;
sum += gasRecord[i].GasPerBlock * (end - currentIndex);
end = currentIndex;
}
while (ustart < uend)
else
{
amount += (Blockchain.DecrementInterval - istart) * Blockchain.GenerationAmount[ustart];
ustart++;
istart = 0;
sum += gasRecord[i].GasPerBlock * (end - start);
break;
}
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), new StorageItem(new GasRecord
{
(0, 5 * GAS.Factor)
}));

engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0]));
Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), TotalAmount);
}
Expand All @@ -100,13 +106,41 @@ 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)
throw new ArgumentOutOfRangeException(nameof(gasPerBlock));
if (!CheckCommittee(engine)) return false;
uint index = engine.Snapshot.PersistingBlock.Index + 1;
GasRecord gasRecord = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_GasPerBlock)).GetInteroperable<GasRecord>();
if (gasRecord[^1].Index == index)
gasRecord[^1] = (index, gasPerBlock);
else
gasRecord.Add((index, gasPerBlock));
return true;
}

[ContractMethod(0_01000000, CallFlags.AllowStates)]
public BigInteger GetGasPerBlock(StoreView snapshot)
{
var index = snapshot.PersistingBlock.Index;
GasRecord gasRecord = snapshot.Storages[CreateStorageKey(Prefix_GasPerBlock)].GetInteroperable<GasRecord>();
for (var i = gasRecord.Count - 1; i >= 0; i--)
{
if (gasRecord[i].Index <= index)
return gasRecord[i].GasPerBlock;
}
throw new InvalidOperationException();
}

[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 Expand Up @@ -263,5 +297,22 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter)
return new Struct(referenceCounter) { Registered, Votes };
}
}

private sealed class GasRecord : List<(uint Index, BigInteger GasPerBlock)>, IInteroperable
{
public void FromStackItem(StackItem stackItem)
{
foreach (StackItem item in (Array)stackItem)
{
Struct @struct = (Struct)item;
Add(((uint)@struct[0].GetInteger(), @struct[1].GetInteger()));
}
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, this.Select(p => new Struct(referenceCounter, new StackItem[] { p.Index, p.GasPerBlock })));
}
}
}
}
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

0 comments on commit cb7a327

Please sign in to comment.