Skip to content

Commit

Permalink
Native: store ID->hash map in ContractManagement, add iterator (#2807)
Browse files Browse the repository at this point in the history
* Native: store ID->hash map in ContractManagement, add iterator

That probably is the lighest design possible, store and retrieve hashes
only. But it can be expanded to return full contract state (getContractByID,
for example) and probably even iterate over contract states.

Fixes #2803.

* ContractManagement: rework GetContractHash into GetContractById

* ContractManagement: fix GetContractById comment

* Update ContractManagement.cs

* Update ContractManagement.cs

* Update src/Neo/SmartContract/Native/ContractManagement.cs

Co-authored-by: Shargon <shargon@gmail.com>

Co-authored-by: Erik Zhang <erik@neo.org>
Co-authored-by: Shargon <shargon@gmail.com>
  • Loading branch information
3 people authored Sep 5, 2022
1 parent 91706ec commit 90cf23b
Showing 1 changed file with 38 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/Neo/SmartContract/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
using Neo.IO;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract.Iterators;
using Neo.SmartContract.Manifest;
using Neo.VM.Types;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
Expand All @@ -30,6 +32,7 @@ public sealed class ContractManagement : NativeContract
private const byte Prefix_MinimumDeploymentFee = 20;
private const byte Prefix_NextAvailableId = 15;
private const byte Prefix_Contract = 8;
private const byte Prefix_ContractHash = 12;

internal ContractManagement()
{
Expand Down Expand Up @@ -143,6 +146,39 @@ public ContractState GetContract(DataCache snapshot, UInt160 hash)
return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable<ContractState>();
}

/// <summary>
/// Maps specified ID to deployed contract.
/// </summary>
/// <param name="snapshot">The snapshot used to read data.</param>
/// <param name="id">Contract ID.</param>
/// <returns>The deployed contract.</returns>
[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)]
public ContractState GetContractById(DataCache snapshot, int id)
{
StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_ContractHash).AddBigEndian(id));
if (item is null) return null;
var hash = new UInt160(item.Value.Span);
return GetContract(snapshot, hash);
}

/// <summary>
/// Gets hashes of all non native deployed contracts.
/// </summary>
/// <param name="snapshot">The snapshot used to read data.</param>
/// <returns>Iterator with hashes of all deployed contracts.</returns>
[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)]
private IIterator GetContractHashes(DataCache snapshot)
{
const FindOptions options = FindOptions.RemovePrefix;
byte[] prefix_key = CreateStorageKey(Prefix_ContractHash).ToArray();
var enumerator = snapshot.Find(prefix_key)
.Select(p => (p.Key, p.Value, Id: BinaryPrimitives.ReadInt32BigEndian(p.Key.Key.Span[1..])))
.Where(p => p.Id >= 0)
.Select(p => (p.Key, p.Value))
.GetEnumerator();
return new StorageIterator(enumerator, 1, options);
}

/// <summary>
/// Check if a method exists in a contract.
/// </summary>
Expand Down Expand Up @@ -215,6 +251,7 @@ private async ContractTask<ContractState> Deploy(ApplicationEngine engine, byte[
if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException($"Invalid Manifest Hash: {hash}");

engine.Snapshot.Add(key, new StorageItem(contract));
engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(hash.ToArray()));

await OnDeploy(engine, contract, data, false);

Expand Down Expand Up @@ -270,6 +307,7 @@ private void Destroy(ApplicationEngine engine)
ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable<ContractState>();
if (contract is null) return;
engine.Snapshot.Delete(ckey);
engine.Snapshot.Delete(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id));
foreach (var (key, _) in engine.Snapshot.Find(StorageKey.CreateSearchPrefix(contract.Id, ReadOnlySpan<byte>.Empty)))
engine.Snapshot.Delete(key);
// lock contract
Expand Down

0 comments on commit 90cf23b

Please sign in to comment.