Skip to content

Commit

Permalink
Native: store ID->hash map in ContractManagement, add iterator
Browse files Browse the repository at this point in the history
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 neo-project#2803.
  • Loading branch information
roman-khimov committed Aug 22, 2022
1 parent 5f4c14e commit 7f1d886
Showing 1 changed file with 41 additions and 0 deletions.
41 changes: 41 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 @@ -28,6 +30,7 @@ namespace Neo.SmartContract.Native
public sealed class ContractManagement : NativeContract
{
private const byte Prefix_MinimumDeploymentFee = 20;
private const byte Prefix_ContractHash = 17;
private const byte Prefix_NextAvailableId = 15;
private const byte Prefix_Contract = 8;

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

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

/// <summary>
/// Gets hashes of all 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;
var enumerator = GetContractHashesInternal(snapshot)
.Select(p => (p.Key, p.Value))
.GetEnumerator();
return new StorageIterator(enumerator, 1, options);
}

internal IEnumerable<(StorageKey Key, StorageItem Value, int Id, UInt256 Hash)> GetContractHashesInternal(DataCache snapshot)
{
byte[] prefix_key = CreateStorageKey(Prefix_ContractHash).ToArray();
return snapshot.Find(prefix_key)
.Select(p => (p.Key, p.Value, Id: BinaryPrimitives.ReadInt32BigEndian(p.Key.Key.Span[1..]), Hash: new UInt256(p.Value.Value.Span)))
.Where(p => p.Id >= 0);
}

/// <summary>
/// Check if a method exists in a contract.
/// </summary>
Expand Down Expand Up @@ -215,6 +255,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

0 comments on commit 7f1d886

Please sign in to comment.