core: move contract-related interop code into appropriate package

And move one of the tests with it.
This commit is contained in:
Roman Khimov 2022-06-08 16:02:07 +03:00
parent 10f94e6119
commit 209b977e9a
6 changed files with 106 additions and 81 deletions

View file

@ -0,0 +1,71 @@
package contract
import (
"crypto/elliptic"
"errors"
"math"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// CreateMultisigAccount calculates multisig contract scripthash for a
// given m and a set of public keys.
func CreateMultisigAccount(ic *interop.Context) error {
m := ic.VM.Estack().Pop().BigInt()
mu64 := m.Uint64()
if !m.IsUint64() || mu64 > math.MaxInt32 {
return errors.New("m must be positive and fit int32")
}
arr := ic.VM.Estack().Pop().Array()
pubs := make(keys.PublicKeys, len(arr))
for i, pk := range arr {
p, err := keys.NewPublicKeyFromBytes(pk.Value().([]byte), elliptic.P256())
if err != nil {
return err
}
pubs[i] = p
}
var invokeFee int64
if ic.IsHardforkEnabled(config.HFAspidochelone) {
invokeFee = fee.ECDSAVerifyPrice * int64(len(pubs))
} else {
invokeFee = 1 << 8
}
invokeFee *= ic.BaseExecFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
script, err := smartcontract.CreateMultiSigRedeemScript(int(mu64), pubs)
if err != nil {
return err
}
ic.VM.Estack().PushItem(stackitem.NewByteArray(hash.Hash160(script).BytesBE()))
return nil
}
// CreateStandardAccount calculates contract scripthash for a given public key.
func CreateStandardAccount(ic *interop.Context) error {
h := ic.VM.Estack().Pop().Bytes()
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
if err != nil {
return err
}
var invokeFee int64
if ic.IsHardforkEnabled(config.HFAspidochelone) {
invokeFee = fee.ECDSAVerifyPrice
} else {
invokeFee = 1 << 8
}
invokeFee *= ic.BaseExecFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
ic.VM.Estack().PushItem(stackitem.NewByteArray(p.GetScriptHash().BytesBE()))
return nil
}

View file

@ -3,6 +3,7 @@ package contract
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/dao"
@ -172,3 +173,9 @@ func CallFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract
} }
return nil return nil
} }
// GetCallFlags returns current context calling flags.
func GetCallFlags(ic *interop.Context) error {
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.VM.Context().GetCallFlags()))))
return nil
}

View file

@ -0,0 +1,25 @@
package contract_test
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"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/opcode"
"github.com/stretchr/testify/require"
)
func TestGetCallFlags(t *testing.T) {
bc, _ := chain.NewSingle(t)
ic := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
ic.VM.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, callflag.All)
require.NoError(t, contract.GetCallFlags(ic))
require.Equal(t, int64(callflag.All), ic.VM.Estack().Pop().Value().(*big.Int).Int64())
}

View file

@ -2,23 +2,15 @@ package core
import ( import (
"context" "context"
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"math"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
@ -206,65 +198,3 @@ func storageFind(ic *interop.Context) error {
return nil return nil
} }
// contractCreateMultisigAccount calculates multisig contract scripthash for a
// given m and a set of public keys.
func contractCreateMultisigAccount(ic *interop.Context) error {
m := ic.VM.Estack().Pop().BigInt()
mu64 := m.Uint64()
if !m.IsUint64() || mu64 > math.MaxInt32 {
return errors.New("m must be positive and fit int32")
}
arr := ic.VM.Estack().Pop().Array()
pubs := make(keys.PublicKeys, len(arr))
for i, pk := range arr {
p, err := keys.NewPublicKeyFromBytes(pk.Value().([]byte), elliptic.P256())
if err != nil {
return err
}
pubs[i] = p
}
var invokeFee int64
if ic.IsHardforkEnabled(config.HFAspidochelone) {
invokeFee = fee.ECDSAVerifyPrice * int64(len(pubs))
} else {
invokeFee = 1 << 8
}
invokeFee *= ic.BaseExecFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
script, err := smartcontract.CreateMultiSigRedeemScript(int(mu64), pubs)
if err != nil {
return err
}
ic.VM.Estack().PushItem(stackitem.NewByteArray(hash.Hash160(script).BytesBE()))
return nil
}
// contractCreateStandardAccount calculates contract scripthash for a given public key.
func contractCreateStandardAccount(ic *interop.Context) error {
h := ic.VM.Estack().Pop().Bytes()
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
if err != nil {
return err
}
var invokeFee int64
if ic.IsHardforkEnabled(config.HFAspidochelone) {
invokeFee = fee.ECDSAVerifyPrice
} else {
invokeFee = 1 << 8
}
invokeFee *= ic.BaseExecFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
ic.VM.Estack().PushItem(stackitem.NewByteArray(p.GetScriptHash().BytesBE()))
return nil
}
// contractGetCallFlags returns current context calling flags.
func contractGetCallFlags(ic *interop.Context) error {
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.VM.Context().GetCallFlags()))))
return nil
}

View file

@ -706,14 +706,6 @@ func TestContractCall(t *testing.T) {
}) })
} }
func TestContractGetCallFlags(t *testing.T) {
v, ic, _ := createVM(t)
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, callflag.All)
require.NoError(t, contractGetCallFlags(ic))
require.Equal(t, int64(callflag.All), v.Estack().Pop().Value().(*big.Int).Int64())
}
func TestRuntimeCheckWitness(t *testing.T) { func TestRuntimeCheckWitness(t *testing.T) {
_, ic, bc := createVM(t) _, ic, bc := createVM(t)

View file

@ -33,9 +33,9 @@ var systemInterops = []interop.Function{
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15,
RequiredFlags: callflag.ReadStates | callflag.AllowCall, ParamCount: 4}, RequiredFlags: callflag.ReadStates | callflag.AllowCall, ParamCount: 4},
{Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1},
{Name: interopnames.SystemContractCreateMultisigAccount, Func: contractCreateMultisigAccount, Price: 0, ParamCount: 2}, {Name: interopnames.SystemContractCreateMultisigAccount, Func: contract.CreateMultisigAccount, Price: 0, ParamCount: 2},
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 0, ParamCount: 1}, {Name: interopnames.SystemContractCreateStandardAccount, Func: contract.CreateStandardAccount, Price: 0, ParamCount: 1},
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10}, {Name: interopnames.SystemContractGetCallFlags, Func: contract.GetCallFlags, Price: 1 << 10},
{Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: callflag.States}, {Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: callflag.States},
{Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: callflag.States}, {Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: callflag.States},
{Name: interopnames.SystemCryptoCheckMultisig, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 2}, {Name: interopnames.SystemCryptoCheckMultisig, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 2},