mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'main' into schouhy/node-state-reconstruction
This commit is contained in:
commit
883fdc583a
@ -45,6 +45,7 @@ bip39 = "2.2.0"
|
|||||||
hmac-sha512 = "1.1.7"
|
hmac-sha512 = "1.1.7"
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
borsh = "1.5.7"
|
borsh = "1.5.7"
|
||||||
|
base58 = "0.2.0"
|
||||||
|
|
||||||
rocksdb = { version = "0.21.0", default-features = false, features = [
|
rocksdb = { version = "0.21.0", default-features = false, features = [
|
||||||
"snappy",
|
"snappy",
|
||||||
|
|||||||
@ -8,3 +8,5 @@ pub mod transaction;
|
|||||||
//TODO: Compile only for tests
|
//TODO: Compile only for tests
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
pub type HashType = [u8; 32];
|
pub type HashType = [u8; 32];
|
||||||
|
|
||||||
|
pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7";
|
||||||
|
|||||||
@ -8,49 +8,49 @@
|
|||||||
"port": 3040,
|
"port": 3040,
|
||||||
"initial_accounts": [
|
"initial_accounts": [
|
||||||
{
|
{
|
||||||
"addr": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44",
|
"addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||||
"balance": 10000
|
"balance": 10000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc",
|
"addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||||
"balance": 20000
|
"balance": 20000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"initial_commitments": [
|
"initial_commitments": [
|
||||||
{
|
{
|
||||||
"npk": [
|
"npk": [
|
||||||
193,
|
63,
|
||||||
209,
|
202,
|
||||||
150,
|
178,
|
||||||
113,
|
231,
|
||||||
47,
|
183,
|
||||||
241,
|
82,
|
||||||
48,
|
237,
|
||||||
145,
|
212,
|
||||||
250,
|
216,
|
||||||
79,
|
|
||||||
235,
|
|
||||||
51,
|
|
||||||
119,
|
|
||||||
40,
|
|
||||||
184,
|
|
||||||
232,
|
|
||||||
5,
|
|
||||||
221,
|
221,
|
||||||
36,
|
215,
|
||||||
21,
|
255,
|
||||||
201,
|
|
||||||
106,
|
|
||||||
90,
|
|
||||||
210,
|
|
||||||
129,
|
|
||||||
106,
|
|
||||||
71,
|
|
||||||
99,
|
|
||||||
208,
|
|
||||||
153,
|
153,
|
||||||
75,
|
101,
|
||||||
215
|
177,
|
||||||
|
161,
|
||||||
|
254,
|
||||||
|
210,
|
||||||
|
128,
|
||||||
|
122,
|
||||||
|
54,
|
||||||
|
190,
|
||||||
|
230,
|
||||||
|
151,
|
||||||
|
183,
|
||||||
|
64,
|
||||||
|
225,
|
||||||
|
229,
|
||||||
|
113,
|
||||||
|
1,
|
||||||
|
228,
|
||||||
|
97
|
||||||
],
|
],
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
@ -70,38 +70,38 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"npk": [
|
"npk": [
|
||||||
27,
|
192,
|
||||||
250,
|
251,
|
||||||
|
166,
|
||||||
|
243,
|
||||||
|
167,
|
||||||
|
236,
|
||||||
|
84,
|
||||||
|
249,
|
||||||
|
35,
|
||||||
136,
|
136,
|
||||||
142,
|
130,
|
||||||
88,
|
172,
|
||||||
128,
|
219,
|
||||||
138,
|
|
||||||
21,
|
|
||||||
49,
|
|
||||||
183,
|
|
||||||
118,
|
|
||||||
160,
|
|
||||||
117,
|
|
||||||
114,
|
|
||||||
110,
|
|
||||||
47,
|
|
||||||
136,
|
|
||||||
87,
|
|
||||||
60,
|
|
||||||
70,
|
|
||||||
59,
|
|
||||||
60,
|
|
||||||
18,
|
|
||||||
223,
|
|
||||||
23,
|
|
||||||
147,
|
|
||||||
241,
|
|
||||||
5,
|
|
||||||
184,
|
|
||||||
103,
|
|
||||||
225,
|
225,
|
||||||
105
|
161,
|
||||||
|
139,
|
||||||
|
229,
|
||||||
|
89,
|
||||||
|
243,
|
||||||
|
125,
|
||||||
|
194,
|
||||||
|
213,
|
||||||
|
209,
|
||||||
|
30,
|
||||||
|
23,
|
||||||
|
174,
|
||||||
|
100,
|
||||||
|
244,
|
||||||
|
124,
|
||||||
|
74,
|
||||||
|
140,
|
||||||
|
47
|
||||||
],
|
],
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
@ -154,4 +154,4 @@
|
|||||||
37,
|
37,
|
||||||
37
|
37
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -9,85 +9,85 @@
|
|||||||
"initial_accounts": [
|
"initial_accounts": [
|
||||||
{
|
{
|
||||||
"Public": {
|
"Public": {
|
||||||
"address": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44",
|
"address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||||
"pub_sign_key": [
|
"pub_sign_key": [
|
||||||
1,
|
16,
|
||||||
1,
|
162,
|
||||||
1,
|
106,
|
||||||
1,
|
154,
|
||||||
1,
|
236,
|
||||||
1,
|
125,
|
||||||
1,
|
52,
|
||||||
1,
|
184,
|
||||||
1,
|
35,
|
||||||
1,
|
100,
|
||||||
1,
|
238,
|
||||||
1,
|
174,
|
||||||
1,
|
69,
|
||||||
1,
|
197,
|
||||||
1,
|
41,
|
||||||
1,
|
77,
|
||||||
1,
|
187,
|
||||||
1,
|
10,
|
||||||
1,
|
118,
|
||||||
1,
|
75,
|
||||||
1,
|
0,
|
||||||
1,
|
11,
|
||||||
1,
|
148,
|
||||||
1,
|
238,
|
||||||
1,
|
185,
|
||||||
1,
|
181,
|
||||||
1,
|
133,
|
||||||
1,
|
17,
|
||||||
1,
|
220,
|
||||||
1,
|
72,
|
||||||
1,
|
124,
|
||||||
1
|
77
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Public": {
|
"Public": {
|
||||||
"address": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc",
|
"address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||||
"pub_sign_key": [
|
"pub_sign_key": [
|
||||||
2,
|
113,
|
||||||
2,
|
121,
|
||||||
2,
|
64,
|
||||||
2,
|
177,
|
||||||
2,
|
204,
|
||||||
2,
|
85,
|
||||||
2,
|
229,
|
||||||
2,
|
214,
|
||||||
2,
|
178,
|
||||||
2,
|
6,
|
||||||
2,
|
109,
|
||||||
2,
|
191,
|
||||||
2,
|
29,
|
||||||
2,
|
154,
|
||||||
2,
|
63,
|
||||||
2,
|
38,
|
||||||
2,
|
242,
|
||||||
2,
|
18,
|
||||||
2,
|
244,
|
||||||
2,
|
219,
|
||||||
2,
|
8,
|
||||||
2,
|
208,
|
||||||
2,
|
35,
|
||||||
2,
|
136,
|
||||||
2,
|
23,
|
||||||
2,
|
127,
|
||||||
2,
|
207,
|
||||||
2,
|
237,
|
||||||
2,
|
216,
|
||||||
2,
|
169,
|
||||||
2,
|
190,
|
||||||
2
|
27
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Private": {
|
"Private": {
|
||||||
"address": "d360d6b5763f71ac6af56253687fd7d556d5c6c64312e53c0b92ef039a4375df",
|
"address": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw",
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
0,
|
0,
|
||||||
@ -105,218 +105,218 @@
|
|||||||
},
|
},
|
||||||
"key_chain": {
|
"key_chain": {
|
||||||
"secret_spending_key": [
|
"secret_spending_key": [
|
||||||
10,
|
251,
|
||||||
125,
|
|
||||||
171,
|
|
||||||
38,
|
|
||||||
201,
|
|
||||||
35,
|
|
||||||
164,
|
|
||||||
43,
|
|
||||||
7,
|
|
||||||
80,
|
|
||||||
7,
|
|
||||||
215,
|
|
||||||
97,
|
|
||||||
42,
|
|
||||||
48,
|
|
||||||
229,
|
|
||||||
101,
|
|
||||||
216,
|
|
||||||
140,
|
|
||||||
21,
|
|
||||||
170,
|
|
||||||
214,
|
|
||||||
82,
|
82,
|
||||||
53,
|
235,
|
||||||
|
1,
|
||||||
|
146,
|
||||||
|
96,
|
||||||
|
30,
|
||||||
|
81,
|
||||||
|
162,
|
||||||
|
234,
|
||||||
|
33,
|
||||||
|
15,
|
||||||
|
123,
|
||||||
|
129,
|
||||||
116,
|
116,
|
||||||
22,
|
0,
|
||||||
62,
|
84,
|
||||||
79,
|
136,
|
||||||
61,
|
176,
|
||||||
76,
|
70,
|
||||||
71,
|
190,
|
||||||
79
|
224,
|
||||||
|
161,
|
||||||
|
54,
|
||||||
|
134,
|
||||||
|
142,
|
||||||
|
154,
|
||||||
|
1,
|
||||||
|
18,
|
||||||
|
251,
|
||||||
|
242,
|
||||||
|
189
|
||||||
],
|
],
|
||||||
"private_key_holder": {
|
"private_key_holder": {
|
||||||
"nullifier_secret_key": [
|
"nullifier_secret_key": [
|
||||||
228,
|
29,
|
||||||
136,
|
250,
|
||||||
4,
|
10,
|
||||||
|
187,
|
||||||
|
35,
|
||||||
|
123,
|
||||||
|
180,
|
||||||
|
250,
|
||||||
|
246,
|
||||||
|
97,
|
||||||
|
216,
|
||||||
|
153,
|
||||||
|
44,
|
||||||
156,
|
156,
|
||||||
33,
|
16,
|
||||||
40,
|
93,
|
||||||
194,
|
241,
|
||||||
172,
|
26,
|
||||||
95,
|
174,
|
||||||
168,
|
219,
|
||||||
201,
|
72,
|
||||||
33,
|
84,
|
||||||
24,
|
34,
|
||||||
30,
|
247,
|
||||||
126,
|
112,
|
||||||
197,
|
101,
|
||||||
156,
|
217,
|
||||||
113,
|
243,
|
||||||
64,
|
189,
|
||||||
162,
|
173,
|
||||||
131,
|
75,
|
||||||
210,
|
20
|
||||||
110,
|
|
||||||
60,
|
|
||||||
24,
|
|
||||||
154,
|
|
||||||
86,
|
|
||||||
59,
|
|
||||||
184,
|
|
||||||
95,
|
|
||||||
245,
|
|
||||||
176
|
|
||||||
],
|
],
|
||||||
"incoming_viewing_secret_key": [
|
"incoming_viewing_secret_key": [
|
||||||
197,
|
251,
|
||||||
33,
|
201,
|
||||||
51,
|
22,
|
||||||
200,
|
154,
|
||||||
1,
|
100,
|
||||||
121,
|
165,
|
||||||
60,
|
218,
|
||||||
52,
|
108,
|
||||||
233,
|
163,
|
||||||
234,
|
190,
|
||||||
12,
|
135,
|
||||||
166,
|
91,
|
||||||
196,
|
145,
|
||||||
227,
|
84,
|
||||||
187,
|
69,
|
||||||
1,
|
241,
|
||||||
10,
|
46,
|
||||||
101,
|
117,
|
||||||
183,
|
|
||||||
105,
|
|
||||||
140,
|
|
||||||
28,
|
|
||||||
152,
|
|
||||||
217,
|
217,
|
||||||
109,
|
|
||||||
220,
|
|
||||||
112,
|
|
||||||
103,
|
|
||||||
253,
|
|
||||||
110,
|
110,
|
||||||
98,
|
197,
|
||||||
6
|
248,
|
||||||
|
91,
|
||||||
|
193,
|
||||||
|
14,
|
||||||
|
104,
|
||||||
|
88,
|
||||||
|
103,
|
||||||
|
67,
|
||||||
|
153,
|
||||||
|
182,
|
||||||
|
158
|
||||||
],
|
],
|
||||||
"outgoing_viewing_secret_key": [
|
"outgoing_viewing_secret_key": [
|
||||||
147,
|
25,
|
||||||
34,
|
67,
|
||||||
193,
|
121,
|
||||||
29,
|
76,
|
||||||
39,
|
175,
|
||||||
173,
|
100,
|
||||||
222,
|
|
||||||
30,
|
30,
|
||||||
118,
|
198,
|
||||||
199,
|
105,
|
||||||
44,
|
123,
|
||||||
204,
|
49,
|
||||||
43,
|
169,
|
||||||
232,
|
75,
|
||||||
107,
|
178,
|
||||||
223,
|
75,
|
||||||
249,
|
210,
|
||||||
207,
|
100,
|
||||||
245,
|
143,
|
||||||
183,
|
210,
|
||||||
63,
|
243,
|
||||||
209,
|
228,
|
||||||
129,
|
243,
|
||||||
48,
|
21,
|
||||||
254,
|
18,
|
||||||
66,
|
36,
|
||||||
22,
|
84,
|
||||||
199,
|
164,
|
||||||
81,
|
186,
|
||||||
145,
|
139,
|
||||||
126,
|
113,
|
||||||
92
|
214,
|
||||||
|
12
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nullifer_public_key": [
|
"nullifer_public_key": [
|
||||||
193,
|
63,
|
||||||
209,
|
202,
|
||||||
150,
|
178,
|
||||||
113,
|
231,
|
||||||
47,
|
183,
|
||||||
241,
|
82,
|
||||||
48,
|
237,
|
||||||
145,
|
212,
|
||||||
250,
|
216,
|
||||||
79,
|
|
||||||
235,
|
|
||||||
51,
|
|
||||||
119,
|
|
||||||
40,
|
|
||||||
184,
|
|
||||||
232,
|
|
||||||
5,
|
|
||||||
221,
|
221,
|
||||||
36,
|
215,
|
||||||
21,
|
255,
|
||||||
201,
|
|
||||||
106,
|
|
||||||
90,
|
|
||||||
210,
|
|
||||||
129,
|
|
||||||
106,
|
|
||||||
71,
|
|
||||||
99,
|
|
||||||
208,
|
|
||||||
153,
|
153,
|
||||||
75,
|
101,
|
||||||
215
|
177,
|
||||||
|
161,
|
||||||
|
254,
|
||||||
|
210,
|
||||||
|
128,
|
||||||
|
122,
|
||||||
|
54,
|
||||||
|
190,
|
||||||
|
230,
|
||||||
|
151,
|
||||||
|
183,
|
||||||
|
64,
|
||||||
|
225,
|
||||||
|
229,
|
||||||
|
113,
|
||||||
|
1,
|
||||||
|
228,
|
||||||
|
97
|
||||||
],
|
],
|
||||||
"incoming_viewing_public_key": [
|
"incoming_viewing_public_key": [
|
||||||
3,
|
3,
|
||||||
78,
|
235,
|
||||||
|
139,
|
||||||
|
131,
|
||||||
|
237,
|
||||||
177,
|
177,
|
||||||
87,
|
122,
|
||||||
193,
|
|
||||||
219,
|
|
||||||
230,
|
|
||||||
160,
|
|
||||||
222,
|
|
||||||
38,
|
|
||||||
182,
|
|
||||||
100,
|
|
||||||
101,
|
|
||||||
223,
|
|
||||||
204,
|
|
||||||
223,
|
|
||||||
198,
|
|
||||||
140,
|
|
||||||
253,
|
|
||||||
94,
|
|
||||||
16,
|
|
||||||
98,
|
|
||||||
77,
|
|
||||||
79,
|
|
||||||
114,
|
|
||||||
30,
|
|
||||||
158,
|
|
||||||
104,
|
|
||||||
34,
|
|
||||||
152,
|
|
||||||
189,
|
189,
|
||||||
31,
|
6,
|
||||||
95
|
177,
|
||||||
|
167,
|
||||||
|
178,
|
||||||
|
202,
|
||||||
|
117,
|
||||||
|
246,
|
||||||
|
58,
|
||||||
|
28,
|
||||||
|
65,
|
||||||
|
132,
|
||||||
|
79,
|
||||||
|
220,
|
||||||
|
139,
|
||||||
|
119,
|
||||||
|
243,
|
||||||
|
187,
|
||||||
|
160,
|
||||||
|
212,
|
||||||
|
121,
|
||||||
|
61,
|
||||||
|
247,
|
||||||
|
116,
|
||||||
|
72,
|
||||||
|
205
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Private": {
|
"Private": {
|
||||||
"address": "f27087ffc29b99035303697dcf6c8e323b1847d4261e6afd49e0d71c6dfa31ea",
|
"address": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX",
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
0,
|
0,
|
||||||
@ -334,214 +334,214 @@
|
|||||||
},
|
},
|
||||||
"key_chain": {
|
"key_chain": {
|
||||||
"secret_spending_key": [
|
"secret_spending_key": [
|
||||||
153,
|
238,
|
||||||
109,
|
171,
|
||||||
202,
|
241,
|
||||||
226,
|
69,
|
||||||
97,
|
111,
|
||||||
212,
|
217,
|
||||||
77,
|
85,
|
||||||
147,
|
64,
|
||||||
75,
|
19,
|
||||||
107,
|
82,
|
||||||
153,
|
18,
|
||||||
106,
|
189,
|
||||||
89,
|
32,
|
||||||
167,
|
91,
|
||||||
49,
|
|
||||||
230,
|
|
||||||
122,
|
|
||||||
78,
|
78,
|
||||||
167,
|
175,
|
||||||
146,
|
|
||||||
14,
|
|
||||||
180,
|
|
||||||
206,
|
|
||||||
107,
|
107,
|
||||||
96,
|
7,
|
||||||
193,
|
109,
|
||||||
255,
|
60,
|
||||||
122,
|
52,
|
||||||
207,
|
44,
|
||||||
30,
|
243,
|
||||||
142,
|
230,
|
||||||
99
|
72,
|
||||||
|
244,
|
||||||
|
192,
|
||||||
|
92,
|
||||||
|
137,
|
||||||
|
33,
|
||||||
|
118,
|
||||||
|
254
|
||||||
],
|
],
|
||||||
"private_key_holder": {
|
"private_key_holder": {
|
||||||
"nullifier_secret_key": [
|
"nullifier_secret_key": [
|
||||||
128,
|
25,
|
||||||
|
211,
|
||||||
215,
|
215,
|
||||||
147,
|
|
||||||
175,
|
|
||||||
119,
|
119,
|
||||||
16,
|
|
||||||
140,
|
|
||||||
219,
|
|
||||||
155,
|
|
||||||
134,
|
|
||||||
27,
|
|
||||||
81,
|
|
||||||
64,
|
|
||||||
40,
|
|
||||||
196,
|
|
||||||
240,
|
|
||||||
61,
|
|
||||||
144,
|
|
||||||
232,
|
|
||||||
164,
|
|
||||||
181,
|
|
||||||
57,
|
57,
|
||||||
139,
|
223,
|
||||||
96,
|
247,
|
||||||
137,
|
37,
|
||||||
121,
|
245,
|
||||||
140,
|
144,
|
||||||
|
122,
|
||||||
29,
|
29,
|
||||||
169,
|
118,
|
||||||
68,
|
245,
|
||||||
187,
|
83,
|
||||||
65
|
228,
|
||||||
|
23,
|
||||||
|
9,
|
||||||
|
101,
|
||||||
|
120,
|
||||||
|
88,
|
||||||
|
33,
|
||||||
|
238,
|
||||||
|
207,
|
||||||
|
128,
|
||||||
|
61,
|
||||||
|
110,
|
||||||
|
2,
|
||||||
|
89,
|
||||||
|
62,
|
||||||
|
164,
|
||||||
|
13
|
||||||
],
|
],
|
||||||
"incoming_viewing_secret_key": [
|
"incoming_viewing_secret_key": [
|
||||||
185,
|
193,
|
||||||
121,
|
181,
|
||||||
146,
|
14,
|
||||||
213,
|
196,
|
||||||
13,
|
142,
|
||||||
3,
|
84,
|
||||||
93,
|
15,
|
||||||
206,
|
65,
|
||||||
25,
|
128,
|
||||||
127,
|
101,
|
||||||
155,
|
70,
|
||||||
21,
|
196,
|
||||||
155,
|
241,
|
||||||
115,
|
47,
|
||||||
130,
|
130,
|
||||||
27,
|
221,
|
||||||
57,
|
23,
|
||||||
5,
|
146,
|
||||||
116,
|
161,
|
||||||
80,
|
237,
|
||||||
62,
|
221,
|
||||||
214,
|
40,
|
||||||
67,
|
19,
|
||||||
228,
|
126,
|
||||||
147,
|
59,
|
||||||
189,
|
15,
|
||||||
28,
|
169,
|
||||||
200,
|
236,
|
||||||
62,
|
25,
|
||||||
152,
|
105,
|
||||||
178,
|
104,
|
||||||
103
|
231
|
||||||
],
|
],
|
||||||
"outgoing_viewing_secret_key": [
|
"outgoing_viewing_secret_key": [
|
||||||
163,
|
20,
|
||||||
58,
|
170,
|
||||||
118,
|
220,
|
||||||
160,
|
108,
|
||||||
|
41,
|
||||||
|
23,
|
||||||
|
155,
|
||||||
|
217,
|
||||||
|
247,
|
||||||
|
190,
|
||||||
175,
|
175,
|
||||||
86,
|
168,
|
||||||
72,
|
247,
|
||||||
|
34,
|
||||||
|
105,
|
||||||
|
134,
|
||||||
|
114,
|
||||||
|
74,
|
||||||
|
104,
|
||||||
91,
|
91,
|
||||||
81,
|
|
||||||
69,
|
|
||||||
150,
|
|
||||||
154,
|
|
||||||
113,
|
|
||||||
211,
|
211,
|
||||||
118,
|
62,
|
||||||
110,
|
|
||||||
25,
|
|
||||||
156,
|
|
||||||
250,
|
|
||||||
67,
|
|
||||||
212,
|
|
||||||
198,
|
|
||||||
147,
|
|
||||||
231,
|
|
||||||
213,
|
|
||||||
136,
|
|
||||||
212,
|
|
||||||
198,
|
|
||||||
192,
|
|
||||||
255,
|
|
||||||
126,
|
126,
|
||||||
122
|
13,
|
||||||
|
130,
|
||||||
|
100,
|
||||||
|
241,
|
||||||
|
214,
|
||||||
|
250,
|
||||||
|
236,
|
||||||
|
38,
|
||||||
|
150
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nullifer_public_key": [
|
"nullifer_public_key": [
|
||||||
27,
|
192,
|
||||||
250,
|
251,
|
||||||
|
166,
|
||||||
|
243,
|
||||||
|
167,
|
||||||
|
236,
|
||||||
|
84,
|
||||||
|
249,
|
||||||
|
35,
|
||||||
136,
|
136,
|
||||||
142,
|
130,
|
||||||
88,
|
172,
|
||||||
128,
|
219,
|
||||||
138,
|
|
||||||
21,
|
|
||||||
49,
|
|
||||||
183,
|
|
||||||
118,
|
|
||||||
160,
|
|
||||||
117,
|
|
||||||
114,
|
|
||||||
110,
|
|
||||||
47,
|
|
||||||
136,
|
|
||||||
87,
|
|
||||||
60,
|
|
||||||
70,
|
|
||||||
59,
|
|
||||||
60,
|
|
||||||
18,
|
|
||||||
223,
|
|
||||||
23,
|
|
||||||
147,
|
|
||||||
241,
|
|
||||||
5,
|
|
||||||
184,
|
|
||||||
103,
|
|
||||||
225,
|
225,
|
||||||
105
|
161,
|
||||||
|
139,
|
||||||
|
229,
|
||||||
|
89,
|
||||||
|
243,
|
||||||
|
125,
|
||||||
|
194,
|
||||||
|
213,
|
||||||
|
209,
|
||||||
|
30,
|
||||||
|
23,
|
||||||
|
174,
|
||||||
|
100,
|
||||||
|
244,
|
||||||
|
124,
|
||||||
|
74,
|
||||||
|
140,
|
||||||
|
47
|
||||||
],
|
],
|
||||||
"incoming_viewing_public_key": [
|
"incoming_viewing_public_key": [
|
||||||
2,
|
2,
|
||||||
56,
|
181,
|
||||||
160,
|
|
||||||
1,
|
|
||||||
22,
|
|
||||||
197,
|
|
||||||
187,
|
|
||||||
214,
|
|
||||||
204,
|
|
||||||
221,
|
|
||||||
84,
|
|
||||||
87,
|
|
||||||
12,
|
|
||||||
204,
|
|
||||||
0,
|
|
||||||
119,
|
|
||||||
116,
|
|
||||||
176,
|
|
||||||
6,
|
|
||||||
149,
|
|
||||||
145,
|
|
||||||
100,
|
|
||||||
211,
|
|
||||||
162,
|
|
||||||
19,
|
|
||||||
158,
|
|
||||||
197,
|
|
||||||
112,
|
|
||||||
142,
|
|
||||||
172,
|
|
||||||
1,
|
|
||||||
98,
|
98,
|
||||||
226
|
93,
|
||||||
|
216,
|
||||||
|
241,
|
||||||
|
241,
|
||||||
|
110,
|
||||||
|
58,
|
||||||
|
198,
|
||||||
|
119,
|
||||||
|
174,
|
||||||
|
250,
|
||||||
|
184,
|
||||||
|
1,
|
||||||
|
204,
|
||||||
|
200,
|
||||||
|
173,
|
||||||
|
44,
|
||||||
|
238,
|
||||||
|
37,
|
||||||
|
247,
|
||||||
|
170,
|
||||||
|
156,
|
||||||
|
100,
|
||||||
|
254,
|
||||||
|
116,
|
||||||
|
242,
|
||||||
|
28,
|
||||||
|
183,
|
||||||
|
187,
|
||||||
|
77,
|
||||||
|
255
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Binary file not shown.
@ -32,18 +32,24 @@ struct Args {
|
|||||||
test_name: String,
|
test_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ACC_SENDER: &str = "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44";
|
pub const ACC_SENDER: &str = "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||||
pub const ACC_RECEIVER: &str = "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc";
|
pub const ACC_RECEIVER: &str = "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw";
|
||||||
|
|
||||||
pub const ACC_SENDER_PRIVATE: &str =
|
pub const ACC_SENDER_PRIVATE: &str = "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw";
|
||||||
"d360d6b5763f71ac6af56253687fd7d556d5c6c64312e53c0b92ef039a4375df";
|
pub const ACC_RECEIVER_PRIVATE: &str = "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX";
|
||||||
pub const ACC_RECEIVER_PRIVATE: &str =
|
|
||||||
"f27087ffc29b99035303697dcf6c8e323b1847d4261e6afd49e0d71c6dfa31ea";
|
|
||||||
|
|
||||||
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
||||||
|
|
||||||
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin");
|
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin");
|
||||||
|
|
||||||
|
fn make_public_account_input_from_str(addr: &str) -> String {
|
||||||
|
format!("Public/{addr}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_private_account_input_from_str(addr: &str) -> String {
|
||||||
|
format!("Private/{addr}")
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub async fn pre_test(
|
pub async fn pre_test(
|
||||||
home_dir: PathBuf,
|
home_dir: PathBuf,
|
||||||
@ -86,7 +92,7 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
|
|||||||
seq_http_server_handle.stop(true).await;
|
seq_http_server_handle.stop(true).await;
|
||||||
|
|
||||||
let wallet_home = wallet::helperfunctions::get_home().unwrap();
|
let wallet_home = wallet::helperfunctions::get_home().unwrap();
|
||||||
let persistent_data_home = wallet_home.join("curr_accounts.json");
|
let persistent_data_home = wallet_home.join("storage.json");
|
||||||
|
|
||||||
//Removing persistent accounts after run to not affect other executions
|
//Removing persistent accounts after run to not affect other executions
|
||||||
//Not necessary an error, if fails as there is tests for failure scenario
|
//Not necessary an error, if fails as there is tests for failure scenario
|
||||||
@ -157,3 +163,20 @@ async fn verify_commitment_is_in_state(
|
|||||||
Ok(Some(_))
|
Ok(Some(_))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{make_private_account_input_from_str, make_public_account_input_from_str};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correct_addr_from_prefix() {
|
||||||
|
let addr1 = "cafecafe";
|
||||||
|
let addr2 = "deadbeaf";
|
||||||
|
|
||||||
|
let addr1_pub = make_public_account_input_from_str(addr1);
|
||||||
|
let addr2_priv = make_private_account_input_from_str(addr2);
|
||||||
|
|
||||||
|
assert_eq!(addr1_pub, "Public/cafecafe".to_string());
|
||||||
|
assert_eq!(addr2_priv, "Private/deadbeaf".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,8 @@ serde.workspace = true
|
|||||||
k256.workspace = true
|
k256.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
hex.workspace = true
|
base58.workspace = true
|
||||||
|
hex = "0.4.3"
|
||||||
aes-gcm.workspace = true
|
aes-gcm.workspace = true
|
||||||
bip39.workspace = true
|
bip39.workspace = true
|
||||||
hmac-sha512.workspace = true
|
hmac-sha512.workspace = true
|
||||||
|
|||||||
@ -55,6 +55,7 @@ impl KeyChain {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use aes_gcm::aead::OsRng;
|
use aes_gcm::aead::OsRng;
|
||||||
|
use base58::ToBase58;
|
||||||
use k256::AffinePoint;
|
use k256::AffinePoint;
|
||||||
use k256::elliptic_curve::group::GroupEncoding;
|
use k256::elliptic_curve::group::GroupEncoding;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
@ -119,7 +120,7 @@ mod tests {
|
|||||||
|
|
||||||
println!("======Public data======");
|
println!("======Public data======");
|
||||||
println!();
|
println!();
|
||||||
println!("Address{:?}", hex::encode(address.value()));
|
println!("Address{:?}", address.value().to_base58());
|
||||||
println!(
|
println!(
|
||||||
"Nulifier public key {:?}",
|
"Nulifier public key {:?}",
|
||||||
hex::encode(nullifer_public_key.to_byte_array())
|
hex::encode(nullifer_public_key.to_byte_array())
|
||||||
|
|||||||
@ -142,5 +142,10 @@ mod tests {
|
|||||||
let is_key_chain_generated = user_data.get_private_account(&addr_private).is_some();
|
let is_key_chain_generated = user_data.get_private_account(&addr_private).is_some();
|
||||||
|
|
||||||
assert!(is_key_chain_generated);
|
assert!(is_key_chain_generated);
|
||||||
|
|
||||||
|
let addr_private_str = addr_private.to_string();
|
||||||
|
println!("{addr_private_str:#?}");
|
||||||
|
let key_chain = &user_data.get_private_account(&addr_private).unwrap().0;
|
||||||
|
println!("{key_chain:#?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,9 @@ thiserror = { version = "2.0.12", optional = true }
|
|||||||
bytemuck = { version = "1.13", optional = true }
|
bytemuck = { version = "1.13", optional = true }
|
||||||
chacha20 = { version = "0.9", default-features = false }
|
chacha20 = { version = "0.9", default-features = false }
|
||||||
k256 = { version = "0.13.3", optional = true }
|
k256 = { version = "0.13.3", optional = true }
|
||||||
hex = { version = "0.4.3", optional = true }
|
base58 = { version = "0.2.0", optional = true }
|
||||||
|
anyhow = { version = "1.0.98", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
host = ["thiserror", "bytemuck", "k256", "hex"]
|
host = ["thiserror", "bytemuck", "k256", "base58", "anyhow"]
|
||||||
|
|||||||
@ -3,6 +3,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg(feature = "host")]
|
#[cfg(feature = "host")]
|
||||||
use std::{fmt::Display, str::FromStr};
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
#[cfg(feature = "host")]
|
||||||
|
use base58::{FromBase58, ToBase58};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
any(feature = "host", test),
|
any(feature = "host", test),
|
||||||
@ -31,8 +34,8 @@ impl AsRef<[u8]> for Address {
|
|||||||
#[cfg(feature = "host")]
|
#[cfg(feature = "host")]
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum AddressError {
|
pub enum AddressError {
|
||||||
#[error("invalid hex")]
|
#[error("invalid base58")]
|
||||||
InvalidHex(#[from] hex::FromHexError),
|
InvalidBase58(#[from] anyhow::Error),
|
||||||
#[error("invalid length: expected 32 bytes, got {0}")]
|
#[error("invalid length: expected 32 bytes, got {0}")]
|
||||||
InvalidLength(usize),
|
InvalidLength(usize),
|
||||||
}
|
}
|
||||||
@ -41,7 +44,9 @@ pub enum AddressError {
|
|||||||
impl FromStr for Address {
|
impl FromStr for Address {
|
||||||
type Err = AddressError;
|
type Err = AddressError;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let bytes = hex::decode(s)?;
|
let bytes = s
|
||||||
|
.from_base58()
|
||||||
|
.map_err(|err| anyhow::anyhow!("Invalid base58 err {err:?}"))?;
|
||||||
if bytes.len() != 32 {
|
if bytes.len() != 32 {
|
||||||
return Err(AddressError::InvalidLength(bytes.len()));
|
return Err(AddressError::InvalidLength(bytes.len()));
|
||||||
}
|
}
|
||||||
@ -54,7 +59,7 @@ impl FromStr for Address {
|
|||||||
#[cfg(feature = "host")]
|
#[cfg(feature = "host")]
|
||||||
impl Display for Address {
|
impl Display for Address {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", hex::encode(self.value))
|
write!(f, "{}", self.value.to_base58())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,29 +70,29 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_valid_address() {
|
fn parse_valid_address() {
|
||||||
let hex_str = "00".repeat(32); // 64 hex chars = 32 bytes
|
let base58_str = "11111111111111111111111111111111";
|
||||||
let addr: Address = hex_str.parse().unwrap();
|
let addr: Address = base58_str.parse().unwrap();
|
||||||
assert_eq!(addr.value, [0u8; 32]);
|
assert_eq!(addr.value, [0u8; 32]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_invalid_hex() {
|
fn parse_invalid_base58() {
|
||||||
let hex_str = "zz".repeat(32); // invalid hex chars
|
let base58_str = "00".repeat(32); // invalid base58 chars
|
||||||
let result = hex_str.parse::<Address>().unwrap_err();
|
let result = base58_str.parse::<Address>().unwrap_err();
|
||||||
assert!(matches!(result, AddressError::InvalidHex(_)));
|
assert!(matches!(result, AddressError::InvalidBase58(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_wrong_length_short() {
|
fn parse_wrong_length_short() {
|
||||||
let hex_str = "00".repeat(31); // 62 chars = 31 bytes
|
let base58_str = "11".repeat(31); // 62 chars = 31 bytes
|
||||||
let result = hex_str.parse::<Address>().unwrap_err();
|
let result = base58_str.parse::<Address>().unwrap_err();
|
||||||
assert!(matches!(result, AddressError::InvalidLength(_)));
|
assert!(matches!(result, AddressError::InvalidLength(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_wrong_length_long() {
|
fn parse_wrong_length_long() {
|
||||||
let hex_str = "00".repeat(33); // 66 chars = 33 bytes
|
let base58_str = "11".repeat(33); // 66 chars = 33 bytes
|
||||||
let result = hex_str.parse::<Address>().unwrap_err();
|
let result = base58_str.parse::<Address>().unwrap_err();
|
||||||
assert!(matches!(result, AddressError::InvalidLength(_)));
|
assert!(matches!(result, AddressError::InvalidLength(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,20 @@ pub struct ProgramInput<T> {
|
|||||||
pub instruction: T,
|
pub instruction: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
||||||
|
pub struct ChainedCall {
|
||||||
|
pub program_id: ProgramId,
|
||||||
|
pub instruction_data: InstructionData,
|
||||||
|
pub account_indices: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
||||||
pub struct ProgramOutput {
|
pub struct ProgramOutput {
|
||||||
pub pre_states: Vec<AccountWithMetadata>,
|
pub pre_states: Vec<AccountWithMetadata>,
|
||||||
pub post_states: Vec<Account>,
|
pub post_states: Vec<Account>,
|
||||||
|
pub chained_call: Option<ChainedCall>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_nssa_inputs<T: DeserializeOwned>() -> ProgramInput<T> {
|
pub fn read_nssa_inputs<T: DeserializeOwned>() -> ProgramInput<T> {
|
||||||
@ -33,6 +42,20 @@ pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec
|
|||||||
let output = ProgramOutput {
|
let output = ProgramOutput {
|
||||||
pre_states,
|
pre_states,
|
||||||
post_states,
|
post_states,
|
||||||
|
chained_call: None,
|
||||||
|
};
|
||||||
|
env::commit(&output);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_nssa_outputs_with_chained_call(
|
||||||
|
pre_states: Vec<AccountWithMetadata>,
|
||||||
|
post_states: Vec<Account>,
|
||||||
|
chained_call: Option<ChainedCall>,
|
||||||
|
) {
|
||||||
|
let output = ProgramOutput {
|
||||||
|
pre_states,
|
||||||
|
post_states,
|
||||||
|
chained_call,
|
||||||
};
|
};
|
||||||
env::commit(&output);
|
env::commit(&output);
|
||||||
}
|
}
|
||||||
@ -79,9 +102,14 @@ pub fn validate_execution(
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. If a post state has default program owner, the pre state must have been a default account
|
||||||
|
if post.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Total balance is preserved
|
// 7. Total balance is preserved
|
||||||
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
||||||
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
||||||
if total_balance_pre_states != total_balance_post_states {
|
if total_balance_pre_states != total_balance_post_states {
|
||||||
|
|||||||
@ -27,8 +27,14 @@ fn main() {
|
|||||||
let ProgramOutput {
|
let ProgramOutput {
|
||||||
pre_states,
|
pre_states,
|
||||||
post_states,
|
post_states,
|
||||||
|
chained_call,
|
||||||
} = program_output;
|
} = program_output;
|
||||||
|
|
||||||
|
// TODO: implement chained calls for privacy preserving transactions
|
||||||
|
if chained_call.is_some() {
|
||||||
|
panic!("Privacy preserving transactions do not support yet chained calls.")
|
||||||
|
}
|
||||||
|
|
||||||
// Check that there are no repeated account ids
|
// Check that there are no repeated account ids
|
||||||
if !validate_uniqueness_of_account_ids(&pre_states) {
|
if !validate_uniqueness_of_account_ids(&pre_states) {
|
||||||
panic!("Repeated account ids found")
|
panic!("Repeated account ids found")
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF};
|
use crate::program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF};
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata},
|
account::AccountWithMetadata,
|
||||||
program::{InstructionData, ProgramId, ProgramOutput},
|
program::{InstructionData, ProgramId, ProgramOutput},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ impl Program {
|
|||||||
&self,
|
&self,
|
||||||
pre_states: &[AccountWithMetadata],
|
pre_states: &[AccountWithMetadata],
|
||||||
instruction_data: &InstructionData,
|
instruction_data: &InstructionData,
|
||||||
) -> Result<Vec<Account>, NssaError> {
|
) -> Result<ProgramOutput, NssaError> {
|
||||||
// Write inputs to the program
|
// Write inputs to the program
|
||||||
let mut env_builder = ExecutorEnv::builder();
|
let mut env_builder = ExecutorEnv::builder();
|
||||||
env_builder.session_limit(Some(MAX_NUM_CYCLES_PUBLIC_EXECUTION));
|
env_builder.session_limit(Some(MAX_NUM_CYCLES_PUBLIC_EXECUTION));
|
||||||
@ -62,12 +62,12 @@ impl Program {
|
|||||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||||
|
|
||||||
// Get outputs
|
// Get outputs
|
||||||
let ProgramOutput { post_states, .. } = session_info
|
let program_output = session_info
|
||||||
.journal
|
.journal
|
||||||
.decode()
|
.decode()
|
||||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||||
|
|
||||||
Ok(post_states)
|
Ok(program_output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes inputs to `env_builder` in the order expected by the programs
|
/// Writes inputs to `env_builder` in the order expected by the programs
|
||||||
@ -107,11 +107,11 @@ impl Program {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
use crate::program_methods::{
|
||||||
use program_methods::{
|
|
||||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF,
|
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF,
|
||||||
TOKEN_ID,
|
TOKEN_ID,
|
||||||
};
|
};
|
||||||
|
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
||||||
|
|
||||||
use crate::program::Program;
|
use crate::program::Program;
|
||||||
|
|
||||||
@ -195,6 +195,15 @@ mod tests {
|
|||||||
elf: BURNER_ELF.to_vec(),
|
elf: BURNER_ELF.to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chain_caller() -> Self {
|
||||||
|
use test_program_methods::{CHAIN_CALLER_ELF, CHAIN_CALLER_ID};
|
||||||
|
|
||||||
|
Program {
|
||||||
|
id: CHAIN_CALLER_ID,
|
||||||
|
elf: CHAIN_CALLER_ELF.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -221,12 +230,12 @@ mod tests {
|
|||||||
balance: balance_to_move,
|
balance: balance_to_move,
|
||||||
..Account::default()
|
..Account::default()
|
||||||
};
|
};
|
||||||
let [sender_post, recipient_post] = program
|
let program_output = program
|
||||||
.execute(&[sender, recipient], &instruction_data)
|
.execute(&[sender, recipient], &instruction_data)
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let [sender_post, recipient_post] = program_output.post_states.try_into().unwrap();
|
||||||
|
|
||||||
assert_eq!(sender_post, expected_sender_post);
|
assert_eq!(sender_post, expected_sender_post);
|
||||||
assert_eq!(recipient_post, expected_recipient_post);
|
assert_eq!(recipient_post, expected_recipient_post);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata},
|
account::{Account, AccountWithMetadata},
|
||||||
address::Address,
|
address::Address,
|
||||||
program::validate_execution,
|
program::{DEFAULT_PROGRAM_ID, validate_execution},
|
||||||
};
|
};
|
||||||
use sha2::{Digest, digest::FixedOutput};
|
use sha2::{Digest, digest::FixedOutput};
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ pub struct PublicTransaction {
|
|||||||
message: Message,
|
message: Message,
|
||||||
witness_set: WitnessSet,
|
witness_set: WitnessSet,
|
||||||
}
|
}
|
||||||
|
const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
||||||
|
|
||||||
impl PublicTransaction {
|
impl PublicTransaction {
|
||||||
pub fn new(message: Message, witness_set: WitnessSet) -> Self {
|
pub fn new(message: Message, witness_set: WitnessSet) -> Self {
|
||||||
@ -88,7 +89,7 @@ impl PublicTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build pre_states for execution
|
// Build pre_states for execution
|
||||||
let pre_states: Vec<_> = message
|
let mut input_pre_states: Vec<_> = message
|
||||||
.addresses
|
.addresses
|
||||||
.iter()
|
.iter()
|
||||||
.map(|address| {
|
.map(|address| {
|
||||||
@ -100,21 +101,86 @@ impl PublicTransaction {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Check the `program_id` corresponds to a deployed program
|
let mut state_diff: HashMap<Address, Account> = HashMap::new();
|
||||||
let Some(program) = state.programs().get(&message.program_id) else {
|
|
||||||
return Err(NssaError::InvalidInput("Unknown program".into()));
|
|
||||||
};
|
|
||||||
|
|
||||||
// // Execute program
|
let mut program_id = message.program_id;
|
||||||
let post_states = program.execute(&pre_states, &message.instruction_data)?;
|
let mut instruction_data = message.instruction_data.clone();
|
||||||
|
|
||||||
// Verify execution corresponds to a well-behaved program.
|
for _i in 0..MAX_NUMBER_CHAINED_CALLS {
|
||||||
// See the # Programs section for the definition of the `validate_execution` method.
|
// Check the `program_id` corresponds to a deployed program
|
||||||
if !validate_execution(&pre_states, &post_states, message.program_id) {
|
let Some(program) = state.programs().get(&program_id) else {
|
||||||
return Err(NssaError::InvalidProgramBehavior);
|
return Err(NssaError::InvalidInput("Unknown program".into()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut program_output = program.execute(&input_pre_states, &instruction_data)?;
|
||||||
|
|
||||||
|
// This check is equivalent to checking that the program output pre_states coinicide
|
||||||
|
// with the values in the public state or with any modifications to those values
|
||||||
|
// during the chain of calls.
|
||||||
|
if input_pre_states != program_output.pre_states {
|
||||||
|
return Err(NssaError::InvalidProgramBehavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify execution corresponds to a well-behaved program.
|
||||||
|
// See the # Programs section for the definition of the `validate_execution` method.
|
||||||
|
if !validate_execution(
|
||||||
|
&program_output.pre_states,
|
||||||
|
&program_output.post_states,
|
||||||
|
program_id,
|
||||||
|
) {
|
||||||
|
return Err(NssaError::InvalidProgramBehavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The invoked program claims the accounts with default program id.
|
||||||
|
for post in program_output.post_states.iter_mut() {
|
||||||
|
if post.program_owner == DEFAULT_PROGRAM_ID {
|
||||||
|
post.program_owner = program_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the state diff
|
||||||
|
for (pre, post) in program_output
|
||||||
|
.pre_states
|
||||||
|
.iter()
|
||||||
|
.zip(program_output.post_states.iter())
|
||||||
|
{
|
||||||
|
state_diff.insert(pre.account_id, post.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(next_chained_call) = program_output.chained_call {
|
||||||
|
program_id = next_chained_call.program_id;
|
||||||
|
instruction_data = next_chained_call.instruction_data;
|
||||||
|
|
||||||
|
// Build post states with metadata for next call
|
||||||
|
let mut post_states_with_metadata = Vec::new();
|
||||||
|
for (pre, post) in program_output
|
||||||
|
.pre_states
|
||||||
|
.iter()
|
||||||
|
.zip(program_output.post_states)
|
||||||
|
{
|
||||||
|
let mut post_with_metadata = pre.clone();
|
||||||
|
post_with_metadata.account = post.clone();
|
||||||
|
post_states_with_metadata.push(post_with_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_pre_states = next_chained_call
|
||||||
|
.account_indices
|
||||||
|
.iter()
|
||||||
|
.map(|&i| {
|
||||||
|
post_states_with_metadata
|
||||||
|
.get(i)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
NssaError::InvalidInput("Invalid account indices".into())
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, NssaError>>()?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(message.addresses.iter().cloned().zip(post_states).collect())
|
Ok(state_diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||||
account::Account,
|
account::Account, address::Address, program::ProgramId,
|
||||||
address::Address,
|
|
||||||
program::{DEFAULT_PROGRAM_ID, ProgramId},
|
|
||||||
};
|
};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
@ -114,10 +112,6 @@ impl V02State {
|
|||||||
let current_account = self.get_account_by_address_mut(address);
|
let current_account = self.get_account_by_address_mut(address);
|
||||||
|
|
||||||
*current_account = post;
|
*current_account = post;
|
||||||
// The invoked program claims the accounts with default program id.
|
|
||||||
if current_account.program_owner == DEFAULT_PROGRAM_ID {
|
|
||||||
current_account.program_owner = tx.message().program_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for address in tx.signer_addresses() {
|
for address in tx.signer_addresses() {
|
||||||
@ -263,6 +257,7 @@ pub mod tests {
|
|||||||
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||||
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
|
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
|
||||||
|
program::ProgramId,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn transfer_transaction(
|
fn transfer_transaction(
|
||||||
@ -436,7 +431,7 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transition_from_chained_authenticated_transfer_program_invocations() {
|
fn transition_from_sequence_of_authenticated_transfer_program_invocations() {
|
||||||
let key1 = PrivateKey::try_new([8; 32]).unwrap();
|
let key1 = PrivateKey::try_new([8; 32]).unwrap();
|
||||||
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
@ -475,6 +470,7 @@ pub mod tests {
|
|||||||
self.insert_program(Program::data_changer());
|
self.insert_program(Program::data_changer());
|
||||||
self.insert_program(Program::minter());
|
self.insert_program(Program::minter());
|
||||||
self.insert_program(Program::burner());
|
self.insert_program(Program::burner());
|
||||||
|
self.insert_program(Program::chain_caller());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2045,4 +2041,80 @@ pub mod tests {
|
|||||||
|
|
||||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_claiming_mechanism() {
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||||
|
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||||
|
let initial_balance = 100;
|
||||||
|
let initial_data = [(address, initial_balance)];
|
||||||
|
let mut state =
|
||||||
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||||
|
let from = address;
|
||||||
|
let from_key = key;
|
||||||
|
let to = Address::new([2; 32]);
|
||||||
|
let amount: u128 = 37;
|
||||||
|
|
||||||
|
// Check the recipient is an uninitialized account
|
||||||
|
assert_eq!(state.get_account_by_address(&to), Account::default());
|
||||||
|
|
||||||
|
let expected_recipient_post = Account {
|
||||||
|
program_owner: program.id(),
|
||||||
|
balance: amount,
|
||||||
|
..Account::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let message =
|
||||||
|
public_transaction::Message::try_new(program.id(), vec![from, to], vec![0], amount)
|
||||||
|
.unwrap();
|
||||||
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||||
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
state.transition_from_public_transaction(&tx).unwrap();
|
||||||
|
|
||||||
|
let recipient_post = state.get_account_by_address(&to);
|
||||||
|
|
||||||
|
assert_eq!(recipient_post, expected_recipient_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chained_call() {
|
||||||
|
let program = Program::chain_caller();
|
||||||
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||||
|
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||||
|
let initial_balance = 100;
|
||||||
|
let initial_data = [(address, initial_balance)];
|
||||||
|
let mut state =
|
||||||
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||||
|
let from = address;
|
||||||
|
let from_key = key;
|
||||||
|
let to = Address::new([2; 32]);
|
||||||
|
let amount: u128 = 37;
|
||||||
|
let instruction: (u128, ProgramId) =
|
||||||
|
(amount, Program::authenticated_transfer_program().id());
|
||||||
|
|
||||||
|
let expected_to_post = Account {
|
||||||
|
program_owner: Program::chain_caller().id(),
|
||||||
|
balance: amount,
|
||||||
|
..Account::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let message = public_transaction::Message::try_new(
|
||||||
|
program.id(),
|
||||||
|
vec![to, from], //The chain_caller program permutes the account order in the chain call
|
||||||
|
vec![0],
|
||||||
|
instruction,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||||
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
state.transition_from_public_transaction(&tx).unwrap();
|
||||||
|
|
||||||
|
let from_post = state.get_account_by_address(&from);
|
||||||
|
let to_post = state.get_account_by_address(&to);
|
||||||
|
assert_eq!(from_post.balance, initial_balance - amount);
|
||||||
|
assert_eq!(to_post, expected_to_post);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
nssa/test_program_methods/guest/Cargo.lock
generated
2
nssa/test_program_methods/guest/Cargo.lock
generated
@ -1824,6 +1824,8 @@ name = "programs"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nssa-core",
|
"nssa-core",
|
||||||
|
"risc0-zkvm",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -6,4 +6,6 @@ edition = "2024"
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
risc0-zkvm = { version = "3.0.3", features = ['std'] }
|
||||||
nssa-core = { path = "../../core" }
|
nssa-core = { path = "../../core" }
|
||||||
|
serde = { version = "1.0.219", default-features = false }
|
||||||
|
|||||||
34
nssa/test_program_methods/guest/src/bin/chain_caller.rs
Normal file
34
nssa/test_program_methods/guest/src/bin/chain_caller.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use nssa_core::program::{
|
||||||
|
ChainedCall, ProgramId, ProgramInput, read_nssa_inputs, write_nssa_outputs_with_chained_call,
|
||||||
|
};
|
||||||
|
use risc0_zkvm::serde::to_vec;
|
||||||
|
|
||||||
|
type Instruction = (u128, ProgramId);
|
||||||
|
|
||||||
|
/// A program that calls another program.
|
||||||
|
/// It permutes the order of the input accounts on the subsequent call
|
||||||
|
fn main() {
|
||||||
|
let ProgramInput {
|
||||||
|
pre_states,
|
||||||
|
instruction: (balance, program_id),
|
||||||
|
} = read_nssa_inputs::<Instruction>();
|
||||||
|
|
||||||
|
let [sender_pre, receiver_pre] = match pre_states.try_into() {
|
||||||
|
Ok(array) => array,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let instruction_data = to_vec(&balance).unwrap();
|
||||||
|
|
||||||
|
let chained_call = Some(ChainedCall {
|
||||||
|
program_id,
|
||||||
|
instruction_data,
|
||||||
|
account_indices: vec![1, 0], // <- Account order permutation here
|
||||||
|
});
|
||||||
|
|
||||||
|
write_nssa_outputs_with_chained_call(
|
||||||
|
vec![sender_pre.clone(), receiver_pre.clone()],
|
||||||
|
vec![sender_pre.account, receiver_pre.account],
|
||||||
|
chained_call,
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex.workspace = true
|
base58.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
#[cfg(feature = "testnet")]
|
||||||
|
use common::PINATA_BASE58;
|
||||||
use common::{
|
use common::{
|
||||||
HashType,
|
HashType,
|
||||||
block::HashableBlockData,
|
block::HashableBlockData,
|
||||||
@ -82,7 +84,7 @@ impl SequencerCore {
|
|||||||
let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
||||||
|
|
||||||
#[cfg(feature = "testnet")]
|
#[cfg(feature = "testnet")]
|
||||||
state.add_pinata_program("cafe".repeat(16).parse().unwrap());
|
state.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
state,
|
state,
|
||||||
@ -239,6 +241,7 @@ impl SequencerCore {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use base58::{FromBase58, ToBase58};
|
||||||
use common::test_utils::sequencer_sign_key_for_testing;
|
use common::test_utils::sequencer_sign_key_for_testing;
|
||||||
use nssa::PrivateKey;
|
use nssa::PrivateKey;
|
||||||
|
|
||||||
@ -273,23 +276,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup_sequencer_config() -> SequencerConfig {
|
fn setup_sequencer_config() -> SequencerConfig {
|
||||||
let acc1_addr = vec![
|
let acc1_addr: Vec<u8> = vec![
|
||||||
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
||||||
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
||||||
];
|
];
|
||||||
|
|
||||||
let acc2_addr = vec![
|
let acc2_addr: Vec<u8> = vec![
|
||||||
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
||||||
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
||||||
];
|
];
|
||||||
|
|
||||||
let initial_acc1 = AccountInitialData {
|
let initial_acc1 = AccountInitialData {
|
||||||
addr: hex::encode(acc1_addr),
|
addr: acc1_addr.to_base58(),
|
||||||
balance: 10000,
|
balance: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_acc2 = AccountInitialData {
|
let initial_acc2 = AccountInitialData {
|
||||||
addr: hex::encode(acc2_addr),
|
addr: acc2_addr.to_base58(),
|
||||||
balance: 20000,
|
balance: 20000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -324,11 +327,17 @@ mod tests {
|
|||||||
assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10);
|
assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10);
|
||||||
assert_eq!(sequencer.sequencer_config.port, 8080);
|
assert_eq!(sequencer.sequencer_config.port, 8080);
|
||||||
|
|
||||||
let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
|
let acc1_addr = config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
|
let acc2_addr = config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -348,23 +357,23 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_start_different_intial_accounts_balances() {
|
fn test_start_different_intial_accounts_balances() {
|
||||||
let acc1_addr = vec![
|
let acc1_addr: Vec<u8> = vec![
|
||||||
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
|
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
|
||||||
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
|
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
|
||||||
];
|
];
|
||||||
|
|
||||||
let acc2_addr = vec![
|
let acc2_addr: Vec<u8> = vec![
|
||||||
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
|
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
|
||||||
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
|
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
|
||||||
];
|
];
|
||||||
|
|
||||||
let initial_acc1 = AccountInitialData {
|
let initial_acc1 = AccountInitialData {
|
||||||
addr: hex::encode(acc1_addr),
|
addr: acc1_addr.to_base58(),
|
||||||
balance: 10000,
|
balance: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_acc2 = AccountInitialData {
|
let initial_acc2 = AccountInitialData {
|
||||||
addr: hex::encode(acc2_addr),
|
addr: acc2_addr.to_base58(),
|
||||||
balance: 20000,
|
balance: 20000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -373,11 +382,17 @@ mod tests {
|
|||||||
let config = setup_sequencer_config_variable_initial_accounts(initial_accounts);
|
let config = setup_sequencer_config_variable_initial_accounts(initial_accounts);
|
||||||
let sequencer = SequencerCore::start_from_config(config.clone());
|
let sequencer = SequencerCore::start_from_config(config.clone());
|
||||||
|
|
||||||
let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
|
let acc1_addr = config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
|
let acc2_addr = config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -418,11 +433,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -444,11 +465,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -480,11 +507,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -516,11 +549,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -606,11 +645,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -647,11 +692,17 @@ mod tests {
|
|||||||
|
|
||||||
common_setup(&mut sequencer);
|
common_setup(&mut sequencer);
|
||||||
|
|
||||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||||
|
.addr
|
||||||
|
.clone()
|
||||||
|
.from_base58()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -688,14 +739,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_restart_from_storage() {
|
fn test_restart_from_storage() {
|
||||||
let config = setup_sequencer_config();
|
let config = setup_sequencer_config();
|
||||||
let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
|
let acc1_addr: nssa::Address = config.initial_accounts[0].addr.parse().unwrap();
|
||||||
.unwrap()
|
let acc2_addr: nssa::Address = config.initial_accounts[1].addr.parse().unwrap();
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
|
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
let balance_to_move = 13;
|
let balance_to_move = 13;
|
||||||
|
|
||||||
// In the following code block a transaction will be processed that moves `balance_to_move`
|
// In the following code block a transaction will be processed that moves `balance_to_move`
|
||||||
@ -706,9 +751,9 @@ mod tests {
|
|||||||
let signing_key = PrivateKey::try_new([1; 32]).unwrap();
|
let signing_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||||
|
|
||||||
let tx = common::test_utils::create_transaction_native_token_transfer(
|
let tx = common::test_utils::create_transaction_native_token_transfer(
|
||||||
acc1_addr,
|
*acc1_addr.value(),
|
||||||
0,
|
0,
|
||||||
acc2_addr,
|
*acc2_addr.value(),
|
||||||
balance_to_move,
|
balance_to_move,
|
||||||
signing_key,
|
signing_key,
|
||||||
);
|
);
|
||||||
@ -727,14 +772,8 @@ mod tests {
|
|||||||
// Instantiating a new sequencer from the same config. This should load the existing block
|
// Instantiating a new sequencer from the same config. This should load the existing block
|
||||||
// with the above transaction and update the state to reflect that.
|
// with the above transaction and update the state to reflect that.
|
||||||
let sequencer = SequencerCore::start_from_config(config.clone());
|
let sequencer = SequencerCore::start_from_config(config.clone());
|
||||||
let balance_acc_1 = sequencer
|
let balance_acc_1 = sequencer.state.get_account_by_address(&acc1_addr).balance;
|
||||||
.state
|
let balance_acc_2 = sequencer.state.get_account_by_address(&acc2_addr).balance;
|
||||||
.get_account_by_address(&nssa::Address::new(acc1_addr))
|
|
||||||
.balance;
|
|
||||||
let balance_acc_2 = sequencer
|
|
||||||
.state
|
|
||||||
.get_account_by_address(&nssa::Address::new(acc2_addr))
|
|
||||||
.balance;
|
|
||||||
|
|
||||||
// Balances should be consistent with the stored block
|
// Balances should be consistent with the stored block
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
74
sequencer_core/src/sequencer_store/mod.rs
Normal file
74
sequencer_core/src/sequencer_store/mod.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use block_store::SequecerBlockStore;
|
||||||
|
use common::block::HashableBlockData;
|
||||||
|
use nssa::{self, Address};
|
||||||
|
use rand::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
|
use crate::config::AccountInitialData;
|
||||||
|
|
||||||
|
pub mod block_store;
|
||||||
|
|
||||||
|
pub struct SequecerChainStore {
|
||||||
|
pub state: nssa::V02State,
|
||||||
|
pub block_store: SequecerBlockStore,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequecerChainStore {
|
||||||
|
pub fn new_with_genesis(
|
||||||
|
home_dir: &Path,
|
||||||
|
genesis_id: u64,
|
||||||
|
is_genesis_random: bool,
|
||||||
|
initial_accounts: &[AccountInitialData],
|
||||||
|
initial_commitments: &[nssa_core::Commitment],
|
||||||
|
signing_key: nssa::PrivateKey,
|
||||||
|
) -> Self {
|
||||||
|
let init_accs: Vec<(Address, u128)> = initial_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "testnet"))]
|
||||||
|
let state = nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments);
|
||||||
|
|
||||||
|
#[cfg(feature = "testnet")]
|
||||||
|
let state = {
|
||||||
|
use common::PINATA_BASE58;
|
||||||
|
|
||||||
|
let mut this =
|
||||||
|
nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments);
|
||||||
|
this.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
||||||
|
this
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data = [0; 32];
|
||||||
|
let mut prev_block_hash = [0; 32];
|
||||||
|
|
||||||
|
if is_genesis_random {
|
||||||
|
OsRng.fill_bytes(&mut data);
|
||||||
|
OsRng.fill_bytes(&mut prev_block_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
let curr_time = chrono::Utc::now().timestamp_millis() as u64;
|
||||||
|
|
||||||
|
let hashable_data = HashableBlockData {
|
||||||
|
block_id: genesis_id,
|
||||||
|
transactions: vec![],
|
||||||
|
prev_block_hash,
|
||||||
|
timestamp: curr_time,
|
||||||
|
};
|
||||||
|
|
||||||
|
let genesis_block = hashable_data.into_block(&signing_key);
|
||||||
|
|
||||||
|
//Sequencer should panic if unable to open db,
|
||||||
|
//as fixing this issue may require actions non-native to program scope
|
||||||
|
let block_store = SequecerBlockStore::open_db_with_genesis(
|
||||||
|
&home_dir.join("rocksdb"),
|
||||||
|
Some(genesis_block),
|
||||||
|
signing_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Self { state, block_store }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,8 @@ log.workspace = true
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
actix-cors.workspace = true
|
actix-cors.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
hex.workspace = true
|
base58.workspace = true
|
||||||
|
hex = "0.4.3"
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use actix_web::Error as HttpError;
|
use actix_web::Error as HttpError;
|
||||||
|
use base58::FromBase58;
|
||||||
use base64::{Engine, engine::general_purpose};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use nssa::{self, program::Program};
|
use nssa::{self, program::Program};
|
||||||
use sequencer_core::config::AccountInitialData;
|
use sequencer_core::config::AccountInitialData;
|
||||||
@ -160,8 +161,10 @@ impl JsonHandler {
|
|||||||
/// The address must be a valid hex string of the correct length.
|
/// The address must be a valid hex string of the correct length.
|
||||||
async fn process_get_account_balance(&self, request: Request) -> Result<Value, RpcErr> {
|
async fn process_get_account_balance(&self, request: Request) -> Result<Value, RpcErr> {
|
||||||
let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?;
|
let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?;
|
||||||
let address_bytes = hex::decode(get_account_req.address)
|
let address_bytes = get_account_req
|
||||||
.map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?;
|
.address
|
||||||
|
.from_base58()
|
||||||
|
.map_err(|_| RpcError::invalid_params("invalid base58".to_string()))?;
|
||||||
let address = nssa::Address::new(
|
let address = nssa::Address::new(
|
||||||
address_bytes
|
address_bytes
|
||||||
.try_into()
|
.try_into()
|
||||||
@ -307,6 +310,7 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{JsonHandler, rpc_handler};
|
use crate::{JsonHandler, rpc_handler};
|
||||||
|
use base58::ToBase58;
|
||||||
use base64::{Engine, engine::general_purpose};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use common::{
|
use common::{
|
||||||
rpc_primitives::RpcPollingConfig, test_utils::sequencer_sign_key_for_testing,
|
rpc_primitives::RpcPollingConfig, test_utils::sequencer_sign_key_for_testing,
|
||||||
@ -324,23 +328,23 @@ mod tests {
|
|||||||
fn sequencer_config_for_tests() -> SequencerConfig {
|
fn sequencer_config_for_tests() -> SequencerConfig {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let home = tempdir.path().to_path_buf();
|
let home = tempdir.path().to_path_buf();
|
||||||
let acc1_addr = vec![
|
let acc1_addr: Vec<u8> = vec![
|
||||||
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
||||||
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
||||||
];
|
];
|
||||||
|
|
||||||
let acc2_addr = vec![
|
let acc2_addr: Vec<u8> = vec![
|
||||||
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
||||||
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
||||||
];
|
];
|
||||||
|
|
||||||
let initial_acc1 = AccountInitialData {
|
let initial_acc1 = AccountInitialData {
|
||||||
addr: hex::encode(acc1_addr),
|
addr: acc1_addr.to_base58(),
|
||||||
balance: 10000,
|
balance: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_acc2 = AccountInitialData {
|
let initial_acc2 = AccountInitialData {
|
||||||
addr: hex::encode(acc2_addr),
|
addr: acc2_addr.to_base58(),
|
||||||
balance: 20000,
|
balance: 20000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -425,7 +429,7 @@ mod tests {
|
|||||||
let request = serde_json::json!({
|
let request = serde_json::json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "get_account_balance",
|
"method": "get_account_balance",
|
||||||
"params": { "address": "efac".repeat(16) },
|
"params": { "address": "11".repeat(16) },
|
||||||
"id": 1
|
"id": 1
|
||||||
});
|
});
|
||||||
let expected_response = serde_json::json!({
|
let expected_response = serde_json::json!({
|
||||||
@ -442,12 +446,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn test_get_account_balance_for_invalid_hex() {
|
async fn test_get_account_balance_for_invalid_base58() {
|
||||||
let (json_handler, _, _) = components_for_tests();
|
let (json_handler, _, _) = components_for_tests();
|
||||||
let request = serde_json::json!({
|
let request = serde_json::json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "get_account_balance",
|
"method": "get_account_balance",
|
||||||
"params": { "address": "not_a_valid_hex" },
|
"params": { "address": "not_a_valid_base58" },
|
||||||
"id": 1
|
"id": 1
|
||||||
});
|
});
|
||||||
let expected_response = serde_json::json!({
|
let expected_response = serde_json::json!({
|
||||||
@ -456,7 +460,7 @@ mod tests {
|
|||||||
"error": {
|
"error": {
|
||||||
"code": -32602,
|
"code": -32602,
|
||||||
"message": "Invalid params",
|
"message": "Invalid params",
|
||||||
"data": "invalid hex"
|
"data": "invalid base58"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let response = call_rpc_handler_with_json(json_handler, request).await;
|
let response = call_rpc_handler_with_json(json_handler, request).await;
|
||||||
@ -518,7 +522,7 @@ mod tests {
|
|||||||
let request = serde_json::json!({
|
let request = serde_json::json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "get_accounts_nonces",
|
"method": "get_accounts_nonces",
|
||||||
"params": { "addresses": ["efac".repeat(16)] },
|
"params": { "addresses": ["11".repeat(16)] },
|
||||||
"id": 1
|
"id": 1
|
||||||
});
|
});
|
||||||
let expected_response = serde_json::json!({
|
let expected_response = serde_json::json!({
|
||||||
@ -566,7 +570,7 @@ mod tests {
|
|||||||
let request = serde_json::json!({
|
let request = serde_json::json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "get_account",
|
"method": "get_account",
|
||||||
"params": { "address": "efac".repeat(16) },
|
"params": { "address": "11".repeat(16) },
|
||||||
"id": 1
|
"id": 1
|
||||||
});
|
});
|
||||||
let expected_response = serde_json::json!({
|
let expected_response = serde_json::json!({
|
||||||
|
|||||||
@ -8,49 +8,49 @@
|
|||||||
"port": 3040,
|
"port": 3040,
|
||||||
"initial_accounts": [
|
"initial_accounts": [
|
||||||
{
|
{
|
||||||
"addr": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44",
|
"addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||||
"balance": 10000
|
"balance": 10000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc",
|
"addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||||
"balance": 20000
|
"balance": 20000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"initial_commitments": [
|
"initial_commitments": [
|
||||||
{
|
{
|
||||||
"npk": [
|
"npk": [
|
||||||
193,
|
63,
|
||||||
209,
|
202,
|
||||||
150,
|
178,
|
||||||
113,
|
231,
|
||||||
47,
|
183,
|
||||||
241,
|
82,
|
||||||
48,
|
237,
|
||||||
145,
|
212,
|
||||||
250,
|
216,
|
||||||
79,
|
|
||||||
235,
|
|
||||||
51,
|
|
||||||
119,
|
|
||||||
40,
|
|
||||||
184,
|
|
||||||
232,
|
|
||||||
5,
|
|
||||||
221,
|
221,
|
||||||
36,
|
215,
|
||||||
21,
|
255,
|
||||||
201,
|
|
||||||
106,
|
|
||||||
90,
|
|
||||||
210,
|
|
||||||
129,
|
|
||||||
106,
|
|
||||||
71,
|
|
||||||
99,
|
|
||||||
208,
|
|
||||||
153,
|
153,
|
||||||
75,
|
101,
|
||||||
215
|
177,
|
||||||
|
161,
|
||||||
|
254,
|
||||||
|
210,
|
||||||
|
128,
|
||||||
|
122,
|
||||||
|
54,
|
||||||
|
190,
|
||||||
|
230,
|
||||||
|
151,
|
||||||
|
183,
|
||||||
|
64,
|
||||||
|
225,
|
||||||
|
229,
|
||||||
|
113,
|
||||||
|
1,
|
||||||
|
228,
|
||||||
|
97
|
||||||
],
|
],
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
@ -70,38 +70,38 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"npk": [
|
"npk": [
|
||||||
27,
|
192,
|
||||||
250,
|
251,
|
||||||
|
166,
|
||||||
|
243,
|
||||||
|
167,
|
||||||
|
236,
|
||||||
|
84,
|
||||||
|
249,
|
||||||
|
35,
|
||||||
136,
|
136,
|
||||||
142,
|
130,
|
||||||
88,
|
172,
|
||||||
128,
|
219,
|
||||||
138,
|
|
||||||
21,
|
|
||||||
49,
|
|
||||||
183,
|
|
||||||
118,
|
|
||||||
160,
|
|
||||||
117,
|
|
||||||
114,
|
|
||||||
110,
|
|
||||||
47,
|
|
||||||
136,
|
|
||||||
87,
|
|
||||||
60,
|
|
||||||
70,
|
|
||||||
59,
|
|
||||||
60,
|
|
||||||
18,
|
|
||||||
223,
|
|
||||||
23,
|
|
||||||
147,
|
|
||||||
241,
|
|
||||||
5,
|
|
||||||
184,
|
|
||||||
103,
|
|
||||||
225,
|
225,
|
||||||
105
|
161,
|
||||||
|
139,
|
||||||
|
229,
|
||||||
|
89,
|
||||||
|
243,
|
||||||
|
125,
|
||||||
|
194,
|
||||||
|
213,
|
||||||
|
209,
|
||||||
|
30,
|
||||||
|
23,
|
||||||
|
174,
|
||||||
|
100,
|
||||||
|
244,
|
||||||
|
124,
|
||||||
|
74,
|
||||||
|
140,
|
||||||
|
47
|
||||||
],
|
],
|
||||||
"account": {
|
"account": {
|
||||||
"program_owner": [
|
"program_owner": [
|
||||||
@ -154,4 +154,4 @@
|
|||||||
37,
|
37,
|
||||||
37
|
37
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -16,7 +16,8 @@ nssa-core = { path = "../nssa/core" }
|
|||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
bytemuck = "1.23.2"
|
bytemuck = "1.23.2"
|
||||||
borsh.workspace = true
|
borsh.workspace = true
|
||||||
hex.workspace = true
|
base58.workspace = true
|
||||||
|
hex = "0.4.3"
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
|
||||||
[dependencies.key_protocol]
|
[dependencies.key_protocol]
|
||||||
|
|||||||
@ -75,19 +75,91 @@ mod tests {
|
|||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
fn create_initial_accounts() -> Vec<InitialAccountData> {
|
fn create_initial_accounts() -> Vec<InitialAccountData> {
|
||||||
let initial_acc1 = serde_json::from_str(r#"{
|
let initial_acc1 = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
"Public": {
|
"Public": {
|
||||||
"address": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44",
|
"address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||||
"pub_sign_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
"pub_sign_key": [
|
||||||
|
16,
|
||||||
|
162,
|
||||||
|
106,
|
||||||
|
154,
|
||||||
|
236,
|
||||||
|
125,
|
||||||
|
52,
|
||||||
|
184,
|
||||||
|
35,
|
||||||
|
100,
|
||||||
|
238,
|
||||||
|
174,
|
||||||
|
69,
|
||||||
|
197,
|
||||||
|
41,
|
||||||
|
77,
|
||||||
|
187,
|
||||||
|
10,
|
||||||
|
118,
|
||||||
|
75,
|
||||||
|
0,
|
||||||
|
11,
|
||||||
|
148,
|
||||||
|
238,
|
||||||
|
185,
|
||||||
|
181,
|
||||||
|
133,
|
||||||
|
17,
|
||||||
|
220,
|
||||||
|
72,
|
||||||
|
124,
|
||||||
|
77
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}"#).unwrap();
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let initial_acc2 = serde_json::from_str(r#"{
|
let initial_acc2 = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
"Public": {
|
"Public": {
|
||||||
"address": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc",
|
"address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||||
"pub_sign_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
|
"pub_sign_key": [
|
||||||
|
113,
|
||||||
|
121,
|
||||||
|
64,
|
||||||
|
177,
|
||||||
|
204,
|
||||||
|
85,
|
||||||
|
229,
|
||||||
|
214,
|
||||||
|
178,
|
||||||
|
6,
|
||||||
|
109,
|
||||||
|
191,
|
||||||
|
29,
|
||||||
|
154,
|
||||||
|
63,
|
||||||
|
38,
|
||||||
|
242,
|
||||||
|
18,
|
||||||
|
244,
|
||||||
|
219,
|
||||||
|
8,
|
||||||
|
208,
|
||||||
|
35,
|
||||||
|
136,
|
||||||
|
23,
|
||||||
|
127,
|
||||||
|
207,
|
||||||
|
237,
|
||||||
|
216,
|
||||||
|
169,
|
||||||
|
190,
|
||||||
|
27
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}"#).unwrap();
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let initial_accounts = vec![initial_acc1, initial_acc2];
|
let initial_accounts = vec![initial_acc1, initial_acc2];
|
||||||
|
|
||||||
|
|||||||
@ -1,213 +1,117 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use base58::ToBase58;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use common::transaction::NSSATransaction;
|
use nssa::{Account, Address, program::Program};
|
||||||
use nssa::Address;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount,
|
SubcommandReturnValue, WalletCore,
|
||||||
|
cli::WalletSubcommand,
|
||||||
|
helperfunctions::{AddressPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix},
|
||||||
|
parse_block_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
||||||
|
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
||||||
|
|
||||||
|
const TOKEN_HOLDING_TYPE: u8 = 1;
|
||||||
|
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
||||||
|
|
||||||
|
struct TokenDefinition {
|
||||||
|
#[allow(unused)]
|
||||||
|
account_type: u8,
|
||||||
|
name: [u8; 6],
|
||||||
|
total_supply: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TokenHolding {
|
||||||
|
#[allow(unused)]
|
||||||
|
account_type: u8,
|
||||||
|
definition_id: Address,
|
||||||
|
balance: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenDefinition {
|
||||||
|
fn parse(data: &[u8]) -> Option<Self> {
|
||||||
|
if data.len() != TOKEN_DEFINITION_DATA_SIZE || data[0] != TOKEN_DEFINITION_TYPE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let account_type = data[0];
|
||||||
|
let name = data[1..7].try_into().unwrap();
|
||||||
|
let total_supply = u128::from_le_bytes(data[7..].try_into().unwrap());
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
account_type,
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenHolding {
|
||||||
|
fn parse(data: &[u8]) -> Option<Self> {
|
||||||
|
if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let account_type = data[0];
|
||||||
|
let definition_id = Address::new(data[1..33].try_into().unwrap());
|
||||||
|
let balance = u128::from_le_bytes(data[33..].try_into().unwrap());
|
||||||
|
Some(Self {
|
||||||
|
definition_id,
|
||||||
|
balance,
|
||||||
|
account_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///Represents generic chain CLI subcommand
|
///Represents generic chain CLI subcommand
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
pub enum AccountSubcommand {
|
pub enum AccountSubcommand {
|
||||||
///Get
|
///Get account data
|
||||||
|
Get {
|
||||||
|
///Flag to get raw account data
|
||||||
|
#[arg(short, long)]
|
||||||
|
raw: bool,
|
||||||
|
///Valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(short, long)]
|
||||||
|
addr: String,
|
||||||
|
},
|
||||||
|
///Produce new public or private account
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Get(GetSubcommand),
|
New(NewSubcommand),
|
||||||
///Fetch
|
///Sync private accounts
|
||||||
#[command(subcommand)]
|
SyncPrivate {},
|
||||||
Fetch(FetchSubcommand),
|
|
||||||
///Register
|
|
||||||
#[command(subcommand)]
|
|
||||||
Register(RegisterSubcommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
///Represents generic getter CLI subcommand
|
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
|
||||||
pub enum GetSubcommand {
|
|
||||||
///Get account `addr` balance
|
|
||||||
PublicAccountBalance {
|
|
||||||
#[arg(short, long)]
|
|
||||||
addr: String,
|
|
||||||
},
|
|
||||||
///Get account `addr` nonce
|
|
||||||
PublicAccountNonce {
|
|
||||||
#[arg(short, long)]
|
|
||||||
addr: String,
|
|
||||||
},
|
|
||||||
///Get account at address `addr`
|
|
||||||
PublicAccount {
|
|
||||||
#[arg(short, long)]
|
|
||||||
addr: String,
|
|
||||||
},
|
|
||||||
///Get private account with `addr` from storage
|
|
||||||
PrivateAccount {
|
|
||||||
#[arg(short, long)]
|
|
||||||
addr: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
///Represents generic getter CLI subcommand
|
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
|
||||||
pub enum FetchSubcommand {
|
|
||||||
///Fetch transaction by `hash`
|
|
||||||
Tx {
|
|
||||||
#[arg(short, long)]
|
|
||||||
tx_hash: String,
|
|
||||||
},
|
|
||||||
///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id`
|
|
||||||
PrivateAccount {
|
|
||||||
///tx_hash - valid 32 byte hex string
|
|
||||||
#[arg(long)]
|
|
||||||
tx_hash: String,
|
|
||||||
///acc_addr - valid 32 byte hex string
|
|
||||||
#[arg(long)]
|
|
||||||
acc_addr: String,
|
|
||||||
///output_id - id of the output in the transaction
|
|
||||||
#[arg(long)]
|
|
||||||
output_id: usize,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///Represents generic register CLI subcommand
|
///Represents generic register CLI subcommand
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
pub enum RegisterSubcommand {
|
pub enum NewSubcommand {
|
||||||
///Register new public account
|
///Register new public account
|
||||||
Public {},
|
Public {},
|
||||||
///Register new private account
|
///Register new private account
|
||||||
Private {},
|
Private {},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletSubcommand for GetSubcommand {
|
impl WalletSubcommand for NewSubcommand {
|
||||||
async fn handle_subcommand(
|
async fn handle_subcommand(
|
||||||
self,
|
self,
|
||||||
wallet_core: &mut WalletCore,
|
wallet_core: &mut WalletCore,
|
||||||
) -> Result<SubcommandReturnValue> {
|
) -> Result<SubcommandReturnValue> {
|
||||||
match self {
|
match self {
|
||||||
GetSubcommand::PublicAccountBalance { addr } => {
|
NewSubcommand::Public {} => {
|
||||||
let addr = Address::from_str(&addr)?;
|
|
||||||
|
|
||||||
let balance = wallet_core.get_account_balance(addr).await?;
|
|
||||||
println!("Accounts {addr} balance is {balance}");
|
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::Empty)
|
|
||||||
}
|
|
||||||
GetSubcommand::PublicAccountNonce { addr } => {
|
|
||||||
let addr = Address::from_str(&addr)?;
|
|
||||||
|
|
||||||
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
|
|
||||||
println!("Accounts {addr} nonce is {nonce}");
|
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::Empty)
|
|
||||||
}
|
|
||||||
GetSubcommand::PublicAccount { addr } => {
|
|
||||||
let addr: Address = addr.parse()?;
|
|
||||||
let account = wallet_core.get_account_public(addr).await?;
|
|
||||||
let account_hr: HumanReadableAccount = account.clone().into();
|
|
||||||
println!("{}", serde_json::to_string(&account_hr).unwrap());
|
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::Account(account))
|
|
||||||
}
|
|
||||||
GetSubcommand::PrivateAccount { addr } => {
|
|
||||||
let addr: Address = addr.parse()?;
|
|
||||||
if let Some(account) = wallet_core.get_account_private(&addr) {
|
|
||||||
println!("{}", serde_json::to_string(&account).unwrap());
|
|
||||||
} else {
|
|
||||||
println!("Private account not found.");
|
|
||||||
}
|
|
||||||
Ok(SubcommandReturnValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WalletSubcommand for FetchSubcommand {
|
|
||||||
async fn handle_subcommand(
|
|
||||||
self,
|
|
||||||
wallet_core: &mut WalletCore,
|
|
||||||
) -> Result<SubcommandReturnValue> {
|
|
||||||
match self {
|
|
||||||
FetchSubcommand::Tx { tx_hash } => {
|
|
||||||
let tx_obj = wallet_core
|
|
||||||
.sequencer_client
|
|
||||||
.get_transaction_by_hash(tx_hash)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("Transaction object {tx_obj:#?}");
|
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::Empty)
|
|
||||||
}
|
|
||||||
FetchSubcommand::PrivateAccount {
|
|
||||||
tx_hash,
|
|
||||||
acc_addr,
|
|
||||||
output_id: ciph_id,
|
|
||||||
} => {
|
|
||||||
let acc_addr: Address = acc_addr.parse().unwrap();
|
|
||||||
|
|
||||||
let account_key_chain = wallet_core
|
|
||||||
.storage
|
|
||||||
.user_data
|
|
||||||
.user_private_accounts
|
|
||||||
.get(&acc_addr);
|
|
||||||
|
|
||||||
let Some((account_key_chain, _)) = account_key_chain else {
|
|
||||||
anyhow::bail!("Account not found");
|
|
||||||
};
|
|
||||||
|
|
||||||
let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?;
|
|
||||||
|
|
||||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
|
||||||
let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone();
|
|
||||||
let to_comm = tx.message.new_commitments[ciph_id].clone();
|
|
||||||
let shared_secret =
|
|
||||||
account_key_chain.calculate_shared_secret_receiver(to_ebc.epk);
|
|
||||||
|
|
||||||
let res_acc_to = nssa_core::EncryptionScheme::decrypt(
|
|
||||||
&to_ebc.ciphertext,
|
|
||||||
&shared_secret,
|
|
||||||
&to_comm,
|
|
||||||
ciph_id as u32,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("RES acc to {res_acc_to:#?}");
|
|
||||||
|
|
||||||
println!("Transaction data is {:?}", tx.message);
|
|
||||||
|
|
||||||
wallet_core
|
|
||||||
.storage
|
|
||||||
.insert_private_account_data(acc_addr, res_acc_to);
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WalletSubcommand for RegisterSubcommand {
|
|
||||||
async fn handle_subcommand(
|
|
||||||
self,
|
|
||||||
wallet_core: &mut WalletCore,
|
|
||||||
) -> Result<SubcommandReturnValue> {
|
|
||||||
match self {
|
|
||||||
RegisterSubcommand::Public {} => {
|
|
||||||
let addr = wallet_core.create_new_account_public();
|
let addr = wallet_core.create_new_account_public();
|
||||||
|
|
||||||
println!("Generated new account with addr {addr}");
|
println!("Generated new account with addr Public/{addr}");
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
Ok(SubcommandReturnValue::RegisterAccount { addr })
|
Ok(SubcommandReturnValue::RegisterAccount { addr })
|
||||||
}
|
}
|
||||||
RegisterSubcommand::Private {} => {
|
NewSubcommand::Private {} => {
|
||||||
let addr = wallet_core.create_new_account_private();
|
let addr = wallet_core.create_new_account_private();
|
||||||
|
|
||||||
let (key, _) = wallet_core
|
let (key, _) = wallet_core
|
||||||
@ -216,14 +120,17 @@ impl WalletSubcommand for RegisterSubcommand {
|
|||||||
.get_private_account(&addr)
|
.get_private_account(&addr)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("Generated new account with addr {addr}");
|
println!(
|
||||||
println!("With npk {}", hex::encode(&key.nullifer_public_key));
|
"Generated new account with addr Private/{}",
|
||||||
|
addr.to_bytes().to_base58()
|
||||||
|
);
|
||||||
|
println!("With npk {}", hex::encode(key.nullifer_public_key.0));
|
||||||
println!(
|
println!(
|
||||||
"With ipk {}",
|
"With ipk {}",
|
||||||
hex::encode(key.incoming_viewing_public_key.to_bytes())
|
hex::encode(key.incoming_viewing_public_key.to_bytes())
|
||||||
);
|
);
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -233,20 +140,155 @@ impl WalletSubcommand for RegisterSubcommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct AuthenticatedTransferAccountView {
|
||||||
|
pub balance: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<nssa::Account> for AuthenticatedTransferAccountView {
|
||||||
|
fn from(value: nssa::Account) -> Self {
|
||||||
|
Self {
|
||||||
|
balance: value.balance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct TokedDefinitionAccountView {
|
||||||
|
pub account_type: String,
|
||||||
|
pub name: String,
|
||||||
|
pub total_supply: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TokenDefinition> for TokedDefinitionAccountView {
|
||||||
|
fn from(value: TokenDefinition) -> Self {
|
||||||
|
Self {
|
||||||
|
account_type: "Token definition".to_string(),
|
||||||
|
name: hex::encode(value.name),
|
||||||
|
total_supply: value.total_supply,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct TokedHoldingAccountView {
|
||||||
|
pub account_type: String,
|
||||||
|
pub definition_id: String,
|
||||||
|
pub balance: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TokenHolding> for TokedHoldingAccountView {
|
||||||
|
fn from(value: TokenHolding) -> Self {
|
||||||
|
Self {
|
||||||
|
account_type: "Token holding".to_string(),
|
||||||
|
definition_id: value.definition_id.to_string(),
|
||||||
|
balance: value.balance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WalletSubcommand for AccountSubcommand {
|
impl WalletSubcommand for AccountSubcommand {
|
||||||
async fn handle_subcommand(
|
async fn handle_subcommand(
|
||||||
self,
|
self,
|
||||||
wallet_core: &mut WalletCore,
|
wallet_core: &mut WalletCore,
|
||||||
) -> Result<SubcommandReturnValue> {
|
) -> Result<SubcommandReturnValue> {
|
||||||
match self {
|
match self {
|
||||||
AccountSubcommand::Get(get_subcommand) => {
|
AccountSubcommand::Get { raw, addr } => {
|
||||||
get_subcommand.handle_subcommand(wallet_core).await
|
let (addr, addr_kind) = parse_addr_with_privacy_prefix(&addr)?;
|
||||||
|
|
||||||
|
let addr = addr.parse()?;
|
||||||
|
|
||||||
|
let account = match addr_kind {
|
||||||
|
AddressPrivacyKind::Public => wallet_core.get_account_public(addr).await?,
|
||||||
|
AddressPrivacyKind::Private => wallet_core
|
||||||
|
.get_account_private(&addr)
|
||||||
|
.ok_or(anyhow::anyhow!("Private account not found in storage"))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if account == Account::default() {
|
||||||
|
println!("Account is Uninitialized");
|
||||||
|
|
||||||
|
return Ok(SubcommandReturnValue::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw {
|
||||||
|
let account_hr: HumanReadableAccount = account.clone().into();
|
||||||
|
println!("{}", serde_json::to_string(&account_hr).unwrap());
|
||||||
|
|
||||||
|
return Ok(SubcommandReturnValue::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let auth_tr_prog_id = Program::authenticated_transfer_program().id();
|
||||||
|
let token_prog_id = Program::token().id();
|
||||||
|
|
||||||
|
let acc_view = match &account.program_owner {
|
||||||
|
_ if account.program_owner == auth_tr_prog_id => {
|
||||||
|
let acc_view: AuthenticatedTransferAccountView = account.into();
|
||||||
|
|
||||||
|
println!("Account owned by authenticated transfer program");
|
||||||
|
|
||||||
|
serde_json::to_string(&acc_view)?
|
||||||
|
}
|
||||||
|
_ if account.program_owner == token_prog_id => {
|
||||||
|
if let Some(token_def) = TokenDefinition::parse(&account.data) {
|
||||||
|
let acc_view: TokedDefinitionAccountView = token_def.into();
|
||||||
|
|
||||||
|
println!("Definition account owned by token program");
|
||||||
|
|
||||||
|
serde_json::to_string(&acc_view)?
|
||||||
|
} else if let Some(token_hold) = TokenHolding::parse(&account.data) {
|
||||||
|
let acc_view: TokedHoldingAccountView = token_hold.into();
|
||||||
|
|
||||||
|
println!("Holding account owned by token program");
|
||||||
|
|
||||||
|
serde_json::to_string(&acc_view)?
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("Invalid data for account {addr:#?} with token program");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let account_hr: HumanReadableAccount = account.clone().into();
|
||||||
|
serde_json::to_string(&account_hr).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", acc_view);
|
||||||
|
|
||||||
|
Ok(SubcommandReturnValue::Empty)
|
||||||
}
|
}
|
||||||
AccountSubcommand::Fetch(fetch_subcommand) => {
|
AccountSubcommand::New(new_subcommand) => {
|
||||||
fetch_subcommand.handle_subcommand(wallet_core).await
|
new_subcommand.handle_subcommand(wallet_core).await
|
||||||
}
|
}
|
||||||
AccountSubcommand::Register(register_subcommand) => {
|
AccountSubcommand::SyncPrivate {} => {
|
||||||
register_subcommand.handle_subcommand(wallet_core).await
|
let last_synced_block = wallet_core.last_synced_block;
|
||||||
|
let curr_last_block = wallet_core
|
||||||
|
.sequencer_client
|
||||||
|
.get_last_block()
|
||||||
|
.await?
|
||||||
|
.last_block;
|
||||||
|
|
||||||
|
if !wallet_core
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.user_private_accounts
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
parse_block_range(
|
||||||
|
last_synced_block + 1,
|
||||||
|
curr_last_block,
|
||||||
|
wallet_core.sequencer_client.clone(),
|
||||||
|
wallet_core,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
wallet_core.last_synced_block = curr_last_block;
|
||||||
|
|
||||||
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
|
println!("Stored persistent data at {path:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,16 @@ use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
|||||||
///Represents generic chain CLI subcommand
|
///Represents generic chain CLI subcommand
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
pub enum ChainSubcommand {
|
pub enum ChainSubcommand {
|
||||||
GetLatestBlockId {},
|
///Get current block id from sequencer
|
||||||
GetBlockAtId {
|
CurrentBlockId {},
|
||||||
|
///Get block at id from sequencer
|
||||||
|
Block {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
id: u64,
|
id: u64,
|
||||||
},
|
},
|
||||||
GetTransactionAtHash {
|
///Get transaction at hash from sequencer
|
||||||
|
Transaction {
|
||||||
|
///hash - valid 32 byte hex string
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
hash: String,
|
hash: String,
|
||||||
},
|
},
|
||||||
@ -23,17 +27,17 @@ impl WalletSubcommand for ChainSubcommand {
|
|||||||
wallet_core: &mut WalletCore,
|
wallet_core: &mut WalletCore,
|
||||||
) -> Result<SubcommandReturnValue> {
|
) -> Result<SubcommandReturnValue> {
|
||||||
match self {
|
match self {
|
||||||
ChainSubcommand::GetLatestBlockId {} => {
|
ChainSubcommand::CurrentBlockId {} => {
|
||||||
let latest_block_res = wallet_core.sequencer_client.get_last_block().await?;
|
let latest_block_res = wallet_core.sequencer_client.get_last_block().await?;
|
||||||
|
|
||||||
println!("Last block id is {}", latest_block_res.last_block);
|
println!("Last block id is {}", latest_block_res.last_block);
|
||||||
}
|
}
|
||||||
ChainSubcommand::GetBlockAtId { id } => {
|
ChainSubcommand::Block { id } => {
|
||||||
let block_res = wallet_core.sequencer_client.get_block(id).await?;
|
let block_res = wallet_core.sequencer_client.get_block(id).await?;
|
||||||
|
|
||||||
println!("Last block id is {:#?}", block_res.block);
|
println!("Last block id is {:#?}", block_res.block);
|
||||||
}
|
}
|
||||||
ChainSubcommand::GetTransactionAtHash { hash } => {
|
ChainSubcommand::Transaction { hash } => {
|
||||||
let tx_res = wallet_core
|
let tx_res = wallet_core
|
||||||
.sequencer_client
|
.sequencer_client
|
||||||
.get_transaction_by_hash(hash)
|
.get_transaction_by_hash(hash)
|
||||||
|
|||||||
@ -3,7 +3,193 @@ use clap::Subcommand;
|
|||||||
use common::transaction::NSSATransaction;
|
use common::transaction::NSSATransaction;
|
||||||
use nssa::Address;
|
use nssa::Address;
|
||||||
|
|
||||||
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
use crate::{
|
||||||
|
SubcommandReturnValue, WalletCore,
|
||||||
|
cli::WalletSubcommand,
|
||||||
|
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||||
|
};
|
||||||
|
|
||||||
|
///Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||||
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
|
pub enum AuthTransferSubcommand {
|
||||||
|
///Initialize account under authenticated transfer program
|
||||||
|
Init {
|
||||||
|
///addr - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
addr: String,
|
||||||
|
},
|
||||||
|
///Send native tokens from one account to another with variable privacy
|
||||||
|
///
|
||||||
|
///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns.
|
||||||
|
///
|
||||||
|
///First is used for owned accounts, second otherwise.
|
||||||
|
Send {
|
||||||
|
///from - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
from: String,
|
||||||
|
///to - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
to: Option<String>,
|
||||||
|
///to_npk - valid 32 byte hex string
|
||||||
|
#[arg(long)]
|
||||||
|
to_npk: Option<String>,
|
||||||
|
///to_ipk - valid 33 byte hex string
|
||||||
|
#[arg(long)]
|
||||||
|
to_ipk: Option<String>,
|
||||||
|
///amount - amount of balance to move
|
||||||
|
#[arg(long)]
|
||||||
|
amount: u128,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSubcommand for AuthTransferSubcommand {
|
||||||
|
async fn handle_subcommand(
|
||||||
|
self,
|
||||||
|
wallet_core: &mut WalletCore,
|
||||||
|
) -> Result<SubcommandReturnValue> {
|
||||||
|
match self {
|
||||||
|
AuthTransferSubcommand::Init { addr } => {
|
||||||
|
let (addr, addr_privacy) = parse_addr_with_privacy_prefix(&addr)?;
|
||||||
|
|
||||||
|
match addr_privacy {
|
||||||
|
AddressPrivacyKind::Public => {
|
||||||
|
let addr = addr.parse()?;
|
||||||
|
|
||||||
|
let res = wallet_core
|
||||||
|
.register_account_under_authenticated_transfers_programs(addr)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("Results of tx send is {res:#?}");
|
||||||
|
|
||||||
|
let transfer_tx =
|
||||||
|
wallet_core.poll_native_token_transfer(res.tx_hash).await?;
|
||||||
|
|
||||||
|
println!("Transaction data is {transfer_tx:?}");
|
||||||
|
|
||||||
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
}
|
||||||
|
AddressPrivacyKind::Private => {
|
||||||
|
let addr = addr.parse()?;
|
||||||
|
|
||||||
|
let (res, [secret]) = wallet_core
|
||||||
|
.register_account_under_authenticated_transfers_programs_private(addr)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("Results of tx send is {res:#?}");
|
||||||
|
|
||||||
|
let tx_hash = res.tx_hash;
|
||||||
|
let transfer_tx = wallet_core
|
||||||
|
.poll_native_token_transfer(tx_hash.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||||
|
let acc_decode_data = vec![(secret, addr)];
|
||||||
|
|
||||||
|
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||||
|
tx,
|
||||||
|
&acc_decode_data,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SubcommandReturnValue::Empty)
|
||||||
|
}
|
||||||
|
AuthTransferSubcommand::Send {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
to_npk,
|
||||||
|
to_ipk,
|
||||||
|
amount,
|
||||||
|
} => {
|
||||||
|
let underlying_subcommand = match (to, to_npk, to_ipk) {
|
||||||
|
(None, None, None) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Provide either account address of receiver or their public keys"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(_), Some(_), Some(_)) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Provide only one variant: either account address of receiver or their public keys"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||||
|
anyhow::bail!("List of public keys is uncomplete");
|
||||||
|
}
|
||||||
|
(Some(to), None, None) => {
|
||||||
|
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||||
|
let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?;
|
||||||
|
|
||||||
|
match (from_privacy, to_privacy) {
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Public { from, to, amount }
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Private(
|
||||||
|
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Deshielded {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Shielded(
|
||||||
|
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(to_npk), Some(to_ipk)) => {
|
||||||
|
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||||
|
|
||||||
|
match from_privacy {
|
||||||
|
AddressPrivacyKind::Private => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Private(
|
||||||
|
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
|
||||||
|
from,
|
||||||
|
to_npk,
|
||||||
|
to_ipk,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AddressPrivacyKind::Public => {
|
||||||
|
NativeTokenTransferProgramSubcommand::Shielded(
|
||||||
|
NativeTokenTransferProgramSubcommandShielded::ShieldedForeign {
|
||||||
|
from,
|
||||||
|
to_npk,
|
||||||
|
to_ipk,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///Represents generic CLI subcommand for a wallet working with native token transfer program
|
///Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
@ -158,7 +344,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -202,7 +388,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -252,7 +438,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -285,7 +471,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
|||||||
|
|
||||||
let tx_hash = res.tx_hash;
|
let tx_hash = res.tx_hash;
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -331,7 +517,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -351,7 +537,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
|||||||
|
|
||||||
println!("Transaction data is {transfer_tx:?}");
|
println!("Transaction data is {transfer_tx:?}");
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,59 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use common::transaction::NSSATransaction;
|
use common::{PINATA_BASE58, transaction::NSSATransaction};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
use crate::{
|
||||||
|
SubcommandReturnValue, WalletCore,
|
||||||
|
cli::WalletSubcommand,
|
||||||
|
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||||
|
};
|
||||||
|
|
||||||
|
///Represents generic CLI subcommand for a wallet working with pinata program
|
||||||
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
|
pub enum PinataProgramAgnosticSubcommand {
|
||||||
|
///Claim pinata
|
||||||
|
Claim {
|
||||||
|
///to_addr - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
to_addr: String,
|
||||||
|
///solution - solution to pinata challenge
|
||||||
|
#[arg(long)]
|
||||||
|
solution: u128,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSubcommand for PinataProgramAgnosticSubcommand {
|
||||||
|
async fn handle_subcommand(
|
||||||
|
self,
|
||||||
|
wallet_core: &mut WalletCore,
|
||||||
|
) -> Result<SubcommandReturnValue> {
|
||||||
|
let underlying_subcommand = match self {
|
||||||
|
PinataProgramAgnosticSubcommand::Claim { to_addr, solution } => {
|
||||||
|
let (to_addr, to_addr_privacy) = parse_addr_with_privacy_prefix(&to_addr)?;
|
||||||
|
|
||||||
|
match to_addr_privacy {
|
||||||
|
AddressPrivacyKind::Public => {
|
||||||
|
PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim {
|
||||||
|
pinata_addr: PINATA_BASE58.to_string(),
|
||||||
|
winner_addr: to_addr,
|
||||||
|
solution,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AddressPrivacyKind::Private => PinataProgramSubcommand::Private(
|
||||||
|
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
|
||||||
|
pinata_addr: PINATA_BASE58.to_string(),
|
||||||
|
winner_addr: to_addr,
|
||||||
|
solution,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///Represents generic CLI subcommand for a wallet working with pinata program
|
///Represents generic CLI subcommand for a wallet working with pinata program
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
@ -131,7 +181,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,197 @@ use clap::Subcommand;
|
|||||||
use common::transaction::NSSATransaction;
|
use common::transaction::NSSATransaction;
|
||||||
use nssa::Address;
|
use nssa::Address;
|
||||||
|
|
||||||
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
use crate::{
|
||||||
|
SubcommandReturnValue, WalletCore,
|
||||||
|
cli::WalletSubcommand,
|
||||||
|
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||||
|
};
|
||||||
|
|
||||||
|
///Represents generic CLI subcommand for a wallet working with token program
|
||||||
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
|
pub enum TokenProgramAgnosticSubcommand {
|
||||||
|
///Produce a new token
|
||||||
|
///
|
||||||
|
///Currently the only supported privacy options is for public definition
|
||||||
|
New {
|
||||||
|
///definition_addr - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
definition_addr: String,
|
||||||
|
///supply_addr - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
supply_addr: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
name: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
total_supply: u128,
|
||||||
|
},
|
||||||
|
///Send tokens from one account to another with variable privacy
|
||||||
|
///
|
||||||
|
///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns.
|
||||||
|
///
|
||||||
|
///First is used for owned accounts, second otherwise.
|
||||||
|
Send {
|
||||||
|
///from - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
from: String,
|
||||||
|
///to - valid 32 byte base58 string with privacy prefix
|
||||||
|
#[arg(long)]
|
||||||
|
to: Option<String>,
|
||||||
|
///to_npk - valid 32 byte hex string
|
||||||
|
#[arg(long)]
|
||||||
|
to_npk: Option<String>,
|
||||||
|
///to_ipk - valid 33 byte hex string
|
||||||
|
#[arg(long)]
|
||||||
|
to_ipk: Option<String>,
|
||||||
|
///amount - amount of balance to move
|
||||||
|
#[arg(long)]
|
||||||
|
amount: u128,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||||
|
async fn handle_subcommand(
|
||||||
|
self,
|
||||||
|
wallet_core: &mut WalletCore,
|
||||||
|
) -> Result<SubcommandReturnValue> {
|
||||||
|
match self {
|
||||||
|
TokenProgramAgnosticSubcommand::New {
|
||||||
|
definition_addr,
|
||||||
|
supply_addr,
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
} => {
|
||||||
|
let (definition_addr, definition_addr_privacy) =
|
||||||
|
parse_addr_with_privacy_prefix(&definition_addr)?;
|
||||||
|
let (supply_addr, supply_addr_privacy) =
|
||||||
|
parse_addr_with_privacy_prefix(&supply_addr)?;
|
||||||
|
|
||||||
|
let underlying_subcommand = match (definition_addr_privacy, supply_addr_privacy) {
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||||
|
TokenProgramSubcommand::Public(
|
||||||
|
TokenProgramSubcommandPublic::CreateNewToken {
|
||||||
|
definition_addr,
|
||||||
|
supply_addr,
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||||
|
TokenProgramSubcommand::Private(
|
||||||
|
TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned {
|
||||||
|
definition_addr,
|
||||||
|
supply_addr,
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||||
|
//ToDo: maybe implement this one. It is not immediately clear why definition should be private.
|
||||||
|
anyhow::bail!("Unavailable privacy pairing")
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||||
|
//ToDo: Probably valid. If definition is not public, but supply is it is very suspicious.
|
||||||
|
anyhow::bail!("Unavailable privacy pairing")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||||
|
}
|
||||||
|
TokenProgramAgnosticSubcommand::Send {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
to_npk,
|
||||||
|
to_ipk,
|
||||||
|
amount,
|
||||||
|
} => {
|
||||||
|
let underlying_subcommand = match (to, to_npk, to_ipk) {
|
||||||
|
(None, None, None) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Provide either account address of receiver or their public keys"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(_), Some(_), Some(_)) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Provide only one variant: either account address of receiver or their public keys"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||||
|
anyhow::bail!("List of public keys is uncomplete");
|
||||||
|
}
|
||||||
|
(Some(to), None, None) => {
|
||||||
|
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||||
|
let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?;
|
||||||
|
|
||||||
|
match (from_privacy, to_privacy) {
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||||
|
TokenProgramSubcommand::Public(
|
||||||
|
TokenProgramSubcommandPublic::TransferToken {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_addr: to,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||||
|
TokenProgramSubcommand::Private(
|
||||||
|
TokenProgramSubcommandPrivate::TransferTokenPrivateOwned {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_addr: to,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||||
|
TokenProgramSubcommand::Deshielded(
|
||||||
|
TokenProgramSubcommandDeshielded::TransferTokenDeshielded {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_addr: to,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||||
|
TokenProgramSubcommand::Shielded(
|
||||||
|
TokenProgramSubcommandShielded::TransferTokenShieldedOwned {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_addr: to,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(to_npk), Some(to_ipk)) => {
|
||||||
|
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||||
|
|
||||||
|
match from_privacy {
|
||||||
|
AddressPrivacyKind::Private => TokenProgramSubcommand::Private(
|
||||||
|
TokenProgramSubcommandPrivate::TransferTokenPrivateForeign {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_npk: to_npk,
|
||||||
|
recipient_ipk: to_ipk,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
AddressPrivacyKind::Public => TokenProgramSubcommand::Shielded(
|
||||||
|
TokenProgramSubcommandShielded::TransferTokenShieldedForeign {
|
||||||
|
sender_addr: from,
|
||||||
|
recipient_npk: to_npk,
|
||||||
|
recipient_ipk: to_ipk,
|
||||||
|
balance_to_move: amount,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
underlying_subcommand.handle_subcommand(wallet_core).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///Represents generic CLI subcommand for a wallet working with token_program
|
///Represents generic CLI subcommand for a wallet working with token_program
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
@ -221,7 +411,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -278,7 +468,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -328,7 +518,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -376,7 +566,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -431,7 +621,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
|||||||
println!("Transaction data is {:?}", tx.message);
|
println!("Transaction data is {:?}", tx.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
@ -485,7 +675,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
let path = wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
println!("Stored persistent accounts at {path:#?}");
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,12 @@ pub enum PersistentAccountData {
|
|||||||
Private(PersistentAccountDataPrivate),
|
Private(PersistentAccountDataPrivate),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PersistentStorage {
|
||||||
|
pub accounts: Vec<PersistentAccountData>,
|
||||||
|
pub last_synced_block: u64,
|
||||||
|
}
|
||||||
|
|
||||||
impl InitialAccountData {
|
impl InitialAccountData {
|
||||||
pub fn address(&self) -> nssa::Address {
|
pub fn address(&self) -> nssa::Address {
|
||||||
match &self {
|
match &self {
|
||||||
|
|||||||
@ -12,8 +12,7 @@ use serde::Serialize;
|
|||||||
use crate::{
|
use crate::{
|
||||||
HOME_DIR_ENV_VAR,
|
HOME_DIR_ENV_VAR,
|
||||||
config::{
|
config::{
|
||||||
PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic,
|
PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig,
|
||||||
WalletConfig,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,21 +29,24 @@ pub async fn fetch_config() -> Result<WalletConfig> {
|
|||||||
Ok(serde_json::from_slice(&config_contents)?)
|
Ok(serde_json::from_slice(&config_contents)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json`
|
/// Fetch data stored at `NSSA_WALLET_HOME_DIR/storage.json`
|
||||||
///
|
///
|
||||||
/// If file not present, it is considered as empty list of persistent accounts
|
/// If file not present, it is considered as empty list of persistent accounts
|
||||||
pub async fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
|
pub async fn fetch_persistent_storage() -> Result<PersistentStorage> {
|
||||||
let home = get_home()?;
|
let home = get_home()?;
|
||||||
let accs_path = home.join("curr_accounts.json");
|
let accs_path = home.join("storage.json");
|
||||||
let mut persistent_accounts_content = vec![];
|
let mut storage_content = vec![];
|
||||||
|
|
||||||
match tokio::fs::File::open(accs_path).await {
|
match tokio::fs::File::open(accs_path).await {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
file.read_to_end(&mut persistent_accounts_content).await?;
|
file.read_to_end(&mut storage_content).await?;
|
||||||
Ok(serde_json::from_slice(&persistent_accounts_content)?)
|
Ok(serde_json::from_slice(&storage_content)?)
|
||||||
}
|
}
|
||||||
Err(err) => match err.kind() {
|
Err(err) => match err.kind() {
|
||||||
std::io::ErrorKind::NotFound => Ok(vec![]),
|
std::io::ErrorKind::NotFound => Ok(PersistentStorage {
|
||||||
|
accounts: vec![],
|
||||||
|
last_synced_block: 0,
|
||||||
|
}),
|
||||||
_ => {
|
_ => {
|
||||||
anyhow::bail!("IO error {err:#?}");
|
anyhow::bail!("IO error {err:#?}");
|
||||||
}
|
}
|
||||||
@ -52,8 +54,11 @@ pub async fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produces a list of accounts for storage
|
/// Produces data for storage
|
||||||
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> {
|
pub fn produce_data_for_storage(
|
||||||
|
user_data: &NSSAUserData,
|
||||||
|
last_synced_block: u64,
|
||||||
|
) -> PersistentStorage {
|
||||||
let mut vec_for_storage = vec![];
|
let mut vec_for_storage = vec![];
|
||||||
|
|
||||||
for (addr, key) in &user_data.pub_account_signing_keys {
|
for (addr, key) in &user_data.pub_account_signing_keys {
|
||||||
@ -77,7 +82,10 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccou
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec_for_storage
|
PersistentStorage {
|
||||||
|
accounts: vec_for_storage,
|
||||||
|
last_synced_block,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
|
pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
|
||||||
@ -86,6 +94,30 @@ pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
|
|||||||
result.into_iter().map(Nonce::from_le_bytes).collect()
|
result.into_iter().map(Nonce::from_le_bytes).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AddressPrivacyKind {
|
||||||
|
Public,
|
||||||
|
Private,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_addr_with_privacy_prefix(
|
||||||
|
addr_base58: &str,
|
||||||
|
) -> Result<(String, AddressPrivacyKind)> {
|
||||||
|
if addr_base58.starts_with("Public/") {
|
||||||
|
Ok((
|
||||||
|
addr_base58.strip_prefix("Public/").unwrap().to_string(),
|
||||||
|
AddressPrivacyKind::Public,
|
||||||
|
))
|
||||||
|
} else if addr_base58.starts_with("Private/") {
|
||||||
|
Ok((
|
||||||
|
addr_base58.strip_prefix("Private/").unwrap().to_string(),
|
||||||
|
AddressPrivacyKind::Private,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("Unsupported privacy kind, available variants is Public/ and Private/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Human-readable representation of an account.
|
/// Human-readable representation of an account.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) struct HumanReadableAccount {
|
pub(crate) struct HumanReadableAccount {
|
||||||
@ -126,4 +158,20 @@ mod tests {
|
|||||||
std::env::remove_var(HOME_DIR_ENV_VAR);
|
std::env::remove_var(HOME_DIR_ENV_VAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addr_parse_with_privacy() {
|
||||||
|
let addr_base58 = "Public/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||||
|
let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(addr_kind, AddressPrivacyKind::Public);
|
||||||
|
|
||||||
|
let addr_base58 = "Private/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||||
|
let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(addr_kind, AddressPrivacyKind::Private);
|
||||||
|
|
||||||
|
let addr_base58 = "asdsada/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||||
|
assert!(parse_addr_with_privacy_prefix(addr_base58).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,16 +20,18 @@ use clap::{Parser, Subcommand};
|
|||||||
use nssa_core::{Commitment, MembershipProof};
|
use nssa_core::{Commitment, MembershipProof};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::{
|
||||||
WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand,
|
cli::{
|
||||||
native_token_transfer_program::NativeTokenTransferProgramSubcommand,
|
WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand,
|
||||||
pinata_program::PinataProgramSubcommand,
|
native_token_transfer_program::AuthTransferSubcommand,
|
||||||
|
pinata_program::PinataProgramAgnosticSubcommand,
|
||||||
|
token_program::TokenProgramAgnosticSubcommand,
|
||||||
|
},
|
||||||
|
config::PersistentStorage,
|
||||||
|
helperfunctions::fetch_persistent_storage,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::token_program::TokenProgramSubcommand,
|
helperfunctions::{fetch_config, get_home, produce_data_for_storage},
|
||||||
helperfunctions::{
|
|
||||||
fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage,
|
|
||||||
},
|
|
||||||
poller::TxPoller,
|
poller::TxPoller,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,6 +51,7 @@ pub struct WalletCore {
|
|||||||
pub storage: WalletChainStore,
|
pub storage: WalletChainStore,
|
||||||
pub poller: TxPoller,
|
pub poller: TxPoller,
|
||||||
pub sequencer_client: Arc<SequencerClient>,
|
pub sequencer_client: Arc<SequencerClient>,
|
||||||
|
pub last_synced_block: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletCore {
|
impl WalletCore {
|
||||||
@ -58,7 +61,10 @@ impl WalletCore {
|
|||||||
|
|
||||||
let mut storage = WalletChainStore::new(config)?;
|
let mut storage = WalletChainStore::new(config)?;
|
||||||
|
|
||||||
let persistent_accounts = fetch_persistent_accounts().await?;
|
let PersistentStorage {
|
||||||
|
accounts: persistent_accounts,
|
||||||
|
last_synced_block,
|
||||||
|
} = fetch_persistent_storage().await?;
|
||||||
for pers_acc_data in persistent_accounts {
|
for pers_acc_data in persistent_accounts {
|
||||||
storage.insert_account_data(pers_acc_data);
|
storage.insert_account_data(pers_acc_data);
|
||||||
}
|
}
|
||||||
@ -67,23 +73,24 @@ impl WalletCore {
|
|||||||
storage,
|
storage,
|
||||||
poller: tx_poller,
|
poller: tx_poller,
|
||||||
sequencer_client: client.clone(),
|
sequencer_client: client.clone(),
|
||||||
|
last_synced_block,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
///Store persistent accounts at home
|
///Store persistent data at home
|
||||||
pub async fn store_persistent_accounts(&self) -> Result<PathBuf> {
|
pub async fn store_persistent_data(&self) -> Result<PathBuf> {
|
||||||
let home = get_home()?;
|
let home = get_home()?;
|
||||||
let accs_path = home.join("curr_accounts.json");
|
let storage_path = home.join("storage.json");
|
||||||
|
|
||||||
let data = produce_data_for_storage(&self.storage.user_data);
|
let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block);
|
||||||
let accs = serde_json::to_vec_pretty(&data)?;
|
let storage = serde_json::to_vec_pretty(&data)?;
|
||||||
|
|
||||||
let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?;
|
let mut storage_file = tokio::fs::File::create(storage_path.as_path()).await?;
|
||||||
accs_file.write_all(&accs).await?;
|
storage_file.write_all(&storage).await?;
|
||||||
|
|
||||||
info!("Stored accounts data at {accs_path:#?}");
|
info!("Stored data at {storage_path:#?}");
|
||||||
|
|
||||||
Ok(accs_path)
|
Ok(storage_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_new_account_public(&mut self) -> Address {
|
pub fn create_new_account_public(&mut self) -> Address {
|
||||||
@ -191,28 +198,32 @@ impl WalletCore {
|
|||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
#[clap(about)]
|
#[clap(about)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
///Transfer command
|
///Authenticated transfer subcommand
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Transfer(NativeTokenTransferProgramSubcommand),
|
AuthTransfer(AuthTransferSubcommand),
|
||||||
///Chain command
|
///Generic chain info subcommand
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Chain(ChainSubcommand),
|
ChainInfo(ChainSubcommand),
|
||||||
///Chain command
|
///Account view and sync subcommand
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Account(AccountSubcommand),
|
Account(AccountSubcommand),
|
||||||
///Pinata command
|
///Pinata program interaction subcommand
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
PinataProgram(PinataProgramSubcommand),
|
Pinata(PinataProgramAgnosticSubcommand),
|
||||||
///Token command
|
///Token program interaction subcommand
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
TokenProgram(TokenProgramSubcommand),
|
Token(TokenProgramAgnosticSubcommand),
|
||||||
AuthenticatedTransferInitializePublicAccount {},
|
/// Check the wallet can connect to the node and builtin local programs
|
||||||
// Check the wallet can connect to the node and builtin local programs
|
/// match the remote versions
|
||||||
// match the remote versions
|
|
||||||
CheckHealth {},
|
CheckHealth {},
|
||||||
}
|
}
|
||||||
|
|
||||||
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
||||||
|
///
|
||||||
|
/// All account adresses must be valid 32 byte base58 strings.
|
||||||
|
///
|
||||||
|
/// All account addresses must be provided as {privacy_prefix}/{addr},
|
||||||
|
/// where valid options for `privacy_prefix` is `Public` and `Private`
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(version, about)]
|
#[clap(version, about)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
@ -230,6 +241,7 @@ pub enum SubcommandReturnValue {
|
|||||||
RegisterAccount { addr: nssa::Address },
|
RegisterAccount { addr: nssa::Address },
|
||||||
Account(nssa::Account),
|
Account(nssa::Account),
|
||||||
Empty,
|
Empty,
|
||||||
|
SyncedToBlock(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValue> {
|
pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValue> {
|
||||||
@ -237,12 +249,12 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
|
|||||||
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?;
|
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?;
|
||||||
|
|
||||||
let subcommand_ret = match command {
|
let subcommand_ret = match command {
|
||||||
Command::Transfer(transfer_subcommand) => {
|
Command::AuthTransfer(transfer_subcommand) => {
|
||||||
transfer_subcommand
|
transfer_subcommand
|
||||||
.handle_subcommand(&mut wallet_core)
|
.handle_subcommand(&mut wallet_core)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Command::Chain(chain_subcommand) => {
|
Command::ChainInfo(chain_subcommand) => {
|
||||||
chain_subcommand.handle_subcommand(&mut wallet_core).await?
|
chain_subcommand.handle_subcommand(&mut wallet_core).await?
|
||||||
}
|
}
|
||||||
Command::Account(account_subcommand) => {
|
Command::Account(account_subcommand) => {
|
||||||
@ -250,7 +262,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
|
|||||||
.handle_subcommand(&mut wallet_core)
|
.handle_subcommand(&mut wallet_core)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Command::PinataProgram(pinata_subcommand) => {
|
Command::Pinata(pinata_subcommand) => {
|
||||||
pinata_subcommand
|
pinata_subcommand
|
||||||
.handle_subcommand(&mut wallet_core)
|
.handle_subcommand(&mut wallet_core)
|
||||||
.await?
|
.await?
|
||||||
@ -285,26 +297,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
|
|||||||
|
|
||||||
SubcommandReturnValue::Empty
|
SubcommandReturnValue::Empty
|
||||||
}
|
}
|
||||||
Command::AuthenticatedTransferInitializePublicAccount {} => {
|
Command::Token(token_subcommand) => {
|
||||||
let addr = wallet_core.create_new_account_public();
|
|
||||||
|
|
||||||
println!("Generated new account with addr {addr}");
|
|
||||||
|
|
||||||
let path = wallet_core.store_persistent_accounts().await?;
|
|
||||||
|
|
||||||
println!("Stored persistent accounts at {path:#?}");
|
|
||||||
|
|
||||||
let res = wallet_core
|
|
||||||
.register_account_under_authenticated_transfers_programs(addr)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("Results of tx send is {res:#?}");
|
|
||||||
|
|
||||||
let _transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
|
|
||||||
|
|
||||||
SubcommandReturnValue::RegisterAccount { addr }
|
|
||||||
}
|
|
||||||
Command::TokenProgram(token_subcommand) => {
|
|
||||||
token_subcommand.handle_subcommand(&mut wallet_core).await?
|
token_subcommand.handle_subcommand(&mut wallet_core).await?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -312,6 +305,80 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
|
|||||||
Ok(subcommand_ret)
|
Ok(subcommand_ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn parse_block_range(
|
||||||
|
start: u64,
|
||||||
|
stop: u64,
|
||||||
|
seq_client: Arc<SequencerClient>,
|
||||||
|
wallet_core: &mut WalletCore,
|
||||||
|
) -> Result<()> {
|
||||||
|
for block_id in start..(stop + 1) {
|
||||||
|
let block =
|
||||||
|
borsh::from_slice::<HashableBlockData>(&seq_client.get_block(block_id).await?.block)?;
|
||||||
|
|
||||||
|
for tx in block.transactions {
|
||||||
|
let nssa_tx = NSSATransaction::try_from(&tx)?;
|
||||||
|
|
||||||
|
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
|
||||||
|
let mut affected_accounts = vec![];
|
||||||
|
|
||||||
|
for (acc_addr, (key_chain, _)) in
|
||||||
|
&wallet_core.storage.user_data.user_private_accounts
|
||||||
|
{
|
||||||
|
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||||
|
key_chain.nullifer_public_key.clone(),
|
||||||
|
key_chain.incoming_viewing_public_key.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (ciph_id, encrypted_data) in tx
|
||||||
|
.message()
|
||||||
|
.encrypted_private_post_states
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if encrypted_data.view_tag == view_tag {
|
||||||
|
let ciphertext = &encrypted_data.ciphertext;
|
||||||
|
let commitment = &tx.message.new_commitments[ciph_id];
|
||||||
|
let shared_secret = key_chain
|
||||||
|
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
|
||||||
|
|
||||||
|
let res_acc = nssa_core::EncryptionScheme::decrypt(
|
||||||
|
ciphertext,
|
||||||
|
&shared_secret,
|
||||||
|
commitment,
|
||||||
|
ciph_id as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(res_acc) = res_acc {
|
||||||
|
println!(
|
||||||
|
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
affected_accounts.push((*acc_addr, res_acc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (affected_addr, new_acc) in affected_accounts {
|
||||||
|
wallet_core
|
||||||
|
.storage
|
||||||
|
.insert_private_account_data(affected_addr, new_acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet_core.last_synced_block = block_id;
|
||||||
|
wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Block at id {block_id} with timestamp {} parsed",
|
||||||
|
block.timestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn execute_continious_run() -> Result<()> {
|
pub async fn execute_continious_run() -> Result<()> {
|
||||||
let config = fetch_config().await?;
|
let config = fetch_config().await?;
|
||||||
let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
||||||
@ -321,70 +388,13 @@ pub async fn execute_continious_run() -> Result<()> {
|
|||||||
let mut curr_last_block = latest_block_num;
|
let mut curr_last_block = latest_block_num;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for block_id in curr_last_block..(latest_block_num + 1) {
|
parse_block_range(
|
||||||
let block = borsh::from_slice::<HashableBlockData>(
|
curr_last_block,
|
||||||
&seq_client.get_block(block_id).await?.block,
|
latest_block_num,
|
||||||
)?;
|
seq_client.clone(),
|
||||||
|
&mut wallet_core,
|
||||||
for tx in block.transactions {
|
)
|
||||||
let nssa_tx = NSSATransaction::try_from(&tx)?;
|
.await?;
|
||||||
|
|
||||||
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
|
|
||||||
let mut affected_accounts = vec![];
|
|
||||||
|
|
||||||
for (acc_addr, (key_chain, _)) in
|
|
||||||
&wallet_core.storage.user_data.user_private_accounts
|
|
||||||
{
|
|
||||||
let view_tag = EncryptedAccountData::compute_view_tag(
|
|
||||||
key_chain.nullifer_public_key.clone(),
|
|
||||||
key_chain.incoming_viewing_public_key.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (ciph_id, encrypted_data) in tx
|
|
||||||
.message()
|
|
||||||
.encrypted_private_post_states
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
if encrypted_data.view_tag == view_tag {
|
|
||||||
let ciphertext = &encrypted_data.ciphertext;
|
|
||||||
let commitment = &tx.message.new_commitments[ciph_id];
|
|
||||||
let shared_secret = key_chain
|
|
||||||
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
|
|
||||||
|
|
||||||
let res_acc = nssa_core::EncryptionScheme::decrypt(
|
|
||||||
ciphertext,
|
|
||||||
&shared_secret,
|
|
||||||
commitment,
|
|
||||||
ciph_id as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(res_acc) = res_acc {
|
|
||||||
println!(
|
|
||||||
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
|
|
||||||
);
|
|
||||||
|
|
||||||
affected_accounts.push((*acc_addr, res_acc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (affected_addr, new_acc) in affected_accounts {
|
|
||||||
wallet_core
|
|
||||||
.storage
|
|
||||||
.insert_private_account_data(affected_addr, new_acc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet_core.store_persistent_accounts().await?;
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Block at id {block_id} with timestamp {} parsed",
|
|
||||||
block.timestamp
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
curr_last_block = latest_block_num + 1;
|
curr_last_block = latest_block_num + 1;
|
||||||
|
|
||||||
|
|||||||
@ -537,4 +537,53 @@ impl WalletCore {
|
|||||||
|
|
||||||
Ok(self.sequencer_client.send_tx_private(tx).await?)
|
Ok(self.sequencer_client.send_tx_private(tx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn register_account_under_authenticated_transfers_programs_private(
|
||||||
|
&self,
|
||||||
|
from: Address,
|
||||||
|
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||||
|
let AccountPreparedData {
|
||||||
|
nsk: _,
|
||||||
|
npk: from_npk,
|
||||||
|
ipk: from_ipk,
|
||||||
|
auth_acc: sender_pre,
|
||||||
|
proof: _,
|
||||||
|
} = self.private_acc_preparation(from, false, false).await?;
|
||||||
|
|
||||||
|
let eph_holder_from = EphemeralKeyHolder::new(&from_npk);
|
||||||
|
let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk);
|
||||||
|
|
||||||
|
let instruction: u128 = 0;
|
||||||
|
|
||||||
|
let (output, proof) = circuit::execute_and_prove(
|
||||||
|
&[sender_pre],
|
||||||
|
&Program::serialize_instruction(instruction).unwrap(),
|
||||||
|
&[2],
|
||||||
|
&produce_random_nonces(1),
|
||||||
|
&[(from_npk.clone(), shared_secret_from.clone())],
|
||||||
|
&[],
|
||||||
|
&Program::authenticated_transfer_program(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let message = Message::try_from_circuit_output(
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![(
|
||||||
|
from_npk.clone(),
|
||||||
|
from_ipk.clone(),
|
||||||
|
eph_holder_from.generate_ephemeral_public_key(),
|
||||||
|
)],
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||||
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
self.sequencer_client.send_tx_private(tx).await?,
|
||||||
|
[shared_secret_from],
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user