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