5b9c6ad4 Andy McCall 1 month ago ↶
1
2
750251b2 Andy McCall 1 month ago ↶
3
4
5
6
7
5b9c6ad4 Andy McCall 1 month ago ↶
8
9
use crate :: attest:: Attest;
750251b2 Andy McCall 1 month ago ↶
10
use crate :: cbor:: { self , Value} ;
5b9c6ad4 Andy McCall 1 month ago ↶
11
use crate :: hash:: { ChangeHash, ObjectRef, ObjectType} ;
12
use crate :: identity:: Signature;
750251b2 Andy McCall 1 month ago ↶
13
use crate :: objects:: { self as o, Change, ObjectError} ;
83e0677b Andy McCall 1 month ago ↶
14
use crate :: policy:: Policy;
5b9c6ad4 Andy McCall 1 month ago ↶
15
use crate :: repo:: { Repo, RepoError, Result } ;
2b5ee371 Andy McCall 1 month ago ↶
16
use std:: path:: { Path, PathBuf} ;
9f9e72b9 Andy McCall 1 month ago ↶
17
use std:: sync:: { Arc, Mutex} ;
5b9c6ad4 Andy McCall 1 month ago ↶
18
750251b2 Andy McCall 1 month ago ↶
19
pub const CONTENT_TYPE : & str = " application/oxy-cbor" ;
20
pub const PROTOCOL_VERSION : u64 = 1 ;
21
5b9c6ad4 Andy McCall 1 month ago ↶
22
23
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
24
pub struct SignedAttest {
25
pub attest : Attest,
26
pub sigs : Vec< Signature> ,
27
}
28
83e0677b Andy McCall 1 month ago ↶
29
30
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
31
pub struct SignedPolicy {
32
pub policy : Policy,
33
pub sigs : Vec< Signature> ,
34
}
35
28fc23d7 Andy McCall 1 month ago ↶
36
impl SignedPolicy {
37
38
pub fn encode ( & self ) -> Vec< u8 > {
39
crate :: cbor:: encode( & wire:: policy_value( self ) )
40
}
41
pub fn decode ( b : & [u8 ]) -> Result< SignedPolicy> {
75167672 Andy McCall 1 month ago
42
wire:: policy_from(
43
& crate :: cbor:: decode( b) .map_err ( | e | RepoError:: Object( ObjectError:: from( e) ) ) ? ,
44
)
28fc23d7 Andy McCall 1 month ago ↶
45
}
46
}
47
132b884b Andy McCall 1 month ago ↶
48
83e0677b Andy McCall 1 month ago ↶
49
# [ derive ( Clone, Debug, Default ) ]
50
pub struct RefsResponse {
51
pub refs : Vec< RemoteRef> ,
52
pub policy : Option< SignedPolicy> ,
132b884b Andy McCall 1 month ago ↶
53
pub tags : Vec< TagRef> ,
28fc23d7 Andy McCall 1 month ago ↶
54
55
56
pub history : Vec< SignedPolicy> ,
14edbaaf Andy McCall 1 month ago ↶
57
58
pub default_bookmark : Option< String> ,
4b9d0677 Andy McCall 18 days ago ↶
59
60
61
62
pub log : Option< String> ,
028545df Andy McCall 17 days ago ↶
63
64
65
66
pub witnesses : Vec< ( String, Vec< u8 > ) > ,
b63fc7a7 Andy McCall 11 days ago ↶
67
68
69
70
71
pub withdrawn : Vec< TagRef> ,
83e0677b Andy McCall 1 month ago ↶
72
}
73
4b9d0677 Andy McCall 18 days ago ↶
74
75
76
77
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
78
pub struct Logged {
79
pub index : u64 ,
80
pub entry : Vec< u8 > ,
81
pub proof : Vec< [ u8 ; 32 ] > ,
82
}
83
028545df Andy McCall 17 days ago ↶
84
85
86
87
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
88
pub struct Integration {
89
pub note : String,
90
pub proof : Vec< [ u8 ; 32 ] > ,
91
}
92
132b884b Andy McCall 1 month ago ↶
93
94
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
95
pub struct TagRef {
96
pub name : String,
97
pub state : ObjectRef,
98
pub heads : Vec< ChangeHash> ,
99
pub attestation : Option< SignedAttest> ,
1ec6fdbc Andy McCall 15 days ago ↶
100
101
102
103
pub accepted : Option< SignedAttest> ,
104
105
106
pub logged : Option< Logged> ,
107
108
109
pub integration : Vec< Integration> ,
110
}
111
112
impl TagRef {
b63fc7a7 Andy McCall 11 days ago ↶
113
114
115
pub fn is_withdrawal ( & self ) -> bool {
116
self .attestation
117
.as_ref ( )
118
.is_some_and ( | a | matches! ( a.attest.body, crate :: attest:: AttestBody:: Untag { .. } ) )
119
}
120
1ec6fdbc Andy McCall 15 days ago ↶
121
122
pub fn new (
123
name : String,
124
state : ObjectRef,
125
heads : Vec< ChangeHash> ,
126
attestation : Option< SignedAttest> ,
127
) -> TagRef {
128
TagRef {
129
name,
130
state,
131
heads,
132
attestation,
133
accepted: None ,
134
logged: None ,
135
integration: vec! [ ] ,
136
}
137
}
132b884b Andy McCall 1 month ago ↶
138
}
139
5b9c6ad4 Andy McCall 1 month ago ↶
140
141
# [ derive ( Clone, Debug ) ]
142
pub struct RemoteRef {
143
pub name : String,
144
pub state : ObjectRef,
145
pub heads : Vec< ChangeHash> ,
4161262e Andy McCall 1 month ago ↶
146
147
5b9c6ad4 Andy McCall 1 month ago ↶
148
pub attestation : Option< SignedAttest> ,
4161262e Andy McCall 1 month ago ↶
149
150
151
pub last_serial : Option< u64 > ,
f53cbe7a Andy McCall 18 days ago ↶
152
153
154
pub accepted : Option< SignedAttest> ,
4b9d0677 Andy McCall 18 days ago ↶
155
156
157
pub logged : Option< Logged> ,
028545df Andy McCall 17 days ago ↶
158
159
160
pub integration : Vec< Integration> ,
5b9c6ad4 Andy McCall 1 month ago ↶
161
}
162
163
164
# [ derive ( Clone, Debug, Default ) ]
165
pub struct Bundle {
166
167
pub changes : Vec< ( Change, Vec< Signature> ) > ,
168
169
pub attests : Vec< SignedAttest> ,
170
171
pub objects : Vec< ( ObjectType, Vec< u8 > ) > ,
28fc23d7 Andy McCall 1 month ago ↶
172
173
174
pub policies : Vec< SignedPolicy> ,
598009a7 Andy McCall 1 month ago ↶
175
176
177
178
pub hashes : Vec< ChangeHash> ,
2c75672f Andy McCall 1 month ago ↶
179
180
181
182
pub raw_changes : Vec< ( Vec< u8 > , Vec< Signature> ) > ,
f0a517d2 Andy McCall 18 days ago ↶
183
184
185
pub certificates : Vec< crate:: cert:: SignedCertificate> ,
5b9c6ad4 Andy McCall 1 month ago ↶
186
}
187
188
impl Bundle {
189
pub fn is_empty ( & self ) -> bool {
190
self .changes.is_empty ( ) & & self .attests.is_empty ( ) & & self .objects.is_empty ( )
191
}
192
}
193
194
195
# [ derive ( Clone, Debug ) ]
196
pub struct RefUpdate {
197
pub name : String,
198
pub old_state : ObjectRef,
199
pub new_state : ObjectRef,
200
pub force : bool ,
201
pub attestation : Option< SignedAttest> ,
202
}
203
204
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
205
pub struct RefResult {
206
pub name : String,
207
pub ok : bool ,
208
pub reason : String,
209
}
210
211
pub trait Remote {
212
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > ;
83e0677b Andy McCall 1 month ago ↶
213
fn refs ( & self ) -> Result< RefsResponse> ;
5b9c6ad4 Andy McCall 1 month ago ↶
214
215
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > ;
b3c5a59a Andy McCall 1 month ago ↶
216
217
218
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> ;
219
220
221
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > ;
2863456a Andy McCall 1 month ago ↶
222
223
224
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> ;
80b069fa Andy McCall 9 days ago ↶
225
226
227
228
229
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
230
self .checkpoint ( heads)
231
}
2863456a Andy McCall 1 month ago ↶
232
233
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > ;
132b884b Andy McCall 1 month ago ↶
234
235
236
75167672 Andy McCall 1 month ago
237
fn push (
238
& self ,
239
bundle : Bundle,
240
updates : Vec< RefUpdate> ,
241
policy : Option< SignedPolicy> ,
242
tags : Vec< TagRef> ,
243
) -> Result< Vec< RefResult> > ;
5b9c6ad4 Andy McCall 1 month ago ↶
244
}
245
750251b2 Andy McCall 1 month ago ↶
246
247
5b9c6ad4 Andy McCall 1 month ago ↶
248
249
pub struct LocalRemote {
250
pub repo : Repo,
84b9e9ac Andy McCall 1 month ago ↶
251
252
253
pub claimed_id : Option< [ u8 ; 16 ] > ,
5b9c6ad4 Andy McCall 1 month ago ↶
254
}
255
256
impl LocalRemote {
257
pub fn open ( path : & Path) -> Result< LocalRemote> {
75167672 Andy McCall 1 month ago
258
Ok ( LocalRemote {
259
repo: Repo:: open( path) ? ,
84b9e9ac Andy McCall 1 month ago ↶
260
claimed_id: None ,
75167672 Andy McCall 1 month ago
261
} )
5b9c6ad4 Andy McCall 1 month ago ↶
262
}
263
}
264
2f0b4b06 Andy McCall 4 days ago ↶
265
266
267
# [ cfg ( test ) ]
268
thread_local! {
269
pub ( crate ) static LOCAL_FETCHES : std:: cell:: Cell< usize > = const { std:: cell:: Cell:: new( 0 ) } ;
270
}
271
5b9c6ad4 Andy McCall 1 month ago ↶
272
impl Remote for LocalRemote {
273
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
274
self .repo.id ( )
275
}
83e0677b Andy McCall 1 month ago ↶
276
fn refs ( & self ) -> Result< RefsResponse> {
5b9c6ad4 Andy McCall 1 month ago ↶
277
self .repo.remote_refs ( )
278
}
279
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
280
self .repo.negotiate ( have, want)
281
}
b3c5a59a Andy McCall 1 month ago ↶
282
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
2f0b4b06 Andy McCall 4 days ago ↶
283
# [ cfg ( test ) ]
284
LOCAL_FETCHES .with ( | n | n.set ( n.get ( ) + 1 ) ) ;
2c75672f Andy McCall 1 month ago ↶
285
self .repo.bundle_raw_for ( changes, max_blob)
b3c5a59a Andy McCall 1 month ago ↶
286
}
287
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
288
self .repo.objects_for ( refs)
5b9c6ad4 Andy McCall 1 month ago ↶
289
}
2863456a Andy McCall 1 month ago ↶
290
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
291
self .repo.checkpoint_bundle_for ( heads)
80b069fa Andy McCall 9 days ago ↶
292
}
293
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
294
self .repo.checkpoint_index_for ( heads)
2863456a Andy McCall 1 month ago ↶
295
}
296
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
297
self .repo.shallow_changes ( heads, depth)
298
}
75167672 Andy McCall 1 month ago
299
fn push (
300
& self ,
301
bundle : Bundle,
302
updates : Vec< RefUpdate> ,
303
policy : Option< SignedPolicy> ,
304
tags : Vec< TagRef> ,
305
) -> Result< Vec< RefResult> > {
84b9e9ac Andy McCall 1 month ago ↶
306
if self .repo.is_unbound ( ) {
307
match self .claimed_id {
308
Some ( id) => self .repo.bind_id ( id) ? ,
eae59556 Andy McCall 1 month ago ↶
309
None => {
310
return Err ( RepoError:: Msg(
311
" the remote is unbound and this client did not say which repository it is"
312
.into ( ) ,
313
) )
314
}
84b9e9ac Andy McCall 1 month ago ↶
315
}
316
}
132b884b Andy McCall 1 month ago ↶
317
self .repo.receive_push ( bundle, updates, policy, tags)
5b9c6ad4 Andy McCall 1 month ago ↶
318
}
319
}
320
750251b2 Andy McCall 1 month ago ↶
321
322
2b5ee371 Andy McCall 1 month ago ↶
323
324
# [ derive ( Clone, Debug, Default ) ]
325
pub struct RemoteOptions {
326
327
pub token : Option< String> ,
328
329
330
pub ca : Option< PathBuf> ,
75167672 Andy McCall 1 month ago
331
332
333
pub repo_id : Option< [ u8 ; 16 ] > ,
9f9e72b9 Andy McCall 1 month ago ↶
334
335
336
337
338
pub cache_dir : Option< PathBuf> ,
68a92484 Andy McCall 21 days ago ↶
339
569802eb Andy McCall 7 days ago ↶
340
341
342
68a92484 Andy McCall 21 days ago ↶
343
pub fallback_tokens : Vec< String> ,
0588de69 Andy McCall 7 days ago ↶
344
345
346
347
348
pub fallback_on_401_only : bool ,
2b5ee371 Andy McCall 1 month ago ↶
349
}
350
9f9e72b9 Andy McCall 1 month ago ↶
351
352
353
354
pub type StoreOpener =
355
fn ( & str , & RemoteOptions) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > ;
356
357
static STORE_OPENERS : Mutex< Vec< StoreOpener> > = Mutex:: new( Vec :: new( ) ) ;
358
359
pub fn register_store_opener ( opener : StoreOpener) {
360
STORE_OPENERS
361
.lock ( )
362
.unwrap_or_else ( | p | p.into_inner ( ) )
363
.push ( opener) ;
364
}
365
366
367
pub fn open_store (
368
url : & str ,
369
options : & RemoteOptions,
370
) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > {
371
if let Some ( path) = url.strip_prefix ( " dir://" ) {
372
return Some (
373
crate :: strata:: LocalStore:: open( path)
374
.map ( | s | Box :: new( s) as Box< dyn crate:: strata:: Store> ) ,
375
) ;
376
}
377
let openers = STORE_OPENERS .lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) ;
378
openers.iter ( ) .find_map ( | open | open ( url, options) )
379
}
380
75167672 Andy McCall 1 month ago
381
382
pub const REPO_ID_HEADER : & str = " Oxy-Repo-Id" ;
383
384
385
pub const UNBOUND_ID : [ u8 ; 16 ] = [ 0 ; 16 ] ;
386
ee130dd9 Andy McCall 15 days ago ↶
387
388
389
390
391
pub const FEATURES_HEADER : & str = " Oxy-Features" ;
392
393
394
395
396
397
pub const FEATURE_CERTIFIED_TAGS : & str = " certified-tags" ;
398
399
400
pub const CLIENT_FEATURES : & [ & str ] = & [ FEATURE_CERTIFIED_TAGS ] ;
401
402
403
pub fn user_agent ( ) -> String {
404
format! ( " oxy/{} " , env! ( " CARGO_PKG_VERSION" ) )
405
}
406
407
408
409
410
pub fn declares_feature ( header : Option< & str > , feature : & str ) -> bool {
411
header.is_some_and ( | h | h.split ( ' ,' ) .any ( | f | f.trim ( ) .eq_ignore_ascii_case ( feature) ) )
412
}
413
750251b2 Andy McCall 1 month ago ↶
414
415
pub struct HttpRemote {
416
base : String,
68a92484 Andy McCall 21 days ago ↶
417
418
token : Mutex< Option< String> > ,
419
fallbacks : Mutex< Vec< String> > ,
0588de69 Andy McCall 7 days ago ↶
420
fallback_on_401_only : bool ,
75167672 Andy McCall 1 month ago
421
repo_id : Option< [ u8 ; 16 ] > ,
750251b2 Andy McCall 1 month ago ↶
422
agent : ureq:: Agent,
423
}
424
11c95802 Andy McCall 1 month ago ↶
425
426
427
pub fn tls_client_config ( ca : Option< & Path> ) -> Result< Arc< rustls:: ClientConfig> > {
2b5ee371 Andy McCall 1 month ago ↶
428
let mut roots = rustls:: RootCertStore:: empty( ) ;
429
roots.extend ( webpki_roots:: TLS_SERVER_ROOTS .iter ( ) .cloned ( ) ) ;
430
if let Some ( path) = ca {
75167672 Andy McCall 1 month ago
431
let pem = std:: fs:: read( path)
432
.map_err ( | e | RepoError:: Msg( format! ( " reading CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
433
let certs = rustls_pemfile:: certs( & mut pem.as_slice ( ) )
434
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( )
435
.map_err ( | e | RepoError:: Msg( format! ( " parsing CA file {} : {e} " , path.display ( ) ) ) ) ? ;
436
if certs.is_empty ( ) {
75167672 Andy McCall 1 month ago
437
return Err ( RepoError:: Msg( format! (
438
" CA file {} holds no certificates" ,
439
path.display ( )
440
) ) ) ;
2b5ee371 Andy McCall 1 month ago ↶
441
}
442
for c in certs {
75167672 Andy McCall 1 month ago
443
roots
444
.add ( c)
445
.map_err ( | e | RepoError:: Msg( format! ( " CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
446
}
447
}
75167672 Andy McCall 1 month ago
448
let cfg = rustls:: ClientConfig:: builder_with_provider( Arc:: new(
449
rustls:: crypto:: ring:: default_provider( ) ,
450
) )
451
.with_safe_default_protocol_versions ( )
452
.map_err ( | e | RepoError:: Msg( e.to_string ( ) ) ) ?
453
.with_root_certificates ( roots)
454
.with_no_client_auth ( ) ;
2b5ee371 Andy McCall 1 month ago ↶
455
Ok ( Arc:: new( cfg) )
456
}
457
750251b2 Andy McCall 1 month ago ↶
458
impl HttpRemote {
2b5ee371 Andy McCall 1 month ago ↶
459
pub fn new ( base : & str , options : & RemoteOptions) -> Result< HttpRemote> {
ff1f62fe Andy McCall 1 month ago ↶
460
refuse_plain_token ( base, options) ? ;
75167672 Andy McCall 1 month ago
461
let agent = ureq:: AgentBuilder:: new( )
462
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
ee130dd9 Andy McCall 15 days ago ↶
463
.user_agent ( & user_agent ( ) )
75167672 Andy McCall 1 month ago
464
.build ( ) ;
465
Ok ( HttpRemote {
466
base: base.trim_end_matches ( ' /' ) .to_string ( ) ,
68a92484 Andy McCall 21 days ago ↶
467
token: Mutex:: new( options.token.clone ( ) ) ,
468
fallbacks: Mutex:: new( options.fallback_tokens.iter ( ) .rev ( ) .cloned ( ) .collect ( ) ) ,
0588de69 Andy McCall 7 days ago ↶
469
fallback_on_401_only: options.fallback_on_401_only,
75167672 Andy McCall 1 month ago
470
repo_id: options.repo_id,
471
agent,
472
} )
750251b2 Andy McCall 1 month ago ↶
473
}
474
68a92484 Andy McCall 21 days ago ↶
475
476
750251b2 Andy McCall 1 month ago ↶
477
fn call ( & self , post : bool , endpoint : & str , body : Option< & [ u8 ] > ) -> Result< Vec< u8 > > {
68a92484 Andy McCall 21 days ago ↶
478
loop {
479
let token = self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) .clone ( ) ;
480
match self .call_once ( post, endpoint, body, token.as_deref ( ) ) {
0588de69 Andy McCall 7 days ago ↶
481
Err ( RepoError:: Msg( m) )
482
if token.is_some ( )
483
& & refused ( & m)
484
& & ( ! self .fallback_on_401_only | | m.contains ( " : HTTP 401" ) ) =>
485
{
68a92484 Andy McCall 21 days ago ↶
486
let next = self
487
.fallbacks
488
.lock ( )
489
.unwrap_or_else ( | p | p.into_inner ( ) )
490
.pop ( ) ;
491
let Some ( next) = next else {
492
return Err ( RepoError:: Msg( m) ) ;
493
} ;
494
crate :: credentials:: warn_once( & format! (
569802eb Andy McCall 7 days ago ↶
495
" {} : the token in use was refused; trying another this computer has for the hub \
496
(a stale remote.NAME.token goes with `oxy config --unset remote.NAME.token`)" ,
68a92484 Andy McCall 21 days ago ↶
497
self .base
498
) ) ;
499
* self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) = Some ( next) ;
500
}
501
other => return other,
502
}
503
}
504
}
505
506
fn call_once (
507
& self ,
508
post : bool ,
509
endpoint : & str ,
510
body : Option< & [ u8 ] > ,
511
token : Option< & str > ,
512
) -> Result< Vec< u8 > > {
750251b2 Andy McCall 1 month ago ↶
513
let url = format! ( " {} /oxy/v1/{endpoint} " , self .base) ;
75167672 Andy McCall 1 month ago
514
let mut req = if post {
515
self .agent.post ( & url)
516
} else {
517
self .agent.get ( & url)
518
} ;
ee130dd9 Andy McCall 15 days ago ↶
519
req = req
520
.set ( " Accept" , CONTENT_TYPE )
521
.set ( FEATURES_HEADER , & CLIENT_FEATURES .join ( " ," ) ) ;
68a92484 Andy McCall 21 days ago ↶
522
if let Some ( t) = token {
750251b2 Andy McCall 1 month ago ↶
523
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
524
}
75167672 Andy McCall 1 month ago
525
if let Some ( id) = & self .repo_id {
526
req = req.set ( REPO_ID_HEADER , & hex:: encode( id) ) ;
527
}
750251b2 Andy McCall 1 month ago ↶
528
let resp = match body {
529
Some ( b) => req.set ( " Content-Type" , CONTENT_TYPE ) .send_bytes ( b) ,
530
None => req.call ( ) ,
531
} ;
532
match resp {
533
Ok ( r) => {
534
let mut out = Vec :: new( ) ;
75167672 Andy McCall 1 month ago
535
r.into_reader ( )
536
.read_to_end ( & mut out)
537
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
538
Ok ( out)
539
}
540
Err ( ureq:: Error:: Status( code, r) ) => {
541
let text = r.into_string ( ) .unwrap_or_default ( ) ;
67a4fe1a Andy McCall 16 days ago ↶
542
543
d3b27090 Andy McCall 11 days ago ↶
544
545
546
67a4fe1a Andy McCall 16 days ago ↶
547
let status = format! ( " {url} : HTTP {code} " ) ;
d3b27090 Andy McCall 11 days ago ↶
548
let msg = format! ( " {status} : {} " , plain_error_text ( & text) ) ;
67a4fe1a Andy McCall 16 days ago ↶
549
Err ( RepoError:: Msg( match hint_for ( & status) {
550
Some ( h) => format! ( " {msg} \n hint: {h} " ) ,
551
None => msg,
552
} ) )
750251b2 Andy McCall 1 month ago ↶
553
}
3e5772cc Andy McCall 1 month ago ↶
554
555
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
750251b2 Andy McCall 1 month ago ↶
556
}
557
}
68a92484 Andy McCall 21 days ago ↶
558
}
559
560
561
562
563
fn refused ( message : & str ) -> bool {
564
[ " : HTTP 401" , " : HTTP 403" , " : HTTP 404" ]
565
.iter ( )
566
.any ( | c | message.contains ( c) )
750251b2 Andy McCall 1 month ago ↶
567
}
568
ff1f62fe Andy McCall 1 month ago ↶
569
570
571
fn refuse_plain_token ( url : & str , options : & RemoteOptions) -> Result< ( ) > {
572
if options.token.is_some ( ) & & ! crate :: credentials:: token_allowed( url) {
573
return Err ( RepoError:: Msg( format! (
574
" {url} : refusing to send a token over plain http; use https"
575
) ) ) ;
576
}
577
Ok ( ( ) )
578
}
579
3e5772cc Andy McCall 1 month ago ↶
580
581
pub fn hint_for ( message : & str ) -> Option< & 'static str > {
582
let m = message.to_ascii_lowercase ( ) ;
583
let hint = if m.contains ( " http 401" ) {
ff1f62fe Andy McCall 1 month ago ↶
584
" the hub needs you signed in, or did not accept the token: run `oxy auth login HUB` (pushing needs repo:write)"
3e5772cc Andy McCall 1 month ago ↶
585
} else if m.contains ( " http 403" ) {
586
" the token or account lacks permission: pushing needs a token with repo:write and write access to the repository"
d3b27090 Andy McCall 11 days ago ↶
587
} else if m.contains ( " http 413" ) {
588
" the request was larger than the server, or a proxy in front of it, takes at once (Cloudflare's limit is 100 MB): push in smaller parts with `oxy config push.batch-bytes 33554432`, then push again"
3e5772cc Andy McCall 1 month ago ↶
589
} else if m.contains ( " http 404" ) {
ff1f62fe Andy McCall 1 month ago ↶
590
" nothing at this URL that you can see: check the owner and repository name; a private repository needs you signed in (`oxy auth login HUB`)"
3e5772cc Andy McCall 1 month ago ↶
591
} else if m.contains ( " unknownissuer" )
592
| | m.contains ( " unknown issuer" )
593
| | m.contains ( " certificate" )
594
| | m.contains ( " invalidcertificate" )
595
{
596
" the server's certificate is not trusted: for a hub with a self-signed certificate, download it from /.well-known/oxyhub-ca.pem on the hub and run `oxy config remote.<remote>.ca FILE`"
597
} else if m.contains ( " connection refused" ) {
598
" nothing is listening there: check the URL and port, and that the server is running"
599
} else if m.contains ( " dns" )
600
| | m.contains ( " failed to lookup" )
601
| | m.contains ( " name or service not known" )
602
{
603
" the host name does not resolve: check the URL"
604
} else if m.contains ( " timed out" ) | | m.contains ( " timeout" ) {
605
" the server did not answer in time: check the network, or try again"
606
} else {
607
return None ;
608
} ;
609
Some ( hint)
610
}
611
d3b27090 Andy McCall 11 days ago ↶
612
613
614
fn plain_error_text ( text : & str ) -> String {
615
let t = text.trim ( ) ;
616
let lower = t.to_ascii_lowercase ( ) ;
617
if ! ( lower.starts_with ( " <!doctype html" ) | | lower.starts_with ( " <html" ) ) {
618
return t.to_string ( ) ;
619
}
620
let title = lower
621
.find ( " <title>" )
622
.and_then ( | i | {
623
lower[ i + 7 .. ]
624
.find ( " </title>" )
625
.map ( | j | t[ i + 7 .. i + 7 + j] .trim ( ) )
626
} )
627
.filter ( | s | ! s.is_empty ( ) ) ;
628
match title {
629
Some ( s) => format! ( " {s} (an HTML error page, from a proxy in front of the server perhaps)" ) ,
630
None => " an HTML error page (from a proxy in front of the server perhaps)" .into ( ) ,
631
}
632
}
633
3e5772cc Andy McCall 1 month ago ↶
634
635
pub fn with_hint ( message : String) -> String {
636
match hint_for ( & message) {
637
Some ( h) => format! ( " {message} \n hint: {h} " ) ,
638
None => message,
ff1f62fe Andy McCall 1 month ago ↶
639
}
640
}
641
642
643
644
645
pub fn http_json (
646
method : & str ,
647
url : & str ,
648
body : Option< & str > ,
649
options : & RemoteOptions,
650
) -> Result< ( u16 , String) > {
651
refuse_plain_token ( url, options) ? ;
652
let agent = ureq:: AgentBuilder:: new( )
653
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
654
.timeout ( std:: time:: Duration:: from_secs( 60 ) )
ee130dd9 Andy McCall 15 days ago ↶
655
.user_agent ( & user_agent ( ) )
ff1f62fe Andy McCall 1 month ago ↶
656
.build ( ) ;
657
let mut req = agent.request ( method, url) .set ( " Accept" , " application/json" ) ;
658
if let Some ( t) = & options.token {
659
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
660
}
661
let resp = match body {
662
Some ( b) => req.set ( " Content-Type" , " application/json" ) .send_string ( b) ,
663
None => req.call ( ) ,
664
} ;
665
match resp {
666
Ok ( r) => {
667
let code = r.status ( ) ;
668
Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) )
669
}
670
Err ( ureq:: Error:: Status( code, r) ) => Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) ) ,
671
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
3e5772cc Andy McCall 1 month ago ↶
672
}
673
}
674
d82e4409 Andy McCall 1 month ago ↶
675
676
677
pub fn http_get ( url : & str , options : & RemoteOptions) -> Result< Vec< u8 > > {
ff1f62fe Andy McCall 1 month ago ↶
678
refuse_plain_token ( url, options) ? ;
d82e4409 Andy McCall 1 month ago ↶
679
let agent = ureq:: AgentBuilder:: new( )
680
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
ee130dd9 Andy McCall 15 days ago ↶
681
.user_agent ( & user_agent ( ) )
d82e4409 Andy McCall 1 month ago ↶
682
.build ( ) ;
683
let mut req = agent.get ( url) .set ( " Accept" , " application/json" ) ;
684
if let Some ( t) = & options.token {
685
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
686
}
687
match req.call ( ) {
688
Ok ( r) => {
689
let mut out = Vec :: new( ) ;
690
r.into_reader ( )
691
.read_to_end ( & mut out)
692
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
693
Ok ( out)
694
}
695
Err ( ureq:: Error:: Status( code, r) ) => {
696
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
697
Err ( RepoError:: Msg( with_hint ( format! (
4213c41d Andy McCall 1 month ago ↶
698
" {url} : HTTP {code} : {} " ,
699
text.trim ( )
3e5772cc Andy McCall 1 month ago ↶
700
) ) ) )
d82e4409 Andy McCall 1 month ago ↶
701
}
3e5772cc Andy McCall 1 month ago ↶
702
703
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
d82e4409 Andy McCall 1 month ago ↶
704
}
705
}
706
750251b2 Andy McCall 1 month ago ↶
707
impl Remote for HttpRemote {
708
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
709
wire:: decode_capabilities( & self .call ( false , " capabilities" , None ) ? )
710
}
83e0677b Andy McCall 1 month ago ↶
711
fn refs ( & self ) -> Result< RefsResponse> {
750251b2 Andy McCall 1 month ago ↶
712
Ok ( wire:: decode_refs( & self .call ( false , " refs" , None ) ? ) ? .1 )
713
}
714
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
715
wire:: decode_hashes( & self .call (
716
true ,
717
" negotiate" ,
718
Some ( & wire:: encode_negotiate( have, want) ) ,
719
) ? )
750251b2 Andy McCall 1 month ago ↶
720
}
b3c5a59a Andy McCall 1 month ago ↶
721
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
75167672 Andy McCall 1 month ago
722
wire:: decode_bundle( & self .call (
723
true ,
724
" fetch" ,
725
Some ( & wire:: encode_fetch( changes, max_blob) ) ,
726
) ? )
750251b2 Andy McCall 1 month ago ↶
727
}
b3c5a59a Andy McCall 1 month ago ↶
728
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
75167672 Andy McCall 1 month ago
729
Ok (
730
wire:: decode_bundle( & self .call ( true , " objects" , Some ( & wire:: encode_hashes( refs) ) ) ? ) ?
731
.objects,
732
)
b3c5a59a Andy McCall 1 month ago ↶
733
}
2863456a Andy McCall 1 month ago ↶
734
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
f3811eb2 Andy McCall 11 days ago ↶
735
let body = {
736
let _t = crate :: timing:: phase( " remote: checkpoint answer" ) ;
737
self .call ( true , " checkpoint" , Some ( & wire:: encode_hashes( heads) ) ) ?
738
} ;
739
let _t = crate :: timing:: phase( " remote: decode the checkpoint" ) ;
80b069fa Andy McCall 9 days ago ↶
740
wire:: decode_bundle( & body)
741
}
742
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
743
let body = {
744
let _t = crate :: timing:: phase( " remote: checkpoint index" ) ;
745
match self .call ( true , " checkpoint-index" , Some ( & wire:: encode_hashes( heads) ) ) {
746
Ok ( b) => b,
747
748
Err ( RepoError:: Msg( m) ) if m.contains ( " HTTP 404" ) => return self .checkpoint ( heads) ,
749
Err ( e) => return Err ( e) ,
750
}
751
} ;
f3811eb2 Andy McCall 11 days ago ↶
752
wire:: decode_bundle( & body)
2863456a Andy McCall 1 month ago ↶
753
}
754
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
755
wire:: decode_hashes( & self .call (
756
true ,
757
" shallow" ,
758
Some ( & wire:: encode_shallow( heads, depth) ) ,
759
) ? )
2863456a Andy McCall 1 month ago ↶
760
}
75167672 Andy McCall 1 month ago
761
fn push (
762
& self ,
763
bundle : Bundle,
764
updates : Vec< RefUpdate> ,
765
policy : Option< SignedPolicy> ,
766
tags : Vec< TagRef> ,
767
) -> Result< Vec< RefResult> > {
768
wire:: decode_push_response( & self .call (
769
true ,
770
" push" ,
771
Some ( & wire:: encode_push(
772
& bundle,
773
& updates,
774
policy.as_ref ( ) ,
775
& tags,
776
) ) ,
777
) ? )
750251b2 Andy McCall 1 month ago ↶
778
}
779
}
780
827281ee Andy McCall 1 month ago ↶
781
782
783
784
pub fn stored_url ( url : & str ) -> String {
8d803563 Andy McCall 1 month ago ↶
785
786
9f9e72b9 Andy McCall 1 month ago ↶
787
let ( prefix, path) = match ( url.strip_prefix ( " file://" ) , url.strip_prefix ( " dir://" ) ) {
788
( Some ( p) , _ ) => ( " file://" , p) ,
789
( _ , Some ( p) ) => ( " dir://" , p) ,
8d803563 Andy McCall 1 month ago ↶
790
_ if url.contains ( " ://" ) => return url.to_string ( ) ,
9f9e72b9 Andy McCall 1 month ago ↶
791
_ => ( " " , url) ,
827281ee Andy McCall 1 month ago ↶
792
} ;
793
match std:: path:: absolute( path) {
794
Ok ( abs) => format! ( " {prefix} {} " , abs.display ( ) ) ,
795
Err ( _ ) => url.to_string ( ) ,
796
}
797
}
798
9f9e72b9 Andy McCall 1 month ago ↶
799
800
2b5ee371 Andy McCall 1 month ago ↶
801
pub fn open_remote ( url : & str , options : & RemoteOptions) -> Result< Box< dyn Remote> > {
750251b2 Andy McCall 1 month ago ↶
802
if url.starts_with ( " http://" ) | | url.starts_with ( " https://" ) {
2b5ee371 Andy McCall 1 month ago ↶
803
return Ok ( Box :: new( HttpRemote:: new( url, options) ? ) ) ;
9f9e72b9 Andy McCall 1 month ago ↶
804
}
805
if let Some ( store) = open_store ( url, options) {
806
return crate :: repo:: strata:: open_store_remote( store? , options) ;
750251b2 Andy McCall 1 month ago ↶
807
}
5b9c6ad4 Andy McCall 1 month ago ↶
808
let path = url.strip_prefix ( " file://" ) .unwrap_or ( url) ;
809
if path.contains ( " ://" ) {
750251b2 Andy McCall 1 month ago ↶
810
return Err ( RepoError:: Msg( format! ( " unsupported remote URL {url:?} " ) ) ) ;
5b9c6ad4 Andy McCall 1 month ago ↶
811
}
84b9e9ac Andy McCall 1 month ago ↶
812
let mut local = LocalRemote:: open( Path:: new( path) ) ? ;
813
local.claimed_id = options.repo_id;
814
Ok ( Box :: new( local) )
5b9c6ad4 Andy McCall 1 month ago ↶
815
}
750251b2 Andy McCall 1 month ago ↶
816
817
818
819
820
pub mod wire {
821
use super :: * ;
822
823
fn schema ( msg : & str ) -> RepoError {
824
RepoError:: Object( ObjectError:: Schema( msg.into ( ) ) )
825
}
826
fn sigs_value ( sigs : & [Signature]) -> Value {
827
Value:: Array( sigs.iter ( ) .map ( | s | s.to_value ( ) ) .collect ( ) )
828
}
829
fn sigs_from ( v : & Value) -> Result< Vec< Signature> > {
75167672 Andy McCall 1 month ago
830
Ok ( o:: array( v, " signatures" ) ?
831
.iter ( )
832
.map ( Signature:: from_value)
833
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
834
}
835
fn hashes_value ( hs : & [ObjectRef]) -> Value {
836
Value:: Array( hs.iter ( ) .map ( | h | Value:: Bytes( h.0. to_vec ( ) ) ) .collect ( ) )
837
}
838
fn hashes_from ( v : & Value) -> Result< Vec< ObjectRef> > {
75167672 Andy McCall 1 month ago
839
Ok ( o:: array( v, " hashes" ) ?
840
.iter ( )
841
.map ( | h | o:: oref( h, " hash" ) )
842
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
843
}
844
fn attest_value ( sa : & SignedAttest) -> Value {
845
Value:: Array( vec! [ Value:: Bytes( sa.attest.encode ( ) ) , sigs_value ( & sa.sigs) ] )
846
}
847
fn attest_from ( v : & Value) -> Result< SignedAttest> {
848
let a = o:: array( v, " signed attest" ) ? ;
849
if a.len ( ) ! = 2 {
850
return Err ( schema ( " signed attest: expected [attest, sigs]" ) ) ;
851
}
75167672 Andy McCall 1 month ago
852
Ok ( SignedAttest {
853
attest: Attest:: decode( o:: bytes( & a[ 0 ] , " attest" ) ? ) ? ,
854
sigs: sigs_from ( & a[ 1 ] ) ? ,
855
} )
750251b2 Andy McCall 1 month ago ↶
856
}
857
fn opt_attest_value ( sa : & Option< SignedAttest> ) -> Value {
858
sa.as_ref ( ) .map ( attest_value) .unwrap_or ( Value:: Null)
859
}
860
fn opt_attest_from ( v : & Value) -> Result< Option< SignedAttest> > {
75167672 Andy McCall 1 month ago
861
if v.is_null ( ) {
862
Ok ( None )
863
} else {
864
attest_from ( v) .map ( Some )
865
}
750251b2 Andy McCall 1 month ago ↶
866
}
28fc23d7 Andy McCall 1 month ago ↶
867
868
869
pub fn policy_value ( sp : & SignedPolicy) -> Value {
870
Value:: Array( vec! [ Value:: Bytes( sp.policy.encode ( ) ) , sigs_value ( & sp.sigs) ] )
83e0677b Andy McCall 1 month ago ↶
871
}
28fc23d7 Andy McCall 1 month ago ↶
872
pub fn policy_from ( v : & Value) -> Result< SignedPolicy> {
83e0677b Andy McCall 1 month ago ↶
873
let a = o:: array( v, " signed policy" ) ? ;
874
if a.len ( ) ! = 2 {
875
return Err ( schema ( " signed policy: expected [policy, sigs]" ) ) ;
876
}
75167672 Andy McCall 1 month ago
877
Ok ( SignedPolicy {
878
policy: Policy:: decode( o:: bytes( & a[ 0 ] , " policy" ) ? ) ? ,
879
sigs: sigs_from ( & a[ 1 ] ) ? ,
880
} )
28fc23d7 Andy McCall 1 month ago ↶
881
}
882
fn opt_policy_value ( sp : Option< & SignedPolicy> ) -> Value {
883
sp.map ( policy_value) .unwrap_or ( Value:: Null)
884
}
885
fn opt_policy_from ( v : & Value) -> Result< Option< SignedPolicy> > {
75167672 Andy McCall 1 month ago
886
if v.is_null ( ) {
887
Ok ( None )
888
} else {
889
policy_from ( v) .map ( Some )
890
}
83e0677b Andy McCall 1 month ago ↶
891
}
750251b2 Andy McCall 1 month ago ↶
892
fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a o:: Map> {
893
Ok ( o:: map( v, what) ? )
894
}
895
fn field < 'a > ( m : & 'a o:: Map, k : u64 , what : & str ) -> Result< & 'a Value> {
896
Ok ( o:: field( m, k, what) ? )
897
}
898
6f8aeb99 Andy McCall 1 month ago ↶
899
900
901
750251b2 Andy McCall 1 month ago ↶
902
pub fn bundle_value ( b : & Bundle) -> Value {
2c75672f Andy McCall 1 month ago ↶
903
let mut changes = Vec :: with_capacity( b.raw_changes.len ( ) + b.changes.len ( ) ) ;
904
905
906
for ( bytes, s) in & b.raw_changes {
907
changes.push ( Value:: Array( vec! [
908
Value:: Bytes( bytes.clone ( ) ) ,
909
sigs_value ( s) ,
910
] ) ) ;
911
}
6f8aeb99 Andy McCall 1 month ago ↶
912
let mut texts: Vec< ( ObjectType, Vec< u8 > ) > = Vec :: new( ) ;
913
let mut listed: Option< std:: collections:: HashSet< ObjectRef> > = None ;
914
for ( c, s) in & b.changes {
915
let ( bytes, stored) = c.encode_parts ( ) ;
916
for t in stored {
917
let listed = listed.get_or_insert_with ( | | {
918
b.objects
919
.iter ( )
920
.map ( | ( ty , body ) | crate :: hash:: hash_object( * ty, body) )
921
.collect ( )
922
} ) ;
923
if ! listed.insert ( t.blob.hash ( ) ) {
924
continue ;
925
}
926
for chunk in crate :: chunk:: chunks( & t.content) {
927
if listed.insert ( crate :: hash:: hash_object( ObjectType:: Chunk, chunk) ) {
928
texts.push ( ( ObjectType:: Chunk, chunk.to_vec ( ) ) ) ;
929
}
930
}
931
texts.push ( ( ObjectType:: Blob, t.blob.encode ( ) ) ) ;
932
}
933
changes.push ( Value:: Array( vec! [ Value:: Bytes( bytes) , sigs_value ( s) ] ) ) ;
934
}
f0a517d2 Andy McCall 18 days ago ↶
935
let mut v = Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
936
( 0 , Value:: Array( changes) ) ,
750251b2 Andy McCall 1 month ago ↶
937
(
75167672 Andy McCall 1 month ago
938
1 ,
939
Value:: Array( b.attests.iter ( ) .map ( attest_value) .collect ( ) ) ,
940
) ,
941
(
750251b2 Andy McCall 1 month ago ↶
942
2 ,
943
Value:: Array(
75167672 Andy McCall 1 month ago
944
b.objects
945
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
946
.chain ( & texts)
75167672 Andy McCall 1 month ago
947
.map ( | ( t , body ) | {
948
Value:: Array( vec! [
949
Value:: Text( t.name ( ) .into ( ) ) ,
950
Value:: Bytes( body.clone ( ) ) ,
951
] )
952
} )
953
.collect ( ) ,
750251b2 Andy McCall 1 month ago ↶
954
) ,
955
) ,
75167672 Andy McCall 1 month ago
956
(
957
3 ,
958
Value:: Array( b.policies.iter ( ) .map ( policy_value) .collect ( ) ) ,
959
) ,
f0a517d2 Andy McCall 18 days ago ↶
960
] ) ;
961
962
if ! b.certificates.is_empty ( ) {
963
if let Value:: Map( m) = & mut v {
964
m.insert (
965
4 ,
966
Value:: Array(
967
b.certificates
968
.iter ( )
969
.map ( | c | Value:: Bytes( c.encode ( ) ) )
970
.collect ( ) ,
971
) ,
972
) ;
973
}
974
}
975
v
750251b2 Andy McCall 1 month ago ↶
976
}
977
978
pub fn bundle_from_value ( v : & Value) -> Result< Bundle> {
979
let m = map ( v, " bundle" ) ? ;
980
let mut b = Bundle:: default( ) ;
981
for ob in o:: array( field ( m, 2 , " objects" ) ? , " objects" ) ? {
982
let pair = o:: array( ob, " object" ) ? ;
983
if pair.len ( ) ! = 2 {
984
return Err ( schema ( " object: expected [type, body]" ) ) ;
985
}
75167672 Andy McCall 1 month ago
986
let ty = ObjectType:: from_name( o:: text( & pair[ 0 ] , " object type" ) ? )
987
.ok_or_else ( | | schema ( " unknown object type" ) ) ? ;
988
b.objects
989
.push ( ( ty, o:: bytes( & pair[ 1 ] , " object body" ) ? .to_vec ( ) ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
990
}
991
992
let mut by_ref: Option< std:: collections:: HashMap< ObjectRef, usize > > = None ;
993
for c in o:: array( field ( m, 0 , " changes" ) ? , " changes" ) ? {
994
let pair = o:: array( c, " change entry" ) ? ;
995
if pair.len ( ) ! = 2 {
996
return Err ( schema ( " change entry: expected [change, sigs]" ) ) ;
997
}
598009a7 Andy McCall 1 month ago ↶
998
let encoded = o:: bytes( & pair[ 0 ] , " change" ) ? ;
999
b.hashes
1000
.push ( crate :: hash:: hash_object( ObjectType:: Change, encoded) ) ;
0f93f49c Andy McCall 1 month ago ↶
1001
let change = Change:: decode_with( encoded, & mut | text, count, form| {
6f8aeb99 Andy McCall 1 month ago ↶
1002
let by_ref = by_ref.get_or_insert_with ( | | {
1003
b.objects
1004
.iter ( )
1005
.enumerate ( )
1006
.map ( | ( i , (ty , body ) ) | ( crate :: hash:: hash_object( * ty, body) , i) )
1007
.collect ( )
1008
} ) ;
1009
let get =
1010
| r : & ObjectRef, want : ObjectType| match by_ref.get ( r) .map ( | i | & b.objects[ * i] ) {
1011
Some ( ( ty, body) ) if * ty = = want => Ok ( body) ,
1012
_ => Err ( ObjectError:: Schema( format! (
1013
" the bundle lacks {} {} " ,
1014
want.name ( ) ,
1015
r.short ( 12 )
1016
) ) ) ,
1017
} ;
1018
let blob = crate :: objects:: Blob:: decode( get ( text, ObjectType:: Blob) ? ) ? ;
1019
let mut content = Vec :: with_capacity( blob.length as usize ) ;
1020
for chunk in & blob.chunks {
1021
content.extend_from_slice ( get ( chunk, ObjectType:: Chunk) ? ) ;
1022
}
0f93f49c Andy McCall 1 month ago ↶
1023
crate :: objects:: text_lines( & content, count, form)
6f8aeb99 Andy McCall 1 month ago ↶
1024
} ) ? ;
1025
b.changes.push ( ( change, sigs_from ( & pair[ 1 ] ) ? ) ) ;
1026
}
1027
for a in o:: array( field ( m, 1 , " attests" ) ? , " attests" ) ? {
1028
b.attests.push ( attest_from ( a) ? ) ;
750251b2 Andy McCall 1 month ago ↶
1029
}
28fc23d7 Andy McCall 1 month ago ↶
1030
if let Some ( ps) = m.get ( & 3 ) {
1031
for p in o:: array( ps, " policies" ) ? {
1032
b.policies.push ( policy_from ( p) ? ) ;
f0a517d2 Andy McCall 18 days ago ↶
1033
}
1034
}
1035
if let Some ( cs) = m.get ( & 4 ) {
1036
for c in o:: array( cs, " certificates" ) ? {
1037
b.certificates.push (
1038
crate :: cert:: SignedCertificate:: decode( o:: bytes( c, " certificate" ) ? )
1039
.map_err ( | e | schema ( & format! ( " certificate: {e} " ) ) ) ? ,
1040
) ;
28fc23d7 Andy McCall 1 month ago ↶
1041
}
1042
}
750251b2 Andy McCall 1 month ago ↶
1043
Ok ( b)
1044
}
1045
1046
pub fn encode_bundle ( b : & Bundle) -> Vec< u8 > {
1047
cbor:: encode( & bundle_value ( b) )
1048
}
1049
pub fn decode_bundle ( bytes : & [u8 ]) -> Result< Bundle> {
1050
bundle_from_value ( & cbor:: decode( bytes) .map_err ( ObjectError:: from) ? )
1051
}
1052
1053
pub fn encode_capabilities ( id : [u8 ; 16]) -> Vec< u8 > {
1054
cbor:: encode( & Value:: map( [
1055
( 0 , Value:: Array( vec! [ Value:: Uint( PROTOCOL_VERSION ) ] ) ) ,
1056
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
1057
( 2 , Value:: Array( vec! [ Value:: Text( " seal" .into ( ) ) ] ) ) ,
1058
] ) )
1059
}
1060
pub fn decode_capabilities ( bytes : & [u8 ]) -> Result< [ u8 ; 16 ] > {
1061
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1062
let m = map ( & v, " capabilities" ) ? ;
1063
let versions = o:: array( field ( m, 0 , " versions" ) ? , " versions" ) ? ;
75167672 Andy McCall 1 month ago
1064
if ! versions
1065
.iter ( )
1066
.any ( | x | x.as_uint ( ) = = Some ( PROTOCOL_VERSION ) )
1067
{
1068
return Err ( RepoError:: Msg(
1069
" remote does not speak protocol version 1" .into ( ) ,
1070
) ) ;
750251b2 Andy McCall 1 month ago ↶
1071
}
75167672 Andy McCall 1 month ago
1072
o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
1073
.try_into ( )
1074
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) )
750251b2 Andy McCall 1 month ago ↶
1075
}
1076
132b884b Andy McCall 1 month ago ↶
1077
fn tags_value ( tags : & [TagRef]) -> Value {
1078
Value:: Array(
1079
tags.iter ( )
1080
.map ( | t | {
1ec6fdbc Andy McCall 15 days ago ↶
1081
let mut v = Value:: map( [
132b884b Andy McCall 1 month ago ↶
1082
( 0 , Value:: Text( t.name.clone ( ) ) ) ,
1083
( 1 , Value:: Bytes( t.state.0. to_vec ( ) ) ) ,
1084
( 2 , hashes_value ( & t.heads) ) ,
1085
( 3 , opt_attest_value ( & t.attestation) ) ,
1ec6fdbc Andy McCall 15 days ago ↶
1086
] ) ;
1087
1088
1089
if let Value:: Map( m) = & mut v {
1090
if t.accepted.is_some ( ) {
1091
m.insert ( 4 , opt_attest_value ( & t.accepted) ) ;
1092
}
1093
if let Some ( l) = & t.logged {
1094
m.insert ( 5 , logged_value ( l) ) ;
1095
}
1096
if ! t.integration.is_empty ( ) {
1097
m.insert ( 6 , integration_value ( & t.integration) ) ;
1098
}
1099
}
1100
v
132b884b Andy McCall 1 month ago ↶
1101
} )
1102
.collect ( ) ,
1103
)
1104
}
1105
fn tags_from ( v : & Value) -> Result< Vec< TagRef> > {
1106
let mut out = Vec :: new( ) ;
1107
for t in o:: array( v, " tags" ) ? {
1108
let tm = map ( t, " tag" ) ? ;
1109
out.push ( TagRef {
1110
name: o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1111
state: o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
1112
heads: hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
1113
attestation: opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
1ec6fdbc Andy McCall 15 days ago ↶
1114
accepted: match tm.get ( & 4 ) {
1115
Some ( a) => opt_attest_from ( a) ? ,
1116
None => None ,
1117
} ,
1118
logged: tm.get ( & 5 ) .map ( logged_from) .transpose ( ) ? ,
1119
integration: match tm.get ( & 6 ) {
1120
Some ( l) => integration_from ( l) ? ,
1121
None => Vec :: new( ) ,
1122
} ,
132b884b Andy McCall 1 month ago ↶
1123
} ) ;
1ec6fdbc Andy McCall 15 days ago ↶
1124
}
1125
Ok ( out)
1126
}
1127
1128
1129
1130
1131
# [ cfg ( test ) ]
1132
pub ( super ) fn tags_as_0_5_read_them ( bytes : & [u8 ]) -> Result< Vec< TagRef> > {
1133
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1134
let m = map ( & v, " refs" ) ? ;
1135
let mut out = Vec :: new( ) ;
1136
for t in o:: array( field ( m, 3 , " tags" ) ? , " tags" ) ? {
1137
let tm = map ( t, " tag" ) ? ;
1138
out.push ( TagRef:: new(
1139
o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1140
o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
1141
hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
1142
opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
1143
) ) ;
132b884b Andy McCall 1 month ago ↶
1144
}
1145
Ok ( out)
1ec6fdbc Andy McCall 15 days ago ↶
1146
}
1147
1148
1149
fn logged_value ( l : & Logged) -> Value {
1150
Value:: Array( vec! [
1151
Value:: Uint( l.index) ,
1152
Value:: Bytes( l.entry.clone ( ) ) ,
1153
proof_value ( & l.proof) ,
1154
] )
132b884b Andy McCall 1 month ago ↶
1155
}
1ec6fdbc Andy McCall 15 days ago ↶
1156
fn logged_from ( v : & Value) -> Result< Logged> {
1157
let a = o:: array( v, " logged" ) ? ;
1158
if a.len ( ) ! = 3 {
1159
return Err ( schema ( " logged: expected [index, entry, proof]" ) ) ;
1160
}
1161
Ok ( Logged {
1162
index: o:: uint( & a[ 0 ] , " index" ) ? ,
1163
entry: o:: bytes( & a[ 1 ] , " entry" ) ? .to_vec ( ) ,
1164
proof: proof_from ( & a[ 2 ] ) ? ,
1165
} )
1166
}
132b884b Andy McCall 1 month ago ↶
1167
1ec6fdbc Andy McCall 15 days ago ↶
1168
1169
fn integration_value ( integration : & [Integration]) -> Value {
1170
Value:: Array(
1171
integration
1172
.iter ( )
1173
.map ( | i | Value:: Array( vec! [ Value:: Text( i.note.clone ( ) ) , proof_value ( & i.proof) ] ) )
1174
.collect ( ) ,
1175
)
1176
}
1177
fn integration_from ( v : & Value) -> Result< Vec< Integration> > {
1178
let mut out = Vec :: new( ) ;
1179
for i in o:: array( v, " integration" ) ? {
1180
let a = o:: array( i, " integration" ) ? ;
1181
if a.len ( ) ! = 2 {
1182
return Err ( schema ( " integration: expected [note, proof]" ) ) ;
1183
}
1184
out.push ( Integration {
1185
note: o:: text( & a[ 0 ] , " note" ) ? .to_owned ( ) ,
1186
proof: proof_from ( & a[ 1 ] ) ? ,
1187
} ) ;
1188
}
1189
Ok ( out)
1190
}
1191
83e0677b Andy McCall 1 month ago ↶
1192
pub fn encode_refs ( id : [u8 ; 16], resp : & RefsResponse) -> Vec< u8 > {
4b9d0677 Andy McCall 18 days ago ↶
1193
let mut v = Value:: map( [
750251b2 Andy McCall 1 month ago ↶
1194
(
1195
0 ,
1196
Value:: Array(
83e0677b Andy McCall 1 month ago ↶
1197
resp.refs
1198
.iter ( )
750251b2 Andy McCall 1 month ago ↶
1199
.map ( | r | {
f53cbe7a Andy McCall 18 days ago ↶
1200
let mut v = Value:: map( [
750251b2 Andy McCall 1 month ago ↶
1201
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
1202
( 1 , Value:: Bytes( r.state.0. to_vec ( ) ) ) ,
1203
( 2 , hashes_value ( & r.heads) ) ,
1204
( 3 , opt_attest_value ( & r.attestation) ) ,
1205
( 4 , r.last_serial.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
f53cbe7a Andy McCall 18 days ago ↶
1206
] ) ;
1207
4b9d0677 Andy McCall 18 days ago ↶
1208
if let Value:: Map( m) = & mut v {
1209
if r.accepted.is_some ( ) {
f53cbe7a Andy McCall 18 days ago ↶
1210
m.insert ( 5 , opt_attest_value ( & r.accepted) ) ;
4b9d0677 Andy McCall 18 days ago ↶
1211
}
1212
if let Some ( l) = & r.logged {
1ec6fdbc Andy McCall 15 days ago ↶
1213
m.insert ( 6 , logged_value ( l) ) ;
f53cbe7a Andy McCall 18 days ago ↶
1214
}
028545df Andy McCall 17 days ago ↶
1215
if ! r.integration.is_empty ( ) {
1ec6fdbc Andy McCall 15 days ago ↶
1216
m.insert ( 7 , integration_value ( & r.integration) ) ;
028545df Andy McCall 17 days ago ↶
1217
}
f53cbe7a Andy McCall 18 days ago ↶
1218
}
1219
v
750251b2 Andy McCall 1 month ago ↶
1220
} )
1221
.collect ( ) ,
1222
) ,
1223
) ,
1224
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
83e0677b Andy McCall 1 month ago ↶
1225
( 2 , opt_policy_value ( resp.policy.as_ref ( ) ) ) ,
132b884b Andy McCall 1 month ago ↶
1226
( 3 , tags_value ( & resp.tags) ) ,
75167672 Andy McCall 1 month ago
1227
(
1228
4 ,
1229
Value:: Array( resp.history.iter ( ) .map ( policy_value) .collect ( ) ) ,
1230
) ,
14edbaaf Andy McCall 1 month ago ↶
1231
(
1232
5 ,
1233
resp.default_bookmark
1234
.clone ( )
1235
.map ( Value:: Text)
1236
.unwrap_or ( Value:: Null) ,
1237
) ,
4b9d0677 Andy McCall 18 days ago ↶
1238
] ) ;
028545df Andy McCall 17 days ago ↶
1239
if let Value:: Map( m) = & mut v {
1240
if let Some ( log) = & resp.log {
1241
m.insert ( 6 , Value:: Text( log.clone ( ) ) ) ;
b63fc7a7 Andy McCall 11 days ago ↶
1242
}
1243
if ! resp.withdrawn.is_empty ( ) {
1244
m.insert ( 8 , tags_value ( & resp.withdrawn) ) ;
028545df Andy McCall 17 days ago ↶
1245
}
1246
if ! resp.witnesses.is_empty ( ) {
1247
m.insert (
1248
7 ,
1249
Value:: Array(
1250
resp.witnesses
1251
.iter ( )
1252
.map ( | ( name , id ) | {
1253
Value:: Array( vec! [
1254
Value:: Text( name.clone ( ) ) ,
1255
Value:: Bytes( id.clone ( ) ) ,
1256
] )
1257
} )
1258
.collect ( ) ,
1259
) ,
1260
) ;
1261
}
4b9d0677 Andy McCall 18 days ago ↶
1262
}
1263
cbor:: encode( & v)
750251b2 Andy McCall 1 month ago ↶
1264
}
83e0677b Andy McCall 1 month ago ↶
1265
pub fn decode_refs ( bytes : & [u8 ]) -> Result< ( [u8 ; 16], RefsResponse) > {
750251b2 Andy McCall 1 month ago ↶
1266
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1267
let m = map ( & v, " refs" ) ? ;
75167672 Andy McCall 1 month ago
1268
let id: [ u8 ; 16 ] = o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
1269
.try_into ( )
1270
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
1271
let mut refs = Vec :: new( ) ;
1272
for r in o:: array( field ( m, 0 , " refs" ) ? , " refs" ) ? {
1273
let rm = map ( r, " ref" ) ? ;
1274
let ls = field ( rm, 4 , " last serial" ) ? ;
1275
refs.push ( RemoteRef {
1276
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1277
state: o:: oref( field ( rm, 1 , " state" ) ? , " state" ) ? ,
1278
heads: hashes_from ( field ( rm, 2 , " heads" ) ? ) ? ,
1279
attestation: opt_attest_from ( field ( rm, 3 , " attestation" ) ? ) ? ,
75167672 Andy McCall 1 month ago
1280
last_serial: if ls.is_null ( ) {
1281
None
1282
} else {
1283
Some ( o:: uint( ls, " last serial" ) ? )
f53cbe7a Andy McCall 18 days ago ↶
1284
} ,
1285
accepted: match rm.get ( & 5 ) {
1286
Some ( a) => opt_attest_from ( a) ? ,
1287
None => None ,
75167672 Andy McCall 1 month ago
1288
} ,
1ec6fdbc Andy McCall 15 days ago ↶
1289
logged: rm.get ( & 6 ) .map ( logged_from) .transpose ( ) ? ,
028545df Andy McCall 17 days ago ↶
1290
integration: match rm.get ( & 7 ) {
1ec6fdbc Andy McCall 15 days ago ↶
1291
Some ( l) => integration_from ( l) ? ,
028545df Andy McCall 17 days ago ↶
1292
None => Vec :: new( ) ,
1293
} ,
750251b2 Andy McCall 1 month ago ↶
1294
} ) ;
1295
}
83e0677b Andy McCall 1 month ago ↶
1296
let policy = match m.get ( & 2 ) {
1297
Some ( p) => opt_policy_from ( p) ? ,
1298
None => None ,
1299
} ;
132b884b Andy McCall 1 month ago ↶
1300
let tags = match m.get ( & 3 ) {
1301
Some ( t) => tags_from ( t) ? ,
1302
None => Vec :: new( ) ,
1303
} ;
28fc23d7 Andy McCall 1 month ago ↶
1304
let mut history = Vec :: new( ) ;
1305
if let Some ( h) = m.get ( & 4 ) {
1306
for p in o:: array( h, " history" ) ? {
1307
history.push ( policy_from ( p) ? ) ;
1308
}
1309
}
14edbaaf Andy McCall 1 month ago ↶
1310
let default_bookmark = match m.get ( & 5 ) {
4b9d0677 Andy McCall 18 days ago ↶
1311
Some ( Value:: Text( t) ) => Some ( t.clone ( ) ) ,
1312
_ => None ,
1313
} ;
1314
let log = match m.get ( & 6 ) {
14edbaaf Andy McCall 1 month ago ↶
1315
Some ( Value:: Text( t) ) => Some ( t.clone ( ) ) ,
1316
_ => None ,
1317
} ;
028545df Andy McCall 17 days ago ↶
1318
let mut witnesses = Vec :: new( ) ;
1319
if let Some ( w) = m.get ( & 7 ) {
1320
for x in o:: array( w, " witnesses" ) ? {
1321
let a = o:: array( x, " witness" ) ? ;
1322
if a.len ( ) ! = 2 {
1323
return Err ( schema ( " witness: expected [name, identity]" ) ) ;
1324
}
1325
witnesses.push ( (
1326
o:: text( & a[ 0 ] , " witness name" ) ? .to_owned ( ) ,
1327
o:: bytes( & a[ 1 ] , " witness identity" ) ? .to_vec ( ) ,
1328
) ) ;
1329
}
1330
}
b63fc7a7 Andy McCall 11 days ago ↶
1331
let withdrawn = match m.get ( & 8 ) {
1332
Some ( t) => tags_from ( t) ? ,
1333
None => Vec :: new( ) ,
1334
} ;
75167672 Andy McCall 1 month ago
1335
Ok ( (
1336
id,
1337
RefsResponse {
1338
refs,
1339
policy,
1340
tags,
1341
history,
14edbaaf Andy McCall 1 month ago ↶
1342
default_bookmark,
4b9d0677 Andy McCall 18 days ago ↶
1343
log,
028545df Andy McCall 17 days ago ↶
1344
witnesses,
b63fc7a7 Andy McCall 11 days ago ↶
1345
withdrawn,
75167672 Andy McCall 1 month ago
1346
} ,
1347
) )
750251b2 Andy McCall 1 month ago ↶
1348
}
1349
028545df Andy McCall 17 days ago ↶
1350
1351
fn proof_value ( proof : & [[u8 ; 32]]) -> Value {
1352
Value:: Array( proof.iter ( ) .map ( | h | Value:: Bytes( h.to_vec ( ) ) ) .collect ( ) )
1353
}
1354
1355
fn proof_from ( v : & Value) -> Result< Vec< [ u8 ; 32 ] > > {
1356
let mut proof = Vec :: new( ) ;
1357
for h in o:: array( v, " proof" ) ? {
1358
proof.push (
1359
o:: bytes( h, " proof hash" ) ?
1360
.try_into ( )
1361
.map_err ( | _| schema ( " proof hash: expected 32 bytes" ) ) ? ,
1362
) ;
1363
}
1364
Ok ( proof)
1365
}
1366
750251b2 Andy McCall 1 month ago ↶
1367
pub fn encode_negotiate ( have : & [ChangeHash], want : & [ChangeHash]) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1368
cbor:: encode( & Value:: map( [
1369
( 0 , hashes_value ( have) ) ,
1370
( 1 , hashes_value ( want) ) ,
1371
] ) )
750251b2 Andy McCall 1 month ago ↶
1372
}
1373
pub fn decode_negotiate ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Vec< ChangeHash> ) > {
1374
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1375
let m = map ( & v, " negotiate" ) ? ;
75167672 Andy McCall 1 month ago
1376
Ok ( (
1377
hashes_from ( field ( m, 0 , " have" ) ? ) ? ,
1378
hashes_from ( field ( m, 1 , " want" ) ? ) ? ,
1379
) )
750251b2 Andy McCall 1 month ago ↶
1380
}
1381
b3c5a59a Andy McCall 1 month ago ↶
1382
1383
pub fn encode_fetch ( hs : & [ChangeHash], max_blob : Option< u64 > ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1384
cbor:: encode( & Value:: map( [
1385
( 0 , hashes_value ( hs) ) ,
1386
( 1 , max_blob.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
1387
] ) )
b3c5a59a Andy McCall 1 month ago ↶
1388
}
1389
pub fn decode_fetch ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Option< u64 > ) > {
1390
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1391
let m = map ( & v, " fetch" ) ? ;
1392
let max = match m.get ( & 1 ) {
1393
Some ( x) if ! x.is_null ( ) => Some ( o:: uint( x, " max blob" ) ? ) ,
1394
_ => None ,
1395
} ;
1396
Ok ( ( hashes_from ( field ( m, 0 , " changes" ) ? ) ? , max) )
1397
}
1398
2863456a Andy McCall 1 month ago ↶
1399
1400
pub fn encode_shallow ( hs : & [ChangeHash], depth : u32 ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1401
cbor:: encode( & Value:: map( [
1402
( 0 , hashes_value ( hs) ) ,
1403
( 1 , Value:: Uint( depth as u64 ) ) ,
1404
] ) )
2863456a Andy McCall 1 month ago ↶
1405
}
1406
pub fn decode_shallow ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , u32 ) > {
1407
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1408
let m = map ( & v, " shallow" ) ? ;
75167672 Andy McCall 1 month ago
1409
Ok ( (
1410
hashes_from ( field ( m, 0 , " heads" ) ? ) ? ,
1411
o:: u32_( field ( m, 1 , " depth" ) ? , " depth" ) ? ,
1412
) )
2863456a Andy McCall 1 month ago ↶
1413
}
1414
750251b2 Andy McCall 1 month ago ↶
1415
pub fn encode_hashes ( hs : & [ChangeHash]) -> Vec< u8 > {
1416
cbor:: encode( & Value:: map( [ ( 0 , hashes_value ( hs) ) ] ) )
1417
}
1418
pub fn decode_hashes ( bytes : & [u8 ]) -> Result< Vec< ChangeHash> > {
1419
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1420
hashes_from ( field ( map ( & v, " hashes" ) ? , 0 , " hashes" ) ? )
1421
}
1422
75167672 Andy McCall 1 month ago
1423
pub fn encode_push (
1424
bundle : & Bundle,
1425
updates : & [RefUpdate],
1426
policy : Option< & SignedPolicy> ,
1427
tags : & [TagRef],
1428
) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
1429
cbor:: encode( & Value:: map( [
1430
( 0 , bundle_value ( bundle) ) ,
83e0677b Andy McCall 1 month ago ↶
1431
( 2 , opt_policy_value ( policy) ) ,
132b884b Andy McCall 1 month ago ↶
1432
( 3 , tags_value ( tags) ) ,
750251b2 Andy McCall 1 month ago ↶
1433
(
1434
1 ,
1435
Value:: Array(
1436
updates
1437
.iter ( )
1438
.map ( | u | {
1439
Value:: map( [
1440
( 0 , Value:: Text( u.name.clone ( ) ) ) ,
1441
( 1 , Value:: Bytes( u.old_state.0. to_vec ( ) ) ) ,
1442
( 2 , Value:: Bytes( u.new_state.0. to_vec ( ) ) ) ,
1443
( 3 , Value:: Bool( u.force) ) ,
1444
( 4 , opt_attest_value ( & u.attestation) ) ,
1445
] )
1446
} )
1447
.collect ( ) ,
1448
) ,
1449
) ,
1450
] ) )
1451
}
132b884b Andy McCall 1 month ago ↶
1452
1453
pub type PushParts = ( Bundle, Vec< RefUpdate> , Option< SignedPolicy> , Vec< TagRef> ) ;
1454
1455
pub fn decode_push ( bytes : & [u8 ]) -> Result< PushParts> {
750251b2 Andy McCall 1 month ago ↶
1456
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1457
let m = map ( & v, " push" ) ? ;
1458
let bundle = bundle_from_value ( field ( m, 0 , " bundle" ) ? ) ? ;
1459
let mut updates = Vec :: new( ) ;
1460
for u in o:: array( field ( m, 1 , " updates" ) ? , " updates" ) ? {
1461
let um = map ( u, " update" ) ? ;
1462
updates.push ( RefUpdate {
1463
name: o:: text( field ( um, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1464
old_state: o:: oref( field ( um, 1 , " old_state" ) ? , " old_state" ) ? ,
1465
new_state: o:: oref( field ( um, 2 , " new_state" ) ? , " new_state" ) ? ,
75167672 Andy McCall 1 month ago
1466
force: field ( um, 3 , " force" ) ?
1467
.as_bool ( )
1468
.ok_or_else ( | | schema ( " force: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1469
attestation: opt_attest_from ( field ( um, 4 , " attestation" ) ? ) ? ,
1470
} ) ;
1471
}
83e0677b Andy McCall 1 month ago ↶
1472
let policy = match m.get ( & 2 ) {
1473
Some ( p) => opt_policy_from ( p) ? ,
1474
None => None ,
1475
} ;
132b884b Andy McCall 1 month ago ↶
1476
let tags = match m.get ( & 3 ) {
1477
Some ( t) => tags_from ( t) ? ,
1478
None => Vec :: new( ) ,
1479
} ;
1480
Ok ( ( bundle, updates, policy, tags) )
750251b2 Andy McCall 1 month ago ↶
1481
}
1482
1483
pub fn encode_push_response ( results : & [RefResult]) -> Vec< u8 > {
1484
cbor:: encode( & Value:: map( [ (
1485
0 ,
1486
Value:: Array(
1487
results
1488
.iter ( )
75167672 Andy McCall 1 month ago
1489
.map ( | r | {
1490
Value:: map( [
1491
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
1492
( 1 , Value:: Bool( r.ok) ) ,
1493
( 2 , Value:: Text( r.reason.clone ( ) ) ) ,
1494
] )
1495
} )
750251b2 Andy McCall 1 month ago ↶
1496
.collect ( ) ,
1497
) ,
1498
) ] ) )
1499
}
1500
pub fn decode_push_response ( bytes : & [u8 ]) -> Result< Vec< RefResult> > {
1501
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1502
let m = map ( & v, " push response" ) ? ;
1503
let mut out = Vec :: new( ) ;
1504
for r in o:: array( field ( m, 0 , " results" ) ? , " results" ) ? {
1505
let rm = map ( r, " result" ) ? ;
1506
out.push ( RefResult {
1507
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
75167672 Andy McCall 1 month ago
1508
ok: field ( rm, 1 , " ok" ) ?
1509
.as_bool ( )
1510
.ok_or_else ( | | schema ( " ok: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1511
reason: o:: text( field ( rm, 2 , " reason" ) ? , " reason" ) ? .to_owned ( ) ,
1512
} ) ;
1513
}
1514
Ok ( out)
1515
}
1516
}
1517
1518
# [ cfg ( test ) ]
1519
mod tests {
1520
use super :: * ;
1521
use crate :: attest:: AttestBody;
1522
use crate :: objects:: Hlc;
d3b27090 Andy McCall 11 days ago ↶
1523
1524
1525
1526
# [ test ]
1527
fn html_error_pages_are_shown_by_their_title ( ) {
1528
let cloudflare = " <html>\r \n <head><title>413 Request Entity Too Large</title></head>\r \n <body><center><h1>413</h1></center><hr><center>cloudflare</center></body></html>" ;
1529
assert_eq! (
1530
plain_error_text ( cloudflare) ,
1531
" 413 Request Entity Too Large (an HTML error page, from a proxy in front of the server perhaps)"
1532
) ;
1533
assert_eq! (
1534
plain_error_text ( " refused: a secret\n at a.txt:3 " ) ,
1535
" refused: a secret\n at a.txt:3"
1536
) ;
1537
assert! ( hint_for ( " https://hub/x: HTTP 413: too large" )
1538
.unwrap ( )
1539
.contains ( " push.batch-bytes" ) ) ;
1540
}
ee130dd9 Andy McCall 15 days ago ↶
1541
1542
# [ test ]
1543
fn client_features_are_declared_and_read ( ) {
1544
assert_eq! ( CLIENT_FEATURES .join ( " ," ) , " certified-tags" ) ;
1545
assert! ( user_agent ( ) .starts_with ( " oxy/" ) ) ;
1546
assert! ( declares_feature (
1547
Some ( " certified-tags" ) ,
1548
FEATURE_CERTIFIED_TAGS
1549
) ) ;
1550
assert! ( declares_feature (
1551
Some ( " later-thing, Certified-Tags ,x" ) ,
1552
FEATURE_CERTIFIED_TAGS
1553
) ) ;
1554
assert! ( ! declares_feature (
1555
Some ( " certified-tags-v2" ) ,
1556
FEATURE_CERTIFIED_TAGS
1557
) ) ;
1558
assert! ( ! declares_feature ( Some ( " " ) , FEATURE_CERTIFIED_TAGS ) ) ;
1559
assert! ( ! declares_feature ( None , FEATURE_CERTIFIED_TAGS ) ) ;
1560
}
750251b2 Andy McCall 1 month ago ↶
1561
1562
# [ test ]
1563
fn wire_round_trips ( ) {
1564
let c = crate :: objects:: tests:: sample_change( ) ;
75167672 Andy McCall 1 month ago
1565
let sk = crate :: identity:: SecretKey {
1566
ed_seed: [ 1 ; 32 ] ,
1567
ml_seed: [ 2 ; 32 ] ,
1568
} ;
750251b2 Andy McCall 1 month ago ↶
1569
let id = sk.identity ( " a" , " b" , 0 ) ;
1570
let key = id.hash ( ) ;
1571
let sig = sk.sign ( crate :: identity:: Domain:: Change, & key, & c.hash ( ) ) ;
1572
let seal = Attest:: seal( [ 3 ; 16 ] , vec! [ c.hash ( ) ] ) ;
1573
let seal_sig = sk.sign ( crate :: identity:: Domain:: Attest, & key, & seal.hash ( ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
1574
let policy = Policy:: single_root( [ 3 ; 16 ] , id.clone ( ) , 99 ) ;
1575
let psig = sk.sign ( crate :: identity:: Domain:: Policy, & key, & policy.hash ( ) ) ;
750251b2 Andy McCall 1 month ago ↶
1576
let bundle = Bundle {
1577
changes: vec! [ ( c.clone ( ) , vec! [ sig.clone ( ) ] ) ] ,
75167672 Andy McCall 1 month ago
1578
attests: vec! [ SignedAttest {
1579
attest: seal.clone ( ) ,
1580
sigs: vec! [ seal_sig] ,
1581
} ] ,
1582
objects: vec! [
1583
( ObjectType:: Identity, id.encode ( ) ) ,
1584
( ObjectType:: Chunk, b " xyz" .to_vec ( ) ) ,
1585
] ,
1586
policies: vec! [ SignedPolicy {
1587
policy: policy.clone ( ) ,
1588
sigs: vec! [ psig.clone ( ) ] ,
1589
} ] ,
598009a7 Andy McCall 1 month ago ↶
1590
hashes: vec! [ ] ,
2c75672f Andy McCall 1 month ago ↶
1591
raw_changes: vec! [ ] ,
f0a517d2 Andy McCall 18 days ago ↶
1592
certificates: vec! [ ] ,
750251b2 Andy McCall 1 month ago ↶
1593
} ;
1594
let d = wire:: decode_bundle( & wire:: encode_bundle( & bundle) ) .unwrap ( ) ;
598009a7 Andy McCall 1 month ago ↶
1595
assert_eq! (
1596
d.hashes,
1597
vec! [ c.hash ( ) ] ,
1598
" the receiver hashes what it was sent"
1599
) ;
750251b2 Andy McCall 1 month ago ↶
1600
assert_eq! ( d.changes, bundle.changes) ;
1601
assert_eq! ( d.attests, bundle.attests) ;
1602
assert_eq! ( d.objects, bundle.objects) ;
28fc23d7 Andy McCall 1 month ago ↶
1603
assert_eq! ( d.policies, bundle.policies) ;
f0a517d2 Andy McCall 18 days ago ↶
1604
assert! ( d.certificates.is_empty ( ) ) ;
1605
1606
1607
let plain = wire:: encode_bundle( & bundle) ;
1608
let mut with = bundle.clone ( ) ;
1609
let issuer_sk = crate :: identity:: SecretKey:: generate( ) ;
1610
let issuer = issuer_sk.identity ( " hub" , " h@x" , 1 ) ;
1611
with.certificates.push ( crate :: cert:: SignedCertificate:: sign(
1612
crate :: cert:: Certificate {
1613
key: id.clone ( ) ,
1614
issuer: issuer.hash ( ) ,
1615
subject: " agent:a@b" .into ( ) ,
1616
claims: vec! [ ] ,
1617
scope: crate :: identity:: Scope:: CHANGES ,
1618
not_before: 1 ,
1619
expires: 2 ,
a2387d54 Andy McCall 18 days ago ↶
1620
grant: None ,
f0a517d2 Andy McCall 18 days ago ↶
1621
} ,
1622
& issuer,
1623
& issuer_sk,
1624
) ) ;
1625
let encoded = wire:: encode_bundle( & with) ;
1626
assert_ne! ( encoded, plain) ;
1627
assert_eq! (
1628
wire:: decode_bundle( & encoded) .unwrap ( ) .certificates,
1629
with.certificates
1630
) ;
1631
with.certificates.clear ( ) ;
1632
assert_eq! ( wire:: encode_bundle( & with) , plain) ;
750251b2 Andy McCall 1 month ago ↶
1633
75167672 Andy McCall 1 month ago
1634
let att = Attest {
1635
repo: [ 3 ; 16 ] ,
1636
hlc: Hlc:: default( ) ,
1637
body: AttestBody:: Ref {
1638
name: " main" .into ( ) ,
1639
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1640
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1641
serial: 4 ,
1642
} ,
1643
} ;
750251b2 Andy McCall 1 month ago ↶
1644
let refs = vec! [
75167672 Andy McCall 1 month ago
1645
RemoteRef {
1646
name: " main" .into ( ) ,
1647
state: ObjectRef( [ 2 ; 32 ] ) ,
1648
heads: vec! [ c.hash ( ) ] ,
1649
attestation: Some ( SignedAttest {
1650
attest: att.clone ( ) ,
1651
sigs: vec! [ sig.clone ( ) ] ,
1652
} ) ,
1653
last_serial: Some ( 4 ) ,
f53cbe7a Andy McCall 18 days ago ↶
1654
accepted: None ,
4b9d0677 Andy McCall 18 days ago ↶
1655
logged: None ,
028545df Andy McCall 17 days ago ↶
1656
integration: vec! [ ] ,
75167672 Andy McCall 1 month ago
1657
} ,
1658
RemoteRef {
1659
name: " dev" .into ( ) ,
1660
state: ObjectRef( [ 5 ; 32 ] ) ,
1661
heads: vec! [ ] ,
1662
attestation: None ,
1663
last_serial: None ,
f53cbe7a Andy McCall 18 days ago ↶
1664
accepted: None ,
4b9d0677 Andy McCall 18 days ago ↶
1665
logged: None ,
028545df Andy McCall 17 days ago ↶
1666
integration: vec! [ ] ,
75167672 Andy McCall 1 month ago
1667
} ,
750251b2 Andy McCall 1 month ago ↶
1668
] ;
1ec6fdbc Andy McCall 15 days ago ↶
1669
let tag = TagRef:: new( " v1" .into ( ) , ObjectRef( [ 2 ; 32 ] ) , vec! [ c.hash ( ) ] , None ) ;
4b9d0677 Andy McCall 18 days ago ↶
1670
let mut resp = RefsResponse {
75167672 Andy McCall 1 month ago
1671
refs,
1672
policy: Some ( SignedPolicy {
1673
policy: policy.clone ( ) ,
1674
sigs: vec! [ psig.clone ( ) ] ,
1675
} ) ,
1676
tags: vec! [ tag.clone ( ) ] ,
1677
history: vec! [ SignedPolicy {
1678
policy: policy.clone ( ) ,
1679
sigs: vec! [ ] ,
1680
} ] ,
14edbaaf Andy McCall 1 month ago ↶
1681
default_bookmark: Some ( " develop" .into ( ) ) ,
4b9d0677 Andy McCall 18 days ago ↶
1682
log: Some ( " hub.example/log\n 2\n root\n \n - sig\n " .into ( ) ) ,
028545df Andy McCall 17 days ago ↶
1683
witnesses: vec! [ ] ,
b63fc7a7 Andy McCall 11 days ago ↶
1684
withdrawn: vec! [ ] ,
75167672 Andy McCall 1 month ago
1685
} ;
028545df Andy McCall 17 days ago ↶
1686
1687
1688
let old = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1689
let back = wire:: decode_refs( & old) .unwrap ( ) .1 ;
1690
assert! ( back.witnesses.is_empty ( ) & & back.refs.iter ( ) .all ( | r | r.integration.is_empty ( ) ) ) ;
4b9d0677 Andy McCall 18 days ago ↶
1691
resp.refs[ 1 ] .logged = Some ( Logged {
1692
index: 41 ,
1693
entry: vec! [ 1 , 2 , 3 ] ,
1694
proof: vec! [ [ 9 ; 32 ] , [ 8 ; 32 ] ] ,
1695
} ) ;
028545df Andy McCall 17 days ago ↶
1696
resp.refs[ 1 ] .integration = vec! [ Integration {
1697
note: " hub.example/log\n 42\n root\n \n - sig\n " .into ( ) ,
1698
proof: vec! [ [ 7 ; 32 ] ] ,
1699
} ] ;
1700
resp.witnesses = vec! [ ( " w.example" .into ( ) , vec! [ 4 , 5 , 6 ] ) ] ;
1701
let new = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1702
let ( id_back, back) = wire:: decode_refs( & new) .unwrap ( ) ;
4b9d0677 Andy McCall 18 days ago ↶
1703
assert_eq! ( back.log, resp.log) ;
1704
assert_eq! ( back.refs[ 1 ] .logged, resp.refs[ 1 ] .logged) ;
028545df Andy McCall 17 days ago ↶
1705
assert_eq! ( back.refs[ 1 ] .integration, resp.refs[ 1 ] .integration) ;
1706
assert_eq! ( back.witnesses, resp.witnesses) ;
4b9d0677 Andy McCall 18 days ago ↶
1707
assert_eq! ( back.refs[ 0 ] .logged, None ) ;
028545df Andy McCall 17 days ago ↶
1708
1709
let crate :: cbor:: Value:: Map( m) = crate :: cbor:: decode( & new) .unwrap ( ) else {
1710
panic! ( )
1711
} ;
1712
assert! ( m.contains_key ( & 7 ) ) ;
750251b2 Andy McCall 1 month ago ↶
1713
assert_eq! ( id_back, [ 3 ; 16 ] ) ;
83e0677b Andy McCall 1 month ago ↶
1714
assert_eq! ( back.refs.len ( ) , 2 ) ;
1715
assert_eq! ( back.refs[ 0 ] .attestation.as_ref ( ) .unwrap ( ) .attest, att) ;
1716
assert_eq! ( back.refs[ 1 ] .last_serial, None ) ;
1717
assert_eq! ( back.policy.as_ref ( ) .unwrap ( ) .policy, policy) ;
28fc23d7 Andy McCall 1 month ago ↶
1718
assert_eq! ( back.history, resp.history) ;
132b884b Andy McCall 1 month ago ↶
1719
assert_eq! ( back.tags, vec! [ tag.clone ( ) ] ) ;
1ec6fdbc Andy McCall 15 days ago ↶
1720
1721
1722
1723
let crate :: cbor:: Value:: Map( m) = crate :: cbor:: decode( & new) .unwrap ( ) else {
1724
panic! ( )
1725
} ;
1726
let crate :: cbor:: Value:: Array( tags) = & m[ & 3 ] else {
1727
panic! ( )
1728
} ;
1729
let crate :: cbor:: Value:: Map( plain) = & tags[ 0 ] else {
1730
panic! ( )
1731
} ;
1732
assert_eq! ( plain.keys ( ) .copied ( ) .collect:: < Vec< _ > > ( ) , vec! [ 0 , 1 , 2 , 3 ] ) ;
1733
let mut full = tag.clone ( ) ;
1734
full.name = " v2" .into ( ) ;
1735
full.attestation = Some ( SignedAttest {
1736
attest: att.clone ( ) ,
1737
sigs: vec! [ sig.clone ( ) ] ,
1738
} ) ;
1739
full.accepted = full.attestation.clone ( ) ;
1740
full.logged = Some ( Logged {
1741
index: 7 ,
1742
entry: vec! [ 4 ] ,
1743
proof: vec! [ [ 3 ; 32 ] ] ,
1744
} ) ;
1745
full.integration = vec! [ Integration {
1746
note: " hub.example/log\n 8\n root\n \n - sig\n " .into ( ) ,
1747
proof: vec! [ ] ,
1748
} ] ;
1749
resp.tags = vec! [ full.clone ( ) , tag.clone ( ) ] ;
1750
let bytes = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1751
assert_eq! ( wire:: decode_refs( & bytes) .unwrap ( ) .1. tags, resp.tags) ;
1752
1753
let pushed = wire:: encode_push( & Bundle:: default( ) , & [ ] , None , & resp.tags) ;
1754
assert_eq! ( wire:: decode_push( & pushed) .unwrap ( ) .3 , resp.tags) ;
1755
1756
1757
let stripped: Vec< TagRef> = resp
1758
.tags
1759
.iter ( )
1760
.map ( | t | {
1761
TagRef:: new(
1762
t.name.clone ( ) ,
1763
t.state,
1764
t.heads.clone ( ) ,
1765
t.attestation.clone ( ) ,
1766
)
1767
} )
1768
.collect ( ) ;
1769
assert_eq! ( wire:: tags_as_0_5_read_them( & bytes) .unwrap ( ) , stripped) ;
750251b2 Andy McCall 1 month ago ↶
1770
75167672 Andy McCall 1 month ago
1771
let ( h, w) = wire:: decode_negotiate( & wire:: encode_negotiate(
1772
& [ ObjectRef( [ 1 ; 32 ] ) ] ,
1773
& [ ObjectRef( [ 2 ; 32 ] ) ] ,
1774
) )
1775
.unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1776
assert_eq! ( ( h, w) , ( vec! [ ObjectRef( [ 1 ; 32 ] ) ] , vec! [ ObjectRef( [ 2 ; 32 ] ) ] ) ) ;
75167672 Andy McCall 1 month ago
1777
assert_eq! (
1778
wire:: decode_hashes( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1779
vec! [ c.hash ( ) ]
1780
) ;
1781
assert_eq! (
1782
wire:: decode_fetch( & wire:: encode_fetch( & [ c.hash ( ) ] , Some ( 4096 ) ) ) .unwrap ( ) ,
1783
( vec! [ c.hash ( ) ] , Some ( 4096 ) )
1784
) ;
1785
assert_eq! (
1786
wire:: decode_fetch( & wire:: encode_fetch( & [ ] , None ) ) .unwrap ( ) ,
1787
( vec! [ ] , None )
1788
) ;
1789
assert_eq! (
1790
wire:: decode_shallow( & wire:: encode_shallow( & [ c.hash ( ) ] , 3 ) ) .unwrap ( ) ,
1791
( vec! [ c.hash ( ) ] , 3 )
1792
) ;
b3c5a59a Andy McCall 1 month ago ↶
1793
75167672 Andy McCall 1 month ago
1794
assert_eq! (
1795
wire:: decode_fetch( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1796
( vec! [ c.hash ( ) ] , None )
1797
) ;
750251b2 Andy McCall 1 month ago ↶
1798
75167672 Andy McCall 1 month ago
1799
let up = RefUpdate {
1800
name: " main" .into ( ) ,
1801
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1802
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1803
force: true ,
1804
attestation: Some ( SignedAttest {
1805
attest: att,
1806
sigs: vec! [ sig] ,
1807
} ) ,
1808
} ;
1809
let sp = SignedPolicy {
1810
policy,
1811
sigs: vec! [ psig] ,
1812
} ;
1813
let ( b2, ups, pol, tags) = wire:: decode_push( & wire:: encode_push(
1814
& bundle,
1815
std:: slice:: from_ref( & up) ,
1816
Some ( & sp) ,
1817
std:: slice:: from_ref( & tag) ,
1818
) )
1819
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1820
assert_eq! ( pol, Some ( sp) ) ;
132b884b Andy McCall 1 month ago ↶
1821
assert_eq! ( tags, vec! [ tag] ) ;
750251b2 Andy McCall 1 month ago ↶
1822
assert_eq! ( b2.changes.len ( ) , 1 ) ;
1823
assert_eq! ( ups[ 0 ] .name, " main" ) ;
1824
assert! ( ups[ 0 ] .force) ;
1825
assert_eq! ( ups[ 0 ] .attestation, up.attestation) ;
75167672 Andy McCall 1 month ago
1826
let res = vec! [ RefResult {
1827
name: " main" .into ( ) ,
1828
ok: false ,
1829
reason: " stale" .into ( ) ,
1830
} ] ;
1831
assert_eq! (
1832
wire:: decode_push_response( & wire:: encode_push_response( & res) ) .unwrap ( ) ,
1833
res
1834
) ;
1835
assert_eq! (
1836
wire:: decode_capabilities( & wire:: encode_capabilities( [ 7 ; 16 ] ) ) .unwrap ( ) ,
1837
[ 7 ; 16 ]
1838
) ;
750251b2 Andy McCall 1 month ago ↶
1839
}
1840
}