1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"testing"
8
9	"github.com/keybase/client/go/libkb"
10	keybase1 "github.com/keybase/client/go/protocol/keybase1"
11)
12
13func getCurrentCryptocurrencyAddr(tc libkb.TestContext, username string, family libkb.CryptocurrencyFamily) string {
14	u, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc.G, username))
15	if err != nil {
16		tc.T.Fatal(err)
17	}
18	cryptoLink := u.IDTable().ActiveCryptocurrency(family)
19	if cryptoLink == nil {
20		return ""
21	}
22	return cryptoLink.ToDisplayString()
23}
24
25const (
26	firstAddress  = "17JyYCvn37BodyLbZdKQrW3WNbW7JcsvAJ"
27	secondAddress = "1kwg3FnLysQAi8Wqu37KqBwTUaUGiL7t1"
28	zcash1        = "zcCk6rKzynC4tT1Rmg325A5Xw81Ck3S6nD6mtPWCXaMtyFczkyU4kYjEhrcz2QKfF5T2siWGyJNxWo43XWT3qk5YpPhFGj2"
29	zcash2        = "t1c3Ebc6FBbWuirNrjJ6HbS4KHLb6Dbh5xL"
30)
31
32func TestCryptocurrency(t *testing.T) {
33	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
34		_testCryptocurrency(t, sigVersion)
35	})
36}
37
38func _testCryptocurrency(t *testing.T, sigVersion libkb.SigVersion) {
39	tc := SetupEngineTest(t, "Cryptocurrency")
40	defer tc.Cleanup()
41
42	u := CreateAndSignupFakeUser(tc, "btc")
43
44	uis := libkb.UIs{
45		LogUI:    tc.G.UI.GetLogUI(),
46		SecretUI: u.NewSecretUI(),
47	}
48
49	// First test setting a bad address; this should fail.
50	sv := keybase1.SigVersion(sigVersion)
51	e := NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: "somejunk", SigVersion: &sv})
52	m := NewMetaContextForTest(tc).WithUIs(uis)
53	err := RunEngine2(m, e)
54	if err == nil {
55		t.Fatalf("Bad address should have failed.")
56	}
57	current := getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
58	if current != "" {
59		t.Fatalf("No address should be set")
60	}
61
62	// Now set a real address, but with the wrong family. This should fail
63	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: firstAddress, WantedFamily: "zcash", SigVersion: &sv})
64	err = RunEngine2(m, e)
65	if err == nil {
66		t.Fatal("Wanted an error for wrong address type")
67	}
68	if current != "" {
69		t.Fatalf("No address should be set")
70	}
71
72	// Now set a real address; this should succeed.
73	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: firstAddress, WantedFamily: "bitcoin", SigVersion: &sv})
74	err = RunEngine2(m, e)
75	if err != nil {
76		t.Fatal(err)
77	}
78	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
79	if current != firstAddress {
80		t.Fatalf("Expected Cryptocurrency address '%s'. Found '%s'.", firstAddress, current)
81	}
82
83	// Test overwriting it without --force; should fail.
84	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: secondAddress, SigVersion: &sv})
85	err = RunEngine2(m, e)
86	if err == nil {
87		t.Fatal("Overwriting a Cryptocurrency address should fail without --force.")
88	} else if _, ok := err.(libkb.ExistsError); !ok {
89		t.Fatal("Error should by typed 'libkb.ExistsError'")
90	}
91	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
92	if current != firstAddress {
93		t.Fatalf("Address should not have changed.")
94	}
95
96	// Now test the overwrite with the --force flag; should succeed.
97	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: secondAddress, Force: true, SigVersion: &sv})
98	err = RunEngine2(m, e)
99	if err != nil {
100		t.Fatal(err)
101	}
102	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
103	if current != secondAddress {
104		t.Fatalf("Expected Cryptocurrency address '%s'. Found '%s'.", secondAddress, current)
105	}
106
107	// Make sure the previous link was revoked.
108	loadedUser, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc.G, u.Username))
109	if err != nil {
110		t.Fatalf("Failed to load user.")
111	}
112	revoked := loadedUser.IDTable().GetRevokedCryptocurrencyForTesting()
113	if len(revoked) != 1 {
114		t.Fatal("Expected 1 revoked link.")
115	} else if revoked[0].ToDisplayString() != firstAddress {
116		t.Fatal("Revoked link should correspond to the first address.")
117	}
118
119	// Check that we can also add a Zcash address
120	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: zcash1, SigVersion: &sv})
121	err = RunEngine2(m, e)
122	if err != nil {
123		t.Fatal("We should be able to add a Zcash in addition to a BTC address")
124	}
125	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
126	if current != secondAddress {
127		t.Fatalf("BTC Address should not have changed.")
128	}
129	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyZCash)
130	if current != zcash1 {
131		t.Fatalf("Zcash address didn't take")
132	}
133
134	// Check that we can't also add a second Zcash address
135	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: zcash2, SigVersion: &sv})
136	err = RunEngine2(m, e)
137	if err == nil {
138		t.Fatal("Overwriting a second Zcash address should fail without --force.")
139	} else if _, ok := err.(libkb.ExistsError); !ok {
140		t.Fatal("Error should by typed 'libkb.ExistsError'")
141	}
142	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
143	if current != secondAddress {
144		t.Fatalf("BTC Address should not have changed.")
145	}
146	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyZCash)
147	if current != zcash1 {
148		t.Fatalf("Zcash address didn't take")
149	}
150
151	// Check that we can't also add a second Zcash address
152	e = NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: zcash2, Force: true, SigVersion: &sv})
153	err = RunEngine2(m, e)
154	if err != nil {
155		t.Fatal("Forcing Zcash overwrite should have worked")
156	}
157	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyBitcoin)
158	if current != secondAddress {
159		t.Fatalf("BTC Address should not have changed.")
160	}
161	current = getCurrentCryptocurrencyAddr(tc, u.Username, libkb.CryptocurrencyFamilyZCash)
162	if current != zcash2 {
163		t.Fatalf("Zcash address force didn't take")
164	}
165
166	// Make sure the previous link was revoked.
167	loadedUser, err = libkb.LoadUser(libkb.NewLoadUserByNameArg(tc.G, u.Username))
168	if err != nil {
169		t.Fatalf("Failed to load user.")
170	}
171	revoked = loadedUser.IDTable().GetRevokedCryptocurrencyForTesting()
172	if len(revoked) != 2 {
173		t.Fatalf("Expected 2 revoked links; got %d", len(revoked))
174	} else if revoked[0].ToDisplayString() != firstAddress {
175		t.Fatal("Revoked link should correspond to the first address.")
176	} else if revoked[1].ToDisplayString() != zcash1 {
177		t.Fatal("Revoked link should correspond to the first Zcash address.")
178	}
179}
180
181func TestCryptocurrencyWithSecretStore(t *testing.T) {
182	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
183		_testCryptocurrencyWithSecretStore(t, sigVersion)
184	})
185}
186
187// Make sure the Cryptocurrency engine uses the secret store.
188func _testCryptocurrencyWithSecretStore(t *testing.T, sigVersion libkb.SigVersion) {
189	testEngineWithSecretStore(t, func(
190		tc libkb.TestContext, fu *FakeUser, secretUI libkb.SecretUI) {
191		sv := keybase1.SigVersion(sigVersion)
192		e := NewCryptocurrencyEngine(tc.G, keybase1.RegisterAddressArg{Address: firstAddress, Force: true, SigVersion: &sv})
193		uis := libkb.UIs{
194			LogUI:    tc.G.UI.GetLogUI(),
195			SecretUI: secretUI,
196		}
197		m := NewMetaContextForTest(tc).WithUIs(uis)
198		err := RunEngine2(m, e)
199		if err != nil {
200			t.Fatal(err)
201		}
202	})
203}
204