From 238c590ddb798a5a6940c6a07712f63db9fd30f9 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 10 Oct 2019 17:56:58 +0300 Subject: [PATCH] core: fix contract state's Properties to use PropertyState PublishTX only had one of these flags, but newer contracts (created via the interop function) can have more and these flags are aggregated into one field that uses PropertyState enumeration (it's used to publish contract, so supposedly it's also a nice choice for contract state storage). --- pkg/core/blockchain.go | 7 ++++- pkg/core/contract_state.go | 43 ++++++++++++++++++------------ pkg/core/contract_state_test.go | 35 ++++++++++++++++-------- pkg/smartcontract/param_context.go | 11 ++++++++ 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 4a4afba09..57da76ff2 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -13,6 +13,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/vm" "github.com/pkg/errors" @@ -390,11 +391,15 @@ func (bc *Blockchain) storeBlock(block *Block) error { case *transaction.EnrollmentTX: case *transaction.StateTX: case *transaction.PublishTX: + var properties smartcontract.PropertyState + if t.NeedStorage { + properties |= smartcontract.HasStorage + } contract := &ContractState{ Script: t.Script, ParamList: t.ParamList, ReturnType: t.ReturnType, - HasStorage: t.NeedStorage, + Properties: properties, Name: t.Name, CodeVersion: t.CodeVersion, Author: t.Author, diff --git a/pkg/core/contract_state.go b/pkg/core/contract_state.go index 1ba19cbaa..157e7dd60 100644 --- a/pkg/core/contract_state.go +++ b/pkg/core/contract_state.go @@ -13,17 +13,15 @@ type Contracts map[util.Uint160]*ContractState // ContractState holds information about a smart contract in the NEO blockchain. type ContractState struct { - Script []byte - ParamList []smartcontract.ParamType - ReturnType smartcontract.ParamType - Properties []byte - Name string - CodeVersion string - Author string - Email string - Description string - HasStorage bool - HasDynamicInvoke bool + Script []byte + ParamList []smartcontract.ParamType + ReturnType smartcontract.ParamType + Properties smartcontract.PropertyState + Name string + CodeVersion string + Author string + Email string + Description string scriptHash util.Uint160 } @@ -52,14 +50,12 @@ func (a *ContractState) DecodeBinary(br *io.BinReader) { a.ParamList[k] = smartcontract.ParamType(paramBytes[k]) } br.ReadLE(&a.ReturnType) - a.Properties = br.ReadBytes() + br.ReadLE(&a.Properties) a.Name = br.ReadString() a.CodeVersion = br.ReadString() a.Author = br.ReadString() a.Email = br.ReadString() a.Description = br.ReadString() - br.ReadLE(&a.HasStorage) - br.ReadLE(&a.HasDynamicInvoke) a.createHash() } @@ -71,14 +67,12 @@ func (a *ContractState) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(a.ParamList[k]) } bw.WriteLE(a.ReturnType) - bw.WriteBytes(a.Properties) + bw.WriteLE(a.Properties) bw.WriteString(a.Name) bw.WriteString(a.CodeVersion) bw.WriteString(a.Author) bw.WriteString(a.Email) bw.WriteString(a.Description) - bw.WriteLE(a.HasStorage) - bw.WriteLE(a.HasDynamicInvoke) } // ScriptHash returns a contract script hash. @@ -93,3 +87,18 @@ func (a *ContractState) ScriptHash() util.Uint160 { func (a *ContractState) createHash() { a.scriptHash = hash.Hash160(a.Script) } + +// HasStorage checks whether the contract has storage property set. +func (cs *ContractState) HasStorage() bool { + return (cs.Properties & smartcontract.HasStorage) != 0 +} + +// HasDynamicInvoke checks whether the contract has dynamic invoke property set. +func (cs *ContractState) HasDynamicInvoke() bool { + return (cs.Properties & smartcontract.HasDynamicInvoke) != 0 +} + +// IsPayable checks whether the contract has payable property set. +func (cs *ContractState) IsPayable() bool { + return (cs.Properties & smartcontract.IsPayable) != 0 +} diff --git a/pkg/core/contract_state_test.go b/pkg/core/contract_state_test.go index a34039aae..276e34e5f 100644 --- a/pkg/core/contract_state_test.go +++ b/pkg/core/contract_state_test.go @@ -13,17 +13,15 @@ func TestEncodeDecodeContractState(t *testing.T) { script := []byte("testscript") contract := &ContractState{ - Script: script, - ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, - ReturnType: smartcontract.BoolType, - Properties: []byte("smth"), - Name: "Contracto", - CodeVersion: "1.0.0", - Author: "Joe Random", - Email: "joe@example.com", - Description: "Test contract", - HasStorage: true, - HasDynamicInvoke: false, + Script: script, + ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, + ReturnType: smartcontract.BoolType, + Properties: smartcontract.HasStorage, + Name: "Contracto", + CodeVersion: "1.0.0", + Author: "Joe Random", + Email: "joe@example.com", + Description: "Test contract", } assert.Equal(t, hash.Hash160(script), contract.ScriptHash()) @@ -37,3 +35,18 @@ func TestEncodeDecodeContractState(t *testing.T) { assert.Equal(t, contract, contractDecoded) assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash()) } + +func TestContractStateProperties(t *testing.T) { + flaggedContract := ContractState{ + Properties: smartcontract.HasStorage | smartcontract.HasDynamicInvoke | smartcontract.IsPayable, + } + nonFlaggedContract := ContractState{ + ReturnType: smartcontract.BoolType, + } + 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()) +} diff --git a/pkg/smartcontract/param_context.go b/pkg/smartcontract/param_context.go index b4b75c45c..e5ad6142b 100644 --- a/pkg/smartcontract/param_context.go +++ b/pkg/smartcontract/param_context.go @@ -18,6 +18,17 @@ const ( ArrayType ) +// PropertyState represents contract properties (flags). +type PropertyState byte + +// List of supported properties. +const ( + NoProperties = 0 + HasStorage PropertyState = 1 << iota + HasDynamicInvoke + IsPayable +) + // Parameter represents a smart contract parameter. type Parameter struct { // Type of the parameter