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

Update contract without change the hash #2044

Merged
merged 59 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
9279b77
Update without hash change
shargon Nov 3, 2020
95e29ba
Specify version
shargon Nov 3, 2020
dbd18ce
Merge branch 'master' into update-no-hash-change
shargon Nov 4, 2020
37828b2
Merge branch 'master' into update-no-hash-change
shargon Nov 5, 2020
b1de3b7
Update ContractState.cs
erikzhang Nov 6, 2020
ab16cea
Update ApplicationEngine.Contract.cs
erikzhang Nov 6, 2020
9ada6b1
Update ApplicationEngine.Contract.cs
erikzhang Nov 6, 2020
4fb82f1
Update ApplicationEngine.Contract.cs
erikzhang Nov 6, 2020
31a4153
Change to ushort
shargon Nov 7, 2020
b4ba71c
Return Id
shargon Nov 7, 2020
b1962d6
Merge branch 'master' into update-no-hash-change
shargon Nov 7, 2020
658a597
Update ContractState.cs
shargon Nov 7, 2020
be896c5
Remove hash_new check
shargon Nov 7, 2020
da309c2
Update ApplicationEngine.Contract.cs
shargon Nov 7, 2020
9835862
Fix UT
shargon Nov 7, 2020
ad441b6
Merge branch 'master' into update-no-hash-change
shargon Nov 10, 2020
1e3d16a
Merge branch 'master' into update-no-hash-change
shargon Nov 12, 2020
7d2d476
Merge branch 'master' into update-no-hash-change
shargon Nov 12, 2020
9d4f65c
Remove Hash from ABI and change hash strategy
shargon Nov 12, 2020
ca93390
Clean code
shargon Nov 12, 2020
6a7912c
Remove MaxLength
shargon Nov 12, 2020
a2618a5
Change script size verification to nefFile
shargon Nov 13, 2020
c9d024c
Rename Version to UpdateCounter
shargon Nov 13, 2020
09d5efd
Rename ContractState.ScriptHash to Hash
shargon Nov 16, 2020
099922d
Merge branch 'master' into update-no-hash-change
erikzhang Nov 18, 2020
457818f
Some Erik's suggestions
shargon Nov 18, 2020
312fb08
Some Erik's suggestions
shargon Nov 18, 2020
12deb7b
Move CanCall
shargon Nov 18, 2020
138d447
Merge remote-tracking branch 'neo-project/master' into update-no-hash…
shargon Nov 19, 2020
15998ac
Remove Script hash from NefFile
shargon Nov 19, 2020
7703317
Merge branch 'master' into update-no-hash-change
erikzhang Nov 21, 2020
4bcf19f
Move to Helper
shargon Nov 21, 2020
92d32ee
Simplify ContractState
shargon Nov 21, 2020
bed862e
Erik's review
shargon Nov 21, 2020
7b1b422
NefFile.GetHash()
erikzhang Nov 22, 2020
4ccf14c
Remove double check
erikzhang Nov 22, 2020
41a67b8
Merge branch 'master' into update-no-hash-change
shargon Nov 24, 2020
a3b45d1
Merge remote-tracking branch 'neo-project/master' into update-no-hash…
shargon Nov 24, 2020
fe709fb
Some fixes
shargon Nov 24, 2020
1b10459
Some fixes
shargon Nov 24, 2020
4deecdc
Merge branch 'master' into update-no-hash-change
shargon Nov 25, 2020
582deb2
Reduce changes
shargon Nov 25, 2020
fa1075e
Merge branch 'master' into update-no-hash-change
shargon Nov 26, 2020
ba92c2e
Move GetContractHash to Helper
erikzhang Nov 26, 2020
d8ec4bc
Use GetContractHash for native contracts
erikzhang Nov 26, 2020
2b1f913
Fix UT paritially
erikzhang Nov 26, 2020
ae342c5
Merge branch 'master' into update-no-hash-change
shargon Nov 26, 2020
f6bc60d
Some fixes
shargon Nov 26, 2020
84b6100
Fix UT
shargon Nov 26, 2020
27b6bf6
Rename parameters
erikzhang Nov 27, 2020
7177b62
Update NefFile.cs
erikzhang Nov 27, 2020
4b08968
Update NefFile.cs
erikzhang Nov 27, 2020
1d26f56
Merge branch 'master' into update-no-hash-change
shargon Nov 27, 2020
13f23a2
Change version to string
shargon Nov 27, 2020
ecaf7ea
Increase to 32
shargon Nov 29, 2020
971be0d
Fix
shargon Nov 29, 2020
24525cd
Merge branch 'master' into update-no-hash-change
shargon Nov 30, 2020
648e646
Fix UT
shargon Nov 30, 2020
de2eefe
Merge branch 'master' into update-no-hash-change
shargon Dec 1, 2020
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
28 changes: 13 additions & 15 deletions src/neo/Ledger/ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,23 @@ namespace Neo.Ledger
public class ContractState : ICloneable<ContractState>, ISerializable, IInteroperable
{
public int Id;
public uint Version;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
public UInt160 ScriptHash;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
public byte[] Script;
public ContractManifest Manifest;

public bool HasStorage => Manifest.Features.HasFlag(ContractFeatures.HasStorage);
public bool Payable => Manifest.Features.HasFlag(ContractFeatures.Payable);

private UInt160 _scriptHash;
public UInt160 ScriptHash
{
get
{
if (_scriptHash == null)
{
_scriptHash = Script.ToScriptHash();
}
return _scriptHash;
}
}

int ISerializable.Size => sizeof(int) + Script.GetVarSize() + Manifest.Size;
int ISerializable.Size => sizeof(int) + sizeof(uint) + UInt160.Length + Script.GetVarSize() + Manifest.Size;

ContractState ICloneable<ContractState>.Clone()
{
return new ContractState
{
Id = Id,
Version = Version,
ScriptHash = ScriptHash,
Script = Script,
Manifest = Manifest.Clone()
};
Expand All @@ -47,13 +38,17 @@ ContractState ICloneable<ContractState>.Clone()
void ISerializable.Deserialize(BinaryReader reader)
{
Id = reader.ReadInt32();
Version = reader.ReadUInt32();
ScriptHash = reader.ReadSerializable<UInt160>();
Script = reader.ReadVarBytes();
Manifest = reader.ReadSerializable<ContractManifest>();
}

void ICloneable<ContractState>.FromReplica(ContractState replica)
{
Id = replica.Id;
Version = replica.Version;
ScriptHash = replica.ScriptHash;
Script = replica.Script;
Manifest = replica.Manifest.Clone();
}
Expand All @@ -66,6 +61,8 @@ void IInteroperable.FromStackItem(StackItem stackItem)
void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(Id);
writer.Write(Version);
writer.Write(ScriptHash);
writer.WriteVarBytes(Script);
writer.Write(Manifest);
}
Expand All @@ -74,6 +71,7 @@ public JObject ToJson()
{
JObject json = new JObject();
json["id"] = Id;
json["version"] = Version;
json["hash"] = ScriptHash.ToString();
json["script"] = Convert.ToBase64String(Script);
json["manifest"] = Manifest.ToJson();
Expand All @@ -82,7 +80,7 @@ public JObject ToJson()

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, new StackItem[] { Script, Manifest.ToString(), HasStorage, Payable });
return new Array(referenceCounter, new StackItem[] { ScriptHash.ToArray(), Script, Version, Manifest.ToString(), HasStorage, Payable });
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
60 changes: 32 additions & 28 deletions src/neo/SmartContract/ApplicationEngine.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,28 @@ partial class ApplicationEngine
/// </summary>
public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 0_00010000, CallFlags.None, true);

protected internal void CreateContract(byte[] script, byte[] manifest)
protected internal void CreateContract(byte[] nefFile, byte[] manifest)
{
if (script.Length == 0 || script.Length > MaxContractLength)
throw new ArgumentException($"Invalid Script Length: {script.Length}");
if (nefFile.Length == 0 || nefFile.Length > NefFile.MaxLength)
throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}");
if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength)
throw new ArgumentException($"Invalid Manifest Length: {manifest.Length}");

AddGas(StoragePrice * (script.Length + manifest.Length));
AddGas(StoragePrice * (nefFile.Length + manifest.Length));

UInt160 hash = script.ToScriptHash();
NefFile nef = nefFile.AsSerializable<NefFile>();
if (nef.Script.Length == 0 || nef.Script.Length > MaxContractLength)
throw new ArgumentException($"Invalid Script Length: {nef.Script.Length}");

UInt160 hash = nefFile.ToScriptHash();
ContractState contract = Snapshot.Contracts.TryGet(hash);
if (contract != null) throw new InvalidOperationException($"Contract Already Exists: {hash}");
contract = new ContractState
{
Id = Snapshot.ContractId.GetAndChange().NextId++,
Script = script.ToArray(),
Version = 0,
Script = nef.Script,
ScriptHash = hash,
Manifest = ContractManifest.Parse(manifest)
};

Expand All @@ -62,44 +68,43 @@ protected internal void CreateContract(byte[] script, byte[] manifest)
CallContractInternal(contract, md, new Array(ReferenceCounter) { false }, CallFlags.All, CheckReturnType.EnsureIsEmpty);
}

protected internal void UpdateContract(byte[] script, byte[] manifest)
protected internal void UpdateContract(byte[] nefFile, byte[] manifest)
{
if (script is null && manifest is null) throw new ArgumentException();
if (nefFile is null && manifest is null) throw new ArgumentException();
if (nefFile != null && nefFile.Length > NefFile.MaxLength)
throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}");

AddGas(StoragePrice * ((script?.Length ?? 0) + (manifest?.Length ?? 0)));
NefFile nef = nefFile?.AsSerializable<NefFile>();
AddGas(StoragePrice * (nef is null ? manifest.Length : nefFile.Length + (manifest?.Length ?? 0)));

var contract = Snapshot.Contracts.TryGet(CurrentScriptHash);
var contract = Snapshot.Contracts.GetAndChange(CurrentScriptHash);
if (contract is null) throw new InvalidOperationException($"Updating Contract Does Not Exist: {CurrentScriptHash}");

if (script != null)
if (nef != null)
{
if (script.Length == 0 || script.Length > MaxContractLength)
throw new ArgumentException($"Invalid Script Length: {script.Length}");
UInt160 hash_new = script.ToScriptHash();
if (nef.Script.Length == 0 || nef.Script.Length > MaxContractLength)
throw new ArgumentException($"Invalid Script Length: {nef.Script.Length}");

UInt160 hash_new = nefFile.ToScriptHash();
shargon marked this conversation as resolved.
Show resolved Hide resolved
if (hash_new.Equals(CurrentScriptHash) || Snapshot.Contracts.TryGet(hash_new) != null)
throw new InvalidOperationException($"Adding Contract Hash Already Exist: {hash_new}");
contract = new ContractState
{
Id = contract.Id,
Script = script.ToArray(),
Manifest = contract.Manifest
};
contract.Manifest.Abi.Hash = hash_new;
Snapshot.Contracts.Add(hash_new, contract);
Snapshot.Contracts.Delete(CurrentScriptHash);

// Update script and script hash
contract.Script = nef.Script.ToArray();
contract.ScriptHash = hash_new;
}
if (manifest != null)
{
if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength)
throw new ArgumentException($"Invalid Manifest Length: {manifest.Length}");
contract = Snapshot.Contracts.GetAndChange(contract.ScriptHash);
contract.Manifest = ContractManifest.Parse(manifest);
if (!contract.Manifest.IsValid(contract.ScriptHash))
shargon marked this conversation as resolved.
Show resolved Hide resolved
throw new InvalidOperationException($"Invalid Manifest Hash: {contract.ScriptHash}");
if (!contract.HasStorage && Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)).Any())
throw new InvalidOperationException($"Contract Does Not Support Storage But Uses Storage");
}
if (script != null)
contract.Version++; // Increase the version
if (nef != null)
{
ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy");
if (md != null)
Expand Down Expand Up @@ -164,10 +169,9 @@ private void CallContractInternal(ContractState contract, ContractMethodDescript
CallFlags callingFlags = state.CallFlags;

if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method.Name} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments");
ExecutionContext context_new = LoadScript(contract.Script, method.Offset);
ExecutionContext context_new = LoadScript(contract.Script, flags & callingFlags, contract.ScriptHash, method.Offset);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
state = context_new.GetState<ExecutionContextState>();
state.CallingScriptHash = callingScriptHash;
state.CallFlags = flags & callingFlags;

if (NativeContract.IsNative(contract.ScriptHash))
{
Expand All @@ -181,7 +185,7 @@ private void CallContractInternal(ContractState contract, ContractMethodDescript
}

method = contract.Manifest.Abi.GetMethod("_initialize");
if (method != null) LoadContext(context_new.Clone(method.Offset));
if (method != null) LoadClonedContext(context_new, method.Offset, false);
}

protected internal bool IsStandardContract(UInt160 hash)
Expand Down
1 change: 1 addition & 0 deletions src/neo/SmartContract/ApplicationEngine.Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected internal void DeployNativeContracts()
{
Id = contract.Id,
Script = contract.Script,
ScriptHash = contract.Hash, // Not NefHash here
Manifest = contract.Manifest
});
contract.Initialize(this);
Expand Down
20 changes: 16 additions & 4 deletions src/neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,29 @@ protected override void LoadContext(ExecutionContext context)
base.LoadContext(context);
}

internal void LoadContext(ExecutionContext context, bool checkReturnValue)
internal protected void LoadClonedContext(ExecutionContext context, int initialPosition, bool checkReturnValue)
{
// Copy script hash
var state = context.GetState<ExecutionContextState>();
context = context.Clone(initialPosition);
var newState = context.GetState<ExecutionContextState>();
newState.CallFlags = state.CallFlags;
newState.ScriptHash = state.ScriptHash;
// Configure CurrentContext and load the cloned one
if (checkReturnValue)
GetInvocationState(CurrentContext).NeedCheckReturnValue = CheckReturnType.EnsureNotEmpty;
LoadContext(context);
}

public ExecutionContext LoadScript(Script script, CallFlags callFlags, int initialPosition = 0)
public ExecutionContext LoadScript(Script script, CallFlags callFlags, UInt160 scriptHash = null, int initialPosition = 0)
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
{
ExecutionContext context = LoadScript(script, initialPosition);
context.GetState<ExecutionContextState>().CallFlags = callFlags;
// Create and configure context
ExecutionContext context = CreateContext(script, initialPosition);
var state = context.GetState<ExecutionContextState>();
state.CallFlags = callFlags;
state.ScriptHash = scriptHash ?? ((byte[])script).ToScriptHash();
// Load context
LoadContext(context);
return context;
}

Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Callbacks/PointerCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public PointerCallback(ExecutionContext context, Pointer pointer, int parameters

public override void LoadContext(ApplicationEngine engine, Array args)
{
engine.LoadContext(context.Clone(pointer), true);
engine.LoadClonedContext(context, pointer, true);
for (int i = args.Count - 1; i >= 0; i--)
engine.Push(args[i]);
}
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap
using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.Clone(), gas))
{
CallFlags callFlags = verifiable.Witnesses[i].StateDependent ? CallFlags.AllowStates : CallFlags.None;
ExecutionContext context = engine.LoadScript(verification, callFlags, offset);
ExecutionContext context = engine.LoadScript(verification, callFlags, hashes[i], offset);
if (NativeContract.IsNative(hashes[i]))
{
using ScriptBuilder sb = new ScriptBuilder();
Expand All @@ -187,7 +187,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap
}
else if (init != null)
{
engine.LoadContext(context.Clone(init.Offset), false);
engine.LoadClonedContext(context, init.Offset, false);
}
engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None);
if (engine.Execute() == VMState.FAULT) return false;
Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Manifest/ContractManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class ContractManifest : ISerializable
/// <summary>
/// Max length for a valid Contract Manifest
/// </summary>
public const int MaxLength = 4096;
public const int MaxLength = ushort.MaxValue;

/// <summary>
/// Serialized size
Expand Down
2 changes: 2 additions & 0 deletions src/neo/SmartContract/NefFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public class NefFile : ISerializable
(sizeof(int) * 4) + // Version
UInt160.Length; // ScriptHash

public static readonly int MaxLength = HeaderSize + sizeof(uint) + IO.Helper.GetVarSize(1024 * 1024) + (1024 * 1024);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

public int Size =>
HeaderSize + // Header
sizeof(uint) + // Checksum
Expand Down
4 changes: 2 additions & 2 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ public long CalculateNetworkFee(StoreView snapshot, Transaction tx)
if (verify is null) throw new ArgumentException($"The smart contract {contract.ScriptHash} haven't got verify method");
ContractMethodDescriptor init = contract.Manifest.Abi.GetMethod("_initialize");
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone());
ExecutionContext context = engine.LoadScript(contract.Script, CallFlags.None, verify.Offset);
if (init != null) engine.LoadContext(context.Clone(init.Offset), false);
ExecutionContext context = engine.LoadScript(contract.Script, CallFlags.None, contract.ScriptHash, verify.Offset);
if (init != null) engine.LoadClonedContext(context, init.Offset, false);
engine.LoadScript(Array.Empty<byte>(), CallFlags.None);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.ScriptHash} verification fault.");
if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.ScriptHash} returns false.");
Expand Down
2 changes: 1 addition & 1 deletion src/neo/neo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" />
<PackageReference Include="Neo.VM" Version="3.0.0-CI00251" />
<PackageReference Include="Neo.VM" Version="3.0.0-CI00252" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion tests/neo.UnitTests/Ledger/UT_ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public void TestSetup()
contract = new ContractState
{
Script = script,
ScriptHash = script.ToScriptHash(),
Manifest = manifest
};
}
Expand Down Expand Up @@ -84,7 +85,7 @@ public void TestDeserialize()
public void TestGetSize()
{
ISerializable newContract = contract;
newContract.Size.Should().Be(265);
newContract.Size.Should().Be(289);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void GetHashData()
},
},
Script = new byte[] { 1, 2, 3 },
ScriptHash = new byte[] { 1, 2, 3 }.ToScriptHash()
};
contract.Manifest.Abi.Hash = contract.ScriptHash;
engine.LoadScript(contract.Script);
Expand Down
3 changes: 2 additions & 1 deletion tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public void TestGetAddress()
}
}
},
Script = new byte[] { 1, 2, 3 }
Script = new byte[] { 1, 2, 3 },
ScriptHash = new byte[] { 1, 2, 3 }.ToScriptHash()
});

Assert.AreEqual("0xb2e3fe334830b4741fa5d762f2ab36b90b86c49b", contract.ScriptHash.ToString());
Expand Down
Loading