forked from TrueCloudLab/neoneo-go
core: add System.Contract.CreateMultisigAccount interop
This commit is contained in:
parent
fe918e28f2
commit
5a37201319
4 changed files with 78 additions and 0 deletions
|
@ -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"
|
||||
|
@ -70,6 +71,7 @@ var names = []string{
|
|||
SystemCallbackInvoke,
|
||||
SystemContractCall,
|
||||
SystemContractCallNative,
|
||||
SystemContractCreateMultisigAccount,
|
||||
SystemContractCreateStandardAccount,
|
||||
SystemContractIsStandard,
|
||||
SystemContractGetCallFlags,
|
||||
|
|
|
@ -4,13 +4,16 @@ import (
|
|||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"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/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"
|
||||
|
@ -221,6 +224,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() || 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.
|
||||
func contractCreateStandardAccount(ic *interop.Context) error {
|
||||
h := ic.VM.Estack().Pop().Bytes()
|
||||
|
|
|
@ -2,6 +2,7 @@ package core
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"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) {
|
||||
v, ic, chain := createVM(t)
|
||||
defer chain.Close()
|
||||
|
|
|
@ -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},
|
||||
|
|
Loading…
Reference in a new issue