diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 2688fcc6c..b4c6ed2c7 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -12,6 +12,7 @@ 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/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -470,3 +471,30 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error { } return nil } + +// contractIsStandard checks if contract is standard (sig or multisig) contract. +func contractIsStandard(ic *interop.Context, v *vm.VM) error { + h := v.Estack().Pop().Bytes() + u, err := util.Uint160DecodeBytesBE(h) + if err != nil { + return err + } + var result bool + cs, _ := ic.DAO.GetContractState(u) + if cs == nil || vm.IsStandardContract(cs.Script) { + result = true + } + v.Estack().PushVal(result) + return nil +} + +// contractCreateStandardAccount calculates contract scripthash for a given public key. +func contractCreateStandardAccount(ic *interop.Context, v *vm.VM) error { + h := v.Estack().Pop().Bytes() + p, err := keys.NewPublicKeyFromBytes(h) + if err != nil { + return err + } + v.Estack().PushVal(p.GetScriptHash().BytesBE()) + return nil +} diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index 3a06fcb4a..613351459 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -4,6 +4,11 @@ import ( "math/big" "testing" + "github.com/nspcc-dev/dbft/crypto" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -145,3 +150,50 @@ func TestBCGetBlock(t *testing.T) { require.True(t, ok) }) } + +func TestContractIsStandard(t *testing.T) { + v, ic, chain := createVM(t) + defer chain.Close() + + t.Run("True", func(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + pub := priv.PublicKey() + err = ic.DAO.PutContractState(&state.Contract{ID: 42, Script: pub.GetVerificationScript()}) + require.NoError(t, err) + + v.Estack().PushVal(pub.GetScriptHash().BytesBE()) + require.NoError(t, contractIsStandard(ic, v)) + require.True(t, v.Estack().Pop().Bool()) + }) + t.Run("False", func(t *testing.T) { + script := []byte{byte(opcode.PUSHT)} + require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script})) + + v.Estack().PushVal(crypto.Hash160(script).BytesBE()) + require.NoError(t, contractIsStandard(ic, v)) + require.False(t, v.Estack().Pop().Bool()) + }) +} + +func TestContractCreateAccount(t *testing.T) { + v, ic, chain := createVM(t) + defer chain.Close() + t.Run("Good", func(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + pub := priv.PublicKey() + v.Estack().PushVal(pub.Bytes()) + require.NoError(t, contractCreateStandardAccount(ic, v)) + + value := v.Estack().Pop().Bytes() + u, err := util.Uint160DecodeBytesBE(value) + require.NoError(t, err) + require.Equal(t, pub.GetScriptHash(), u) + }) + t.Run("InvalidKey", func(t *testing.T) { + v.Estack().PushVal([]byte{1, 2, 3}) + require.Error(t, contractCreateStandardAccount(ic, v)) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 437705ab6..e3af4d4ec 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -71,26 +71,28 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin var systemInterops = []interop.Function{ {Name: "System.Binary.Deserialize", Func: runtimeDeserialize, Price: 500000}, {Name: "System.Binary.Serialize", Func: runtimeSerialize, Price: 100000}, - {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250, + {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 2500000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100, + {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 1000000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1, + {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 400, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100, + {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 1000000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100, + {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 1000000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100, + {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 1000000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Contract.Call", Func: contractCall, Price: 1, + {Name: "System.Contract.Call", Func: contractCall, Price: 1000000, AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, - {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1, + {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1000000, AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, {Name: "System.Contract.Create", Func: contractCreate, Price: 0, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, - {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1, + {Name: "System.Contract.CreateStandardAccount", Func: contractCreateStandardAccount, Price: 10000}, + {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1000000, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Contract.IsStandard", Func: contractIsStandard, Price: 30000}, {Name: "System.Contract.Update", Func: contractUpdate, Price: 0, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, {Name: "System.Enumerator.Concat", Func: enumerator.Concat, Price: 400},