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

Contract redirectiton #1391

Closed
wants to merge 19 commits into from
20 changes: 18 additions & 2 deletions src/neo/Ledger/ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,45 @@ public UInt160 ScriptHash
}
}

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

public UInt160 RedirectionHash = UInt160.Zero;

public bool IsDeleted = false;
doubiliu marked this conversation as resolved.
Show resolved Hide resolved

doubiliu marked this conversation as resolved.
Show resolved Hide resolved
ContractState ICloneable<ContractState>.Clone()
{
return new ContractState
{
Script = Script,
Manifest = Manifest.Clone()
Manifest = Manifest.Clone(),
RedirectionHash = RedirectionHash,
IsDeleted = IsDeleted
};
}

void ISerializable.Deserialize(BinaryReader reader)
{
Script = reader.ReadVarBytes();
Manifest = reader.ReadSerializable<ContractManifest>();
RedirectionHash = reader.ReadSerializable<UInt160>();
IsDeleted = reader.ReadBoolean();
}

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

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

public JObject ToJson()
Expand All @@ -66,6 +78,8 @@ public JObject ToJson()
json["hash"] = ScriptHash.ToString();
json["script"] = Convert.ToBase64String(Script);
json["manifest"] = Manifest.ToJson();
json["redirectionHash"] = RedirectionHash.ToString();
json["isDeleted"] = IsDeleted;
return json;
}

Expand All @@ -74,6 +88,8 @@ public static ContractState FromJson(JObject json)
ContractState contractState = new ContractState();
contractState.Script = Convert.FromBase64String(json["script"].AsString());
contractState.Manifest = ContractManifest.FromJson(json["manifest"]);
contractState.RedirectionHash = UInt160.Parse(json["RedirectionHash"].AsString());
contractState.IsDeleted = json["isDeleted"].AsBoolean();
return contractState;
}

Expand Down
79 changes: 52 additions & 27 deletions src/neo/SmartContract/InteropService.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ private static bool Contract_Create(ApplicationEngine engine)
Script = script,
Manifest = ContractManifest.Parse(manifest)
};

if (!contract.Manifest.IsValid(hash)) return false;

engine.Snapshot.Contracts.Add(hash, contract);
Expand All @@ -56,50 +55,76 @@ private static bool Contract_Update(ApplicationEngine engine)
var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString();
if (manifest.Length > ContractManifest.MaxLength) return false;

var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash);
if (contract is null) return false;

var oldcontract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash);
if (oldcontract is null) return false;
doubiliu marked this conversation as resolved.
Show resolved Hide resolved
if (oldcontract.IsDeleted) return false;
ContractState newcontract = null;
if (script.Length > 0)
{
UInt160 hash_new = script.ToScriptHash();
if (hash_new.Equals(engine.CurrentScriptHash)) return false;
if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false;
contract = new ContractState
newcontract = new ContractState
{
Script = script,
Manifest = contract.Manifest
Manifest = oldcontract.Manifest
};
contract.Manifest.Abi.Hash = hash_new;
engine.Snapshot.Contracts.Add(hash_new, contract);
if (contract.HasStorage)
if (oldcontract.RedirectionHash.Equals(UInt160.Zero))
{
newcontract.RedirectionHash = oldcontract.ScriptHash;
}
else
{
newcontract.RedirectionHash = oldcontract.RedirectionHash;
}
newcontract.IsDeleted = false;
doubiliu marked this conversation as resolved.
Show resolved Hide resolved
newcontract.Manifest.Abi.Hash = hash_new;
engine.Snapshot.Contracts.Add(hash_new, newcontract);
if (oldcontract.RedirectionHash.Equals(UInt160.Zero))
{
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
});
}
oldcontract = engine.Snapshot.Contracts.GetAndChange(oldcontract.ScriptHash);
oldcontract.IsDeleted = true;
}
else
{
Contract_UnAppend_Destroy(engine);
}
doubiliu marked this conversation as resolved.
Show resolved Hide resolved
Contract_Destroy(engine);
}
if (manifest.Length > 0)
{
contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash);
contract.Manifest = ContractManifest.Parse(manifest);
if (!contract.Manifest.IsValid(contract.ScriptHash)) return false;
if (!contract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false;
newcontract = engine.Snapshot.Contracts.GetAndChange(newcontract.ScriptHash);
newcontract.Manifest = ContractManifest.Parse(manifest);
if (!newcontract.Manifest.IsValid(newcontract.ScriptHash)) return false;
if (!newcontract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false;
}

return true;
}

private static bool Contract_Destroy(ApplicationEngine engine)
{
UInt160 hash = engine.CurrentScriptHash;
ContractState contract = engine.Snapshot.Contracts.TryGet(hash);
if (contract == null) return true;
if (!contract.RedirectionHash.Equals(UInt160.Zero))
{
ContractState initcontract = engine.Snapshot.Contracts.TryGet(contract.RedirectionHash);
if (initcontract != null)
{
engine.Snapshot.Contracts.Delete(contract.RedirectionHash);
if (initcontract.HasStorage)
foreach (var (key, _) in engine.Snapshot.Storages.Find(contract.RedirectionHash.ToArray()))
engine.Snapshot.Storages.Delete(key);
}
}
engine.Snapshot.Contracts.Delete(hash);
if (contract.HasStorage)
foreach (var (key, _) in engine.Snapshot.Storages.Find(hash.ToArray()))
engine.Snapshot.Storages.Delete(key);
return true;
}

doubiliu marked this conversation as resolved.
Show resolved Hide resolved
private static bool Contract_UnAppend_Destroy(ApplicationEngine engine)
{
UInt160 hash = engine.CurrentScriptHash;
ContractState contract = engine.Snapshot.Contracts.TryGet(hash);
Expand Down Expand Up @@ -140,7 +165,7 @@ private static bool Contract_CallEx(ApplicationEngine engine)
private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, StackItem method, StackItem args, CallFlags flags)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash);
if (contract is null) return false;
if (contract is null || contract.IsDeleted) return false;

ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest;

Expand Down
4 changes: 4 additions & 0 deletions src/neo/SmartContract/InteropService.Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ private static bool CheckStorageContext(ApplicationEngine engine, StorageContext
ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash);
if (contract == null) return false;
if (!contract.HasStorage) return false;
if (!contract.RedirectionHash.Equals(UInt160.Zero))
doubiliu marked this conversation as resolved.
Show resolved Hide resolved
{
context.ScriptHash = contract.RedirectionHash;
}
return true;
}

Expand Down
6 changes: 5 additions & 1 deletion tests/neo.UnitTests/Ledger/UT_ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,15 @@ public void TestDeserialize()
}
((ContractState)newContract).Manifest.ToJson().ToString().Should().Be(contract.Manifest.ToJson().ToString());
((ContractState)newContract).Script.Should().BeEquivalentTo(contract.Script);
((ContractState)newContract).RedirectionHash.Should().BeEquivalentTo(contract.RedirectionHash);
((ContractState)newContract).IsDeleted.Should().Be(contract.IsDeleted);
}

[TestMethod]
public void TestGetSize()
{
ISerializable newContract = contract;
newContract.Size.Should().Be(368);
newContract.Size.Should().Be(390);
}

[TestMethod]
Expand All @@ -94,6 +96,8 @@ public void TestToJson()
json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5");
json["script"].AsString().Should().Be("AQ==");
json["manifest"].AsString().Should().Be(manifest.ToJson().AsString());
json["redirectionHash"].AsString().Should().Be(UInt160.Zero.ToString());
json["isDeleted"].AsBoolean().Should().Be(false);
}
}
}
3 changes: 1 addition & 2 deletions tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ public void TestContract_Update()
Signature = signature
}
};
manifest.Features = ContractFeatures.HasStorage;
var snapshot = Blockchain.Singleton.GetSnapshot();
var state = TestUtils.GetContract();
state.Manifest.Features = ContractFeatures.HasStorage;
Expand All @@ -243,8 +244,6 @@ public void TestContract_Update()
// Remove Storage flag with something stored

state.Manifest.Features = ContractFeatures.NoProperty;
snapshot.Contracts.Add(state.ScriptHash, state);
snapshot.Storages.Add(storageKey, storageItem);

engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0);
doubiliu marked this conversation as resolved.
Show resolved Hide resolved
engine.LoadScript(state.Script);
Expand Down