mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2025-01-10 15:54:05 +00:00
wallet: support creating multisig accounts
(*Account).ConvertMultisig() will convert an existing account into a multisig one.
This commit is contained in:
parent
baa68e1d46
commit
25ffb56982
2 changed files with 76 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
package wallet
|
package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -8,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"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/smartcontract"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -177,6 +179,34 @@ func NewAccountFromEncryptedWIF(wif string, pass string) (*Account, error) {
|
||||||
return a, nil
|
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.
|
// newAccountFromPrivateKey creates a wallet from the given PrivateKey.
|
||||||
func newAccountFromPrivateKey(p *keys.PrivateKey) *Account {
|
func newAccountFromPrivateKey(p *keys.PrivateKey) *Account {
|
||||||
pubKey := p.PublicKey()
|
pubKey := p.PublicKey()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"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/CityOfZion/neo-go/pkg/internal/keytestcases"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -87,6 +88,51 @@ func TestContract_ScriptHash(t *testing.T) {
|
||||||
require.Equal(t, hash.Hash160(script), c.ScriptHash())
|
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) {
|
func compareFields(t *testing.T, tk keytestcases.Ktype, acc *Account) {
|
||||||
if want, have := tk.Address, acc.Address; want != have {
|
if want, have := tk.Address, acc.Address; want != have {
|
||||||
t.Fatalf("expected %s got %s", want, have)
|
t.Fatalf("expected %s got %s", want, have)
|
||||||
|
|
Loading…
Reference in a new issue