Skip to content

Commit

Permalink
Add ID to ContractState (#1400)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored Jan 27, 2020
1 parent 35c73ce commit 6521582
Show file tree
Hide file tree
Showing 29 changed files with 168 additions and 185 deletions.
37 changes: 37 additions & 0 deletions src/neo/Ledger/ContractIdState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Neo.IO;
using System;
using System.IO;

namespace Neo.Ledger
{
public class ContractIdState : ICloneable<ContractIdState>, ISerializable
{
public int NextId;

int ISerializable.Size => sizeof(int);

ContractIdState ICloneable<ContractIdState>.Clone()
{
return new ContractIdState
{
NextId = NextId
};
}

void ISerializable.Deserialize(BinaryReader reader)
{
NextId = reader.ReadInt32();
if (NextId < 0) throw new FormatException();
}

void ICloneable<ContractIdState>.FromReplica(ContractIdState replica)
{
NextId = replica.NextId;
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(NextId);
}
}
}
16 changes: 7 additions & 9 deletions src/neo/Ledger/ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Neo.Ledger
{
public class ContractState : ICloneable<ContractState>, ISerializable, IInteroperable
{
public int Id;
public byte[] Script;
public ContractManifest Manifest;

Expand All @@ -31,52 +32,49 @@ public UInt160 ScriptHash
}
}

int ISerializable.Size => Script.GetVarSize() + Manifest.ToJson().ToString().GetVarSize();
int ISerializable.Size => sizeof(int) + Script.GetVarSize() + Manifest.ToJson().ToString().GetVarSize();

ContractState ICloneable<ContractState>.Clone()
{
return new ContractState
{
Id = Id,
Script = Script,
Manifest = Manifest.Clone()
};
}

void ISerializable.Deserialize(BinaryReader reader)
{
Id = reader.ReadInt32();
Script = reader.ReadVarBytes();
Manifest = reader.ReadSerializable<ContractManifest>();
}

void ICloneable<ContractState>.FromReplica(ContractState replica)
{
Id = replica.Id;
Script = replica.Script;
Manifest = replica.Manifest.Clone();
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(Id);
writer.WriteVarBytes(Script);
writer.Write(Manifest);
}

public JObject ToJson()
{
JObject json = new JObject();
json["id"] = Id;
json["hash"] = ScriptHash.ToString();
json["script"] = Convert.ToBase64String(Script);
json["manifest"] = Manifest.ToJson();
return json;
}

public static ContractState FromJson(JObject json)
{
ContractState contractState = new ContractState();
contractState.Script = Convert.FromBase64String(json["script"].AsString());
contractState.Manifest = ContractManifest.FromJson(json["manifest"]);
return contractState;
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, new StackItem[] { Script, HasStorage, Payable });
Expand Down
16 changes: 8 additions & 8 deletions src/neo/Ledger/StorageKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ namespace Neo.Ledger
{
public class StorageKey : IEquatable<StorageKey>, ISerializable
{
public UInt160 ScriptHash;
public int Id;
public byte[] Key;

int ISerializable.Size => ScriptHash.Size + (Key.Length / 16 + 1) * 17;
int ISerializable.Size => sizeof(int) + (Key.Length / 16 + 1) * 17;

internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix)
internal static byte[] CreateSearchPrefix(int id, byte[] prefix)
{
using (MemoryStream ms = new MemoryStream())
{
Expand All @@ -27,13 +27,13 @@ internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix)
}
if (remain > 0)
ms.Write(prefix, index, remain);
return Helper.Concat(hash.ToArray(), ms.ToArray());
return Helper.Concat(BitConverter.GetBytes(id), ms.ToArray());
}
}

void ISerializable.Deserialize(BinaryReader reader)
{
ScriptHash = reader.ReadSerializable<UInt160>();
Id = reader.ReadInt32();
Key = reader.ReadBytesWithGrouping();
}

Expand All @@ -43,7 +43,7 @@ public bool Equals(StorageKey other)
return false;
if (ReferenceEquals(this, other))
return true;
return ScriptHash.Equals(other.ScriptHash) && MemoryExtensions.SequenceEqual<byte>(Key, other.Key);
return Id == other.Id && MemoryExtensions.SequenceEqual<byte>(Key, other.Key);
}

public override bool Equals(object obj)
Expand All @@ -54,12 +54,12 @@ public override bool Equals(object obj)

public override int GetHashCode()
{
return ScriptHash.GetHashCode() + (int)Key.Murmur32(0);
return Id.GetHashCode() + (int)Key.Murmur32(0);
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(ScriptHash);
writer.Write(Id);
writer.WriteBytesWithGrouping(Key);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/neo/Persistence/ClonedView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal class ClonedView : StoreView
public override DataCache<SerializableWrapper<uint>, HeaderHashList> HeaderHashList { get; }
public override MetaDataCache<HashIndexState> BlockHashIndex { get; }
public override MetaDataCache<HashIndexState> HeaderHashIndex { get; }
public override MetaDataCache<ContractIdState> ContractId { get; }

public ClonedView(StoreView view)
{
Expand All @@ -24,6 +25,7 @@ public ClonedView(StoreView view)
this.HeaderHashList = view.HeaderHashList.CreateSnapshot();
this.BlockHashIndex = view.BlockHashIndex.CreateSnapshot();
this.HeaderHashIndex = view.HeaderHashIndex.CreateSnapshot();
this.ContractId = view.ContractId.CreateSnapshot();
}
}
}
1 change: 1 addition & 0 deletions src/neo/Persistence/Prefixes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal static class Prefixes
public const byte IX_HeaderHashList = 0x80;
public const byte IX_CurrentBlock = 0xc0;
public const byte IX_CurrentHeader = 0xc1;
public const byte IX_ContractId = 0xc2;

/* Prefixes 0xf0 to 0xff are reserved for external use.
*
Expand Down
1 change: 1 addition & 0 deletions src/neo/Persistence/ReadOnlyView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class ReadOnlyView : StoreView
public override DataCache<SerializableWrapper<uint>, HeaderHashList> HeaderHashList => new StoreDataCache<SerializableWrapper<uint>, HeaderHashList>(store, Prefixes.IX_HeaderHashList);
public override MetaDataCache<HashIndexState> BlockHashIndex => new StoreMetaDataCache<HashIndexState>(store, Prefixes.IX_CurrentBlock);
public override MetaDataCache<HashIndexState> HeaderHashIndex => new StoreMetaDataCache<HashIndexState>(store, Prefixes.IX_CurrentHeader);
public override MetaDataCache<ContractIdState> ContractId => new StoreMetaDataCache<ContractIdState>(store, Prefixes.IX_ContractId);

public ReadOnlyView(IReadOnlyStore store)
{
Expand Down
2 changes: 2 additions & 0 deletions src/neo/Persistence/SnapshotView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class SnapshotView : StoreView, IDisposable
public override DataCache<SerializableWrapper<uint>, HeaderHashList> HeaderHashList { get; }
public override MetaDataCache<HashIndexState> BlockHashIndex { get; }
public override MetaDataCache<HashIndexState> HeaderHashIndex { get; }
public override MetaDataCache<ContractIdState> ContractId { get; }

public SnapshotView(IStore store)
{
Expand All @@ -30,6 +31,7 @@ public SnapshotView(IStore store)
HeaderHashList = new StoreDataCache<SerializableWrapper<uint>, HeaderHashList>(snapshot, Prefixes.IX_HeaderHashList);
BlockHashIndex = new StoreMetaDataCache<HashIndexState>(snapshot, Prefixes.IX_CurrentBlock);
HeaderHashIndex = new StoreMetaDataCache<HashIndexState>(snapshot, Prefixes.IX_CurrentHeader);
ContractId = new StoreMetaDataCache<ContractIdState>(snapshot, Prefixes.IX_ContractId);
}

public override void Commit()
Expand Down
2 changes: 2 additions & 0 deletions src/neo/Persistence/StoreView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public abstract class StoreView
public abstract DataCache<SerializableWrapper<uint>, HeaderHashList> HeaderHashList { get; }
public abstract MetaDataCache<HashIndexState> BlockHashIndex { get; }
public abstract MetaDataCache<HashIndexState> HeaderHashIndex { get; }
public abstract MetaDataCache<ContractIdState> ContractId { get; }

public uint Height => BlockHashIndex.Get().Index;
public uint HeaderHeight => HeaderHashIndex.Get().Index;
Expand All @@ -38,6 +39,7 @@ public virtual void Commit()
HeaderHashList.Commit();
BlockHashIndex.Commit();
HeaderHashIndex.Commit();
ContractId.Commit();
}

public bool ContainsBlock(UInt256 hash)
Expand Down
19 changes: 3 additions & 16 deletions src/neo/SmartContract/InteropService.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ private static bool Contract_Create(ApplicationEngine engine)
if (contract != null) return false;
contract = new ContractState
{
Id = engine.Snapshot.ContractId.GetAndChange().NextId++,
Script = script,
Manifest = ContractManifest.Parse(manifest)
};
Expand Down Expand Up @@ -66,27 +67,13 @@ private static bool Contract_Update(ApplicationEngine engine)
if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false;
contract = new ContractState
{
Id = contract.Id,
Script = script,
Manifest = contract.Manifest
};
contract.Manifest.Abi.Hash = hash_new;
engine.Snapshot.Contracts.Add(hash_new, contract);
if (contract.HasStorage)
{
foreach (var (key, value) in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray())
{
engine.Snapshot.Storages.Add(new StorageKey
{
ScriptHash = hash_new,
Key = key.Key
}, new StorageItem
{
Value = value.Value,
IsConstant = false
});
}
}
Contract_Destroy(engine);
engine.Snapshot.Contracts.Delete(engine.CurrentScriptHash);
}
if (manifest.Length > 0)
{
Expand Down
1 change: 1 addition & 0 deletions src/neo/SmartContract/InteropService.Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private static bool Native_Deploy(ApplicationEngine engine)
{
engine.Snapshot.Contracts.Add(contract.Hash, new ContractState
{
Id = contract.Id,
Script = contract.Script,
Manifest = contract.Manifest
});
Expand Down
32 changes: 13 additions & 19 deletions src/neo/SmartContract/InteropService.Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ public static class Storage
public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates);

private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash);
if (contract == null) return false;
if (!contract.HasStorage) return false;
return true;
}

private static long GetStoragePrice(EvaluationStack stack)
{
return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte;
Expand All @@ -42,11 +34,10 @@ private static bool PutExInternal(ApplicationEngine engine, StorageContext conte
if (key.Length > MaxKeySize) return false;
if (value.Length > MaxValueSize) return false;
if (context.IsReadOnly) return false;
if (!CheckStorageContext(engine, context)) return false;

StorageKey skey = new StorageKey
{
ScriptHash = context.ScriptHash,
Id = context.Id,
Key = key
};

Expand All @@ -68,19 +59,25 @@ private static bool PutExInternal(ApplicationEngine engine, StorageContext conte

private static bool Storage_GetContext(ApplicationEngine engine)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash);
if (contract == null) return false;
if (!contract.HasStorage) return false;
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext
{
ScriptHash = engine.CurrentScriptHash,
Id = contract.Id,
IsReadOnly = false
}));
return true;
}

private static bool Storage_GetReadOnlyContext(ApplicationEngine engine)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash);
if (contract == null) return false;
if (!contract.HasStorage) return false;
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext
{
ScriptHash = engine.CurrentScriptHash,
Id = contract.Id,
IsReadOnly = true
}));
return true;
Expand All @@ -94,7 +91,7 @@ private static bool Storage_AsReadOnly(ApplicationEngine engine)
if (!context.IsReadOnly)
context = new StorageContext
{
ScriptHash = context.ScriptHash,
Id = context.Id,
IsReadOnly = true
};
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(context));
Expand All @@ -108,11 +105,10 @@ private static bool Storage_Get(ApplicationEngine engine)
if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)
{
StorageContext context = _interface.GetInterface<StorageContext>();
if (!CheckStorageContext(engine, context)) return false;
byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray();
StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey
{
ScriptHash = context.ScriptHash,
Id = context.Id,
Key = key
});
engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null);
Expand All @@ -126,9 +122,8 @@ private static bool Storage_Find(ApplicationEngine engine)
if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)
{
StorageContext context = _interface.GetInterface<StorageContext>();
if (!CheckStorageContext(engine, context)) return false;
byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray();
byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix);
byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix);
StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator()));
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator));
return true;
Expand Down Expand Up @@ -163,10 +158,9 @@ private static bool Storage_Delete(ApplicationEngine engine)
{
StorageContext context = _interface.GetInterface<StorageContext>();
if (context.IsReadOnly) return false;
if (!CheckStorageContext(engine, context)) return false;
StorageKey key = new StorageKey
{
ScriptHash = context.ScriptHash,
Id = context.Id,
Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray()
};
if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false;
Expand Down
3 changes: 2 additions & 1 deletion src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public abstract class NativeContract
public uint ServiceHash { get; }
public byte[] Script { get; }
public UInt160 Hash { get; }
public abstract int Id { get; }
public ContractManifest Manifest { get; }
public virtual string[] SupportedStandards { get; } = { "NEP-10" };

Expand Down Expand Up @@ -71,7 +72,7 @@ protected StorageKey CreateStorageKey(byte prefix, byte[] key = null)
{
StorageKey storageKey = new StorageKey
{
ScriptHash = Hash,
Id = Id,
Key = new byte[sizeof(byte) + (key?.Length ?? 0)]
};
storageKey.Key[0] = prefix;
Expand Down
1 change: 1 addition & 0 deletions src/neo/SmartContract/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Neo.SmartContract.Native
public sealed class PolicyContract : NativeContract
{
public override string ServiceName => "Neo.Native.Policy";
public override int Id => -3;

private const byte Prefix_MaxTransactionsPerBlock = 23;
private const byte Prefix_FeePerByte = 10;
Expand Down
Loading

0 comments on commit 6521582

Please sign in to comment.