Skip to content

Commit

Permalink
core: add System.Contract.CreateMultisigAccount interop
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnaShaleva committed Feb 19, 2021
1 parent fe918e2 commit eeb650d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pkg/core/interop/interopnames/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
SystemCallbackInvoke = "System.Callback.Invoke"
SystemContractCall = "System.Contract.Call"
SystemContractCallNative = "System.Contract.CallNative"
SystemContractCreateMultisigAccount = "System.Contract.CreateMultisigAccount"
SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount"
SystemContractIsStandard = "System.Contract.IsStandard"
SystemContractGetCallFlags = "System.Contract.GetCallFlags"
Expand Down Expand Up @@ -70,6 +71,7 @@ var names = []string{
SystemCallbackInvoke,
SystemContractCall,
SystemContractCallNative,
SystemContractCreateMultisigAccount,
SystemContractCreateStandardAccount,
SystemContractIsStandard,
SystemContractGetCallFlags,
Expand Down
26 changes: 26 additions & 0 deletions pkg/core/interop_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"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/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/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
Expand Down Expand Up @@ -221,6 +223,30 @@ func contractIsStandard(ic *interop.Context) error {
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() {
return errors.New("m should fit int64")
}
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.
func contractCreateStandardAccount(ic *interop.Context) error {
h := ic.VM.Estack().Pop().Bytes()
Expand Down
48 changes: 48 additions & 0 deletions pkg/core/interop_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"errors"
"math"
"math/big"
"testing"

Expand Down Expand Up @@ -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) {
v, ic, chain := createVM(t)
defer chain.Close()
Expand Down
1 change: 1 addition & 0 deletions pkg/core/interops.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var systemInterops = []interop.Function{
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15,
RequiredFlags: callflag.AllowCall, ParamCount: 4},
{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.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: callflag.ReadStates, ParamCount: 1},
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10},
Expand Down

0 comments on commit eeb650d

Please sign in to comment.