2018-03-02 15:24:09 +00:00
|
|
|
package wallet
|
|
|
|
|
|
|
|
import (
|
2020-01-15 13:36:57 +00:00
|
|
|
"encoding/json"
|
2018-03-02 15:24:09 +00:00
|
|
|
"testing"
|
2019-08-27 13:29:42 +00:00
|
|
|
|
2020-11-23 11:09:00 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/internal/keytestcases"
|
2022-08-24 14:09:57 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2022-09-01 16:04:47 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
2022-08-24 14:09:57 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2019-11-24 15:40:07 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2020-01-09 15:30:25 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-03-02 15:24:09 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestNewAccount(t *testing.T) {
|
2020-01-09 15:33:26 +00:00
|
|
|
acc, err := NewAccount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, acc)
|
2022-09-01 16:04:47 +00:00
|
|
|
require.Equal(t, acc.Address, address.Uint160ToString(acc.ScriptHash()))
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDecryptAccount(t *testing.T) {
|
2019-08-27 13:29:42 +00:00
|
|
|
for _, testCase := range keytestcases.Arr {
|
2020-01-09 15:30:25 +00:00
|
|
|
acc := &Account{EncryptedWIF: testCase.EncryptedWif}
|
|
|
|
assert.Nil(t, acc.PrivateKey())
|
2021-06-04 11:27:22 +00:00
|
|
|
err := acc.Decrypt(testCase.Passphrase, keys.NEP2ScryptParams())
|
2019-11-24 15:40:07 +00:00
|
|
|
if testCase.Invalid {
|
|
|
|
assert.Error(t, err)
|
|
|
|
continue
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
2019-11-24 15:40:07 +00:00
|
|
|
|
|
|
|
assert.NoError(t, err)
|
2020-01-09 15:30:25 +00:00
|
|
|
assert.NotNil(t, acc.PrivateKey())
|
|
|
|
assert.Equal(t, testCase.PrivateKey, acc.privateKey.String())
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
2020-01-09 15:30:25 +00:00
|
|
|
// No encrypted key.
|
|
|
|
acc := &Account{}
|
2021-06-04 11:27:22 +00:00
|
|
|
require.Error(t, acc.Decrypt("qwerty", keys.NEP2ScryptParams()))
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewFromWif(t *testing.T) {
|
2019-08-27 13:29:42 +00:00
|
|
|
for _, testCase := range keytestcases.Arr {
|
|
|
|
acc, err := NewAccountFromWIF(testCase.Wif)
|
2019-11-24 15:40:07 +00:00
|
|
|
if testCase.Invalid {
|
|
|
|
assert.Error(t, err)
|
|
|
|
continue
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
2019-11-24 15:40:07 +00:00
|
|
|
|
|
|
|
assert.NoError(t, err)
|
2018-03-02 15:24:09 +00:00
|
|
|
compareFields(t, testCase, acc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 09:22:40 +00:00
|
|
|
func TestNewAccountFromEncryptedWIF(t *testing.T) {
|
|
|
|
for _, tc := range keytestcases.Arr {
|
2021-06-04 11:27:22 +00:00
|
|
|
acc, err := NewAccountFromEncryptedWIF(tc.EncryptedWif, tc.Passphrase, keys.NEP2ScryptParams())
|
2020-02-20 09:22:40 +00:00
|
|
|
if tc.Invalid {
|
|
|
|
assert.Error(t, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
compareFields(t, tc, acc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 13:36:57 +00:00
|
|
|
func TestContract_MarshalJSON(t *testing.T) {
|
|
|
|
var c Contract
|
|
|
|
|
2020-07-02 08:37:50 +00:00
|
|
|
data := []byte(`{"script":"AQI=","parameters":[{"name":"name0", "type":"Signature"}],"deployed":false}`)
|
2020-01-15 13:36:57 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2022-08-24 14:09:57 +00:00
|
|
|
func TestContractSignTx(t *testing.T) {
|
|
|
|
acc, err := NewAccount()
|
|
|
|
require.NoError(t, err)
|
2022-08-24 15:50:31 +00:00
|
|
|
require.True(t, acc.CanSign())
|
2022-08-24 14:09:57 +00:00
|
|
|
|
|
|
|
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)
|
2022-08-24 15:50:31 +00:00
|
|
|
require.True(t, acc2.CanSign())
|
2022-08-24 14:09:57 +00:00
|
|
|
|
|
|
|
require.Error(t, acc2.SignTx(0, tx))
|
|
|
|
|
2022-09-01 14:52:44 +00:00
|
|
|
pubs := keys.PublicKeys{acc.PublicKey(), acc2.PublicKey()}
|
2022-08-24 14:09:57 +00:00
|
|
|
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))
|
|
|
|
|
2022-08-24 14:40:14 +00:00
|
|
|
acc2.Locked = true
|
2022-08-24 15:50:31 +00:00
|
|
|
require.False(t, acc2.CanSign())
|
2022-09-01 17:42:42 +00:00
|
|
|
require.Error(t, acc2.SignTx(0, tx)) // Locked account.
|
|
|
|
require.Nil(t, acc2.PublicKey()) // Locked account.
|
|
|
|
require.Nil(t, acc2.SignHashable(0, tx)) // Locked account.
|
2022-08-24 14:40:14 +00:00
|
|
|
|
|
|
|
acc2.Locked = false
|
2022-09-01 12:28:35 +00:00
|
|
|
acc2.Close()
|
2022-08-24 15:50:31 +00:00
|
|
|
require.False(t, acc2.CanSign())
|
2022-08-24 14:09:57 +00:00
|
|
|
require.Error(t, acc2.SignTx(0, tx)) // No private key.
|
2022-09-01 12:28:35 +00:00
|
|
|
acc2.Close() // No-op.
|
|
|
|
require.False(t, acc2.CanSign())
|
2022-08-24 14:09:57 +00:00
|
|
|
|
|
|
|
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))
|
2022-09-01 17:42:42 +00:00
|
|
|
require.NotNil(t, acc.SignHashable(0, tx)) // Works via Hashable too.
|
2022-08-24 14:09:57 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2020-01-15 15:08:25 +00:00
|
|
|
func TestContract_ScriptHash(t *testing.T) {
|
|
|
|
script := []byte{0, 1, 2, 3}
|
|
|
|
c := &Contract{Script: script}
|
|
|
|
|
|
|
|
require.Equal(t, hash.Hash160(script), c.ScriptHash())
|
|
|
|
}
|
|
|
|
|
2020-02-20 12:47:36 +00:00
|
|
|
func TestAccount_ConvertMultisig(t *testing.T) {
|
|
|
|
// test is based on a wallet1_solo.json accounts from neo-local
|
2020-04-22 08:41:09 +00:00
|
|
|
a, err := NewAccountFromWIF("KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY")
|
2020-02-20 12:47:36 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
hexs := []string{
|
|
|
|
"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2", // <- this is our key
|
|
|
|
"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
|
|
|
|
"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
|
|
|
|
"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
|
|
|
|
}
|
|
|
|
|
2022-09-01 13:39:48 +00:00
|
|
|
t.Run("locked", func(t *testing.T) {
|
|
|
|
a.Locked = true
|
|
|
|
pubs := convertPubs(t, hexs)
|
|
|
|
require.Error(t, a.ConvertMultisig(1, pubs))
|
|
|
|
a.Locked = false
|
|
|
|
})
|
2022-09-01 13:44:50 +00:00
|
|
|
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
|
|
|
|
})
|
2020-02-20 12:47:36 +00:00
|
|
|
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))
|
2021-05-11 14:13:33 +00:00
|
|
|
require.Equal(t, "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP", a.Address)
|
2020-02-20 12:47:36 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("3/4 multisig", func(t *testing.T) {
|
|
|
|
pubs := convertPubs(t, hexs)
|
|
|
|
require.NoError(t, a.ConvertMultisig(3, pubs))
|
2021-05-11 14:13:33 +00:00
|
|
|
require.Equal(t, "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", a.Address)
|
2020-02-20 12:47:36 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-08-27 13:29:42 +00:00
|
|
|
func compareFields(t *testing.T, tk keytestcases.Ktype, acc *Account) {
|
2020-02-29 15:55:16 +00:00
|
|
|
want, have := tk.Address, acc.Address
|
2021-03-05 07:18:03 +00:00
|
|
|
require.Equalf(t, want, have, "expected address %s got %s", want, have)
|
2022-09-01 12:27:38 +00:00
|
|
|
want, have = tk.Wif, acc.privateKey.WIF()
|
2021-03-05 07:18:03 +00:00
|
|
|
require.Equalf(t, want, have, "expected wif %s got %s", want, have)
|
2024-04-09 09:13:43 +00:00
|
|
|
want, have = tk.PublicKey, acc.PublicKey().StringCompressed()
|
2021-03-05 07:18:03 +00:00
|
|
|
require.Equalf(t, want, have, "expected pub key %s got %s", want, have)
|
2020-02-29 15:55:16 +00:00
|
|
|
want, have = tk.PrivateKey, acc.privateKey.String()
|
2021-03-05 07:18:03 +00:00
|
|
|
require.Equalf(t, want, have, "expected priv key %s got %s", want, have)
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|