github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/wallet/wallet_test.go (about)

     1  package wallet
     2  
     3  import (
     4  	"encoding/json"
     5  	"path"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    10  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  const (
    17  	walletTemplate = "testWallet"
    18  )
    19  
    20  func TestNewWallet(t *testing.T) {
    21  	wallet := checkWalletConstructor(t)
    22  	require.NotNil(t, wallet)
    23  }
    24  
    25  func TestNewWalletFromFile_Negative_EmptyFile(t *testing.T) {
    26  	_ = checkWalletConstructor(t)
    27  	walletFromFile, err2 := NewWalletFromFile(walletTemplate)
    28  	require.Errorf(t, err2, "EOF")
    29  	require.Nil(t, walletFromFile)
    30  }
    31  
    32  func TestNewWalletFromFile_Negative_NoFile(t *testing.T) {
    33  	_, err := NewWalletFromFile(walletTemplate)
    34  	require.Errorf(t, err, "open testWallet: no such file or directory")
    35  }
    36  
    37  func TestCreateAccountAndClose(t *testing.T) {
    38  	wallet := checkWalletConstructor(t)
    39  
    40  	errAcc := wallet.CreateAccount("testName", "testPass")
    41  	require.NoError(t, errAcc)
    42  	accounts := wallet.Accounts
    43  	require.Len(t, accounts, 1)
    44  	require.True(t, wallet.Accounts[0].CanSign())
    45  	wallet.Close()
    46  	require.False(t, wallet.Accounts[0].CanSign())
    47  }
    48  
    49  func TestAddAccount(t *testing.T) {
    50  	wallets := []*Wallet{
    51  		checkWalletConstructor(t),
    52  		NewInMemoryWallet(),
    53  	}
    54  
    55  	for _, w := range wallets {
    56  		w.AddAccount(&Account{
    57  			privateKey:   nil,
    58  			Address:      "real",
    59  			EncryptedWIF: "",
    60  			Label:        "",
    61  			Contract:     nil,
    62  			Locked:       false,
    63  			Default:      false,
    64  		})
    65  		accounts := w.Accounts
    66  		require.Len(t, accounts, 1)
    67  
    68  		require.Error(t, w.RemoveAccount("abc"))
    69  		require.Len(t, w.Accounts, 1)
    70  		require.NoError(t, w.RemoveAccount("real"))
    71  		require.Len(t, w.Accounts, 0)
    72  	}
    73  }
    74  
    75  func TestPath(t *testing.T) {
    76  	wallet := checkWalletConstructor(t)
    77  
    78  	path := wallet.Path()
    79  	require.NotEmpty(t, path)
    80  }
    81  
    82  func TestSave(t *testing.T) {
    83  	inMemWallet := NewInMemoryWallet()
    84  
    85  	tmpDir := t.TempDir()
    86  	file := filepath.Join(tmpDir, walletTemplate)
    87  	inMemWallet.SetPath(file)
    88  
    89  	wallets := []*Wallet{
    90  		checkWalletConstructor(t),
    91  		inMemWallet,
    92  	}
    93  
    94  	for _, w := range wallets {
    95  		w.AddAccount(&Account{
    96  			privateKey:   nil,
    97  			Address:      "",
    98  			EncryptedWIF: "",
    99  			Label:        "",
   100  			Contract:     nil,
   101  			Locked:       false,
   102  			Default:      false,
   103  		})
   104  
   105  		errForSave := w.Save()
   106  		require.NoError(t, errForSave)
   107  
   108  		openedWallet, err := NewWalletFromFile(w.path)
   109  		require.NoError(t, err)
   110  		require.Equal(t, w.Accounts, openedWallet.Accounts)
   111  
   112  		t.Run("change and rewrite", func(t *testing.T) {
   113  			err := openedWallet.CreateAccount("test", "pass")
   114  			require.NoError(t, err)
   115  
   116  			w2, err := NewWalletFromFile(openedWallet.path)
   117  			require.NoError(t, err)
   118  			require.Equal(t, 2, len(w2.Accounts))
   119  			require.NoError(t, w2.Accounts[1].Decrypt("pass", w2.Scrypt))
   120  			_ = w2.Accounts[1].ScriptHash() // openedWallet has it for acc 1.
   121  			require.Equal(t, openedWallet.Accounts, w2.Accounts)
   122  		})
   123  	}
   124  }
   125  
   126  func TestJSONMarshallUnmarshal(t *testing.T) {
   127  	wallet := checkWalletConstructor(t)
   128  
   129  	bytes, err := wallet.JSON()
   130  	require.NoError(t, err)
   131  	require.NotNil(t, bytes)
   132  
   133  	unmarshalledWallet := &Wallet{}
   134  	errUnmarshal := json.Unmarshal(bytes, unmarshalledWallet)
   135  
   136  	require.NoError(t, errUnmarshal)
   137  	require.Equal(t, wallet.Version, unmarshalledWallet.Version)
   138  	require.Equal(t, wallet.Accounts, unmarshalledWallet.Accounts)
   139  	require.Equal(t, wallet.Scrypt, unmarshalledWallet.Scrypt)
   140  }
   141  
   142  func checkWalletConstructor(t *testing.T) *Wallet {
   143  	tmpDir := t.TempDir()
   144  	file := filepath.Join(tmpDir, walletTemplate)
   145  	wallet, err := NewWallet(file)
   146  	require.NoError(t, err)
   147  	return wallet
   148  }
   149  
   150  func TestWallet_AddToken(t *testing.T) {
   151  	w := checkWalletConstructor(t)
   152  	tok := NewToken(util.Uint160{1, 2, 3}, "Rubl", "RUB", 2, manifest.NEP17StandardName)
   153  	require.Equal(t, 0, len(w.Extra.Tokens))
   154  	w.AddToken(tok)
   155  	require.Equal(t, 1, len(w.Extra.Tokens))
   156  	require.Error(t, w.RemoveToken(util.Uint160{4, 5, 6}))
   157  	require.Equal(t, 1, len(w.Extra.Tokens))
   158  	require.NoError(t, w.RemoveToken(tok.Hash))
   159  	require.Equal(t, 0, len(w.Extra.Tokens))
   160  }
   161  
   162  func TestWallet_GetAccount(t *testing.T) {
   163  	wallet := checkWalletConstructor(t)
   164  	accounts := []*Account{
   165  		{
   166  			Contract: &Contract{
   167  				Script: []byte{0, 1, 2, 3},
   168  			},
   169  		},
   170  		{
   171  			Contract: &Contract{
   172  				Script: []byte{3, 2, 1, 0},
   173  			},
   174  		},
   175  	}
   176  
   177  	for _, acc := range accounts {
   178  		acc.Address = address.Uint160ToString(acc.Contract.ScriptHash())
   179  		wallet.AddAccount(acc)
   180  	}
   181  
   182  	for i, acc := range accounts {
   183  		h := acc.Contract.ScriptHash()
   184  		assert.Equal(t, acc, wallet.GetAccount(h), "can't get %d account", i)
   185  	}
   186  }
   187  
   188  func TestWalletGetChangeAddress(t *testing.T) {
   189  	w1, err := NewWalletFromFile("testdata/wallet1.json")
   190  	require.NoError(t, err)
   191  	sh := w1.GetChangeAddress()
   192  	// No default address, the first one is used.
   193  	require.Equal(t, "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", address.Uint160ToString(sh))
   194  	w2, err := NewWalletFromFile("testdata/wallet2.json")
   195  	require.NoError(t, err)
   196  	sh = w2.GetChangeAddress()
   197  	// Default address.
   198  	require.Equal(t, "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275", address.Uint160ToString(sh))
   199  }
   200  
   201  func TestWalletForExamples(t *testing.T) {
   202  	const (
   203  		examplesDir  = "../../examples"
   204  		walletFile   = "my_wallet.json"
   205  		walletPass   = "qwerty"
   206  		accountLabel = "my_account"
   207  	)
   208  	w, err := NewWalletFromFile(path.Join(examplesDir, walletFile))
   209  	require.NoError(t, err)
   210  	require.Equal(t, 1, len(w.Accounts))
   211  	require.Equal(t, accountLabel, w.Accounts[0].Label)
   212  	require.NoError(t, w.Accounts[0].Decrypt(walletPass, w.Scrypt))
   213  
   214  	// we need to keep the owner of the example contracts the same as the wallet account
   215  	require.Equal(t, "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB", w.Accounts[0].Address, "need to change `owner` in the example contracts")
   216  }
   217  
   218  func TestFromBytes(t *testing.T) {
   219  	wallet := checkWalletConstructor(t)
   220  	bts, err := wallet.JSON()
   221  	require.NoError(t, err)
   222  
   223  	w, err := NewWalletFromBytes(bts)
   224  	require.NoError(t, err)
   225  
   226  	require.Len(t, w.path, 0)
   227  	w.SetPath(wallet.path)
   228  
   229  	require.Equal(t, wallet, w)
   230  }