From df958caf93bcd51531b0bc80ba6e1d9d1fff61da Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 9 Jun 2020 12:12:56 +0300 Subject: [PATCH] core: add Manifest to state.Contract --- pkg/core/dao/cacheddao_test.go | 18 +++- pkg/core/dao/dao_test.go | 4 +- pkg/core/gas_price.go | 5 - pkg/core/gas_price_test.go | 60 ------------ pkg/core/helper_test.go | 24 ++--- pkg/core/interop/context.go | 3 +- pkg/core/interop_neo.go | 110 ++++++++++------------ pkg/core/interop_neo_test.go | 23 +++-- pkg/core/native/interop.go | 6 +- pkg/core/state/contract.go | 74 +++++++++------ pkg/core/state/contract_test.go | 64 +++++++------ pkg/interop/contract/contract.go | 35 +------ pkg/rpc/client/rpc.go | 5 +- pkg/rpc/client/rpc_test.go | 42 ++++----- pkg/rpc/response/result/contract_state.go | 53 ----------- pkg/rpc/server/server.go | 5 +- pkg/rpc/server/server_test.go | 15 ++- pkg/rpc/server/testdata/testblocks.acc | Bin 6516 -> 6784 bytes pkg/smartcontract/manifest/manifest.go | 21 +++++ 19 files changed, 229 insertions(+), 338 deletions(-) delete mode 100644 pkg/rpc/response/result/contract_state.go diff --git a/pkg/core/dao/cacheddao_test.go b/pkg/core/dao/cacheddao_test.go index f8c2bdf75..bd79af520 100644 --- a/pkg/core/dao/cacheddao_test.go +++ b/pkg/core/dao/cacheddao_test.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -59,10 +60,19 @@ func TestCachedDaoContracts(t *testing.T) { _, err := dao.GetContractState(sh) require.NotNil(t, err) - cs := &state.Contract{} - cs.Name = "test" - cs.Script = script - cs.ParamList = []smartcontract.ParamType{1, 2} + m := manifest.NewManifest(hash.Hash160(script)) + m.ABI.EntryPoint.Name = "somename" + m.ABI.EntryPoint.Parameters = []manifest.Parameter{ + manifest.NewParameter("first", smartcontract.IntegerType), + manifest.NewParameter("second", smartcontract.StringType), + } + m.ABI.EntryPoint.ReturnType = smartcontract.BoolType + + cs := &state.Contract{ + ID: 123, + Script: script, + Manifest: *m, + } require.NoError(t, dao.PutContractState(cs)) cs2, err := dao.GetContractState(sh) diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 640620a11..ce29491f1 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -60,7 +60,7 @@ func TestPutAndGetAccountStateOrNew(t *testing.T) { func TestPutAndGetContractState(t *testing.T) { dao := NewSimple(storage.NewMemoryStore()) - contractState := &state.Contract{Script: []byte{}, ParamList: []smartcontract.ParamType{}} + contractState := &state.Contract{Script: []byte{}} hash := contractState.ScriptHash() err := dao.PutContractState(contractState) require.NoError(t, err) @@ -71,7 +71,7 @@ func TestPutAndGetContractState(t *testing.T) { func TestDeleteContractState(t *testing.T) { dao := NewSimple(storage.NewMemoryStore()) - contractState := &state.Contract{Script: []byte{}, ParamList: []smartcontract.ParamType{}} + contractState := &state.Contract{Script: []byte{}} hash := contractState.ScriptHash() err := dao.PutContractState(contractState) require.NoError(t, err) diff --git a/pkg/core/gas_price.go b/pkg/core/gas_price.go index 4d67955d2..e7318b52e 100644 --- a/pkg/core/gas_price.go +++ b/pkg/core/gas_price.go @@ -1,7 +1,6 @@ package core import ( - "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -41,8 +40,6 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 { } const ( - neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create - neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate systemStoragePut = 0x84183fe6 // System.Storage.Put systemStoragePutEx = 0x3a9be173 // System.Storage.PutEx neoStoragePut = 0xf541a152 // Neo.Storage.Put @@ -51,8 +48,6 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 { estack := v.Estack() switch id { - case neoContractCreate, neoContractMigrate: - return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64())) case systemStoragePut, systemStoragePutEx, neoStoragePut: // price for storage PUT is 1 GAS per 1 KiB keySize := len(estack.Peek(1).Bytes()) diff --git a/pkg/core/gas_price_test.go b/pkg/core/gas_price_test.go index 87faae008..f618a949c 100644 --- a/pkg/core/gas_price_test.go +++ b/pkg/core/gas_price_test.go @@ -23,66 +23,6 @@ func TestGetPrice(t *testing.T) { v := SpawnVM(systemInterop) v.SetPriceGetter(getPrice) - t.Run("Neo.Contract.Create (no props)", func(t *testing.T) { - // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - v.Load([]byte{byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), - byte(opcode.SYSCALL), 0xf6, 0x6c, 0xa5, 0x6e}) - require.NoError(t, v.StepInto()) // push 0 - ContractPropertyState.NoProperty - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - - checkGas(t, util.Fixed8FromInt64(100), v) - }) - - t.Run("Neo.Contract.Create (has storage)", func(t *testing.T) { - // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - v.Load([]byte{byte(opcode.PUSH1), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), - byte(opcode.SYSCALL), 0xf6, 0x6c, 0xa5, 0x6e}) - require.NoError(t, v.StepInto()) // push 01 - ContractPropertyState.HasStorage - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - - checkGas(t, util.Fixed8FromInt64(500), v) - }) - - t.Run("Neo.Contract.Create (has dynamic invoke)", func(t *testing.T) { - // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - v.Load([]byte{byte(opcode.PUSH2), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), - byte(opcode.SYSCALL), 0xf6, 0x6c, 0xa5, 0x6e}) - require.NoError(t, v.StepInto()) // push 02 - ContractPropertyState.HasDynamicInvoke - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - - checkGas(t, util.Fixed8FromInt64(600), v) - }) - - t.Run("Neo.Contract.Create (has both storage and dynamic invoke)", func(t *testing.T) { - // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - v.Load([]byte{byte(opcode.PUSH3), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), - byte(opcode.SYSCALL), 0xf6, 0x6c, 0xa5, 0x6e}) - require.NoError(t, v.StepInto()) // push 03 - HasStorage and HasDynamicInvoke - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - - checkGas(t, util.Fixed8FromInt64(1000), v) - }) - - t.Run("Neo.Contract.Migrate", func(t *testing.T) { - // Neo.Contract.Migrate: 471b6290 (requires push properties on fourth position) - v.Load([]byte{byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), - byte(opcode.SYSCALL), 0x47, 0x1b, 0x62, 0x90}) - require.NoError(t, v.StepInto()) // push 0 - ContractPropertyState.NoProperty - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - require.NoError(t, v.StepInto()) // push 0 - - checkGas(t, util.Fixed8FromInt64(100), v) - }) - t.Run("System.Storage.Put", func(t *testing.T) { // System.Storage.Put: e63f1884 (requires push key and value) v.Load([]byte{byte(opcode.PUSH3), byte(opcode.PUSH3), byte(opcode.PUSH0), diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 06e394ca0..f8a20f059 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -222,19 +223,18 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, err) t.Logf("contractHash: %s", hash.Hash160(avm).StringLE()) - var props smartcontract.PropertyState script := io.NewBufBinWriter() - emit.Bytes(script.BinWriter, []byte("Da contract dat hallos u")) - emit.Bytes(script.BinWriter, []byte("joe@example.com")) - emit.Bytes(script.BinWriter, []byte("Random Guy")) - emit.Bytes(script.BinWriter, []byte("0.99")) - emit.Bytes(script.BinWriter, []byte("Helloer")) - props |= smartcontract.HasStorage - emit.Int(script.BinWriter, int64(props)) - emit.Int(script.BinWriter, int64(5)) - params := make([]byte, 1) - params[0] = byte(7) - emit.Bytes(script.BinWriter, params) + m := manifest.NewManifest(hash.Hash160(avm)) + m.ABI.EntryPoint.Name = "Main" + m.ABI.EntryPoint.Parameters = []manifest.Parameter{ + manifest.NewParameter("method", smartcontract.StringType), + manifest.NewParameter("params", smartcontract.ArrayType), + } + m.ABI.EntryPoint.ReturnType = smartcontract.BoolType + m.Features = smartcontract.HasStorage + bs, err := testserdes.EncodeBinary(m) + require.NoError(t, err) + emit.Bytes(script.BinWriter, bs) emit.Bytes(script.BinWriter, avm) emit.Syscall(script.BinWriter, "Neo.Contract.Create") txScript := script.Bytes() diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 5d96a9493..026045baa 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -94,8 +94,7 @@ func (ic *Context) GetContract(h util.Uint160) ([]byte, bool) { if err != nil { return nil, false } - hasDynamicInvoke := (cs.Properties & smartcontract.HasDynamicInvoke) != 0 - return cs.Script, hasDynamicInvoke + return cs.Script, cs.HasDynamicInvoke() } // NewContractMD returns Contract with the specified list of methods. diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index a79757cb7..08f07de56 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -8,7 +8,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -68,48 +69,20 @@ func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, if len(script) > MaxContractScriptSize { return nil, errors.New("the script is too big") } - paramBytes := v.Estack().Pop().Bytes() - if len(paramBytes) > MaxContractParametersNum { - return nil, errors.New("too many parameters for a script") + manifestBytes := v.Estack().Pop().Bytes() + if len(manifestBytes) > manifest.MaxManifestSize { + return nil, errors.New("manifest is too big") } - paramList := make([]smartcontract.ParamType, len(paramBytes)) - for k, v := range paramBytes { - paramList[k] = smartcontract.ParamType(v) + var m manifest.Manifest + r := io.NewBinReaderFromBuf(manifestBytes) + m.DecodeBinary(r) + if r.Err != nil { + return nil, r.Err } - retType := smartcontract.ParamType(v.Estack().Pop().BigInt().Int64()) - properties := smartcontract.PropertyState(v.Estack().Pop().BigInt().Int64()) - name := v.Estack().Pop().Bytes() - if len(name) > MaxContractStringLen { - return nil, errors.New("too big name") - } - version := v.Estack().Pop().Bytes() - if len(version) > MaxContractStringLen { - return nil, errors.New("too big version") - } - author := v.Estack().Pop().Bytes() - if len(author) > MaxContractStringLen { - return nil, errors.New("too big author") - } - email := v.Estack().Pop().Bytes() - if len(email) > MaxContractStringLen { - return nil, errors.New("too big email") - } - desc := v.Estack().Pop().Bytes() - if len(desc) > MaxContractDescriptionLen { - return nil, errors.New("too big description") - } - contract := &state.Contract{ - Script: script, - ParamList: paramList, - ReturnType: retType, - Properties: properties, - Name: string(name), - CodeVersion: string(version), - Author: string(author), - Email: string(email), - Description: string(desc), - } - return contract, nil + return &state.Contract{ + Script: script, + Manifest: m, + }, nil } // contractCreate creates a contract. @@ -119,14 +92,12 @@ func contractCreate(ic *interop.Context, v *vm.VM) error { return err } contract, err := ic.DAO.GetContractState(newcontract.ScriptHash()) - if err != nil { - contract = newcontract - err := ic.DAO.PutContractState(contract) - if err != nil { - return err - } + if contract != nil { + return errors.New("contract already exists") + } else if err := ic.DAO.PutContractState(newcontract); err != nil { + return err } - v.Estack().PushVal(stackitem.NewInterop(contract)) + v.Estack().PushVal(stackitem.NewInterop(newcontract)) return nil } @@ -154,30 +125,45 @@ func contractIsPayable(ic *interop.Context, v *vm.VM) error { // contractMigrate migrates a contract. func contractMigrate(ic *interop.Context, v *vm.VM) error { + contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) + if contract == nil { + return errors.New("contract doesn't exist") + } newcontract, err := createContractStateFromVM(ic, v) if err != nil { return err } - contract, err := ic.DAO.GetContractState(newcontract.ScriptHash()) - if err != nil { - contract = newcontract - err := ic.DAO.PutContractState(contract) + if newcontract.Script != nil { + if l := len(newcontract.Script); l == 0 || l > MaxContractScriptSize { + return errors.New("invalid script len") + } + h := newcontract.ScriptHash() + if h.Equals(contract.ScriptHash()) { + return errors.New("the script is the same") + } else if _, err := ic.DAO.GetContractState(h); err == nil { + return errors.New("contract already exists") + } + newcontract.ID = contract.ID + if err := ic.DAO.PutContractState(newcontract); err != nil { + return err + } + if err := ic.DAO.DeleteContractState(contract.ScriptHash()); err != nil { + return err + } + } + if contract.HasStorage() { + // TODO store items by ID #1037 + hash := v.GetCurrentScriptHash() + siMap, err := ic.DAO.GetStorageItems(hash) if err != nil { return err } - if contract.HasStorage() { - hash := v.GetCurrentScriptHash() - siMap, err := ic.DAO.GetStorageItems(hash) + for k, v := range siMap { + v.IsConst = false + err = ic.DAO.PutStorageItem(contract.ScriptHash(), []byte(k), v) if err != nil { return err } - for k, v := range siMap { - v.IsConst = false - err = ic.DAO.PutStorageItem(contract.ScriptHash(), []byte(k), v) - if err != nil { - return err - } - } } } v.Estack().PushVal(stackitem.NewInterop(contract)) diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 853e9850d..0170cb00e 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -13,9 +13,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -265,16 +266,18 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) { v := vm.New() + script := []byte("testscript") + m := manifest.NewManifest(hash.Hash160(script)) + m.ABI.EntryPoint.Parameters = []manifest.Parameter{ + manifest.NewParameter("Name", smartcontract.StringType), + manifest.NewParameter("Amount", smartcontract.IntegerType), + manifest.NewParameter("Hash", smartcontract.Hash160Type), + } + m.ABI.EntryPoint.ReturnType = smartcontract.ArrayType + m.Features = smartcontract.HasStorage contractState := &state.Contract{ - Script: []byte("testscript"), - ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, - ReturnType: smartcontract.ArrayType, - Properties: smartcontract.HasStorage, - Name: random.String(10), - CodeVersion: random.String(10), - Author: random.String(10), - Email: random.String(10), - Description: random.String(10), + Script: script, + Manifest: *m, } chain := newTestChain(t) diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index 24a6a28b4..c36200acf 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -26,9 +26,9 @@ func Deploy(ic *interop.Context, _ *vm.VM) error { } cs := &state.Contract{ - Script: md.Script, - ParamList: params, - ReturnType: md.Manifest.ABI.EntryPoint.ReturnType, + ID: md.ContractID, + Script: md.Script, + Manifest: md.Manifest, } if err := ic.DAO.PutContractState(cs); err != nil { return err diff --git a/pkg/core/state/contract.go b/pkg/core/state/contract.go index 33e791fd3..86e9a231d 100644 --- a/pkg/core/state/contract.go +++ b/pkg/core/state/contract.go @@ -1,52 +1,38 @@ package state import ( + "encoding/json" + "errors" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" ) // Contract holds information about a smart contract in the NEO blockchain. type Contract struct { - Script []byte - ParamList []smartcontract.ParamType - ReturnType smartcontract.ParamType - Properties smartcontract.PropertyState - Name string - CodeVersion string - Author string - Email string - Description string + ID int32 + Script []byte + Manifest manifest.Manifest scriptHash util.Uint160 } // DecodeBinary implements Serializable interface. func (cs *Contract) DecodeBinary(br *io.BinReader) { + cs.ID = int32(br.ReadU32LE()) cs.Script = br.ReadVarBytes() - br.ReadArray(&cs.ParamList) - cs.ReturnType = smartcontract.ParamType(br.ReadB()) - cs.Properties = smartcontract.PropertyState(br.ReadB()) - cs.Name = br.ReadString() - cs.CodeVersion = br.ReadString() - cs.Author = br.ReadString() - cs.Email = br.ReadString() - cs.Description = br.ReadString() + cs.Manifest.DecodeBinary(br) cs.createHash() } // EncodeBinary implements Serializable interface. func (cs *Contract) EncodeBinary(bw *io.BinWriter) { + bw.WriteU32LE(uint32(cs.ID)) bw.WriteVarBytes(cs.Script) - bw.WriteArray(cs.ParamList) - bw.WriteB(byte(cs.ReturnType)) - bw.WriteB(byte(cs.Properties)) - bw.WriteString(cs.Name) - bw.WriteString(cs.CodeVersion) - bw.WriteString(cs.Author) - bw.WriteString(cs.Email) - bw.WriteString(cs.Description) + cs.Manifest.EncodeBinary(bw) } // ScriptHash returns a contract script hash. @@ -64,15 +50,47 @@ func (cs *Contract) createHash() { // HasStorage checks whether the contract has storage property set. func (cs *Contract) HasStorage() bool { - return (cs.Properties & smartcontract.HasStorage) != 0 + return (cs.Manifest.Features & smartcontract.HasStorage) != 0 } // HasDynamicInvoke checks whether the contract has dynamic invoke property set. func (cs *Contract) HasDynamicInvoke() bool { - return (cs.Properties & smartcontract.HasDynamicInvoke) != 0 + return (cs.Manifest.Features & smartcontract.HasDynamicInvoke) != 0 } // IsPayable checks whether the contract has payable property set. func (cs *Contract) IsPayable() bool { - return (cs.Properties & smartcontract.IsPayable) != 0 + return (cs.Manifest.Features & smartcontract.IsPayable) != 0 +} + +type contractJSON struct { + ID int32 `json:"id"` + Script []byte `json:"script"` + Manifest *manifest.Manifest `json:"manifest"` + ScriptHash util.Uint160 `json:"hash"` +} + +// MarshalJSON implements json.Marshaler. +func (cs *Contract) MarshalJSON() ([]byte, error) { + return json.Marshal(&contractJSON{ + ID: cs.ID, + Script: cs.Script, + Manifest: &cs.Manifest, + ScriptHash: cs.ScriptHash(), + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (cs *Contract) UnmarshalJSON(data []byte) error { + var cj contractJSON + if err := json.Unmarshal(data, &cj); err != nil { + return err + } else if cj.Manifest == nil { + return errors.New("empty manifest") + } + cs.ID = cj.ID + cs.Script = cj.Script + cs.Manifest = *cj.Manifest + cs.createHash() + return nil } diff --git a/pkg/core/state/contract_test.go b/pkg/core/state/contract_test.go index 9101dfab1..052f11c39 100644 --- a/pkg/core/state/contract_test.go +++ b/pkg/core/state/contract_test.go @@ -6,42 +6,46 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/stretchr/testify/assert" ) func TestEncodeDecodeContractState(t *testing.T) { script := []byte("testscript") - contract := &Contract{ - Script: script, - ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, - ReturnType: smartcontract.BoolType, - Properties: smartcontract.HasStorage, - Name: "Contrato", - CodeVersion: "1.0.0", - Author: "Joe Random", - Email: "joe@example.com", - Description: "Test contract", - } - - assert.Equal(t, hash.Hash160(script), contract.ScriptHash()) - - contractDecoded := &Contract{} - testserdes.EncodeDecodeBinary(t, contract, contractDecoded) - assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash()) -} - -func TestContractStateProperties(t *testing.T) { - flaggedContract := Contract{ - Properties: smartcontract.HasStorage | smartcontract.HasDynamicInvoke | smartcontract.IsPayable, - } - nonFlaggedContract := Contract{ + h := hash.Hash160(script) + m := manifest.NewManifest(h) + m.ABI.Methods = []manifest.Method{{ + Name: "main", + Parameters: []manifest.Parameter{ + { + Name: "amount", + Type: smartcontract.IntegerType, + }, + { + Name: "hash", + Type: smartcontract.Hash160Type, + }, + }, ReturnType: smartcontract.BoolType, + }} + m.Features = smartcontract.HasStorage + contract := &Contract{ + ID: 123, + Script: script, + Manifest: *m, } - assert.Equal(t, true, flaggedContract.HasStorage()) - assert.Equal(t, true, flaggedContract.HasDynamicInvoke()) - assert.Equal(t, true, flaggedContract.IsPayable()) - assert.Equal(t, false, nonFlaggedContract.HasStorage()) - assert.Equal(t, false, nonFlaggedContract.HasDynamicInvoke()) - assert.Equal(t, false, nonFlaggedContract.IsPayable()) + + assert.Equal(t, h, contract.ScriptHash()) + + t.Run("Serializable", func(t *testing.T) { + contractDecoded := new(Contract) + testserdes.EncodeDecodeBinary(t, contract, contractDecoded) + assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash()) + }) + t.Run("JSON", func(t *testing.T) { + contractDecoded := new(Contract) + testserdes.MarshalUnmarshalJSON(t, contract, contractDecoded) + assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash()) + }) } diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index d220b85e2..4a67f0748 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -33,30 +33,10 @@ func GetStorageContext(c Contract) storage.Context { // Create creates a new contract using a set of input parameters: // script contract's bytecode (limited in length by 1M) -// params contract's input parameter types, one byte per parameter, see -// ParamType in the `smartcontract` package for value -// definitions. Maximum number of parameters: 252. -// returnType return value type, also a ParamType constant -// properties bit field with contract's permissions (storage, dynamic -// invoke, payable), see PropertyState in the `smartcontract` -// package -// name human-readable contract name (no longer than 252 bytes) -// version human-readable contract version (no longer than 252 bytes) -// author contract's author (no longer than 252 bytes) -// email contract's author/support e-mail (no longer than 252 bytes) -// description human-readable contract description (no longer than 64K bytes) +// manifest contract's manifest (limited in length by 2 KiB) // It returns this new created Contract when successful (and fails transaction // if not). It uses `Neo.Contract.Create` syscall. -func Create( - script []byte, - params []byte, - returnType byte, - properties byte, - name, - version, - author, - email, - description string) Contract { +func Create(script []byte, manifest []byte) Contract { return Contract{} } @@ -65,16 +45,7 @@ func Create( // Create. The old contract will be deleted by this call, if it has any storage // associated it will be migrated to the new contract. New contract is returned. // This function uses `Neo.Contract.Migrate` syscall. -func Migrate( - script []byte, - params []byte, - returnType byte, - properties byte, - name, - version, - author, - email, - description string) Contract { +func Migrate(script []byte, manifest []byte) Contract { return Contract{} } diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index d0d572e85..81add1b93 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" @@ -177,10 +178,10 @@ func (c *Client) GetConnectionCount() (int, error) { } // GetContractState queries contract information, according to the contract script hash. -func (c *Client) GetContractState(hash util.Uint160) (*result.ContractState, error) { +func (c *Client) GetContractState(hash util.Uint160) (*state.Contract, error) { var ( params = request.NewRawParams(hash.StringLE()) - resp = &result.ContractState{} + resp = &state.Contract{} ) if err := c.performRequest("getcontractstate", params, resp); err != nil { return resp, err diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index c3c955d12..eccbdb99b 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -2,6 +2,7 @@ package client import ( "context" + "encoding/base64" "encoding/hex" "net/http" "net/http/httptest" @@ -11,12 +12,15 @@ import ( "github.com/gorilla/websocket" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/assert" @@ -293,39 +297,33 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ { name: "positive", invoke: func(c *Client) (interface{}, error) { - hash, err := util.Uint160DecodeStringLE("dc675afc61a7c0f7b3d2682bf6e1d8ed865a0e5f") + hash, err := util.Uint160DecodeStringLE("1b4357bff5a01bdf2a6581247cf9ed1e24629176") if err != nil { panic(err) } return c.GetContractState(hash) }, - serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"version":0,"hash":"0xdc675afc61a7c0f7b3d2682bf6e1d8ed865a0e5f","script":"X8VrbHZrAFJ6xGx2a1FSesRhB1dvb2xvbmdsdmtSUnrEA1dOR2x2a1NSesQAbHZrVFJ6xCEDVK5JgiEEbGZu/ruu6b0OtII0acmOdISUqSpx80axpmFsdmtVUnrEbHZrAMMGZGVwbG95h2x2a1ZSesRsdmtWw2QWAGx2a1XDYWXyAmx2a1dSesRi2AFsdmtVw2Fl2AFhbHZrAMMLdG90YWxTdXBwbHmHbHZrWFJ6xGx2a1jDZEAAYWgWTmVvLlN0b3JhZ2UuR2V0Q29udGV4dAZzdXBwbHlhfGgPTmVvLlN0b3JhZ2UuR2V0bHZrV1J6xGJwAWx2awDDBG5hbWWHbHZrWVJ6xGx2a1nDZBIAbHZrUsNsdmtXUnrEYkcBbHZrAMMGc3ltYm9sh2x2a1pSesRsdmtaw2QSAGx2a1PDbHZrV1J6xGIcAWx2awDDCGRlY2ltYWxzh2x2a1tSesRsdmtbw2QSAGx2a1TDbHZrV1J6xGLvAGx2awDDCWJhbGFuY2VPZodsdmtcUnrEbHZrXMNkQABhaBZOZW8uU3RvcmFnZS5HZXRDb250ZXh0bHZrUcNRw2F8aA9OZW8uU3RvcmFnZS5HZXRsdmtXUnrEYpMAbHZrUcMAw2FoGE5lby5SdW50aW1lLkNoZWNrV2l0bmVzcwCcbHZrXVJ6xGx2a13DZA4AAGx2a1dSesRiVQBsdmsAwwh0cmFuc2ZlcodsdmteUnrEbHZrXsNkLABsdmtRwwDDbHZrUcNRw2x2a1HDUsNhZdQDYVJyZckBbHZrV1J6xGIOAABsdmtXUnrEYgMAbHZrV8NhbHVmU8VrbHZrAFJ6xGFhaBZOZW8uU3RvcmFnZS5HZXRDb250ZXh0bHZrAMNhfGgPTmVvLlN0b3JhZ2UuR2V0YWVwA1GTbHZrUVJ6xGFoFk5lby5TdG9yYWdlLkdldENvbnRleHRsdmsAw2x2a1HDYWURA2FScmgPTmVvLlN0b3JhZ2UuUHV0YWFoFk5lby5TdG9yYWdlLkdldENvbnRleHQGc3VwcGx5YXxoD05lby5TdG9yYWdlLkdldGFl9AJRk2x2a1JSesRhaBZOZW8uU3RvcmFnZS5HZXRDb250ZXh0BnN1cHBseWx2a1LDYWWTAmFScmgPTmVvLlN0b3JhZ2UuUHV0YWFsdWZTxWtsdmsAUnrEYVFsdmtRUnrEYWgWTmVvLlN0b3JhZ2UuR2V0Q29udGV4dGx2awDDbHZrUcNhZUACYVJyaA9OZW8uU3RvcmFnZS5QdXRhYWgWTmVvLlN0b3JhZ2UuR2V0Q29udGV4dAZzdXBwbHlsdmtRw2FlAgJhUnJoD05lby5TdG9yYWdlLlB1dGFRbHZrUlJ6xGIDAGx2a1LDYWx1ZlnFa2x2awBSesRsdmtRUnrEbHZrUlJ6xGFhaBZOZW8uU3RvcmFnZS5HZXRDb250ZXh0bHZrAMNhfGgPTmVvLlN0b3JhZ2UuR2V0bHZrU1J6xGFoFk5lby5TdG9yYWdlLkdldENvbnRleHRsdmtRw2F8aA9OZW8uU3RvcmFnZS5HZXRsdmtUUnrEbHZrU8NhZXYBbHZrUsOUbHZrVVJ6xGx2a1TDYWVgAWx2a1LDk2x2a1ZSesRsdmtVwwCiZA0AbHZrUsMAomIEAABsdmtXUnrEbHZrV8Nk7ABhYWgWTmVvLlN0b3JhZ2UuR2V0Q29udGV4dGx2awDDbHZrVcNhZdgAYVJyaA9OZW8uU3RvcmFnZS5QdXRhYWgWTmVvLlN0b3JhZ2UuR2V0Q29udGV4dGx2a1HDbHZrVsNhZZwAYVJyaA9OZW8uU3RvcmFnZS5QdXRhVcV2ABNUcmFuc2ZlciBTdWNjZXNzZnVsxHZRbHZrAMPEdlJsdmtRw8R2U2x2a1LDxHZUYWgYTmVvLkJsb2NrY2hhaW4uR2V0SGVpZ2h0xGFoEk5lby5SdW50aW1lLk5vdGlmeWFRbHZrWFJ6xGIOAABsdmtYUnrEYgMAbHZrWMNhbHVmU8VrbHZrAFJ6xGFsdmsAw2x2a1FSesRsdmtRw2x2a1JSesRiAwBsdmtSw2FsdWZTxWtsdmsAUnrEYVFsdmsAw2pSelJ6xGx2a1HDbHZrUlJ6xGIDAGx2a1LDYWx1Zg==","parameters":["ByteArray"],"returntype":"ByteArray","name":"Woolong","code_version":"0.9.2","author":"lllwvlvwlll","email":"lllwvlvwlll@gmail.com","description":"GO NEO!!!","properties":{"storage":true,"dynamic_invoke":false}}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","entryPoint":{"name":"Main","parameters":[{"name":"method","type":"String"},{"name":"params","type":"Array"}],"returnType":"Boolean"},"methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"safeMethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, result: func(c *Client) interface{} { - hash, err := util.Uint160DecodeStringLE("dc675afc61a7c0f7b3d2682bf6e1d8ed865a0e5f") + script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==") if err != nil { panic(err) } - script, err := hex.DecodeString("5fc56b6c766b00527ac46c766b51527ac46107576f6f6c6f6e676c766b52527ac403574e476c766b53527ac4006c766b54527ac4210354ae498221046c666efebbaee9bd0eb4823469c98e748494a92a71f346b1a6616c766b55527ac46c766b00c3066465706c6f79876c766b56527ac46c766b56c36416006c766b55c36165f2026c766b57527ac462d8016c766b55c36165d801616c766b00c30b746f74616c537570706c79876c766b58527ac46c766b58c36440006168164e656f2e53746f726167652e476574436f6e7465787406737570706c79617c680f4e656f2e53746f726167652e4765746c766b57527ac46270016c766b00c3046e616d65876c766b59527ac46c766b59c36412006c766b52c36c766b57527ac46247016c766b00c30673796d626f6c876c766b5a527ac46c766b5ac36412006c766b53c36c766b57527ac4621c016c766b00c308646563696d616c73876c766b5b527ac46c766b5bc36412006c766b54c36c766b57527ac462ef006c766b00c30962616c616e63654f66876c766b5c527ac46c766b5cc36440006168164e656f2e53746f726167652e476574436f6e746578746c766b51c351c3617c680f4e656f2e53746f726167652e4765746c766b57527ac46293006c766b51c300c36168184e656f2e52756e74696d652e436865636b5769746e657373009c6c766b5d527ac46c766b5dc3640e00006c766b57527ac46255006c766b00c3087472616e73666572876c766b5e527ac46c766b5ec3642c006c766b51c300c36c766b51c351c36c766b51c352c36165d40361527265c9016c766b57527ac4620e00006c766b57527ac46203006c766b57c3616c756653c56b6c766b00527ac4616168164e656f2e53746f726167652e476574436f6e746578746c766b00c3617c680f4e656f2e53746f726167652e4765746165700351936c766b51527ac46168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b51c361651103615272680f4e656f2e53746f726167652e507574616168164e656f2e53746f726167652e476574436f6e7465787406737570706c79617c680f4e656f2e53746f726167652e4765746165f40251936c766b52527ac46168164e656f2e53746f726167652e476574436f6e7465787406737570706c796c766b52c361659302615272680f4e656f2e53746f726167652e50757461616c756653c56b6c766b00527ac461516c766b51527ac46168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b51c361654002615272680f4e656f2e53746f726167652e507574616168164e656f2e53746f726167652e476574436f6e7465787406737570706c796c766b51c361650202615272680f4e656f2e53746f726167652e50757461516c766b52527ac46203006c766b52c3616c756659c56b6c766b00527ac46c766b51527ac46c766b52527ac4616168164e656f2e53746f726167652e476574436f6e746578746c766b00c3617c680f4e656f2e53746f726167652e4765746c766b53527ac46168164e656f2e53746f726167652e476574436f6e746578746c766b51c3617c680f4e656f2e53746f726167652e4765746c766b54527ac46c766b53c3616576016c766b52c3946c766b55527ac46c766b54c3616560016c766b52c3936c766b56527ac46c766b55c300a2640d006c766b52c300a2620400006c766b57527ac46c766b57c364ec00616168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b55c36165d800615272680f4e656f2e53746f726167652e507574616168164e656f2e53746f726167652e476574436f6e746578746c766b51c36c766b56c361659c00615272680f4e656f2e53746f726167652e5075746155c57600135472616e73666572205375636365737366756cc476516c766b00c3c476526c766b51c3c476536c766b52c3c476546168184e656f2e426c6f636b636861696e2e476574486569676874c46168124e656f2e52756e74696d652e4e6f7469667961516c766b58527ac4620e00006c766b58527ac46203006c766b58c3616c756653c56b6c766b00527ac4616c766b00c36c766b51527ac46c766b51c36c766b52527ac46203006c766b52c3616c756653c56b6c766b00527ac461516c766b00c36a527a527ac46c766b51c36c766b52527ac46203006c766b52c3616c7566") - if err != nil { - panic(err) + m := manifest.NewManifest(hash.Hash160(script)) + m.ABI.EntryPoint.Name = "Main" + m.ABI.EntryPoint.Parameters = []manifest.Parameter{ + manifest.NewParameter("method", smartcontract.StringType), + manifest.NewParameter("params", smartcontract.ArrayType), } - return &result.ContractState{ - Version: 0, - ScriptHash: hash, - Script: script, - ParamList: []smartcontract.ParamType{smartcontract.ByteArrayType}, - ReturnType: smartcontract.ByteArrayType, - Name: "Woolong", - CodeVersion: "0.9.2", - Author: "lllwvlvwlll", - Email: "lllwvlvwlll@gmail.com", - Description: "GO NEO!!!", - Properties: result.Properties{ - HasStorage: true, - HasDynamicInvoke: false, - IsPayable: false, - }, + m.ABI.EntryPoint.ReturnType = smartcontract.BoolType + m.Features = smartcontract.HasStorage + cs := &state.Contract{ + ID: 0, + Script: script, + Manifest: *m, } + _ = cs.ScriptHash() + return cs }, }, }, diff --git a/pkg/rpc/response/result/contract_state.go b/pkg/rpc/response/result/contract_state.go deleted file mode 100644 index 2d3f36fd5..000000000 --- a/pkg/rpc/response/result/contract_state.go +++ /dev/null @@ -1,53 +0,0 @@ -package result - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/util" -) - -// ContractState wrapper used for the representation of -// state.Contract on the RPC Server. -type ContractState struct { - Version byte `json:"version"` - ScriptHash util.Uint160 `json:"hash"` - Script []byte `json:"script"` - ParamList []smartcontract.ParamType `json:"parameters"` - ReturnType smartcontract.ParamType `json:"returntype"` - Name string `json:"name"` - CodeVersion string `json:"code_version"` - Author string `json:"author"` - Email string `json:"email"` - Description string `json:"description"` - Properties Properties `json:"properties"` -} - -// Properties response wrapper. -type Properties struct { - HasStorage bool `json:"storage"` - HasDynamicInvoke bool `json:"dynamic_invoke"` - IsPayable bool `json:"is_payable"` -} - -// NewContractState creates a new Contract wrapper. -func NewContractState(c *state.Contract) ContractState { - properties := Properties{ - HasStorage: c.HasStorage(), - HasDynamicInvoke: c.HasDynamicInvoke(), - IsPayable: c.IsPayable(), - } - - return ContractState{ - Version: 0, - ScriptHash: c.ScriptHash(), - Script: c.Script, - ParamList: c.ParamList, - ReturnType: c.ReturnType, - Properties: properties, - Name: c.Name, - CodeVersion: c.CodeVersion, - Author: c.Author, - Email: c.Email, - Description: c.Description, - } -} diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index c42ddf6ed..c75af6fad 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -738,11 +738,10 @@ func (s *Server) getContractState(reqParams request.Params) (interface{}, *respo return nil, response.ErrInvalidParams } else { cs := s.chain.GetContractState(scriptHash) - if cs != nil { - results = result.NewContractState(cs) - } else { + if cs == nil { return nil, response.NewRPCError("Unknown contract", "", nil) } + results = cs } return results, nil } diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index c10180304..bcc28e0f5 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -17,6 +17,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/internal/testchain" @@ -55,12 +56,12 @@ var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { { name: "positive", - params: `["328190e78f1b5b8dcf86e71ec1894d6144f4e4cdb25abc20ea02b618851941dd"]`, + params: `["9d84eee99b8fda7cba4931ae54b316c1c0468bd4526b8b4eb6cf62d771abe9c7"]`, result: func(e *executor) interface{} { return &result.ApplicationLog{} }, check: func(t *testing.T, e *executor, acc interface{}) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) - expectedTxHash, err := util.Uint256DecodeStringLE("328190e78f1b5b8dcf86e71ec1894d6144f4e4cdb25abc20ea02b618851941dd") + expectedTxHash, err := util.Uint256DecodeStringLE("9d84eee99b8fda7cba4931ae54b316c1c0468bd4526b8b4eb6cf62d771abe9c7") require.NoError(t, err) assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, 1, len(res.Executions)) @@ -88,13 +89,11 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: fmt.Sprintf(`["%s"]`, testContractHash), - result: func(e *executor) interface{} { return &result.ContractState{} }, + result: func(e *executor) interface{} { return &state.Contract{} }, check: func(t *testing.T, e *executor, cs interface{}) { - res, ok := cs.(*result.ContractState) + res, ok := cs.(*state.Contract) require.True(t, ok) - assert.Equal(t, byte(0), res.Version) - assert.Equal(t, testContractHash, res.ScriptHash.StringLE()) - assert.Equal(t, "0.99", res.CodeVersion) + assert.Equal(t, testContractHash, res.ScriptHash().StringLE()) }, }, { @@ -484,7 +483,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "gettransactionheight": { { name: "positive", - params: `["328190e78f1b5b8dcf86e71ec1894d6144f4e4cdb25abc20ea02b618851941dd"]`, + params: `["9d84eee99b8fda7cba4931ae54b316c1c0468bd4526b8b4eb6cf62d771abe9c7"]`, result: func(e *executor) interface{} { h := 0 return &h diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index bb02750f547480a62d4c1832ad2c833b447d67e3..39cd4d68c1e202b51e21b1a945c199c9f359007d 100644 GIT binary patch delta 2426 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