forked from TrueCloudLab/neoneo-go
Merge pull request #1763 from nspcc-dev/multisig-interop
core: add System.Contract.CreateMultisigAccount
This commit is contained in:
commit
61e04f04de
6 changed files with 86 additions and 0 deletions
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue