Skip to content

Commit

Permalink
Different approach and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lock9 committed Dec 14, 2019
1 parent 35138f7 commit dca1eb2
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 306 deletions.
2 changes: 1 addition & 1 deletion src/neo/IO/Caching/DataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public TValue GetOrAdd(TKey key, Func<TValue> factory)
}
}

public TValue TryGet(TKey key)
public virtual TValue TryGet(TKey key)
{
lock (dictionary)
{
Expand Down
37 changes: 18 additions & 19 deletions src/neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public partial class ApplicationEngine : ExecutionEngine
private readonly bool testMode;
private readonly List<NotifyEventArgs> notifications = new List<NotifyEventArgs>();
private readonly List<IDisposable> disposables = new List<IDisposable>();
private long MaxConsumedGas = 0;
private readonly List<byte[]> updatedKeys = new List<byte[]>();
private long maxConsumedGas = 0;

public TriggerType Trigger { get; }
public IVerifiable ScriptContainer { get; }
Expand All @@ -47,8 +48,21 @@ internal T AddDisposable<T>(T disposable) where T : IDisposable
return disposable;
}

internal bool TryAddUpdatedKey(byte[] key)
{
bool keyAdded = false;
if (!updatedKeys.Contains(key))
{
updatedKeys.Add(key);
keyAdded = true;
}
return keyAdded;
}

private bool AddGas(long gas)
{
if (gas < 0 && GasConsumed > maxConsumedGas)
maxConsumedGas = GasConsumed;
GasConsumed = checked(GasConsumed + gas);
return testMode || GasConsumed <= gas_amount;
}
Expand All @@ -58,23 +72,8 @@ private bool AddGas(long gas)
/// </summary>
private void RecalculateConsumedGas()
{
if (MaxConsumedGas > GasConsumed) GasConsumed = MaxConsumedGas;
}

public void StorageReused(int reusedBytes)
{
var discountPerByte = InteropService.GasPerReusedByte;
var gasDiscount = checked(reusedBytes * discountPerByte);
if (GasConsumed > MaxConsumedGas) MaxConsumedGas = GasConsumed;
AddGas(gasDiscount);
}

public void StorageReleased(int releasedBytes)
{
var discountPerByte = InteropService.GasPerReleasedByte;
var gasDiscount = checked(releasedBytes * discountPerByte);
if (GasConsumed > MaxConsumedGas) MaxConsumedGas = GasConsumed;
AddGas(gasDiscount);
if (maxConsumedGas > GasConsumed)
GasConsumed = maxConsumedGas;
}

protected override void LoadContext(ExecutionContext context)
Expand All @@ -96,7 +95,7 @@ public override void Dispose()

protected override bool OnSysCall(uint method)
{
if (!AddGas(InteropService.GetPrice(method, CurrentContext.EvaluationStack)))
if (!AddGas(InteropService.GetPrice(method, this)))
return false;
return InteropService.Invoke(this, method);
}
Expand Down
25 changes: 25 additions & 0 deletions src/neo/SmartContract/InteropDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ internal class InteropDescriptor
public Func<ApplicationEngine, bool> Handler { get; }
public long Price { get; }
public Func<EvaluationStack, long> PriceCalculator { get; }
public Func<ApplicationEngine, long> StoragePriceCalculator { get; }
public bool IsStateful { get; }
public TriggerType AllowedTriggers { get; }

public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, long price, TriggerType allowedTriggers)
Expand All @@ -24,6 +26,13 @@ public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, F
this.PriceCalculator = priceCalculator;
}

public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, Func<ApplicationEngine, long> priceCalculator, TriggerType allowedTriggers)
: this(method, handler, allowedTriggers)
{
this.StoragePriceCalculator = priceCalculator;
this.IsStateful = true;
}

private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, TriggerType allowedTriggers)
{
this.Method = method;
Expand All @@ -32,8 +41,24 @@ private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler,
this.AllowedTriggers = allowedTriggers;
}

public long GetPrice()
{
return Price;
}

public long GetPrice(ApplicationEngine applicationEngine)
{
return StoragePriceCalculator is null ? Price : StoragePriceCalculator(applicationEngine);
}

public long GetPrice(EvaluationStack stack)
{
#if DEBUG
if (IsStateful)
{
throw new InvalidOperationException();
}
#endif
return PriceCalculator is null ? Price : PriceCalculator(stack);
}
}
Expand Down
93 changes: 64 additions & 29 deletions src/neo/SmartContract/InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static partial class InteropService
public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application);
public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application);
public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application);
public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application);
public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, GetDeleteStoragePrice, TriggerType.Application);
public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application);

private static bool CheckItemForNotification(StackItem state)
Expand Down Expand Up @@ -111,19 +111,73 @@ private static bool CheckStorageContext(ApplicationEngine engine, StorageContext
return true;
}

public static long GetPrice(uint hash)
{
return methods[hash].GetPrice();
}

public static long GetPrice(uint hash, EvaluationStack stack)
{
return methods[hash].GetPrice(stack);
}

public static long GetPrice(uint hash, ApplicationEngine applicationEngine)
{
var interopDescriptor = methods[hash];
return interopDescriptor.IsStateful ? interopDescriptor.GetPrice(applicationEngine) : interopDescriptor.GetPrice(applicationEngine.CurrentContext.EvaluationStack);
}

public static Dictionary<uint, string> SupportedMethods()
{
return methods.ToDictionary(p => p.Key, p => p.Value.Method);
}

private static long GetStoragePrice(EvaluationStack stack)
private static long GetDeleteStoragePrice(ApplicationEngine engine)
{
return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte;
var stack = engine.CurrentContext.EvaluationStack;
var key = stack.Peek(1);
StorageKey skey = new StorageKey
{
ScriptHash = engine.CurrentScriptHash,
Key = key.GetSpan().ToArray()
};

var skeyValue = engine.Snapshot.Storages.TryGet(skey);

if (skey == null || !engine.TryAddUpdatedKey(key.GetSpan().ToArray()))
return 0_01000000;

return skeyValue.Value.Length * GasPerReleasedByte;
}

private static long GetStoragePrice(ApplicationEngine engine)
{
var stack = engine.CurrentContext.EvaluationStack;
var key = stack.Peek(1);
var newDataSize = stack.Peek(2).GetByteLength();
StorageKey skey = new StorageKey
{
ScriptHash = engine.CurrentScriptHash,
Key = key.GetSpan().ToArray()
};

var skeyValue = engine.Snapshot.Storages.TryGet(skey);

if (skeyValue == null || !engine.TryAddUpdatedKey(skey.Key))
return (key.GetByteLength() + newDataSize) * GasPerByte;

var currentOccupiedBytes = skeyValue.Value.Length;

if (newDataSize <= currentOccupiedBytes)
{
var releasedBytes = currentOccupiedBytes - newDataSize;
return releasedBytes * GasPerReleasedByte;
}
else
{
var reusedBytes = currentOccupiedBytes;
return (newDataSize - reusedBytes) * GasPerByte;
}
}

internal static bool Invoke(ApplicationEngine engine, uint method)
Expand All @@ -149,6 +203,13 @@ private static uint Register(string method, Func<ApplicationEngine, bool> handle
return descriptor.Hash;
}

private static uint Register(string method, Func<ApplicationEngine, bool> handler, Func<ApplicationEngine, long> priceCalculator, TriggerType allowedTriggers)
{
InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers);
methods.Add(descriptor.Hash, descriptor);
return descriptor.Hash;
}

private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine)
{
engine.CurrentContext.EvaluationStack.Push(
Expand Down Expand Up @@ -522,40 +583,16 @@ private static bool PutEx(ApplicationEngine engine, StorageContext context, byte
var skeyValue = engine.Snapshot.Storages.TryGet(skey);
if (skeyValue?.IsConstant == true) return false;

var currentOccupiedBytes = skeyValue == null ? 0 : skeyValue.Size;
var releasedBytes = 0;
var reusedBytes = 0;

if (value.Length == 0 && !flags.HasFlag(StorageFlags.Constant))
{
// If put 'value' is empty (and non-const), we remove it (implicit `Storage.Delete`)
engine.Snapshot.Storages.Delete(skey);
releasedBytes = currentOccupiedBytes;
}
else
{
StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem());
item.Value = value;
item.IsConstant = flags.HasFlag(StorageFlags.Constant);
if (value.Length < currentOccupiedBytes)
{
releasedBytes = currentOccupiedBytes - value.Length;
reusedBytes = currentOccupiedBytes - releasedBytes;
}
else
{
reusedBytes = currentOccupiedBytes;
}
}

if (releasedBytes > 0)
{
engine.StorageReleased(releasedBytes);
}

if (reusedBytes > 0)
{
engine.StorageReused(reusedBytes);
}

return true;
Expand Down Expand Up @@ -598,8 +635,6 @@ private static bool Storage_Delete(ApplicationEngine engine)
var value = engine.Snapshot.Storages.TryGet(key);
if (value?.IsConstant == true) return false;
engine.Snapshot.Storages.Delete(key);
if (value != null)
engine.StorageReleased(value.Size);
return true;
}
return false;
Expand Down
54 changes: 0 additions & 54 deletions src/neo/SmartContract/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public sealed class PolicyContract : NativeContract

private const byte Prefix_MaxTransactionsPerBlock = 23;
private const byte Prefix_FeePerByte = 10;
private const byte Prefix_ReleasedStorageDiscountPerByte = 11;
private const byte Prefix_ReusedStorageDiscountPerByte = 12;
private const byte Prefix_BlockedAccounts = 15;
private const byte Prefix_MaxBlockSize = 16;

Expand Down Expand Up @@ -65,14 +63,6 @@ internal override bool Initialize(ApplicationEngine engine)
{
Value = new UInt160[0].ToByteArray()
});
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_ReleasedStorageDiscountPerByte), new StorageItem
{
Value = BitConverter.GetBytes(500L)
});
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_ReusedStorageDiscountPerByte), new StorageItem
{
Value = BitConverter.GetBytes(200L)
});
return true;
}

Expand Down Expand Up @@ -109,30 +99,6 @@ public long GetFeePerByte(StoreView snapshot)
return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_FeePerByte)].Value, 0);
}

[ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)]
private StackItem GetDiscountPerByteReleased(ApplicationEngine engine, Array args)
{
return GetDiscountPerByteReleased(engine.Snapshot);
}

public long GetDiscountPerByteReleased(StoreView snapshot)
{
return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_ReleasedStorageDiscountPerByte)].Value, 0);
}

[ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)]
private StackItem GetDiscountPerByteReused(ApplicationEngine engine, Array args)
{
return GetDiscountPerByteReused(engine.Snapshot);
}

public long GetDiscountPerByteReused(StoreView snapshot)
{
return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_ReusedStorageDiscountPerByte)].Value, 0);
}



[ContractMethod(0_01000000, ContractParameterType.Array, SafeMethod = true)]
private StackItem GetBlockedAccounts(ApplicationEngine engine, Array args)
{
Expand Down Expand Up @@ -165,26 +131,6 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array arg
return true;
}

[ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })]
private StackItem SetDiscountPerByteReleased(ApplicationEngine engine, Array args)
{
if (!CheckValidators(engine)) return false;
long value = (long)args[0].GetBigInteger();
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ReleasedStorageDiscountPerByte));
storage.Value = BitConverter.GetBytes(value);
return true;
}

[ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })]
private StackItem SetDiscountPerByteReused(ApplicationEngine engine, Array args)
{
if (!CheckValidators(engine)) return false;
long value = (long)args[0].GetBigInteger();
StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ReusedStorageDiscountPerByte));
storage.Value = BitConverter.GetBytes(value);
return true;
}

[ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })]
private StackItem SetFeePerByte(ApplicationEngine engine, Array args)
{
Expand Down
4 changes: 2 additions & 2 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size)
if (witness_script.IsSignatureContract())
{
size += 67 + witness_script.GetVarSize();
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null);
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify);
}
else if (witness_script.IsMultiSigContract(out int m, out int n))
{
Expand All @@ -366,7 +366,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size)
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n;
using (ScriptBuilder sb = new ScriptBuilder())
networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]];
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null) * n;
networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify) * n;
}
else
{
Expand Down
Loading

0 comments on commit dca1eb2

Please sign in to comment.