Skip to content

Commit

Permalink
Simplify access to storage for native contracts. (#1583)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored Apr 20, 2020
1 parent 34962a7 commit a5cf5b2
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 334 deletions.
4 changes: 2 additions & 2 deletions src/neo/IO/Caching/DataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public TValue GetAndChange(TKey key, Func<TValue> factory = null)
{
if (trackable.State == TrackState.Deleted)
{
if (factory == null) throw new KeyNotFoundException();
if (factory == null) return null;
trackable.Item = factory();
trackable.State = TrackState.Changed;
}
Expand All @@ -237,7 +237,7 @@ public TValue GetAndChange(TKey key, Func<TValue> factory = null)
};
if (trackable.Item == null)
{
if (factory == null) throw new KeyNotFoundException();
if (factory == null) return null;
trackable.Item = factory();
trackable.State = TrackState.Added;
}
Expand Down
5 changes: 5 additions & 0 deletions src/neo/Ledger/ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ void ICloneable<ContractState>.FromReplica(ContractState replica)
Manifest = replica.Manifest.Clone();
}

void IInteroperable.FromStackItem(StackItem stackItem)
{
throw new NotSupportedException();
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(Id);
Expand Down
46 changes: 45 additions & 1 deletion src/neo/Ledger/StorageItem.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
using Neo.IO;
using Neo.SmartContract;
using System.IO;

namespace Neo.Ledger
{
public class StorageItem : ICloneable<StorageItem>, ISerializable
{
public byte[] Value;
private byte[] value;
private IInteroperable interoperable;
public bool IsConstant;

public int Size => Value.GetVarSize() + sizeof(bool);

public byte[] Value
{
get
{
if (value is null && interoperable != null)
value = BinarySerializer.Serialize(interoperable.ToStackItem(null), 4096);
return value;
}
set
{
interoperable = null;
this.value = value;
}
}

public StorageItem()
{
}

public StorageItem(byte[] value, bool isConstant = false)
{
this.value = value;
this.IsConstant = isConstant;
}

public StorageItem(IInteroperable interoperable, bool isConstant = false)
{
this.interoperable = interoperable;
this.IsConstant = isConstant;
}

StorageItem ICloneable<StorageItem>.Clone()
{
return new StorageItem
Expand All @@ -31,6 +64,17 @@ void ICloneable<StorageItem>.FromReplica(StorageItem replica)
IsConstant = replica.IsConstant;
}

public T GetInteroperable<T>() where T : IInteroperable, new()
{
if (interoperable is null)
{
interoperable = new T();
interoperable.FromStackItem(BinarySerializer.Deserialize(value, 16, 34));
}
value = null;
return (T)interoperable;
}

public void Serialize(BinaryWriter writer)
{
writer.WriteVarBytes(Value);
Expand Down
5 changes: 5 additions & 0 deletions src/neo/Network/P2P/Payloads/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public override bool Equals(object obj)
return Equals(obj as Block);
}

void IInteroperable.FromStackItem(StackItem stackItem)
{
throw new NotSupportedException();
}

public override int GetHashCode()
{
return Hash.GetHashCode();
Expand Down
5 changes: 5 additions & 0 deletions src/neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public override bool Equals(object obj)
return Equals(obj as Transaction);
}

void IInteroperable.FromStackItem(StackItem stackItem)
{
throw new NotSupportedException();
}

public override int GetHashCode()
{
return Hash.GetHashCode();
Expand Down
1 change: 1 addition & 0 deletions src/neo/SmartContract/IInteroperable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Neo.SmartContract
{
public interface IInteroperable
{
void FromStackItem(StackItem stackItem);
StackItem ToStackItem(ReferenceCounter referenceCounter);
}
}
68 changes: 21 additions & 47 deletions src/neo/SmartContract/Native/Tokens/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco
if (state.VoteTo != null)
{
StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Candidate, state.VoteTo.ToArray()));
CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value);
CandidateState state_validator = storage_validator.GetInteroperable<CandidateState>();
state_validator.Votes += amount;
storage_validator.Value = state_validator.ToByteArray();
}
}

Expand All @@ -55,7 +54,6 @@ private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountSta
BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index);
state.BalanceHeight = engine.Snapshot.PersistingBlock.Index;
GAS.Mint(engine, account, gas);
engine.Snapshot.Storages.GetAndChange(CreateAccountKey(account)).Value = state.ToByteArray();
}

private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end)
Expand Down Expand Up @@ -130,7 +128,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end)
{
StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account));
if (storage is null) return BigInteger.Zero;
AccountState state = new AccountState(storage.Value);
AccountState state = storage.GetInteroperable<AccountState>();
return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end);
}

Expand All @@ -146,13 +144,9 @@ private StackItem RegisterCandidate(ApplicationEngine engine, Array args)
private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey)
{
StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey);
StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem
{
Value = new CandidateState().ToByteArray()
});
CandidateState state = CandidateState.FromByteArray(item.Value);
StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem(new CandidateState()));
CandidateState state = item.GetInteroperable<CandidateState>();
state.Registered = true;
item.Value = state.ToByteArray();
return true;
}

Expand All @@ -170,16 +164,11 @@ private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey)
StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey);
if (snapshot.Storages.TryGet(key) is null) return true;
StorageItem item = snapshot.Storages.GetAndChange(key);
CandidateState state = CandidateState.FromByteArray(item.Value);
CandidateState state = item.GetInteroperable<CandidateState>();
if (state.Votes.IsZero)
{
snapshot.Storages.Delete(key);
}
else
{
state.Registered = false;
item.Value = state.ToByteArray();
}
return true;
}

Expand All @@ -197,29 +186,25 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo)
StorageKey key_account = CreateAccountKey(account);
if (snapshot.Storages.TryGet(key_account) is null) return false;
StorageItem storage_account = snapshot.Storages.GetAndChange(key_account);
AccountState state_account = new AccountState(storage_account.Value);
AccountState state_account = storage_account.GetInteroperable<AccountState>();
if (state_account.VoteTo != null)
{
StorageKey key = CreateStorageKey(Prefix_Candidate, state_account.VoteTo.ToArray());
StorageItem storage_validator = snapshot.Storages.GetAndChange(key);
CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value);
CandidateState state_validator = storage_validator.GetInteroperable<CandidateState>();
state_validator.Votes -= state_account.Balance;
if (!state_validator.Registered && state_validator.Votes.IsZero)
snapshot.Storages.Delete(key);
else
storage_validator.Value = state_validator.ToByteArray();
}
state_account.VoteTo = voteTo;
storage_account.Value = state_account.ToByteArray();
if (voteTo != null)
{
StorageKey key = CreateStorageKey(Prefix_Candidate, voteTo.ToArray());
if (snapshot.Storages.TryGet(key) is null) return false;
StorageItem storage_validator = snapshot.Storages.GetAndChange(key);
CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value);
CandidateState state_validator = storage_validator.GetInteroperable<CandidateState>();
if (!state_validator.Registered) return false;
state_validator.Votes += state_account.Balance;
storage_validator.Value = state_validator.ToByteArray();
}
return true;
}
Expand All @@ -236,7 +221,7 @@ private StackItem GetCandidates(ApplicationEngine engine, Array args)
return snapshot.Storages.Find(prefix_key).Select(p =>
(
p.Key.Key.AsSerializable<ECPoint>(1),
CandidateState.FromByteArray(p.Value.Value)
p.Value.GetInteroperable<CandidateState>()
)).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes));
}

Expand Down Expand Up @@ -285,49 +270,38 @@ public class AccountState : Nep5AccountState
public uint BalanceHeight;
public ECPoint VoteTo;

public AccountState()
{
}

public AccountState(byte[] data)
: base(data)
{
}

protected override void FromStruct(Struct @struct)
public override void FromStackItem(StackItem stackItem)
{
base.FromStruct(@struct);
base.FromStackItem(stackItem);
Struct @struct = (Struct)stackItem;
BalanceHeight = (uint)@struct[1].GetBigInteger();
VoteTo = @struct[2].IsNull ? null : @struct[2].GetSpan().AsSerializable<ECPoint>();
}

protected override Struct ToStruct()
public override StackItem ToStackItem(ReferenceCounter referenceCounter)
{
Struct @struct = base.ToStruct();
Struct @struct = (Struct)base.ToStackItem(referenceCounter);
@struct.Add(BalanceHeight);
@struct.Add(VoteTo?.ToArray() ?? StackItem.Null);
return @struct;
}
}

internal class CandidateState
internal class CandidateState : IInteroperable
{
public bool Registered = true;
public BigInteger Votes;

public static CandidateState FromByteArray(byte[] data)
public void FromStackItem(StackItem stackItem)
{
Struct @struct = (Struct)BinarySerializer.Deserialize(data, 16, 32);
return new CandidateState
{
Registered = @struct[0].ToBoolean(),
Votes = @struct[1].GetBigInteger()
};
Struct @struct = (Struct)stackItem;
Registered = @struct[0].ToBoolean();
Votes = @struct[1].GetBigInteger();
}

public byte[] ToByteArray()
public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return BinarySerializer.Serialize(new Struct { Registered, Votes }, 32);
return new Struct(referenceCounter) { Registered, Votes };
}
}
}
Expand Down
29 changes: 5 additions & 24 deletions src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,18 @@

namespace Neo.SmartContract.Native.Tokens
{
public class Nep5AccountState
public class Nep5AccountState : IInteroperable
{
public BigInteger Balance;

public Nep5AccountState()
public virtual void FromStackItem(StackItem stackItem)
{
Balance = ((Struct)stackItem)[0].GetBigInteger();
}

public Nep5AccountState(byte[] data)
public virtual StackItem ToStackItem(ReferenceCounter referenceCounter)
{
FromByteArray(data);
}

public void FromByteArray(byte[] data)
{
FromStruct((Struct)BinarySerializer.Deserialize(data, 16, 34));
}

protected virtual void FromStruct(Struct @struct)
{
Balance = @struct[0].GetBigInteger();
}

public byte[] ToByteArray()
{
return BinarySerializer.Serialize(ToStruct(), 4096);
}

protected virtual Struct ToStruct()
{
return new Struct(new StackItem[] { Balance });
return new Struct(referenceCounter) { Balance };
}
}
}
Loading

0 comments on commit a5cf5b2

Please sign in to comment.