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

Simplify access to storage for native contracts. #1583

Merged
merged 3 commits into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
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);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
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 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreVie
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