Skip to content

Commit

Permalink
core: decouple native contracts from interop service
Browse files Browse the repository at this point in the history
Closes #1191.
  • Loading branch information
AnnaShaleva committed Jul 23, 2020
1 parent d6342ab commit 2c7fba3
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 75 deletions.
2 changes: 2 additions & 0 deletions pkg/core/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ func TestCreateBasicChain(t *testing.T) {

gasHash := bc.contracts.GAS.Hash
neoHash := bc.contracts.NEO.Hash
policyHash := bc.contracts.Policy.Hash
t.Logf("native GAS hash: %v", gasHash)
t.Logf("native NEO hash: %v", neoHash)
t.Logf("native Policy hash: %v", policyHash)

priv0 := testchain.PrivateKeyByID(0)
priv0ScriptHash := priv0.GetScriptHash()
Expand Down
22 changes: 11 additions & 11 deletions pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,25 @@ type Contract interface {

// ContractMD represents native contract instance.
type ContractMD struct {
Manifest manifest.Manifest
ServiceName string
ServiceID uint32
ContractID int32
Script []byte
Hash util.Uint160
Methods map[string]MethodAndPrice
Manifest manifest.Manifest
Name string
ContractID int32
Script []byte
Hash util.Uint160
Methods map[string]MethodAndPrice
}

// NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string) *ContractMD {
c := &ContractMD{
ServiceName: name,
ServiceID: emit.InteropNameToID([]byte(name)),
Methods: make(map[string]MethodAndPrice),
Name: name,
Methods: make(map[string]MethodAndPrice),
}

w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, c.ServiceName)
emit.Bytes(w.BinWriter, []byte(c.Name))
emit.Syscall(w.BinWriter, "Neo.Native.Call")

c.Script = w.Bytes()
c.Hash = hash.Hash160(c.Script)
c.Manifest = *manifest.DefaultManifest(c.Hash)
Expand Down
4 changes: 1 addition & 3 deletions pkg/core/interops.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ func SpawnVM(ic *interop.Context) *vm.VM {
vm := vm.NewWithTrigger(ic.Trigger)
vm.RegisterInteropGetter(getSystemInterop(ic))
vm.RegisterInteropGetter(getNeoInterop(ic))
if ic.Chain != nil {
vm.RegisterInteropGetter(ic.Chain.(*Blockchain).contracts.GetNativeInterop(ic))
}
ic.ScriptGetter = vm
return vm
}
Expand Down Expand Up @@ -149,6 +146,7 @@ var neoInterops = []interop.Function{
{Name: "Neo.Crypto.SHA256", Func: crypto.Sha256, Price: 1000000},
{Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 0,
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
{Name: "Neo.Native.Call", Func: native.Call, Price: 0},
}

// initIDinInteropsSlice initializes IDs from names in one given
Expand Down
47 changes: 3 additions & 44 deletions pkg/core/native/contract.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package native

import (
"fmt"

"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/pkg/errors"
)

// Contracts is a set of registered native contracts.
Expand All @@ -32,10 +28,10 @@ func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
return nil
}

// ByID returns native contract with the specified id.
func (cs *Contracts) ByID(id uint32) interop.Contract {
// ByName returns native contract with the specified name.
func (cs *Contracts) ByName(name string) interop.Contract {
for _, ctr := range cs.Contracts {
if ctr.Metadata().ServiceID == id {
if ctr.Metadata().Name == name {
return ctr
}
}
Expand Down Expand Up @@ -79,40 +75,3 @@ func (cs *Contracts) GetPersistScript() []byte {
cs.persistScript = w.Bytes()
return cs.persistScript
}

// GetNativeInterop returns an interop getter for a given set of contracts.
func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.InteropFuncPrice {
return func(id uint32) *vm.InteropFuncPrice {
if c := cs.ByID(id); c != nil {
return &vm.InteropFuncPrice{
Func: getNativeInterop(ic, c),
}
}
return nil
}
}

// getNativeInterop returns native contract interop.
func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error {
return func(v *vm.VM) error {
h := v.GetCurrentScriptHash()
if !h.Equals(c.Metadata().Hash) {
return errors.New("invalid hash")
}
name := string(v.Estack().Pop().Bytes())
args := v.Estack().Pop().Array()
m, ok := c.Metadata().Methods[name]
if !ok {
return fmt.Errorf("method %s not found", name)
}
if !v.Context().GetCallFlags().Has(m.RequiredFlags) {
return errors.New("missing call flags")
}
if !v.AddGas(m.Price) {
return errors.New("gas limit exceeded")
}
result := m.Func(ic, args)
v.Estack().PushVal(result)
return nil
}
}
35 changes: 34 additions & 1 deletion pkg/core/native/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,41 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
return err
}
if err := native.Initialize(ic); err != nil {
return fmt.Errorf("initializing %s native contract: %v", md.ServiceName, err)
return fmt.Errorf("initializing %s native contract: %v", md.Name, err)
}
}
return nil
}

// Call calls specified native contract method.
func Call(ic *interop.Context, v *vm.VM) error {
name := string(v.Estack().Pop().Bytes())
var c interop.Contract
for _, ctr := range ic.Natives {
if ctr.Metadata().Name == name {
c = ctr
}
}
if c == nil {
return fmt.Errorf("native contract %s not found", name)
}
h := v.GetCurrentScriptHash()
if !h.Equals(c.Metadata().Hash) {
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
}
operation := string(v.Estack().Pop().Bytes())
args := v.Estack().Pop().Array()
m, ok := c.Metadata().Methods[operation]
if !ok {
return fmt.Errorf("method %s not found", operation)
}
if !v.Context().GetCallFlags().Has(m.RequiredFlags) {
return errors.New("missing call flags")
}
if !v.AddGas(m.Price) {
return errors.New("gas limit exceeded")
}
result := m.Func(ic, args)
v.Estack().PushVal(result)
return nil
}
5 changes: 2 additions & 3 deletions pkg/core/native/native_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type GAS struct {
NEO *NEO
}

const gasSyscallName = "Neo.Native.Tokens.GAS"
const gasName = "GAS"
const gasContractID = -2

// GASFactor is a divisor for finding GAS integral value.
Expand All @@ -29,8 +29,7 @@ const initialGAS = 30000000
// NewGAS returns GAS native contract.
func NewGAS() *GAS {
g := &GAS{}
nep5 := newNEP5Native(gasSyscallName)
nep5.name = "GAS"
nep5 := newNEP5Native(gasName)
nep5.symbol = "gas"
nep5.decimals = 8
nep5.factor = GASFactor
Expand Down
7 changes: 3 additions & 4 deletions pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ type keyWithVotes struct {
}

const (
neoSyscallName = "Neo.Native.Tokens.NEO"
neoContractID = -1
neoName = "NEO"
neoContractID = -1
// NEOTotalSupply is the total amount of NEO in the system.
NEOTotalSupply = 100000000
// prefixValidator is a prefix used to store validator's data.
Expand Down Expand Up @@ -68,8 +68,7 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
// NewNEO returns NEO native contract.
func NewNEO() *NEO {
n := &NEO{}
nep5 := newNEP5Native(neoSyscallName)
nep5.name = "NEO"
nep5 := newNEP5Native(neoName)
nep5.symbol = "neo"
nep5.decimals = 0
nep5.factor = 1
Expand Down
3 changes: 1 addition & 2 deletions pkg/core/native/native_nep5.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func makeAccountKey(h util.Uint160) []byte {
// nep5TokenNative represents NEP-5 token contract.
type nep5TokenNative struct {
interop.ContractMD
name string
symbol string
decimals int64
factor int64
Expand Down Expand Up @@ -92,7 +91,7 @@ func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
}

func (c *nep5TokenNative) Name(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewByteArray([]byte(c.name))
return stackitem.NewByteArray([]byte(c.ContractMD.Name))
}

func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
Expand Down
6 changes: 3 additions & 3 deletions pkg/core/native/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
)

const (
policySyscallName = "Neo.Native.Policy"
policyContractID = -3
policyName = "Policy"
policyContractID = -3

defaultMaxBlockSize = 1024 * 256
defaultMaxTransactionsPerBlock = 512
Expand Down Expand Up @@ -57,7 +57,7 @@ var _ interop.Contract = (*Policy)(nil)

// newPolicy returns Policy native contract.
func newPolicy() *Policy {
p := &Policy{ContractMD: *interop.NewContractMD(policySyscallName)}
p := &Policy{ContractMD: *interop.NewContractMD(policyName)}

p.ContractID = policyContractID
p.Manifest.Features |= smartcontract.HasStorage
Expand Down
4 changes: 2 additions & 2 deletions pkg/rpc/client/nep5.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (

var (
// NeoContractHash is a hash of the NEO native contract.
NeoContractHash, _ = util.Uint160DecodeStringLE("9bde8f209c88dd0e7ca3bf0af0f476cdd8207789")
NeoContractHash, _ = util.Uint160DecodeStringBE("25059ecb4878d3a875f91c51ceded330d4575fde")
// GasContractHash is a hash of the GAS native contract.
GasContractHash, _ = util.Uint160DecodeStringLE("8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b")
GasContractHash, _ = util.Uint160DecodeStringBE("bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e66")
)

// NEP5Decimals invokes `decimals` NEP5 method on a specified contract.
Expand Down
4 changes: 2 additions & 2 deletions pkg/rpc/client/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/pkg/errors"
)

// PolicyContractHash represents BE hash of native Policy contract.
var PolicyContractHash = util.Uint160{154, 97, 164, 110, 236, 151, 184, 147, 6, 215, 206, 129, 241, 91, 70, 32, 145, 208, 9, 50}
// PolicyContractHash represents a hash of native Policy contract.
var PolicyContractHash, _ = util.Uint160DecodeStringBE("e9ff4ca7cc252e1dfddb26315869cd79505906ce")

// GetMaxTransactionsPerBlock invokes `getMaxTransactionsPerBlock` method on a
// native Policy contract.
Expand Down
Binary file modified pkg/rpc/server/testdata/testblocks.acc
Binary file not shown.

0 comments on commit 2c7fba3

Please sign in to comment.