Merge pull request #1763 from nspcc-dev/multisig-interop

core: add System.Contract.CreateMultisigAccount
This commit is contained in:
Roman Khimov 2021-02-19 18:15:33 +03:00 committed by GitHub
commit 61e04f04de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 0 deletions

View file

@ -16,6 +16,7 @@ var syscalls = map[string]map[string]string{
}, },
"contract": { "contract": {
"Call": interopnames.SystemContractCall, "Call": interopnames.SystemContractCall,
"CreateMultisigAccount": interopnames.SystemContractCreateMultisigAccount,
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount, "CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
"IsStandard": interopnames.SystemContractIsStandard, "IsStandard": interopnames.SystemContractIsStandard,
"GetCallFlags": interopnames.SystemContractGetCallFlags, "GetCallFlags": interopnames.SystemContractGetCallFlags,

View file

@ -16,6 +16,7 @@ const (
SystemCallbackInvoke = "System.Callback.Invoke" SystemCallbackInvoke = "System.Callback.Invoke"
SystemContractCall = "System.Contract.Call" SystemContractCall = "System.Contract.Call"
SystemContractCallNative = "System.Contract.CallNative" SystemContractCallNative = "System.Contract.CallNative"
SystemContractCreateMultisigAccount = "System.Contract.CreateMultisigAccount"
SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount" SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount"
SystemContractIsStandard = "System.Contract.IsStandard" SystemContractIsStandard = "System.Contract.IsStandard"
SystemContractGetCallFlags = "System.Contract.GetCallFlags" SystemContractGetCallFlags = "System.Contract.GetCallFlags"
@ -70,6 +71,7 @@ var names = []string{
SystemCallbackInvoke, SystemCallbackInvoke,
SystemContractCall, SystemContractCall,
SystemContractCallNative, SystemContractCallNative,
SystemContractCreateMultisigAccount,
SystemContractCreateStandardAccount, SystemContractCreateStandardAccount,
SystemContractIsStandard, SystemContractIsStandard,
SystemContractGetCallFlags, SystemContractGetCallFlags,

View file

@ -4,13 +4,16 @@ import (
"crypto/elliptic" "crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"math"
"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/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"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/state" "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/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/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/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -221,6 +224,30 @@ func contractIsStandard(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()
if !m.IsInt64() || m.Int64() > math.MaxInt32 {
return errors.New("m should 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
}
script, err := smartcontract.CreateMultiSigRedeemScript(int(m.Int64()), pubs)
if err != nil {
return err
}
ic.VM.Estack().PushVal(hash.Hash160(script).BytesBE())
return nil
}
// contractCreateStandardAccount calculates contract scripthash for a given public key. // contractCreateStandardAccount calculates contract scripthash for a given public key.
func contractCreateStandardAccount(ic *interop.Context) error { func contractCreateStandardAccount(ic *interop.Context) error {
h := ic.VM.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()

View file

@ -2,6 +2,7 @@ package core
import ( import (
"errors" "errors"
"math"
"math/big" "math/big"
"testing" "testing"
@ -110,6 +111,53 @@ func TestContractCreateAccount(t *testing.T) {
}) })
} }
func TestContractCreateMultisigAccount(t *testing.T) {
v, ic, chain := createVM(t)
defer chain.Close()
t.Run("Good", func(t *testing.T) {
m, n := 3, 5
pubs := make(keys.PublicKeys, n)
arr := make([]stackitem.Item, n)
for i := range pubs {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
pubs[i] = pk.PublicKey()
arr[i] = stackitem.Make(pubs[i].Bytes())
}
v.Estack().PushVal(stackitem.Make(arr))
v.Estack().PushVal(m)
require.NoError(t, contractCreateMultisigAccount(ic))
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
require.NoError(t, err)
value := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, hash.Hash160(expected), u)
})
t.Run("InvalidKey", func(t *testing.T) {
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make([]byte{1, 2, 3})}))
v.Estack().PushVal(1)
require.Error(t, contractCreateMultisigAccount(ic))
})
t.Run("Invalid m", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
v.Estack().PushVal(2)
require.Error(t, contractCreateMultisigAccount(ic))
})
t.Run("m overflows int64", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
m := big.NewInt(math.MaxInt64)
m.Add(m, big.NewInt(1))
v.Estack().PushVal(stackitem.NewBigInteger(m))
require.Error(t, contractCreateMultisigAccount(ic))
})
}
func TestRuntimeGasLeft(t *testing.T) { func TestRuntimeGasLeft(t *testing.T) {
v, ic, chain := createVM(t) v, ic, chain := createVM(t)
defer chain.Close() defer chain.Close()

View file

@ -43,6 +43,7 @@ var systemInterops = []interop.Function{
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15,
RequiredFlags: callflag.AllowCall, ParamCount: 4}, RequiredFlags: 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: 1 << 8, ParamCount: 1},
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, ParamCount: 1}, {Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, ParamCount: 1},
{Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: callflag.ReadStates, ParamCount: 1}, {Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: callflag.ReadStates, ParamCount: 1},
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10}, {Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10},

View file

@ -27,6 +27,13 @@ func IsStandard(h interop.Hash160) bool {
return false return false
} }
// CreateMultisigAccount calculates script hash of an m out of n multisignature
// script using given m and a set of public keys bytes. This function uses
// `System.Contract.CreateMultisigAccount` syscall.
func CreateMultisigAccount(m int, pubs []interop.PublicKey) []byte {
return nil
}
// CreateStandardAccount calculates script hash of a given public key. // CreateStandardAccount calculates script hash of a given public key.
// This function uses `System.Contract.CreateStandardAccount` syscall. // This function uses `System.Contract.CreateStandardAccount` syscall.
func CreateStandardAccount(pub interop.PublicKey) []byte { func CreateStandardAccount(pub interop.PublicKey) []byte {