wallet: support creating multisig accounts

(*Account).ConvertMultisig() will convert an existing account
into a multisig one.
This commit is contained in:
Evgenii Stratonikov 2020-02-20 15:47:36 +03:00
parent baa68e1d46
commit 25ffb56982
2 changed files with 76 additions and 0 deletions

View file

@ -1,6 +1,7 @@
package wallet
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
@ -8,6 +9,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -177,6 +179,34 @@ func NewAccountFromEncryptedWIF(wif string, pass string) (*Account, error) {
return a, nil
}
// ConvertMultisig sets a's contract to multisig contract with m sufficient signatures.
func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
var found bool
for i := range pubs {
if bytes.Equal(a.publicKey, pubs[i].Bytes()) {
found = true
break
}
}
if !found {
return errors.New("own public key was not found among multisig keys")
}
script, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
if err != nil {
return err
}
a.Address = address.Uint160ToString(hash.Hash160(script))
a.Contract = &Contract{
Script: script,
Parameters: getContractParams(m),
}
return nil
}
// newAccountFromPrivateKey creates a wallet from the given PrivateKey.
func newAccountFromPrivateKey(p *keys.PrivateKey) *Account {
pubKey := p.PublicKey()

View file

@ -6,6 +6,7 @@ import (
"testing"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/internal/keytestcases"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -87,6 +88,51 @@ func TestContract_ScriptHash(t *testing.T) {
require.Equal(t, hash.Hash160(script), c.ScriptHash())
}
func TestAccount_ConvertMultisig(t *testing.T) {
// test is based on a wallet1_solo.json accounts from neo-local
a, err := NewAccountFromEncryptedWIF("6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y", "one")
require.NoError(t, err)
hexs := []string{
"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2", // <- this is our key
"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
}
t.Run("invalid number of signatures", func(t *testing.T) {
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(0, pubs))
})
t.Run("account key is missing from multisig", func(t *testing.T) {
pubs := convertPubs(t, hexs[1:])
require.Error(t, a.ConvertMultisig(1, pubs))
})
t.Run("1/1 multisig", func(t *testing.T) {
pubs := convertPubs(t, hexs[:1])
require.NoError(t, a.ConvertMultisig(1, pubs))
require.Equal(t, "AbU69m8WUZJSWanfr1Cy66cpEcsmMcX7BR", a.Address)
})
t.Run("3/4 multisig", func(t *testing.T) {
pubs := convertPubs(t, hexs)
require.NoError(t, a.ConvertMultisig(3, pubs))
require.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", a.Address)
})
}
func convertPubs(t *testing.T, hexKeys []string) []*keys.PublicKey {
pubs := make([]*keys.PublicKey, len(hexKeys))
for i := range pubs {
var err error
pubs[i], err = keys.NewPublicKeyFromString(hexKeys[i])
require.NoError(t, err)
}
return pubs
}
func compareFields(t *testing.T, tk keytestcases.Ktype, acc *Account) {
if want, have := tk.Address, acc.Address; want != have {
t.Fatalf("expected %s got %s", want, have)