diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index 6a33a32abc..dff17f2025 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -85,7 +85,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications - script.EmitPush(new byte[0]); + script.Emit(OpCode.PUSHNULL); script.EmitSysCall(InteropService.System_Runtime_GetNotifications); // Execute diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index 65066b9079..658d125e7f 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,7 +1,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.VM; +using Neo.VM.Types; using System.Linq; namespace Neo.UnitTests.SmartContract @@ -9,58 +11,184 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_Syscalls { + [TestMethod] + public void System_Blockchain_GetBlock() + { + var tx = new Transaction() + { + Script = new byte[] { 0x01 }, + Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], + NetworkFee = 0x02, + SystemFee = 0x03, + Nonce = 0x04, + ValidUntilBlock = 0x05, + Version = 0x06, + Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, + Sender = UInt160.Parse("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + }; + + var block = new Block() + { + Index = 1, + Timestamp = 2, + Version = 3, + Witness = new Witness() + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + }, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + ConsensusData = new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }, + Transactions = new Transaction[] { tx } + }; + + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + + using (var script = new ScriptBuilder()) + { + script.EmitPush(block.Hash.ToArray()); + script.EmitSysCall(InteropService.System_Blockchain_GetBlock); + + // Without block + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Peek().IsNull); + + // With block + + var blocks = (TestDataCache)snapshot.Blocks; + var txs = (TestDataCache)snapshot.Transactions; + blocks.Add(block.Hash, block.Trim()); + txs.Add(tx.Hash, new TransactionState() { Transaction = tx, BlockIndex = block.Index, VMState = VMState.HALT }); + + script.EmitSysCall(InteropService.Neo_Json_Serialize); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), + "5b22515c7546464644795c75464646445c75464646445c75303030335c75464646445c75464646445c754646464475465c7530303046715c75303132415c625b595c75303434335c75464646445c75464646447d5d767b385c7546464644785c75303032375c75464646445c7546464644222c332c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c322c312c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c315d"); + Assert.AreEqual(0, engine.ResultStack.Count); + + // Clean + blocks.Delete(block.Hash); + txs.Delete(tx.Hash); + } + } + + [TestMethod] + public void System_ExecutionEngine_GetScriptContainer() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_ExecutionEngine_GetScriptContainer); + + // Without tx + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Peek().IsNull); + + // With tx + + script.EmitSysCall(InteropService.Neo_Json_Serialize); + + var tx = new Transaction() + { + Script = new byte[] { 0x01 }, + Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], + NetworkFee = 0x02, + SystemFee = 0x03, + Nonce = 0x04, + ValidUntilBlock = 0x05, + Version = 0x06, + Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, + Sender = UInt160.Parse("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + }; + + engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), + @"5b225c7546464644445c7546464644615c7546464644732c5c75464646445c7546464644665c75303030375d5c75303030305c75464646445c75303632325c7546464644545c7546464644375c7530303133335c75303031385c7530303033655c75464646445c75464646445c75303032375a5c75464646445c2f5c7546464644222c362c342c225c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c7546464644222c332c322c352c225c7530303031225d"); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + [TestMethod] public void System_Runtime_GetInvocationCounter() { + ContractState contractA, contractB, contractC; var snapshot = TestBlockchain.GetStore().GetSnapshot(); var contracts = (TestDataCache)snapshot.Contracts; - // Call System.Runtime.GetInvocationCounter syscall + // Create dummy contracts - var script = new ScriptBuilder(); - script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); - // Init A,B,C contracts - // First two drops is for drop method and arguments + contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() }; + contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - var contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() }; - var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + // Init A,B,C contracts + // First two drops is for drop method and arguments - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); - contracts.Add(contractA.ScriptHash, contractA); - contracts.Add(contractB.ScriptHash, contractB); - contracts.Add(contractC.ScriptHash, contractC); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); + contracts.Add(contractA.ScriptHash, contractA); + contracts.Add(contractB.ScriptHash, contractB); + contracts.Add(contractC.ScriptHash, contractC); + } // Call A,B,B,C - script = new ScriptBuilder(); - script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); - // Execute + // Execute - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(engine.Execute(), VMState.HALT); - // Check the results + // Check the results - CollectionAssert.AreEqual - ( - engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), - new int[] - { - 1, /* A */ - 1, /* B */ - 2, /* B */ - 1 /* C */ - } - ); + CollectionAssert.AreEqual + ( + engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), + new int[] + { + 1, /* A */ + 1, /* B */ + 2, /* B */ + 1 /* C */ + } + ); + } } } } diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index f6c641d7d5..c369e5d59f 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -2,11 +2,13 @@ using Neo.IO.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; +using Neo.VM; +using Neo.VM.Types; using System.IO; namespace Neo.Ledger { - public class ContractState : ICloneable, ISerializable + public class ContractState : ICloneable, ISerializable, IInteroperable { public byte[] Script; public ContractManifest Manifest; @@ -72,5 +74,18 @@ public static ContractState FromJson(JObject json) contractState.Manifest = ContractManifest.FromJson(json["manifest"]); return contractState; } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + new ByteArray(Script), + new Boolean(HasStorage), + new Boolean(Payable), + } + ); + } } } diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index d339b536a0..305536c38d 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -2,6 +2,9 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.IO; @@ -9,7 +12,7 @@ namespace Neo.Network.P2P.Payloads { - public class Block : BlockBase, IInventory, IEquatable + public class Block : BlockBase, IInventory, IEquatable, IInteroperable { public const int MaxContentsPerBlock = ushort.MaxValue; public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; @@ -131,5 +134,31 @@ public TrimmedBlock Trim() ConsensusData = ConsensusData }; } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + // Computed properties + new ByteArray(Hash.ToArray()), + + // BlockBase properties + new Integer(Version), + new ByteArray(PrevHash.ToArray()), + new ByteArray(MerkleRoot.ToArray()), + new Integer(Timestamp), + new Integer(Index), + new ByteArray(NextConsensus.ToArray()), + // Witness + + // Block properties + // Count + // ConsensusData + new Integer(Transactions.Length) + } + ); + } } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 74fc1ef3c7..af38ef6dcc 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -4,6 +4,8 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; using Neo.Wallets; using System; using System.Collections.Generic; @@ -13,7 +15,7 @@ namespace Neo.Network.P2P.Payloads { - public class Transaction : IEquatable, IInventory + public class Transaction : IEquatable, IInventory, IInteroperable { public const int MaxTransactionSize = 102400; public const uint MaxValidUntilBlockIncrement = 2102400; @@ -68,7 +70,7 @@ public UInt256 Hash sizeof(byte) + //Version sizeof(uint) + //Nonce 20 + //Sender - sizeof(long) + //Gas + sizeof(long) + //SystemFee sizeof(long) + //NetworkFee sizeof(uint); //ValidUntilBlock @@ -216,5 +218,29 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) if (net_fee < 0) return false; return this.VerifyWitnesses(snapshot, net_fee); } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + // Computed properties + new ByteArray(Hash.ToArray()), + + // Transaction properties + new Integer(Version), + new Integer(Nonce), + new ByteArray(Sender.ToArray()), + new Integer(SystemFee), + new Integer(NetworkFee), + new Integer(ValidUntilBlock), + // Attributes + // Cosigners + new ByteArray(Script), + // Witnesses + } + ); + } } } diff --git a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 271a76f935..b6b65b37da 100644 --- a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -87,6 +87,7 @@ partial class ApplicationEngine [OpCode.PUSHDATA2] = 13000, [OpCode.PUSHDATA4] = 110000, [OpCode.PUSHM1] = 30, + [OpCode.PUSHNULL] = 30, [OpCode.PUSH1] = 30, [OpCode.PUSH2] = 30, [OpCode.PUSH3] = 30, @@ -114,6 +115,7 @@ partial class ApplicationEngine [OpCode.DUPFROMALTSTACK] = 60, [OpCode.TOALTSTACK] = 60, [OpCode.FROMALTSTACK] = 60, + [OpCode.ISNULL] = 60, [OpCode.XDROP] = 400, [OpCode.XSWAP] = 60, [OpCode.XTUCK] = 400, diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 6a22249cd7..809ec8345f 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -67,6 +67,9 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray undeserialized += count * 2; } break; + case StackItemType.Null: + deserialized.Push(StackItem.Null); + break; default: throw new FormatException(); } @@ -232,6 +235,9 @@ private static void SerializeStackItem(StackItem item, BinaryWriter writer) unserialized.Push(pair.Key); } break; + case Null _: + writer.Write((byte)StackItemType.Null); + break; } } } diff --git a/neo/SmartContract/IInteroperable.cs b/neo/SmartContract/IInteroperable.cs new file mode 100644 index 0000000000..972147877c --- /dev/null +++ b/neo/SmartContract/IInteroperable.cs @@ -0,0 +1,9 @@ +using Neo.VM; + +namespace Neo.SmartContract +{ + public interface IInteroperable + { + StackItem ToStackItem(); + } +} diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 7f0d857fe9..e500eeb235 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -2,7 +2,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; @@ -20,17 +19,9 @@ static partial class InteropService public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All); public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All); - public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400, TriggerType.All); - public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000, TriggerType.All); - public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400, TriggerType.All); public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); - public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400, TriggerType.Application); public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); @@ -157,80 +148,6 @@ private static bool Crypto_CheckMultiSig(ApplicationEngine engine) return true; } - private static bool Header_GetVersion(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Version); - return true; - } - return false; - } - - private static bool Header_GetMerkleRoot(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.MerkleRoot.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetNextConsensus(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.NextConsensus.ToArray()); - return true; - } - return false; - } - - private static bool Transaction_GetScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - engine.CurrentContext.EvaluationStack.Push(tx.Script); - return true; - } - return false; - } - - private static bool Transaction_GetWitnesses(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - if (tx.Witnesses.Length > engine.MaxArraySize) - return false; - engine.CurrentContext.EvaluationStack.Push(WitnessWrapper.Create(tx, engine.Snapshot).Select(p => StackItem.FromInterface(p)).ToArray()); - return true; - } - return false; - } - - private static bool Witness_GetVerificationScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - WitnessWrapper witness = _interface.GetInterface(); - if (witness == null) return false; - engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); - return true; - } - return false; - } - private static bool Account_IsStandard(ApplicationEngine engine) { UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); @@ -313,30 +230,6 @@ private static bool Contract_Update(ApplicationEngine engine) return true; } - private static bool Contract_GetScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - ContractState contract = _interface.GetInterface(); - if (contract == null) return false; - engine.CurrentContext.EvaluationStack.Push(contract.Script); - return true; - } - return false; - } - - private static bool Contract_IsPayable(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - ContractState contract = _interface.GetInterface(); - if (contract == null) return false; - engine.CurrentContext.EvaluationStack.Push(contract.Payable); - return true; - } - return false; - } - private static bool Storage_Find(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 7aa7fd7284..0e891de162 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -41,19 +41,11 @@ public static partial class InteropService public static readonly uint System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); - public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000, TriggerType.Application); public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application); public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); - public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400, TriggerType.Application); - public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400, TriggerType.Application); - public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000, TriggerType.Application); - public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400, TriggerType.Application); - public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400, TriggerType.All); public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); @@ -112,7 +104,9 @@ private static uint Register(string method, Func handle private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) { - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(engine.ScriptContainer)); + engine.CurrentContext.EvaluationStack.Push( + engine.ScriptContainer is IInteroperable value ? value.ToStackItem() : + StackItem.FromInterface(engine.ScriptContainer)); return true; } @@ -124,7 +118,7 @@ private static bool ExecutionEngine_GetExecutingScriptHash(ApplicationEngine eng private static bool ExecutionEngine_GetCallingScriptHash(ApplicationEngine engine) { - engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? new byte[0]); + engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? StackItem.Null); return true; } @@ -236,13 +230,12 @@ private static bool Runtime_Serialize(ApplicationEngine engine) private static bool Runtime_GetNotifications(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - if ((data.Length != 0) && (data.Length != UInt160.Length)) return false; + StackItem item = engine.CurrentContext.EvaluationStack.Pop(); IEnumerable notifications = engine.Notifications; - if (data.Length == UInt160.Length) // must filter by scriptHash + if (!item.IsNull) // must filter by scriptHash { - var hash = new UInt160(data); + var hash = new UInt160(item.GetByteArray()); notifications = notifications.Where(p => p.ScriptHash == hash); } @@ -304,28 +297,6 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) return true; } - private static bool Blockchain_GetHeader(ApplicationEngine engine) - { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - UInt256 hash; - if (data.Length <= 5) - hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) - hash = new UInt256(data); - else - return false; - if (hash == null) - { - engine.CurrentContext.EvaluationStack.Push(new byte[0]); - } - else - { - Header header = engine.Snapshot.GetHeader(hash); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(header)); - } - return true; - } - private static bool Blockchain_GetBlock(ApplicationEngine engine) { byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); @@ -336,15 +307,12 @@ private static bool Blockchain_GetBlock(ApplicationEngine engine) hash = new UInt256(data); else return false; - if (hash == null) - { - engine.CurrentContext.EvaluationStack.Push(new byte[0]); - } + + Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; + if (block == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - { - Block block = engine.Snapshot.GetBlock(hash); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(block)); - } + engine.CurrentContext.EvaluationStack.Push(block.ToStackItem()); return true; } @@ -352,128 +320,60 @@ private static bool Blockchain_GetTransaction(ApplicationEngine engine) { byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem()); return true; } private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) { byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - int? height = (int?)engine.Snapshot.Transactions.TryGet(new UInt256(hash))?.BlockIndex; - engine.CurrentContext.EvaluationStack.Push(height ?? -1); + var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); + engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); return true; } - private static bool Blockchain_GetContract(ApplicationEngine engine) + private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) - engine.CurrentContext.EvaluationStack.Push(new byte[0]); + byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + UInt256 hash; + if (data.Length <= 5) + hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); + else if (data.Length == 32) + hash = new UInt256(data); else - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract)); - return true; - } - - private static bool Header_GetIndex(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Index); - return true; - } - return false; - } - - private static bool Header_GetHash(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Hash.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetPrevHash(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.PrevHash.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetTimestamp(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Timestamp); - return true; - } - return false; - } + return false; - private static bool Block_GetTransactionCount(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; + if (block == null) { - Block block = _interface.GetInterface(); - if (block == null) return false; - engine.CurrentContext.EvaluationStack.Push(block.Transactions.Length); - return true; + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); } - return false; - } - - private static bool Block_GetTransactions(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Block block = _interface.GetInterface(); - if (block == null) return false; - if (block.Transactions.Length > engine.MaxArraySize) - return false; - engine.CurrentContext.EvaluationStack.Push(block.Transactions.Select(p => StackItem.FromInterface(p)).ToArray()); - return true; - } - return false; - } - - private static bool Block_GetTransaction(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + else { - Block block = _interface.GetInterface(); int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - if (block == null) return false; - if (index < 0 || index >= block.Transactions.Length) return false; - Transaction tx = block.Transactions[index]; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); - return true; + if (index < 0 || index >= block.Hashes.Length) return false; + + Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index]); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); } - return false; + return true; } - private static bool Transaction_GetHash(ApplicationEngine engine) + private static bool Blockchain_GetContract(ApplicationEngine engine) { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - engine.CurrentContext.EvaluationStack.Push(tx.Hash.ToArray()); - return true; - } - return false; + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + if (contract == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem()); + return true; } private static bool Storage_GetContext(ApplicationEngine engine) @@ -508,7 +408,7 @@ private static bool Storage_Get(ApplicationEngine engine) ScriptHash = context.ScriptHash, Key = key }); - engine.CurrentContext.EvaluationStack.Push(item?.Value ?? new byte[0]); + engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); return true; } return false; @@ -533,13 +433,9 @@ private static bool StorageContext_AsReadOnly(ApplicationEngine engine) private static bool Contract_Call(ApplicationEngine engine) { - StackItem contractOrHash = engine.CurrentContext.EvaluationStack.Pop(); + StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - ContractState contract; - if (contractOrHash is InteropInterface _interface) - contract = _interface; - else - contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractOrHash.GetByteArray())); + ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetByteArray())); if (contract is null) return false; StackItem method = engine.CurrentContext.EvaluationStack.Pop(); diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index a4853fc39a..1c674540f0 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -53,6 +53,10 @@ public static JObject Serialize(StackItem item) return ret; } + case Null _: + { + return JObject.Null; + } default: throw new FormatException(); } } @@ -66,6 +70,10 @@ public static StackItem Deserialize(JObject json) { switch (json) { + case null: + { + return StackItem.Null; + } case JArray array: { return array.Select(p => Deserialize(p)).ToList(); diff --git a/neo/SmartContract/StackItemType.cs b/neo/SmartContract/StackItemType.cs index b47999e157..7e19c2982b 100644 --- a/neo/SmartContract/StackItemType.cs +++ b/neo/SmartContract/StackItemType.cs @@ -9,5 +9,6 @@ internal enum StackItemType : byte Array = 0x80, Struct = 0x81, Map = 0x82, + Null = 0xff } } diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs deleted file mode 100644 index 28019be0c2..0000000000 --- a/neo/SmartContract/WitnessWrapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using System.Linq; - -namespace Neo.SmartContract -{ - internal class WitnessWrapper - { - public byte[] VerificationScript; - - public static WitnessWrapper[] Create(IVerifiable verifiable, Snapshot snapshot) - { - WitnessWrapper[] wrappers = verifiable.Witnesses.Select(p => new WitnessWrapper - { - VerificationScript = p.VerificationScript - }).ToArray(); - if (wrappers.Any(p => p.VerificationScript.Length == 0)) - { - UInt160[] hashes = verifiable.GetScriptHashesForVerifying(snapshot); - for (int i = 0; i < wrappers.Length; i++) - if (wrappers[i].VerificationScript.Length == 0) - wrappers[i].VerificationScript = snapshot.Contracts[hashes[i]].Script; - } - return wrappers; - } - } -} diff --git a/neo/VM/Helper.cs b/neo/VM/Helper.cs index d63bebb89a..5d2a2aa485 100644 --- a/neo/VM/Helper.cs +++ b/neo/VM/Helper.cs @@ -149,6 +149,9 @@ public static ScriptBuilder EmitPush(this ScriptBuilder sb, object obj) case Enum data: sb.EmitPush(BigInteger.Parse(data.ToString("d"))); break; + case null: + sb.Emit(OpCode.PUSHNULL); + break; default: throw new ArgumentException(); } @@ -242,7 +245,7 @@ private static ContractParameter ToParameter(StackItem item, List - +