@layerzerolabs/protocol-stellar-v2 0.2.60 → 0.2.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +64 -65
- package/.turbo/turbo-lint.log +68 -73
- package/.turbo/turbo-test.log +1953 -1882
- package/contracts/workers/dvn/src/auth.rs +67 -30
- package/contracts/workers/dvn/src/dvn.rs +31 -6
- package/contracts/workers/dvn/src/errors.rs +4 -1
- package/contracts/workers/dvn/src/events.rs +7 -1
- package/contracts/workers/dvn/src/interfaces/dvn.rs +34 -8
- package/contracts/workers/dvn/src/storage.rs +5 -1
- package/contracts/workers/dvn/src/tests/auth.rs +539 -90
- package/docs/layerzero-v2-on-stellar.md +28 -10
- package/package.json +4 -4
- package/sdk/.turbo/turbo-test.log +303 -269
- package/sdk/dist/generated/dvn.d.ts +59 -11
- package/sdk/dist/generated/dvn.js +21 -10
- package/sdk/package.json +1 -1
- package/sdk/test/counter-uln.test.ts +34 -15
- package/sdk/test/upgrader.test.ts +21 -0
- package/sdk/test/utils.ts +101 -14
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
errors::DvnError,
|
|
3
3
|
tests::setup::{TestSetup, VID},
|
|
4
|
-
LzDVNClient, Sender, TransactionAuthData,
|
|
4
|
+
Call, LzDVNClient, Sender, TransactionAuthData,
|
|
5
5
|
};
|
|
6
6
|
use ed25519_dalek::{Signer, SigningKey};
|
|
7
7
|
use rand::thread_rng;
|
|
8
|
-
use soroban_sdk::{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const ENVELOPE_TYPE_SOROBAN_AUTHORIZATION: u32 = 9;
|
|
14
|
-
|
|
15
|
-
/// Build the full preimage bytes from auth fields.
|
|
16
|
-
fn build_preimage(env: &Env, auth_nonce: i64, auth_expiry_ledger: u32, invocation: &Bytes) -> Bytes {
|
|
17
|
-
BufferWriter::new(env)
|
|
18
|
-
.write_u32(ENVELOPE_TYPE_SOROBAN_AUTHORIZATION)
|
|
19
|
-
.write_bytes_n(&env.ledger().network_id())
|
|
20
|
-
.write_i64(auth_nonce)
|
|
21
|
-
.write_u32(auth_expiry_ledger)
|
|
22
|
-
.write_bytes(invocation)
|
|
23
|
-
.to_bytes()
|
|
24
|
-
}
|
|
8
|
+
use soroban_sdk::{
|
|
9
|
+
auth::{ContractContext, Context},
|
|
10
|
+
testutils::Address as _,
|
|
11
|
+
vec, Address, BytesN, Env, IntoVal, Symbol, Val, Vec,
|
|
12
|
+
};
|
|
25
13
|
|
|
26
14
|
struct Ed25519KeyPair {
|
|
27
15
|
signing_key: SigningKey,
|
|
@@ -46,18 +34,97 @@ impl Ed25519KeyPair {
|
|
|
46
34
|
}
|
|
47
35
|
}
|
|
48
36
|
|
|
49
|
-
///
|
|
50
|
-
fn
|
|
51
|
-
env
|
|
37
|
+
/// Creates a single self-call auth context and matching Call for testing.
|
|
38
|
+
fn make_self_call_context(
|
|
39
|
+
env: &Env,
|
|
40
|
+
contract_id: &soroban_sdk::Address,
|
|
41
|
+
) -> (Vec<Context>, Vec<Call>) {
|
|
42
|
+
let fn_name = Symbol::new(env, "set_paused");
|
|
43
|
+
let args: Vec<Val> = vec![env, true.into_val(env)];
|
|
44
|
+
|
|
45
|
+
let context = Context::Contract(ContractContext {
|
|
46
|
+
contract: contract_id.clone(),
|
|
47
|
+
fn_name: fn_name.clone(),
|
|
48
|
+
args: args.clone(),
|
|
49
|
+
});
|
|
50
|
+
let auth_contexts = vec![env, context];
|
|
51
|
+
|
|
52
|
+
let calls = vec![env, Call { to: contract_id.clone(), func: fn_name, args }];
|
|
53
|
+
|
|
54
|
+
(auth_contexts, calls)
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
///
|
|
55
|
-
fn
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
/// Creates upgrade auth contexts (3 entries: upgrader call, upgrade, migrate).
|
|
58
|
+
fn make_upgrade_contexts(
|
|
59
|
+
env: &Env,
|
|
60
|
+
contract_id: &soroban_sdk::Address,
|
|
61
|
+
upgrader_id: &soroban_sdk::Address,
|
|
62
|
+
) -> (Vec<Context>, Vec<Call>) {
|
|
63
|
+
let wasm_hash_val: Val = BytesN::from_array(env, &[0xABu8; 32]).into_val(env);
|
|
64
|
+
let migration_data_val: Val = soroban_sdk::Bytes::new(env).into_val(env);
|
|
65
|
+
|
|
66
|
+
// [0]: Upgrader call
|
|
67
|
+
let upgrader_ctx = Context::Contract(ContractContext {
|
|
68
|
+
contract: upgrader_id.clone(),
|
|
69
|
+
fn_name: Symbol::new(env, "upgrade_and_migrate"),
|
|
70
|
+
args: vec![
|
|
71
|
+
env,
|
|
72
|
+
contract_id.clone().into_val(env),
|
|
73
|
+
wasm_hash_val.clone(),
|
|
74
|
+
migration_data_val.clone(),
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
// [1]: upgrade self-call
|
|
78
|
+
let upgrade_ctx = Context::Contract(ContractContext {
|
|
79
|
+
contract: contract_id.clone(),
|
|
80
|
+
fn_name: Symbol::new(env, "upgrade"),
|
|
81
|
+
args: vec![env, wasm_hash_val],
|
|
82
|
+
});
|
|
83
|
+
// [2]: migrate self-call
|
|
84
|
+
let migrate_ctx = Context::Contract(ContractContext {
|
|
85
|
+
contract: contract_id.clone(),
|
|
86
|
+
fn_name: Symbol::new(env, "migrate"),
|
|
87
|
+
args: vec![env, migration_data_val],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
let auth_contexts = vec![env, upgrader_ctx.clone(), upgrade_ctx.clone(), migrate_ctx.clone()];
|
|
91
|
+
|
|
92
|
+
// Build matching Call vec
|
|
93
|
+
let calls = vec![
|
|
94
|
+
env,
|
|
95
|
+
Call {
|
|
96
|
+
to: upgrader_id.clone(),
|
|
97
|
+
func: Symbol::new(env, "upgrade_and_migrate"),
|
|
98
|
+
args: match &upgrader_ctx {
|
|
99
|
+
Context::Contract(c) => c.args.clone(),
|
|
100
|
+
_ => unreachable!(),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
Call {
|
|
104
|
+
to: contract_id.clone(),
|
|
105
|
+
func: Symbol::new(env, "upgrade"),
|
|
106
|
+
args: match &upgrade_ctx {
|
|
107
|
+
Context::Contract(c) => c.args.clone(),
|
|
108
|
+
_ => unreachable!(),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
Call {
|
|
112
|
+
to: contract_id.clone(),
|
|
113
|
+
func: Symbol::new(env, "migrate"),
|
|
114
|
+
args: match &migrate_ctx {
|
|
115
|
+
Context::Contract(c) => c.args.clone(),
|
|
116
|
+
_ => unreachable!(),
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
(auth_contexts, calls)
|
|
59
122
|
}
|
|
60
123
|
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Single Self-Call Tests
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
61
128
|
#[test]
|
|
62
129
|
fn test_check_auth_success() {
|
|
63
130
|
extern crate std;
|
|
@@ -67,15 +134,14 @@ fn test_check_auth_success() {
|
|
|
67
134
|
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
68
135
|
let env = setup.env.clone();
|
|
69
136
|
let expiration = env.ledger().timestamp() + 1000;
|
|
70
|
-
let auth_contexts
|
|
137
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
71
138
|
|
|
72
|
-
let
|
|
73
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
74
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
139
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
75
140
|
let public_key = admin_kp.public_key(&env);
|
|
76
141
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
77
142
|
|
|
78
|
-
let
|
|
143
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
144
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
79
145
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
80
146
|
|
|
81
147
|
let tx_auth = TransactionAuthData {
|
|
@@ -83,7 +149,6 @@ fn test_check_auth_success() {
|
|
|
83
149
|
expiration,
|
|
84
150
|
signatures: vec![&env, sig],
|
|
85
151
|
sender: Sender::Admin(public_key, signature),
|
|
86
|
-
preimage,
|
|
87
152
|
};
|
|
88
153
|
|
|
89
154
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -104,15 +169,14 @@ fn test_check_auth_not_admin() {
|
|
|
104
169
|
let setup = TestSetup::new(1);
|
|
105
170
|
let env = setup.env.clone();
|
|
106
171
|
let expiration = env.ledger().timestamp() + 1000;
|
|
107
|
-
let auth_contexts
|
|
172
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
108
173
|
|
|
109
|
-
let
|
|
110
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
111
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
174
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
112
175
|
let public_key = non_admin_kp.public_key(&env);
|
|
113
176
|
let signature = non_admin_kp.sign(&env, &payload.to_array());
|
|
114
177
|
|
|
115
|
-
let
|
|
178
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
179
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
116
180
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
117
181
|
|
|
118
182
|
let tx_auth = TransactionAuthData {
|
|
@@ -120,7 +184,6 @@ fn test_check_auth_not_admin() {
|
|
|
120
184
|
expiration,
|
|
121
185
|
signatures: vec![&env, sig],
|
|
122
186
|
sender: Sender::Admin(public_key, signature),
|
|
123
|
-
preimage,
|
|
124
187
|
};
|
|
125
188
|
|
|
126
189
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -142,15 +205,14 @@ fn test_check_auth_wrong_signer_fails() {
|
|
|
142
205
|
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
143
206
|
let env = setup.env.clone();
|
|
144
207
|
let expiration = env.ledger().timestamp() + 1000;
|
|
145
|
-
let auth_contexts
|
|
208
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
146
209
|
|
|
147
|
-
let
|
|
148
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
149
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
210
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
150
211
|
let public_key = admin_kp.public_key(&env);
|
|
151
212
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
152
213
|
|
|
153
|
-
let
|
|
214
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
215
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
154
216
|
let wrong_sig = crate::tests::key_pair::KeyPair::generate().sign_bytes(&env, &hash);
|
|
155
217
|
|
|
156
218
|
let tx_auth = TransactionAuthData {
|
|
@@ -158,9 +220,9 @@ fn test_check_auth_wrong_signer_fails() {
|
|
|
158
220
|
expiration,
|
|
159
221
|
signatures: vec![&env, wrong_sig],
|
|
160
222
|
sender: Sender::Admin(public_key, signature),
|
|
161
|
-
preimage,
|
|
162
223
|
};
|
|
163
224
|
|
|
225
|
+
// verify_signatures panics with MultiSigError::SignerNotFound when signer is not found
|
|
164
226
|
let res = env.try_invoke_contract_check_auth::<utils::errors::MultiSigError>(
|
|
165
227
|
&setup.contract_id,
|
|
166
228
|
&payload,
|
|
@@ -181,15 +243,14 @@ fn test_check_auth_invalid_vid_fails() {
|
|
|
181
243
|
let env = setup.env.clone();
|
|
182
244
|
let expiration = env.ledger().timestamp() + 1000;
|
|
183
245
|
let wrong_vid = VID + 1;
|
|
184
|
-
let auth_contexts
|
|
246
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
185
247
|
|
|
186
|
-
let
|
|
187
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
188
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
248
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
189
249
|
let public_key = admin_kp.public_key(&env);
|
|
190
250
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
191
251
|
|
|
192
|
-
let
|
|
252
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
253
|
+
let hash = dvn_client.hash_call_data(&wrong_vid, &expiration, &calls);
|
|
193
254
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
194
255
|
|
|
195
256
|
let tx_auth = TransactionAuthData {
|
|
@@ -197,7 +258,6 @@ fn test_check_auth_invalid_vid_fails() {
|
|
|
197
258
|
expiration,
|
|
198
259
|
signatures: vec![&env, sig],
|
|
199
260
|
sender: Sender::Admin(public_key, signature),
|
|
200
|
-
preimage,
|
|
201
261
|
};
|
|
202
262
|
|
|
203
263
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -219,15 +279,14 @@ fn test_check_auth_expired_fails() {
|
|
|
219
279
|
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
220
280
|
let env = setup.env.clone();
|
|
221
281
|
let expiration = env.ledger().timestamp();
|
|
222
|
-
let auth_contexts
|
|
282
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
223
283
|
|
|
224
|
-
let
|
|
225
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
226
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
284
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
227
285
|
let public_key = admin_kp.public_key(&env);
|
|
228
286
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
229
287
|
|
|
230
|
-
let
|
|
288
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
289
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
231
290
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
232
291
|
|
|
233
292
|
let tx_auth = TransactionAuthData {
|
|
@@ -235,7 +294,6 @@ fn test_check_auth_expired_fails() {
|
|
|
235
294
|
expiration,
|
|
236
295
|
signatures: vec![&env, sig],
|
|
237
296
|
sender: Sender::Admin(public_key, signature),
|
|
238
|
-
preimage,
|
|
239
297
|
};
|
|
240
298
|
|
|
241
299
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -257,15 +315,14 @@ fn test_check_auth_hash_already_used_fails() {
|
|
|
257
315
|
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
258
316
|
let env = setup.env.clone();
|
|
259
317
|
let expiration = env.ledger().timestamp() + 1000;
|
|
260
|
-
let auth_contexts
|
|
318
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
261
319
|
|
|
262
|
-
let
|
|
263
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
264
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
320
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
265
321
|
let public_key = admin_kp.public_key(&env);
|
|
266
322
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
267
323
|
|
|
268
|
-
let
|
|
324
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
325
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
269
326
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
270
327
|
|
|
271
328
|
let tx_auth = TransactionAuthData {
|
|
@@ -273,7 +330,6 @@ fn test_check_auth_hash_already_used_fails() {
|
|
|
273
330
|
expiration,
|
|
274
331
|
signatures: vec![&env, sig.clone()],
|
|
275
332
|
sender: Sender::Admin(public_key.clone(), signature.clone()),
|
|
276
|
-
preimage: preimage.clone(),
|
|
277
333
|
};
|
|
278
334
|
|
|
279
335
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -289,7 +345,6 @@ fn test_check_auth_hash_already_used_fails() {
|
|
|
289
345
|
expiration,
|
|
290
346
|
signatures: vec![&env, sig],
|
|
291
347
|
sender: Sender::Admin(public_key, signature),
|
|
292
|
-
preimage,
|
|
293
348
|
};
|
|
294
349
|
|
|
295
350
|
let res2 = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -305,21 +360,26 @@ fn test_check_auth_hash_already_used_fails() {
|
|
|
305
360
|
#[test]
|
|
306
361
|
fn test_check_auth_sender_none_fails_when_admin_required() {
|
|
307
362
|
extern crate std;
|
|
363
|
+
use crate::Sender;
|
|
308
364
|
|
|
309
365
|
let setup = TestSetup::new(1);
|
|
310
366
|
let env = setup.env.clone();
|
|
311
367
|
let expiration = env.ledger().timestamp() + 1000;
|
|
312
|
-
let auth_contexts
|
|
368
|
+
let (auth_contexts, calls) = make_self_call_context(&env, &setup.contract_id);
|
|
313
369
|
|
|
314
|
-
let
|
|
315
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
316
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
370
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
317
371
|
|
|
318
|
-
let
|
|
372
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
373
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
319
374
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
320
375
|
|
|
321
|
-
|
|
322
|
-
|
|
376
|
+
// Use Sender::None which should fail when admin is required
|
|
377
|
+
let tx_auth = TransactionAuthData {
|
|
378
|
+
vid: VID,
|
|
379
|
+
expiration,
|
|
380
|
+
signatures: vec![&env, sig],
|
|
381
|
+
sender: Sender::None,
|
|
382
|
+
};
|
|
323
383
|
|
|
324
384
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
325
385
|
&setup.contract_id,
|
|
@@ -334,29 +394,42 @@ fn test_check_auth_sender_none_fails_when_admin_required() {
|
|
|
334
394
|
#[test]
|
|
335
395
|
fn test_check_auth_set_admin_bypasses_admin_verification() {
|
|
336
396
|
extern crate std;
|
|
337
|
-
use
|
|
397
|
+
use crate::Sender;
|
|
338
398
|
|
|
339
399
|
let setup = TestSetup::new(1);
|
|
340
400
|
let env = setup.env.clone();
|
|
341
401
|
let expiration = env.ledger().timestamp() + 1000;
|
|
342
402
|
|
|
403
|
+
// Create a context for set_admin call on the DVN contract
|
|
343
404
|
let set_admin_context = Context::Contract(ContractContext {
|
|
344
405
|
contract: setup.contract_id.clone(),
|
|
345
406
|
fn_name: Symbol::new(&env, "set_admin"),
|
|
346
407
|
args: Vec::new(&env),
|
|
347
408
|
});
|
|
348
|
-
let auth_contexts: Vec<Context> = vec![&env, set_admin_context
|
|
409
|
+
let auth_contexts: Vec<Context> = vec![&env, set_admin_context];
|
|
410
|
+
|
|
411
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
349
412
|
|
|
350
|
-
let
|
|
351
|
-
|
|
352
|
-
|
|
413
|
+
let calls: Vec<Call> = vec![
|
|
414
|
+
&env,
|
|
415
|
+
Call {
|
|
416
|
+
to: setup.contract_id.clone(),
|
|
417
|
+
func: Symbol::new(&env, "set_admin"),
|
|
418
|
+
args: Vec::new(&env),
|
|
419
|
+
},
|
|
420
|
+
];
|
|
353
421
|
|
|
354
422
|
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
355
|
-
let hash = dvn_client.hash_call_data(&VID, &expiration, &
|
|
423
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
356
424
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
357
425
|
|
|
358
|
-
|
|
359
|
-
|
|
426
|
+
// Use Sender::None - should succeed for set_admin since it bypasses admin verification
|
|
427
|
+
let tx_auth = TransactionAuthData {
|
|
428
|
+
vid: VID,
|
|
429
|
+
expiration,
|
|
430
|
+
signatures: vec![&env, sig],
|
|
431
|
+
sender: Sender::None,
|
|
432
|
+
};
|
|
360
433
|
|
|
361
434
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
362
435
|
&setup.contract_id,
|
|
@@ -365,11 +438,60 @@ fn test_check_auth_set_admin_bypasses_admin_verification() {
|
|
|
365
438
|
&auth_contexts,
|
|
366
439
|
);
|
|
367
440
|
|
|
441
|
+
// Should succeed because set_admin bypasses admin verification
|
|
368
442
|
assert!(res.is_ok(), "Expected success for set_admin call, got {:?}", res);
|
|
369
443
|
}
|
|
370
444
|
|
|
371
445
|
#[test]
|
|
372
|
-
fn
|
|
446
|
+
fn test_check_auth_non_contract_context_fails() {
|
|
447
|
+
extern crate std;
|
|
448
|
+
use crate::Sender;
|
|
449
|
+
use soroban_sdk::auth::{ContractExecutable, CreateContractHostFnContext};
|
|
450
|
+
|
|
451
|
+
let admin_kp = Ed25519KeyPair::generate();
|
|
452
|
+
let admin_bytes = admin_kp.public_key_bytes();
|
|
453
|
+
|
|
454
|
+
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
455
|
+
let env = setup.env.clone();
|
|
456
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
457
|
+
|
|
458
|
+
// Create a non-Contract context (CreateContractHostFn)
|
|
459
|
+
let non_contract_context = Context::CreateContractHostFn(CreateContractHostFnContext {
|
|
460
|
+
executable: ContractExecutable::Wasm(BytesN::from_array(&env, &[0; 32])),
|
|
461
|
+
salt: BytesN::from_array(&env, &[0; 32]),
|
|
462
|
+
});
|
|
463
|
+
let auth_contexts: Vec<Context> = vec![&env, non_contract_context];
|
|
464
|
+
|
|
465
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
466
|
+
let public_key = admin_kp.public_key(&env);
|
|
467
|
+
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
468
|
+
|
|
469
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
470
|
+
let dummy_calls: Vec<Call> =
|
|
471
|
+
vec![&env, Call { to: setup.contract_id.clone(), func: Symbol::new(&env, "noop"), args: Vec::new(&env) }];
|
|
472
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &dummy_calls);
|
|
473
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
474
|
+
|
|
475
|
+
let tx_auth = TransactionAuthData {
|
|
476
|
+
vid: VID,
|
|
477
|
+
expiration,
|
|
478
|
+
signatures: vec![&env, sig],
|
|
479
|
+
sender: Sender::Admin(public_key, signature),
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
483
|
+
&setup.contract_id,
|
|
484
|
+
&payload,
|
|
485
|
+
tx_auth.into_val(&env),
|
|
486
|
+
&auth_contexts,
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
// Should fail with NonContractInvoke error
|
|
490
|
+
assert_eq!(res, Err(Ok(DvnError::NonContractInvoke)));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
#[test]
|
|
494
|
+
fn test_check_auth_rejects_empty_contexts() {
|
|
373
495
|
extern crate std;
|
|
374
496
|
let admin_kp = Ed25519KeyPair::generate();
|
|
375
497
|
let admin_bytes = admin_kp.public_key_bytes();
|
|
@@ -379,27 +501,68 @@ fn test_check_auth_invalid_signature_payload_fails() {
|
|
|
379
501
|
let expiration = env.ledger().timestamp() + 1000;
|
|
380
502
|
let auth_contexts: Vec<Context> = Vec::new(&env);
|
|
381
503
|
|
|
382
|
-
let
|
|
383
|
-
// Compute the correct payload so admin signature is valid
|
|
384
|
-
let preimage = build_preimage(&env, AUTH_NONCE, AUTH_EXPIRY_LEDGER, &invocation);
|
|
385
|
-
let payload = compute_signature_payload(&env, &preimage);
|
|
504
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
386
505
|
let public_key = admin_kp.public_key(&env);
|
|
387
506
|
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
388
507
|
|
|
389
|
-
let
|
|
508
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
509
|
+
let dummy_calls: Vec<Call> =
|
|
510
|
+
vec![&env, Call { to: setup.contract_id.clone(), func: Symbol::new(&env, "noop"), args: Vec::new(&env) }];
|
|
511
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &dummy_calls);
|
|
390
512
|
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
391
513
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
514
|
+
let tx_auth = TransactionAuthData {
|
|
515
|
+
vid: VID,
|
|
516
|
+
expiration,
|
|
517
|
+
signatures: vec![&env, sig],
|
|
518
|
+
sender: Sender::Admin(public_key, signature),
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
522
|
+
&setup.contract_id,
|
|
523
|
+
&payload,
|
|
524
|
+
tx_auth.into_val(&env),
|
|
525
|
+
&auth_contexts,
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
assert_eq!(res, Err(Ok(DvnError::InvalidAuthContext)));
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
#[test]
|
|
532
|
+
fn test_check_auth_rejects_external_target() {
|
|
533
|
+
extern crate std;
|
|
534
|
+
let admin_kp = Ed25519KeyPair::generate();
|
|
535
|
+
let admin_bytes = admin_kp.public_key_bytes();
|
|
536
|
+
|
|
537
|
+
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
538
|
+
let env = setup.env.clone();
|
|
539
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
540
|
+
|
|
541
|
+
// Create a context targeting a different contract
|
|
542
|
+
let other_setup = TestSetup::new(1);
|
|
543
|
+
let other_contract = other_setup.contract_id;
|
|
544
|
+
let external_context = Context::Contract(ContractContext {
|
|
545
|
+
contract: other_contract,
|
|
546
|
+
fn_name: Symbol::new(&env, "some_fn"),
|
|
547
|
+
args: Vec::new(&env),
|
|
548
|
+
});
|
|
549
|
+
let auth_contexts: Vec<Context> = vec![&env, external_context];
|
|
550
|
+
|
|
551
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
552
|
+
let public_key = admin_kp.public_key(&env);
|
|
553
|
+
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
554
|
+
|
|
555
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
556
|
+
let dummy_calls: Vec<Call> =
|
|
557
|
+
vec![&env, Call { to: setup.contract_id.clone(), func: Symbol::new(&env, "noop"), args: Vec::new(&env) }];
|
|
558
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &dummy_calls);
|
|
559
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
396
560
|
|
|
397
561
|
let tx_auth = TransactionAuthData {
|
|
398
562
|
vid: VID,
|
|
399
563
|
expiration,
|
|
400
564
|
signatures: vec![&env, sig],
|
|
401
565
|
sender: Sender::Admin(public_key, signature),
|
|
402
|
-
preimage: tampered_preimage,
|
|
403
566
|
};
|
|
404
567
|
|
|
405
568
|
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
@@ -409,5 +572,291 @@ fn test_check_auth_invalid_signature_payload_fails() {
|
|
|
409
572
|
&auth_contexts,
|
|
410
573
|
);
|
|
411
574
|
|
|
412
|
-
assert_eq!(res, Err(Ok(DvnError::
|
|
575
|
+
assert_eq!(res, Err(Ok(DvnError::InvalidAuthContext)));
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ============================================================================
|
|
579
|
+
// set_upgrader Tests
|
|
580
|
+
// ============================================================================
|
|
581
|
+
|
|
582
|
+
#[test]
|
|
583
|
+
fn test_set_upgrader_requires_admin() {
|
|
584
|
+
extern crate std;
|
|
585
|
+
use crate::Sender;
|
|
586
|
+
|
|
587
|
+
let setup = TestSetup::new(1);
|
|
588
|
+
let env = setup.env.clone();
|
|
589
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
590
|
+
|
|
591
|
+
let set_upgrader_context = Context::Contract(ContractContext {
|
|
592
|
+
contract: setup.contract_id.clone(),
|
|
593
|
+
fn_name: Symbol::new(&env, "set_upgrader"),
|
|
594
|
+
args: Vec::new(&env),
|
|
595
|
+
});
|
|
596
|
+
let auth_contexts: Vec<Context> = vec![&env, set_upgrader_context];
|
|
597
|
+
|
|
598
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
599
|
+
|
|
600
|
+
let calls: Vec<Call> = vec![
|
|
601
|
+
&env,
|
|
602
|
+
Call {
|
|
603
|
+
to: setup.contract_id.clone(),
|
|
604
|
+
func: Symbol::new(&env, "set_upgrader"),
|
|
605
|
+
args: Vec::new(&env),
|
|
606
|
+
},
|
|
607
|
+
];
|
|
608
|
+
|
|
609
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
610
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
611
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
612
|
+
|
|
613
|
+
let tx_auth = TransactionAuthData {
|
|
614
|
+
vid: VID,
|
|
615
|
+
expiration,
|
|
616
|
+
signatures: vec![&env, sig],
|
|
617
|
+
sender: Sender::None,
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
621
|
+
&setup.contract_id,
|
|
622
|
+
&payload,
|
|
623
|
+
tx_auth.into_val(&env),
|
|
624
|
+
&auth_contexts,
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
assert_eq!(res, Err(Ok(DvnError::OnlyAdmin)));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ============================================================================
|
|
631
|
+
// Upgrade Path Tests
|
|
632
|
+
// ============================================================================
|
|
633
|
+
|
|
634
|
+
#[test]
|
|
635
|
+
fn test_check_auth_upgrade_success() {
|
|
636
|
+
extern crate std;
|
|
637
|
+
|
|
638
|
+
let admin_kp = Ed25519KeyPair::generate();
|
|
639
|
+
let admin_bytes = admin_kp.public_key_bytes();
|
|
640
|
+
|
|
641
|
+
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
642
|
+
let env = setup.env.clone();
|
|
643
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
644
|
+
|
|
645
|
+
let upgrader_id = Address::generate(&env);
|
|
646
|
+
env.as_contract(&setup.contract_id, || {
|
|
647
|
+
crate::storage::DvnStorage::set_upgrader(&env, &upgrader_id);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
let (auth_contexts, calls) = make_upgrade_contexts(&env, &setup.contract_id, &upgrader_id);
|
|
651
|
+
|
|
652
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
653
|
+
let public_key = admin_kp.public_key(&env);
|
|
654
|
+
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
655
|
+
|
|
656
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
657
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
658
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
659
|
+
|
|
660
|
+
let tx_auth = TransactionAuthData {
|
|
661
|
+
vid: VID,
|
|
662
|
+
expiration,
|
|
663
|
+
signatures: vec![&env, sig],
|
|
664
|
+
sender: Sender::Admin(public_key, signature),
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
668
|
+
&setup.contract_id,
|
|
669
|
+
&payload,
|
|
670
|
+
tx_auth.into_val(&env),
|
|
671
|
+
&auth_contexts,
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
assert!(res.is_ok(), "Expected success for upgrade, got {:?}", res);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
#[test]
|
|
678
|
+
fn test_check_auth_upgrade_no_upgrader_set() {
|
|
679
|
+
extern crate std;
|
|
680
|
+
|
|
681
|
+
let setup = TestSetup::new(1);
|
|
682
|
+
let env = setup.env.clone();
|
|
683
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
684
|
+
|
|
685
|
+
// Don't register any upgrader
|
|
686
|
+
let fake_upgrader = TestSetup::new(1).contract_id;
|
|
687
|
+
let (auth_contexts, calls) = make_upgrade_contexts(&env, &setup.contract_id, &fake_upgrader);
|
|
688
|
+
|
|
689
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
690
|
+
|
|
691
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
692
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
693
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
694
|
+
|
|
695
|
+
let tx_auth = TransactionAuthData {
|
|
696
|
+
vid: VID,
|
|
697
|
+
expiration,
|
|
698
|
+
signatures: vec![&env, sig],
|
|
699
|
+
sender: Sender::None,
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
703
|
+
&setup.contract_id,
|
|
704
|
+
&payload,
|
|
705
|
+
tx_auth.into_val(&env),
|
|
706
|
+
&auth_contexts,
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
assert_eq!(res, Err(Ok(DvnError::UpgraderNotSet)));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
#[test]
|
|
713
|
+
fn test_check_auth_upgrade_wrong_upgrader() {
|
|
714
|
+
extern crate std;
|
|
715
|
+
|
|
716
|
+
let setup = TestSetup::new(1);
|
|
717
|
+
let env = setup.env.clone();
|
|
718
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
719
|
+
|
|
720
|
+
// Register one upgrader
|
|
721
|
+
let registered_upgrader = Address::generate(&env);
|
|
722
|
+
env.as_contract(&setup.contract_id, || {
|
|
723
|
+
crate::storage::DvnStorage::set_upgrader(&env, ®istered_upgrader);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
// But use a different upgrader in the auth contexts
|
|
727
|
+
let wrong_upgrader = Address::generate(&env);
|
|
728
|
+
let (auth_contexts, calls) = make_upgrade_contexts(&env, &setup.contract_id, &wrong_upgrader);
|
|
729
|
+
|
|
730
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
731
|
+
|
|
732
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
733
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
734
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
735
|
+
|
|
736
|
+
let tx_auth = TransactionAuthData {
|
|
737
|
+
vid: VID,
|
|
738
|
+
expiration,
|
|
739
|
+
signatures: vec![&env, sig],
|
|
740
|
+
sender: Sender::None,
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
744
|
+
&setup.contract_id,
|
|
745
|
+
&payload,
|
|
746
|
+
tx_auth.into_val(&env),
|
|
747
|
+
&auth_contexts,
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
assert_eq!(res, Err(Ok(DvnError::InvalidUpgradeContext)));
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
#[test]
|
|
754
|
+
fn test_check_auth_upgrade_missing_migrate() {
|
|
755
|
+
extern crate std;
|
|
756
|
+
|
|
757
|
+
let setup = TestSetup::new(1);
|
|
758
|
+
let env = setup.env.clone();
|
|
759
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
760
|
+
|
|
761
|
+
let upgrader_id = TestSetup::new(1).contract_id;
|
|
762
|
+
env.as_contract(&setup.contract_id, || {
|
|
763
|
+
crate::storage::DvnStorage::set_upgrader(&env, &upgrader_id);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Only 2 contexts: upgrader + upgrade (missing migrate) → InvalidAuthContext (len != 1 and != 3)
|
|
767
|
+
let upgrader_ctx = Context::Contract(ContractContext {
|
|
768
|
+
contract: upgrader_id.clone(),
|
|
769
|
+
fn_name: Symbol::new(&env, "upgrade_and_migrate"),
|
|
770
|
+
args: Vec::new(&env),
|
|
771
|
+
});
|
|
772
|
+
let upgrade_ctx = Context::Contract(ContractContext {
|
|
773
|
+
contract: setup.contract_id.clone(),
|
|
774
|
+
fn_name: Symbol::new(&env, "upgrade"),
|
|
775
|
+
args: Vec::new(&env),
|
|
776
|
+
});
|
|
777
|
+
let auth_contexts: Vec<Context> = vec![&env, upgrader_ctx, upgrade_ctx];
|
|
778
|
+
|
|
779
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
780
|
+
let dummy_calls: Vec<Call> =
|
|
781
|
+
vec![&env, Call { to: setup.contract_id.clone(), func: Symbol::new(&env, "noop"), args: Vec::new(&env) }];
|
|
782
|
+
|
|
783
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
784
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &dummy_calls);
|
|
785
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
786
|
+
|
|
787
|
+
let tx_auth = TransactionAuthData {
|
|
788
|
+
vid: VID,
|
|
789
|
+
expiration,
|
|
790
|
+
signatures: vec![&env, sig],
|
|
791
|
+
sender: Sender::None,
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
795
|
+
&setup.contract_id,
|
|
796
|
+
&payload,
|
|
797
|
+
tx_auth.into_val(&env),
|
|
798
|
+
&auth_contexts,
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
// 2 contexts is neither 1 (single-call) nor 3 (upgrade), so InvalidAuthContext
|
|
802
|
+
assert_eq!(res, Err(Ok(DvnError::InvalidAuthContext)));
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
#[test]
|
|
806
|
+
fn test_check_auth_upgrade_replay_protection() {
|
|
807
|
+
extern crate std;
|
|
808
|
+
|
|
809
|
+
let admin_kp = Ed25519KeyPair::generate();
|
|
810
|
+
let admin_bytes = admin_kp.public_key_bytes();
|
|
811
|
+
|
|
812
|
+
let setup = TestSetup::with_admin_bytes(1, std::vec![admin_bytes]);
|
|
813
|
+
let env = setup.env.clone();
|
|
814
|
+
let expiration = env.ledger().timestamp() + 1000;
|
|
815
|
+
|
|
816
|
+
let upgrader_id = Address::generate(&env);
|
|
817
|
+
env.as_contract(&setup.contract_id, || {
|
|
818
|
+
crate::storage::DvnStorage::set_upgrader(&env, &upgrader_id);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
let (auth_contexts, calls) = make_upgrade_contexts(&env, &setup.contract_id, &upgrader_id);
|
|
822
|
+
|
|
823
|
+
let payload = BytesN::from_array(&env, &[0u8; 32]);
|
|
824
|
+
let public_key = admin_kp.public_key(&env);
|
|
825
|
+
let signature = admin_kp.sign(&env, &payload.to_array());
|
|
826
|
+
|
|
827
|
+
let dvn_client = LzDVNClient::new(&env, &setup.contract_id);
|
|
828
|
+
let hash = dvn_client.hash_call_data(&VID, &expiration, &calls);
|
|
829
|
+
let sig = setup.key_pairs[0].sign_bytes(&env, &hash);
|
|
830
|
+
|
|
831
|
+
let tx_auth = TransactionAuthData {
|
|
832
|
+
vid: VID,
|
|
833
|
+
expiration,
|
|
834
|
+
signatures: vec![&env, sig.clone()],
|
|
835
|
+
sender: Sender::Admin(public_key.clone(), signature.clone()),
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
let res = env.try_invoke_contract_check_auth::<DvnError>(
|
|
839
|
+
&setup.contract_id,
|
|
840
|
+
&payload,
|
|
841
|
+
tx_auth.into_val(&env),
|
|
842
|
+
&auth_contexts,
|
|
843
|
+
);
|
|
844
|
+
assert!(res.is_ok());
|
|
845
|
+
|
|
846
|
+
// Second attempt with same data should fail
|
|
847
|
+
let tx_auth2 = TransactionAuthData {
|
|
848
|
+
vid: VID,
|
|
849
|
+
expiration,
|
|
850
|
+
signatures: vec![&env, sig],
|
|
851
|
+
sender: Sender::Admin(public_key, signature),
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
let res2 = env.try_invoke_contract_check_auth::<DvnError>(
|
|
855
|
+
&setup.contract_id,
|
|
856
|
+
&payload,
|
|
857
|
+
tx_auth2.into_val(&env),
|
|
858
|
+
&auth_contexts,
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
assert_eq!(res2, Err(Ok(DvnError::HashAlreadyUsed)));
|
|
413
862
|
}
|