core: add Manifest to state.Contract

This commit is contained in:
Evgenii Stratonikov 2020-06-09 12:12:56 +03:00
parent 425277098c
commit df958caf93
19 changed files with 229 additions and 338 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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())

View file

@ -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),

View file

@ -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()

View file

@ -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.

View file

@ -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))

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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())
})
}

View file

@ -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{}
}

View file

@ -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

View file

@ -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
},
},
},

View file

@ -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,
}
}

View file

@ -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
}

View file

@ -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

Binary file not shown.

View file

@ -3,6 +3,7 @@ package manifest
import (
"encoding/json"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
@ -126,3 +127,23 @@ func (m *Manifest) UnmarshalJSON(data []byte) error {
return nil
}
// EncodeBinary implements io.Serializable.
func (m *Manifest) EncodeBinary(w *io.BinWriter) {
data, err := json.Marshal(m)
if err != nil {
w.Err = err
return
}
w.WriteVarBytes(data)
}
// DecodeBinary implements io.Serializable.
func (m *Manifest) DecodeBinary(r *io.BinReader) {
data := r.ReadVarBytes()
if r.Err != nil {
return
} else if err := json.Unmarshal(data, m); err != nil {
r.Err = err
}
}