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