neoneo-go/pkg/wallet/account_test.go
Roman Khimov 8d33206bb8 *: don't get private key from account if just public one is needed
Add PublicKey() API to the Account and use it as appropriate, avoid creating
additional references to the private key.
2022-09-02 14:43:28 +03:00

240 lines
7.2 KiB
Go

package wallet
import (
"encoding/hex"
"encoding/json"
"testing"
"github.com/nspcc-dev/neo-go/internal/keytestcases"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewAccount(t *testing.T) {
acc, err := NewAccount()
require.NoError(t, err)
require.NotNil(t, acc)
}
func TestDecryptAccount(t *testing.T) {
for _, testCase := range keytestcases.Arr {
acc := &Account{EncryptedWIF: testCase.EncryptedWif}
assert.Nil(t, acc.PrivateKey())
err := acc.Decrypt(testCase.Passphrase, keys.NEP2ScryptParams())
if testCase.Invalid {
assert.Error(t, err)
continue
}
assert.NoError(t, err)
assert.NotNil(t, acc.PrivateKey())
assert.Equal(t, testCase.PrivateKey, acc.privateKey.String())
}
// No encrypted key.
acc := &Account{}
require.Error(t, acc.Decrypt("qwerty", keys.NEP2ScryptParams()))
}
func TestNewFromWif(t *testing.T) {
for _, testCase := range keytestcases.Arr {
acc, err := NewAccountFromWIF(testCase.Wif)
if testCase.Invalid {
assert.Error(t, err)
continue
}
assert.NoError(t, err)
compareFields(t, testCase, acc)
}
}
func TestNewAccountFromEncryptedWIF(t *testing.T) {
for _, tc := range keytestcases.Arr {
acc, err := NewAccountFromEncryptedWIF(tc.EncryptedWif, tc.Passphrase, keys.NEP2ScryptParams())
if tc.Invalid {
assert.Error(t, err)
continue
}
assert.NoError(t, err)
compareFields(t, tc, acc)
}
}
func TestContract_MarshalJSON(t *testing.T) {
var c Contract
data := []byte(`{"script":"AQI=","parameters":[{"name":"name0", "type":"Signature"}],"deployed":false}`)
require.NoError(t, json.Unmarshal(data, &c))
require.Equal(t, []byte{1, 2}, c.Script)
result, err := json.Marshal(c)
require.NoError(t, err)
require.JSONEq(t, string(data), string(result))
data = []byte(`1`)
require.Error(t, json.Unmarshal(data, &c))
data = []byte(`{"script":"ERROR","parameters":[1],"deployed":false}`)
require.Error(t, json.Unmarshal(data, &c))
}
func TestContractSignTx(t *testing.T) {
acc, err := NewAccount()
require.NoError(t, err)
require.True(t, acc.CanSign())
accNoContr := *acc
accNoContr.Contract = nil
tx := &transaction.Transaction{
Script: []byte{1, 2, 3},
Signers: []transaction.Signer{{
Account: acc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}},
}
require.Error(t, accNoContr.SignTx(0, tx))
acc2, err := NewAccount()
require.NoError(t, err)
require.True(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx))
pubs := keys.PublicKeys{acc.PublicKey(), acc2.PublicKey()}
multiS, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs)
require.NoError(t, err)
multiAcc := NewAccountFromPrivateKey(acc.privateKey)
require.NoError(t, multiAcc.ConvertMultisig(2, pubs))
multiAcc2 := NewAccountFromPrivateKey(acc2.privateKey)
require.NoError(t, multiAcc2.ConvertMultisig(2, pubs))
tx = &transaction.Transaction{
Script: []byte{1, 2, 3},
Signers: []transaction.Signer{{
Account: acc2.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}, {
Account: acc.Contract.ScriptHash(),
Scopes: transaction.None,
}, {
Account: hash.Hash160(multiS),
Scopes: transaction.None,
}},
}
require.Error(t, acc.SignTx(0, tx)) // Can't append, no witness for acc2.
require.NoError(t, acc2.SignTx(0, tx)) // Append script for acc2.
require.Equal(t, 1, len(tx.Scripts))
require.Equal(t, 66, len(tx.Scripts[0].InvocationScript))
require.NoError(t, acc2.SignTx(0, tx)) // Sign again, effectively a no-op.
require.Equal(t, 1, len(tx.Scripts))
require.Equal(t, 66, len(tx.Scripts[0].InvocationScript))
acc2.Locked = true
require.False(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx)) // Locked account.
require.Nil(t, acc2.PublicKey()) // Locked account.
acc2.Locked = false
acc2.Close()
require.False(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx)) // No private key.
acc2.Close() // No-op.
require.False(t, acc2.CanSign())
tx.Scripts = append(tx.Scripts, transaction.Witness{
VerificationScript: acc.Contract.Script,
})
require.NoError(t, acc.SignTx(0, tx)) // Add invocation script for existing witness.
require.Equal(t, 66, len(tx.Scripts[1].InvocationScript))
require.NoError(t, multiAcc.SignTx(0, tx))
require.Equal(t, 3, len(tx.Scripts))
require.Equal(t, 66, len(tx.Scripts[2].InvocationScript))
require.NoError(t, multiAcc2.SignTx(0, tx)) // Append to existing script.
require.Equal(t, 3, len(tx.Scripts))
require.Equal(t, 132, len(tx.Scripts[2].InvocationScript))
}
func TestContract_ScriptHash(t *testing.T) {
script := []byte{0, 1, 2, 3}
c := &Contract{Script: script}
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 := NewAccountFromWIF("KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY")
require.NoError(t, err)
hexs := []string{
"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2", // <- this is our key
"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
}
t.Run("locked", func(t *testing.T) {
a.Locked = true
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(1, pubs))
a.Locked = false
})
t.Run("no private key", func(t *testing.T) {
pk := a.privateKey
a.privateKey = nil
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(0, pubs))
a.privateKey = pk
})
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, "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP", 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, "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", 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) {
want, have := tk.Address, acc.Address
require.Equalf(t, want, have, "expected address %s got %s", want, have)
want, have = tk.Wif, acc.privateKey.WIF()
require.Equalf(t, want, have, "expected wif %s got %s", want, have)
want, have = tk.PublicKey, hex.EncodeToString(acc.PublicKey().Bytes())
require.Equalf(t, want, have, "expected pub key %s got %s", want, have)
want, have = tk.PrivateKey, acc.privateKey.String()
require.Equalf(t, want, have, "expected priv key %s got %s", want, have)
}