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} ;
17
use std:: sync:: Arc;
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> ,
83e0677b Andy McCall 1 month ago ↶
57
}
58
132b884b Andy McCall 1 month ago ↶
59
60
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
61
pub struct TagRef {
62
pub name : String,
63
pub state : ObjectRef,
64
pub heads : Vec< ChangeHash> ,
65
pub attestation : Option< SignedAttest> ,
66
}
67
5b9c6ad4 Andy McCall 1 month ago ↶
68
69
# [ derive ( Clone, Debug ) ]
70
pub struct RemoteRef {
71
pub name : String,
72
pub state : ObjectRef,
73
pub heads : Vec< ChangeHash> ,
74
pub attestation : Option< SignedAttest> ,
75
}
76
77
78
# [ derive ( Clone, Debug, Default ) ]
79
pub struct Bundle {
80
81
pub changes : Vec< ( Change, Vec< Signature> ) > ,
82
83
pub attests : Vec< SignedAttest> ,
84
85
pub objects : Vec< ( ObjectType, Vec< u8 > ) > ,
28fc23d7 Andy McCall 1 month ago ↶
86
87
88
pub policies : Vec< SignedPolicy> ,
598009a7 Andy McCall 1 month ago ↶
89
90
91
92
pub hashes : Vec< ChangeHash> ,
2c75672f Andy McCall 1 month ago ↶
93
94
95
96
pub raw_changes : Vec< ( Vec< u8 > , Vec< Signature> ) > ,
5b9c6ad4 Andy McCall 1 month ago ↶
97
}
98
99
impl Bundle {
100
pub fn is_empty ( & self ) -> bool {
101
self .changes.is_empty ( ) & & self .attests.is_empty ( ) & & self .objects.is_empty ( )
102
}
103
}
104
105
106
# [ derive ( Clone, Debug ) ]
107
pub struct RefUpdate {
108
pub name : String,
109
pub old_state : ObjectRef,
110
pub new_state : ObjectRef,
111
pub force : bool ,
112
pub attestation : Option< SignedAttest> ,
113
}
114
115
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
116
pub struct RefResult {
117
pub name : String,
118
pub ok : bool ,
119
pub reason : String,
120
}
121
122
pub trait Remote {
123
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > ;
83e0677b Andy McCall 1 month ago ↶
124
fn refs ( & self ) -> Result< RefsResponse> ;
5b9c6ad4 Andy McCall 1 month ago ↶
125
126
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > ;
b3c5a59a Andy McCall 1 month ago ↶
127
128
129
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> ;
130
131
132
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > ;
2863456a Andy McCall 1 month ago ↶
133
134
135
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> ;
136
137
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > ;
132b884b Andy McCall 1 month ago ↶
138
139
140
75167672 Andy McCall 1 month ago
141
fn push (
142
& self ,
143
bundle : Bundle,
144
updates : Vec< RefUpdate> ,
145
policy : Option< SignedPolicy> ,
146
tags : Vec< TagRef> ,
147
) -> Result< Vec< RefResult> > ;
5b9c6ad4 Andy McCall 1 month ago ↶
148
}
149
750251b2 Andy McCall 1 month ago ↶
150
151
5b9c6ad4 Andy McCall 1 month ago ↶
152
153
pub struct LocalRemote {
154
pub repo : Repo,
84b9e9ac Andy McCall 1 month ago ↶
155
156
157
pub claimed_id : Option< [ u8 ; 16 ] > ,
5b9c6ad4 Andy McCall 1 month ago ↶
158
}
159
160
impl LocalRemote {
161
pub fn open ( path : & Path) -> Result< LocalRemote> {
75167672 Andy McCall 1 month ago
162
Ok ( LocalRemote {
163
repo: Repo:: open( path) ? ,
84b9e9ac Andy McCall 1 month ago ↶
164
claimed_id: None ,
75167672 Andy McCall 1 month ago
165
} )
5b9c6ad4 Andy McCall 1 month ago ↶
166
}
167
}
168
169
impl Remote for LocalRemote {
170
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
171
self .repo.id ( )
172
}
83e0677b Andy McCall 1 month ago ↶
173
fn refs ( & self ) -> Result< RefsResponse> {
5b9c6ad4 Andy McCall 1 month ago ↶
174
self .repo.remote_refs ( )
175
}
176
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
177
self .repo.negotiate ( have, want)
178
}
b3c5a59a Andy McCall 1 month ago ↶
179
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
2c75672f Andy McCall 1 month ago ↶
180
self .repo.bundle_raw_for ( changes, max_blob)
b3c5a59a Andy McCall 1 month ago ↶
181
}
182
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
183
self .repo.objects_for ( refs)
5b9c6ad4 Andy McCall 1 month ago ↶
184
}
2863456a Andy McCall 1 month ago ↶
185
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
186
self .repo.checkpoint_bundle_for ( heads)
187
}
188
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
189
self .repo.shallow_changes ( heads, depth)
190
}
75167672 Andy McCall 1 month ago
191
fn push (
192
& self ,
193
bundle : Bundle,
194
updates : Vec< RefUpdate> ,
195
policy : Option< SignedPolicy> ,
196
tags : Vec< TagRef> ,
197
) -> Result< Vec< RefResult> > {
84b9e9ac Andy McCall 1 month ago ↶
198
if self .repo.is_unbound ( ) {
199
match self .claimed_id {
200
Some ( id) => self .repo.bind_id ( id) ? ,
eae59556 Andy McCall 1 month ago ↶
201
None => {
202
return Err ( RepoError:: Msg(
203
" the remote is unbound and this client did not say which repository it is"
204
.into ( ) ,
205
) )
206
}
84b9e9ac Andy McCall 1 month ago ↶
207
}
208
}
132b884b Andy McCall 1 month ago ↶
209
self .repo.receive_push ( bundle, updates, policy, tags)
5b9c6ad4 Andy McCall 1 month ago ↶
210
}
211
}
212
750251b2 Andy McCall 1 month ago ↶
213
214
2b5ee371 Andy McCall 1 month ago ↶
215
216
# [ derive ( Clone, Debug, Default ) ]
217
pub struct RemoteOptions {
218
219
pub token : Option< String> ,
220
221
222
pub ca : Option< PathBuf> ,
75167672 Andy McCall 1 month ago
223
224
225
pub repo_id : Option< [ u8 ; 16 ] > ,
2b5ee371 Andy McCall 1 month ago ↶
226
}
227
75167672 Andy McCall 1 month ago
228
229
pub const REPO_ID_HEADER : & str = " Oxy-Repo-Id" ;
230
231
232
pub const UNBOUND_ID : [ u8 ; 16 ] = [ 0 ; 16 ] ;
233
750251b2 Andy McCall 1 month ago ↶
234
235
pub struct HttpRemote {
236
base : String,
237
token : Option< String> ,
75167672 Andy McCall 1 month ago
238
repo_id : Option< [ u8 ; 16 ] > ,
750251b2 Andy McCall 1 month ago ↶
239
agent : ureq:: Agent,
240
}
241
2b5ee371 Andy McCall 1 month ago ↶
242
243
fn tls_client_config ( ca : Option< & Path> ) -> Result< Arc< rustls:: ClientConfig> > {
244
let mut roots = rustls:: RootCertStore:: empty( ) ;
245
roots.extend ( webpki_roots:: TLS_SERVER_ROOTS .iter ( ) .cloned ( ) ) ;
246
if let Some ( path) = ca {
75167672 Andy McCall 1 month ago
247
let pem = std:: fs:: read( path)
248
.map_err ( | e | RepoError:: Msg( format! ( " reading CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
249
let certs = rustls_pemfile:: certs( & mut pem.as_slice ( ) )
250
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( )
251
.map_err ( | e | RepoError:: Msg( format! ( " parsing CA file {} : {e} " , path.display ( ) ) ) ) ? ;
252
if certs.is_empty ( ) {
75167672 Andy McCall 1 month ago
253
return Err ( RepoError:: Msg( format! (
254
" CA file {} holds no certificates" ,
255
path.display ( )
256
) ) ) ;
2b5ee371 Andy McCall 1 month ago ↶
257
}
258
for c in certs {
75167672 Andy McCall 1 month ago
259
roots
260
.add ( c)
261
.map_err ( | e | RepoError:: Msg( format! ( " CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
262
}
263
}
75167672 Andy McCall 1 month ago
264
let cfg = rustls:: ClientConfig:: builder_with_provider( Arc:: new(
265
rustls:: crypto:: ring:: default_provider( ) ,
266
) )
267
.with_safe_default_protocol_versions ( )
268
.map_err ( | e | RepoError:: Msg( e.to_string ( ) ) ) ?
269
.with_root_certificates ( roots)
270
.with_no_client_auth ( ) ;
2b5ee371 Andy McCall 1 month ago ↶
271
Ok ( Arc:: new( cfg) )
272
}
273
750251b2 Andy McCall 1 month ago ↶
274
impl HttpRemote {
2b5ee371 Andy McCall 1 month ago ↶
275
pub fn new ( base : & str , options : & RemoteOptions) -> Result< HttpRemote> {
ff1f62fe Andy McCall 1 month ago ↶
276
refuse_plain_token ( base, options) ? ;
75167672 Andy McCall 1 month ago
277
let agent = ureq:: AgentBuilder:: new( )
278
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
279
.build ( ) ;
280
Ok ( HttpRemote {
281
base: base.trim_end_matches ( ' /' ) .to_string ( ) ,
282
token: options.token.clone ( ) ,
283
repo_id: options.repo_id,
284
agent,
285
} )
750251b2 Andy McCall 1 month ago ↶
286
}
287
288
fn call ( & self , post : bool , endpoint : & str , body : Option< & [ u8 ] > ) -> Result< Vec< u8 > > {
289
let url = format! ( " {} /oxy/v1/{endpoint} " , self .base) ;
75167672 Andy McCall 1 month ago
290
let mut req = if post {
291
self .agent.post ( & url)
292
} else {
293
self .agent.get ( & url)
294
} ;
750251b2 Andy McCall 1 month ago ↶
295
req = req.set ( " Accept" , CONTENT_TYPE ) ;
296
if let Some ( t) = & self .token {
297
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
298
}
75167672 Andy McCall 1 month ago
299
if let Some ( id) = & self .repo_id {
300
req = req.set ( REPO_ID_HEADER , & hex:: encode( id) ) ;
301
}
750251b2 Andy McCall 1 month ago ↶
302
let resp = match body {
303
Some ( b) => req.set ( " Content-Type" , CONTENT_TYPE ) .send_bytes ( b) ,
304
None => req.call ( ) ,
305
} ;
306
match resp {
307
Ok ( r) => {
308
let mut out = Vec :: new( ) ;
75167672 Andy McCall 1 month ago
309
r.into_reader ( )
310
.read_to_end ( & mut out)
311
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
312
Ok ( out)
313
}
314
Err ( ureq:: Error:: Status( code, r) ) => {
315
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
316
let msg = format! ( " {url} : HTTP {code} : {} " , text.trim ( ) ) ;
317
Err ( RepoError:: Msg( with_hint ( msg) ) )
750251b2 Andy McCall 1 month ago ↶
318
}
3e5772cc Andy McCall 1 month ago ↶
319
320
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
750251b2 Andy McCall 1 month ago ↶
321
}
322
}
323
}
324
ff1f62fe Andy McCall 1 month ago ↶
325
326
327
fn refuse_plain_token ( url : & str , options : & RemoteOptions) -> Result< ( ) > {
328
if options.token.is_some ( ) & & ! crate :: credentials:: token_allowed( url) {
329
return Err ( RepoError:: Msg( format! (
330
" {url} : refusing to send a token over plain http; use https"
331
) ) ) ;
332
}
333
Ok ( ( ) )
334
}
335
3e5772cc Andy McCall 1 month ago ↶
336
337
pub fn hint_for ( message : & str ) -> Option< & 'static str > {
338
let m = message.to_ascii_lowercase ( ) ;
339
let hint = if m.contains ( " http 401" ) {
ff1f62fe Andy McCall 1 month ago ↶
340
" 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 ↶
341
} else if m.contains ( " http 403" ) {
342
" the token or account lacks permission: pushing needs a token with repo:write and write access to the repository"
343
} else if m.contains ( " http 404" ) {
ff1f62fe Andy McCall 1 month ago ↶
344
" 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 ↶
345
} else if m.contains ( " unknownissuer" )
346
| | m.contains ( " unknown issuer" )
347
| | m.contains ( " certificate" )
348
| | m.contains ( " invalidcertificate" )
349
{
350
" 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`"
351
} else if m.contains ( " connection refused" ) {
352
" nothing is listening there: check the URL and port, and that the server is running"
353
} else if m.contains ( " dns" )
354
| | m.contains ( " failed to lookup" )
355
| | m.contains ( " name or service not known" )
356
{
357
" the host name does not resolve: check the URL"
358
} else if m.contains ( " timed out" ) | | m.contains ( " timeout" ) {
359
" the server did not answer in time: check the network, or try again"
360
} else {
361
return None ;
362
} ;
363
Some ( hint)
364
}
365
366
367
pub fn with_hint ( message : String) -> String {
368
match hint_for ( & message) {
369
Some ( h) => format! ( " {message} \n hint: {h} " ) ,
370
None => message,
ff1f62fe Andy McCall 1 month ago ↶
371
}
372
}
373
374
375
376
377
pub fn http_json (
378
method : & str ,
379
url : & str ,
380
body : Option< & str > ,
381
options : & RemoteOptions,
382
) -> Result< ( u16 , String) > {
383
refuse_plain_token ( url, options) ? ;
384
let agent = ureq:: AgentBuilder:: new( )
385
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
386
.timeout ( std:: time:: Duration:: from_secs( 60 ) )
387
.build ( ) ;
388
let mut req = agent.request ( method, url) .set ( " Accept" , " application/json" ) ;
389
if let Some ( t) = & options.token {
390
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
391
}
392
let resp = match body {
393
Some ( b) => req.set ( " Content-Type" , " application/json" ) .send_string ( b) ,
394
None => req.call ( ) ,
395
} ;
396
match resp {
397
Ok ( r) => {
398
let code = r.status ( ) ;
399
Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) )
400
}
401
Err ( ureq:: Error:: Status( code, r) ) => Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) ) ,
402
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
3e5772cc Andy McCall 1 month ago ↶
403
}
404
}
405
d82e4409 Andy McCall 1 month ago ↶
406
407
408
pub fn http_get ( url : & str , options : & RemoteOptions) -> Result< Vec< u8 > > {
ff1f62fe Andy McCall 1 month ago ↶
409
refuse_plain_token ( url, options) ? ;
d82e4409 Andy McCall 1 month ago ↶
410
let agent = ureq:: AgentBuilder:: new( )
411
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
412
.build ( ) ;
413
let mut req = agent.get ( url) .set ( " Accept" , " application/json" ) ;
414
if let Some ( t) = & options.token {
415
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
416
}
417
match req.call ( ) {
418
Ok ( r) => {
419
let mut out = Vec :: new( ) ;
420
r.into_reader ( )
421
.read_to_end ( & mut out)
422
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
423
Ok ( out)
424
}
425
Err ( ureq:: Error:: Status( code, r) ) => {
426
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
427
Err ( RepoError:: Msg( with_hint ( format! (
4213c41d Andy McCall 1 month ago ↶
428
" {url} : HTTP {code} : {} " ,
429
text.trim ( )
3e5772cc Andy McCall 1 month ago ↶
430
) ) ) )
d82e4409 Andy McCall 1 month ago ↶
431
}
3e5772cc Andy McCall 1 month ago ↶
432
433
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
d82e4409 Andy McCall 1 month ago ↶
434
}
435
}
436
750251b2 Andy McCall 1 month ago ↶
437
impl Remote for HttpRemote {
438
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
439
wire:: decode_capabilities( & self .call ( false , " capabilities" , None ) ? )
440
}
83e0677b Andy McCall 1 month ago ↶
441
fn refs ( & self ) -> Result< RefsResponse> {
750251b2 Andy McCall 1 month ago ↶
442
Ok ( wire:: decode_refs( & self .call ( false , " refs" , None ) ? ) ? .1 )
443
}
444
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
445
wire:: decode_hashes( & self .call (
446
true ,
447
" negotiate" ,
448
Some ( & wire:: encode_negotiate( have, want) ) ,
449
) ? )
750251b2 Andy McCall 1 month ago ↶
450
}
b3c5a59a Andy McCall 1 month ago ↶
451
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
75167672 Andy McCall 1 month ago
452
wire:: decode_bundle( & self .call (
453
true ,
454
" fetch" ,
455
Some ( & wire:: encode_fetch( changes, max_blob) ) ,
456
) ? )
750251b2 Andy McCall 1 month ago ↶
457
}
b3c5a59a Andy McCall 1 month ago ↶
458
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
75167672 Andy McCall 1 month ago
459
Ok (
460
wire:: decode_bundle( & self .call ( true , " objects" , Some ( & wire:: encode_hashes( refs) ) ) ? ) ?
461
.objects,
462
)
b3c5a59a Andy McCall 1 month ago ↶
463
}
2863456a Andy McCall 1 month ago ↶
464
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
465
wire:: decode_bundle( & self .call ( true , " checkpoint" , Some ( & wire:: encode_hashes( heads) ) ) ? )
466
}
467
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
468
wire:: decode_hashes( & self .call (
469
true ,
470
" shallow" ,
471
Some ( & wire:: encode_shallow( heads, depth) ) ,
472
) ? )
2863456a Andy McCall 1 month ago ↶
473
}
75167672 Andy McCall 1 month ago
474
fn push (
475
& self ,
476
bundle : Bundle,
477
updates : Vec< RefUpdate> ,
478
policy : Option< SignedPolicy> ,
479
tags : Vec< TagRef> ,
480
) -> Result< Vec< RefResult> > {
481
wire:: decode_push_response( & self .call (
482
true ,
483
" push" ,
484
Some ( & wire:: encode_push(
485
& bundle,
486
& updates,
487
policy.as_ref ( ) ,
488
& tags,
489
) ) ,
490
) ? )
750251b2 Andy McCall 1 month ago ↶
491
}
492
}
493
494
2b5ee371 Andy McCall 1 month ago ↶
495
pub fn open_remote ( url : & str , options : & RemoteOptions) -> Result< Box< dyn Remote> > {
750251b2 Andy McCall 1 month ago ↶
496
if url.starts_with ( " http://" ) | | url.starts_with ( " https://" ) {
2b5ee371 Andy McCall 1 month ago ↶
497
return Ok ( Box :: new( HttpRemote:: new( url, options) ? ) ) ;
750251b2 Andy McCall 1 month ago ↶
498
}
5b9c6ad4 Andy McCall 1 month ago ↶
499
let path = url.strip_prefix ( " file://" ) .unwrap_or ( url) ;
500
if path.contains ( " ://" ) {
750251b2 Andy McCall 1 month ago ↶
501
return Err ( RepoError:: Msg( format! ( " unsupported remote URL {url:?} " ) ) ) ;
5b9c6ad4 Andy McCall 1 month ago ↶
502
}
84b9e9ac Andy McCall 1 month ago ↶
503
let mut local = LocalRemote:: open( Path:: new( path) ) ? ;
504
local.claimed_id = options.repo_id;
505
Ok ( Box :: new( local) )
5b9c6ad4 Andy McCall 1 month ago ↶
506
}
750251b2 Andy McCall 1 month ago ↶
507
508
509
510
511
pub mod wire {
512
use super :: * ;
513
514
fn schema ( msg : & str ) -> RepoError {
515
RepoError:: Object( ObjectError:: Schema( msg.into ( ) ) )
516
}
517
fn sigs_value ( sigs : & [Signature]) -> Value {
518
Value:: Array( sigs.iter ( ) .map ( | s | s.to_value ( ) ) .collect ( ) )
519
}
520
fn sigs_from ( v : & Value) -> Result< Vec< Signature> > {
75167672 Andy McCall 1 month ago
521
Ok ( o:: array( v, " signatures" ) ?
522
.iter ( )
523
.map ( Signature:: from_value)
524
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
525
}
526
fn hashes_value ( hs : & [ObjectRef]) -> Value {
527
Value:: Array( hs.iter ( ) .map ( | h | Value:: Bytes( h.0. to_vec ( ) ) ) .collect ( ) )
528
}
529
fn hashes_from ( v : & Value) -> Result< Vec< ObjectRef> > {
75167672 Andy McCall 1 month ago
530
Ok ( o:: array( v, " hashes" ) ?
531
.iter ( )
532
.map ( | h | o:: oref( h, " hash" ) )
533
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
534
}
535
fn attest_value ( sa : & SignedAttest) -> Value {
536
Value:: Array( vec! [ Value:: Bytes( sa.attest.encode ( ) ) , sigs_value ( & sa.sigs) ] )
537
}
538
fn attest_from ( v : & Value) -> Result< SignedAttest> {
539
let a = o:: array( v, " signed attest" ) ? ;
540
if a.len ( ) ! = 2 {
541
return Err ( schema ( " signed attest: expected [attest, sigs]" ) ) ;
542
}
75167672 Andy McCall 1 month ago
543
Ok ( SignedAttest {
544
attest: Attest:: decode( o:: bytes( & a[ 0 ] , " attest" ) ? ) ? ,
545
sigs: sigs_from ( & a[ 1 ] ) ? ,
546
} )
750251b2 Andy McCall 1 month ago ↶
547
}
548
fn opt_attest_value ( sa : & Option< SignedAttest> ) -> Value {
549
sa.as_ref ( ) .map ( attest_value) .unwrap_or ( Value:: Null)
550
}
551
fn opt_attest_from ( v : & Value) -> Result< Option< SignedAttest> > {
75167672 Andy McCall 1 month ago
552
if v.is_null ( ) {
553
Ok ( None )
554
} else {
555
attest_from ( v) .map ( Some )
556
}
750251b2 Andy McCall 1 month ago ↶
557
}
28fc23d7 Andy McCall 1 month ago ↶
558
559
560
pub fn policy_value ( sp : & SignedPolicy) -> Value {
561
Value:: Array( vec! [ Value:: Bytes( sp.policy.encode ( ) ) , sigs_value ( & sp.sigs) ] )
83e0677b Andy McCall 1 month ago ↶
562
}
28fc23d7 Andy McCall 1 month ago ↶
563
pub fn policy_from ( v : & Value) -> Result< SignedPolicy> {
83e0677b Andy McCall 1 month ago ↶
564
let a = o:: array( v, " signed policy" ) ? ;
565
if a.len ( ) ! = 2 {
566
return Err ( schema ( " signed policy: expected [policy, sigs]" ) ) ;
567
}
75167672 Andy McCall 1 month ago
568
Ok ( SignedPolicy {
569
policy: Policy:: decode( o:: bytes( & a[ 0 ] , " policy" ) ? ) ? ,
570
sigs: sigs_from ( & a[ 1 ] ) ? ,
571
} )
28fc23d7 Andy McCall 1 month ago ↶
572
}
573
fn opt_policy_value ( sp : Option< & SignedPolicy> ) -> Value {
574
sp.map ( policy_value) .unwrap_or ( Value:: Null)
575
}
576
fn opt_policy_from ( v : & Value) -> Result< Option< SignedPolicy> > {
75167672 Andy McCall 1 month ago
577
if v.is_null ( ) {
578
Ok ( None )
579
} else {
580
policy_from ( v) .map ( Some )
581
}
83e0677b Andy McCall 1 month ago ↶
582
}
750251b2 Andy McCall 1 month ago ↶
583
fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a o:: Map> {
584
Ok ( o:: map( v, what) ? )
585
}
586
fn field < 'a > ( m : & 'a o:: Map, k : u64 , what : & str ) -> Result< & 'a Value> {
587
Ok ( o:: field( m, k, what) ? )
588
}
589
6f8aeb99 Andy McCall 1 month ago ↶
590
591
592
750251b2 Andy McCall 1 month ago ↶
593
pub fn bundle_value ( b : & Bundle) -> Value {
2c75672f Andy McCall 1 month ago ↶
594
let mut changes = Vec :: with_capacity( b.raw_changes.len ( ) + b.changes.len ( ) ) ;
595
596
597
for ( bytes, s) in & b.raw_changes {
598
changes.push ( Value:: Array( vec! [
599
Value:: Bytes( bytes.clone ( ) ) ,
600
sigs_value ( s) ,
601
] ) ) ;
602
}
6f8aeb99 Andy McCall 1 month ago ↶
603
let mut texts: Vec< ( ObjectType, Vec< u8 > ) > = Vec :: new( ) ;
604
let mut listed: Option< std:: collections:: HashSet< ObjectRef> > = None ;
605
for ( c, s) in & b.changes {
606
let ( bytes, stored) = c.encode_parts ( ) ;
607
for t in stored {
608
let listed = listed.get_or_insert_with ( | | {
609
b.objects
610
.iter ( )
611
.map ( | ( ty , body ) | crate :: hash:: hash_object( * ty, body) )
612
.collect ( )
613
} ) ;
614
if ! listed.insert ( t.blob.hash ( ) ) {
615
continue ;
616
}
617
for chunk in crate :: chunk:: chunks( & t.content) {
618
if listed.insert ( crate :: hash:: hash_object( ObjectType:: Chunk, chunk) ) {
619
texts.push ( ( ObjectType:: Chunk, chunk.to_vec ( ) ) ) ;
620
}
621
}
622
texts.push ( ( ObjectType:: Blob, t.blob.encode ( ) ) ) ;
623
}
624
changes.push ( Value:: Array( vec! [ Value:: Bytes( bytes) , sigs_value ( s) ] ) ) ;
625
}
750251b2 Andy McCall 1 month ago ↶
626
Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
627
( 0 , Value:: Array( changes) ) ,
750251b2 Andy McCall 1 month ago ↶
628
(
75167672 Andy McCall 1 month ago
629
1 ,
630
Value:: Array( b.attests.iter ( ) .map ( attest_value) .collect ( ) ) ,
631
) ,
632
(
750251b2 Andy McCall 1 month ago ↶
633
2 ,
634
Value:: Array(
75167672 Andy McCall 1 month ago
635
b.objects
636
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
637
.chain ( & texts)
75167672 Andy McCall 1 month ago
638
.map ( | ( t , body ) | {
639
Value:: Array( vec! [
640
Value:: Text( t.name ( ) .into ( ) ) ,
641
Value:: Bytes( body.clone ( ) ) ,
642
] )
643
} )
644
.collect ( ) ,
750251b2 Andy McCall 1 month ago ↶
645
) ,
646
) ,
75167672 Andy McCall 1 month ago
647
(
648
3 ,
649
Value:: Array( b.policies.iter ( ) .map ( policy_value) .collect ( ) ) ,
650
) ,
750251b2 Andy McCall 1 month ago ↶
651
] )
652
}
653
654
pub fn bundle_from_value ( v : & Value) -> Result< Bundle> {
655
let m = map ( v, " bundle" ) ? ;
656
let mut b = Bundle:: default( ) ;
657
for ob in o:: array( field ( m, 2 , " objects" ) ? , " objects" ) ? {
658
let pair = o:: array( ob, " object" ) ? ;
659
if pair.len ( ) ! = 2 {
660
return Err ( schema ( " object: expected [type, body]" ) ) ;
661
}
75167672 Andy McCall 1 month ago
662
let ty = ObjectType:: from_name( o:: text( & pair[ 0 ] , " object type" ) ? )
663
.ok_or_else ( | | schema ( " unknown object type" ) ) ? ;
664
b.objects
665
.push ( ( ty, o:: bytes( & pair[ 1 ] , " object body" ) ? .to_vec ( ) ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
666
}
667
668
let mut by_ref: Option< std:: collections:: HashMap< ObjectRef, usize > > = None ;
669
for c in o:: array( field ( m, 0 , " changes" ) ? , " changes" ) ? {
670
let pair = o:: array( c, " change entry" ) ? ;
671
if pair.len ( ) ! = 2 {
672
return Err ( schema ( " change entry: expected [change, sigs]" ) ) ;
673
}
598009a7 Andy McCall 1 month ago ↶
674
let encoded = o:: bytes( & pair[ 0 ] , " change" ) ? ;
675
b.hashes
676
.push ( crate :: hash:: hash_object( ObjectType:: Change, encoded) ) ;
677
let change = Change:: decode_with( encoded, & mut | text, count| {
6f8aeb99 Andy McCall 1 month ago ↶
678
let by_ref = by_ref.get_or_insert_with ( | | {
679
b.objects
680
.iter ( )
681
.enumerate ( )
682
.map ( | ( i , (ty , body ) ) | ( crate :: hash:: hash_object( * ty, body) , i) )
683
.collect ( )
684
} ) ;
685
let get =
686
| r : & ObjectRef, want : ObjectType| match by_ref.get ( r) .map ( | i | & b.objects[ * i] ) {
687
Some ( ( ty, body) ) if * ty = = want => Ok ( body) ,
688
_ => Err ( ObjectError:: Schema( format! (
689
" the bundle lacks {} {} " ,
690
want.name ( ) ,
691
r.short ( 12 )
692
) ) ) ,
693
} ;
694
let blob = crate :: objects:: Blob:: decode( get ( text, ObjectType:: Blob) ? ) ? ;
695
let mut content = Vec :: with_capacity( blob.length as usize ) ;
696
for chunk in & blob.chunks {
697
content.extend_from_slice ( get ( chunk, ObjectType:: Chunk) ? ) ;
698
}
699
crate :: objects:: text_lines( & content, count)
700
} ) ? ;
701
b.changes.push ( ( change, sigs_from ( & pair[ 1 ] ) ? ) ) ;
702
}
703
for a in o:: array( field ( m, 1 , " attests" ) ? , " attests" ) ? {
704
b.attests.push ( attest_from ( a) ? ) ;
750251b2 Andy McCall 1 month ago ↶
705
}
28fc23d7 Andy McCall 1 month ago ↶
706
if let Some ( ps) = m.get ( & 3 ) {
707
for p in o:: array( ps, " policies" ) ? {
708
b.policies.push ( policy_from ( p) ? ) ;
709
}
710
}
750251b2 Andy McCall 1 month ago ↶
711
Ok ( b)
712
}
713
714
pub fn encode_bundle ( b : & Bundle) -> Vec< u8 > {
715
cbor:: encode( & bundle_value ( b) )
716
}
717
pub fn decode_bundle ( bytes : & [u8 ]) -> Result< Bundle> {
718
bundle_from_value ( & cbor:: decode( bytes) .map_err ( ObjectError:: from) ? )
719
}
720
721
pub fn encode_capabilities ( id : [u8 ; 16]) -> Vec< u8 > {
722
cbor:: encode( & Value:: map( [
723
( 0 , Value:: Array( vec! [ Value:: Uint( PROTOCOL_VERSION ) ] ) ) ,
724
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
725
( 2 , Value:: Array( vec! [ Value:: Text( " seal" .into ( ) ) ] ) ) ,
726
] ) )
727
}
728
pub fn decode_capabilities ( bytes : & [u8 ]) -> Result< [ u8 ; 16 ] > {
729
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
730
let m = map ( & v, " capabilities" ) ? ;
731
let versions = o:: array( field ( m, 0 , " versions" ) ? , " versions" ) ? ;
75167672 Andy McCall 1 month ago
732
if ! versions
733
.iter ( )
734
.any ( | x | x.as_uint ( ) = = Some ( PROTOCOL_VERSION ) )
735
{
736
return Err ( RepoError:: Msg(
737
" remote does not speak protocol version 1" .into ( ) ,
738
) ) ;
750251b2 Andy McCall 1 month ago ↶
739
}
75167672 Andy McCall 1 month ago
740
o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
741
.try_into ( )
742
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) )
750251b2 Andy McCall 1 month ago ↶
743
}
744
132b884b Andy McCall 1 month ago ↶
745
fn tags_value ( tags : & [TagRef]) -> Value {
746
Value:: Array(
747
tags.iter ( )
748
.map ( | t | {
749
Value:: map( [
750
( 0 , Value:: Text( t.name.clone ( ) ) ) ,
751
( 1 , Value:: Bytes( t.state.0. to_vec ( ) ) ) ,
752
( 2 , hashes_value ( & t.heads) ) ,
753
( 3 , opt_attest_value ( & t.attestation) ) ,
754
] )
755
} )
756
.collect ( ) ,
757
)
758
}
759
fn tags_from ( v : & Value) -> Result< Vec< TagRef> > {
760
let mut out = Vec :: new( ) ;
761
for t in o:: array( v, " tags" ) ? {
762
let tm = map ( t, " tag" ) ? ;
763
out.push ( TagRef {
764
name: o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
765
state: o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
766
heads: hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
767
attestation: opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
768
} ) ;
769
}
770
Ok ( out)
771
}
772
83e0677b Andy McCall 1 month ago ↶
773
pub fn encode_refs ( id : [u8 ; 16], resp : & RefsResponse) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
774
cbor:: encode( & Value:: map( [
775
(
776
0 ,
777
Value:: Array(
83e0677b Andy McCall 1 month ago ↶
778
resp.refs
779
.iter ( )
750251b2 Andy McCall 1 month ago ↶
780
.map ( | r | {
781
Value:: map( [
782
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
783
( 1 , Value:: Bytes( r.state.0. to_vec ( ) ) ) ,
784
( 2 , hashes_value ( & r.heads) ) ,
785
( 3 , opt_attest_value ( & r.attestation) ) ,
786
( 4 , r.last_serial.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
787
] )
788
} )
789
.collect ( ) ,
790
) ,
791
) ,
792
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
83e0677b Andy McCall 1 month ago ↶
793
( 2 , opt_policy_value ( resp.policy.as_ref ( ) ) ) ,
132b884b Andy McCall 1 month ago ↶
794
( 3 , tags_value ( & resp.tags) ) ,
75167672 Andy McCall 1 month ago
795
(
796
4 ,
797
Value:: Array( resp.history.iter ( ) .map ( policy_value) .collect ( ) ) ,
798
) ,
750251b2 Andy McCall 1 month ago ↶
799
] ) )
800
}
83e0677b Andy McCall 1 month ago ↶
801
pub fn decode_refs ( bytes : & [u8 ]) -> Result< ( [u8 ; 16], RefsResponse) > {
750251b2 Andy McCall 1 month ago ↶
802
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
803
let m = map ( & v, " refs" ) ? ;
75167672 Andy McCall 1 month ago
804
let id: [ u8 ; 16 ] = o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
805
.try_into ( )
806
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
807
let mut refs = Vec :: new( ) ;
808
for r in o:: array( field ( m, 0 , " refs" ) ? , " refs" ) ? {
809
let rm = map ( r, " ref" ) ? ;
810
let ls = field ( rm, 4 , " last serial" ) ? ;
811
refs.push ( RemoteRef {
812
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
813
state: o:: oref( field ( rm, 1 , " state" ) ? , " state" ) ? ,
814
heads: hashes_from ( field ( rm, 2 , " heads" ) ? ) ? ,
815
attestation: opt_attest_from ( field ( rm, 3 , " attestation" ) ? ) ? ,
75167672 Andy McCall 1 month ago
816
last_serial: if ls.is_null ( ) {
817
None
818
} else {
819
Some ( o:: uint( ls, " last serial" ) ? )
820
} ,
750251b2 Andy McCall 1 month ago ↶
821
} ) ;
822
}
83e0677b Andy McCall 1 month ago ↶
823
let policy = match m.get ( & 2 ) {
824
Some ( p) => opt_policy_from ( p) ? ,
825
None => None ,
826
} ;
132b884b Andy McCall 1 month ago ↶
827
let tags = match m.get ( & 3 ) {
828
Some ( t) => tags_from ( t) ? ,
829
None => Vec :: new( ) ,
830
} ;
28fc23d7 Andy McCall 1 month ago ↶
831
let mut history = Vec :: new( ) ;
832
if let Some ( h) = m.get ( & 4 ) {
833
for p in o:: array( h, " history" ) ? {
834
history.push ( policy_from ( p) ? ) ;
835
}
836
}
75167672 Andy McCall 1 month ago
837
Ok ( (
838
id,
839
RefsResponse {
840
refs,
841
policy,
842
tags,
843
history,
844
} ,
845
) )
750251b2 Andy McCall 1 month ago ↶
846
}
847
848
pub fn encode_negotiate ( have : & [ChangeHash], want : & [ChangeHash]) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
849
cbor:: encode( & Value:: map( [
850
( 0 , hashes_value ( have) ) ,
851
( 1 , hashes_value ( want) ) ,
852
] ) )
750251b2 Andy McCall 1 month ago ↶
853
}
854
pub fn decode_negotiate ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Vec< ChangeHash> ) > {
855
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
856
let m = map ( & v, " negotiate" ) ? ;
75167672 Andy McCall 1 month ago
857
Ok ( (
858
hashes_from ( field ( m, 0 , " have" ) ? ) ? ,
859
hashes_from ( field ( m, 1 , " want" ) ? ) ? ,
860
) )
750251b2 Andy McCall 1 month ago ↶
861
}
862
b3c5a59a Andy McCall 1 month ago ↶
863
864
pub fn encode_fetch ( hs : & [ChangeHash], max_blob : Option< u64 > ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
865
cbor:: encode( & Value:: map( [
866
( 0 , hashes_value ( hs) ) ,
867
( 1 , max_blob.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
868
] ) )
b3c5a59a Andy McCall 1 month ago ↶
869
}
870
pub fn decode_fetch ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Option< u64 > ) > {
871
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
872
let m = map ( & v, " fetch" ) ? ;
873
let max = match m.get ( & 1 ) {
874
Some ( x) if ! x.is_null ( ) => Some ( o:: uint( x, " max blob" ) ? ) ,
875
_ => None ,
876
} ;
877
Ok ( ( hashes_from ( field ( m, 0 , " changes" ) ? ) ? , max) )
878
}
879
2863456a Andy McCall 1 month ago ↶
880
881
pub fn encode_shallow ( hs : & [ChangeHash], depth : u32 ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
882
cbor:: encode( & Value:: map( [
883
( 0 , hashes_value ( hs) ) ,
884
( 1 , Value:: Uint( depth as u64 ) ) ,
885
] ) )
2863456a Andy McCall 1 month ago ↶
886
}
887
pub fn decode_shallow ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , u32 ) > {
888
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
889
let m = map ( & v, " shallow" ) ? ;
75167672 Andy McCall 1 month ago
890
Ok ( (
891
hashes_from ( field ( m, 0 , " heads" ) ? ) ? ,
892
o:: u32_( field ( m, 1 , " depth" ) ? , " depth" ) ? ,
893
) )
2863456a Andy McCall 1 month ago ↶
894
}
895
750251b2 Andy McCall 1 month ago ↶
896
pub fn encode_hashes ( hs : & [ChangeHash]) -> Vec< u8 > {
897
cbor:: encode( & Value:: map( [ ( 0 , hashes_value ( hs) ) ] ) )
898
}
899
pub fn decode_hashes ( bytes : & [u8 ]) -> Result< Vec< ChangeHash> > {
900
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
901
hashes_from ( field ( map ( & v, " hashes" ) ? , 0 , " hashes" ) ? )
902
}
903
75167672 Andy McCall 1 month ago
904
pub fn encode_push (
905
bundle : & Bundle,
906
updates : & [RefUpdate],
907
policy : Option< & SignedPolicy> ,
908
tags : & [TagRef],
909
) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
910
cbor:: encode( & Value:: map( [
911
( 0 , bundle_value ( bundle) ) ,
83e0677b Andy McCall 1 month ago ↶
912
( 2 , opt_policy_value ( policy) ) ,
132b884b Andy McCall 1 month ago ↶
913
( 3 , tags_value ( tags) ) ,
750251b2 Andy McCall 1 month ago ↶
914
(
915
1 ,
916
Value:: Array(
917
updates
918
.iter ( )
919
.map ( | u | {
920
Value:: map( [
921
( 0 , Value:: Text( u.name.clone ( ) ) ) ,
922
( 1 , Value:: Bytes( u.old_state.0. to_vec ( ) ) ) ,
923
( 2 , Value:: Bytes( u.new_state.0. to_vec ( ) ) ) ,
924
( 3 , Value:: Bool( u.force) ) ,
925
( 4 , opt_attest_value ( & u.attestation) ) ,
926
] )
927
} )
928
.collect ( ) ,
929
) ,
930
) ,
931
] ) )
932
}
132b884b Andy McCall 1 month ago ↶
933
934
pub type PushParts = ( Bundle, Vec< RefUpdate> , Option< SignedPolicy> , Vec< TagRef> ) ;
935
936
pub fn decode_push ( bytes : & [u8 ]) -> Result< PushParts> {
750251b2 Andy McCall 1 month ago ↶
937
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
938
let m = map ( & v, " push" ) ? ;
939
let bundle = bundle_from_value ( field ( m, 0 , " bundle" ) ? ) ? ;
940
let mut updates = Vec :: new( ) ;
941
for u in o:: array( field ( m, 1 , " updates" ) ? , " updates" ) ? {
942
let um = map ( u, " update" ) ? ;
943
updates.push ( RefUpdate {
944
name: o:: text( field ( um, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
945
old_state: o:: oref( field ( um, 1 , " old_state" ) ? , " old_state" ) ? ,
946
new_state: o:: oref( field ( um, 2 , " new_state" ) ? , " new_state" ) ? ,
75167672 Andy McCall 1 month ago
947
force: field ( um, 3 , " force" ) ?
948
.as_bool ( )
949
.ok_or_else ( | | schema ( " force: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
950
attestation: opt_attest_from ( field ( um, 4 , " attestation" ) ? ) ? ,
951
} ) ;
952
}
83e0677b Andy McCall 1 month ago ↶
953
let policy = match m.get ( & 2 ) {
954
Some ( p) => opt_policy_from ( p) ? ,
955
None => None ,
956
} ;
132b884b Andy McCall 1 month ago ↶
957
let tags = match m.get ( & 3 ) {
958
Some ( t) => tags_from ( t) ? ,
959
None => Vec :: new( ) ,
960
} ;
961
Ok ( ( bundle, updates, policy, tags) )
750251b2 Andy McCall 1 month ago ↶
962
}
963
964
pub fn encode_push_response ( results : & [RefResult]) -> Vec< u8 > {
965
cbor:: encode( & Value:: map( [ (
966
0 ,
967
Value:: Array(
968
results
969
.iter ( )
75167672 Andy McCall 1 month ago
970
.map ( | r | {
971
Value:: map( [
972
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
973
( 1 , Value:: Bool( r.ok) ) ,
974
( 2 , Value:: Text( r.reason.clone ( ) ) ) ,
975
] )
976
} )
750251b2 Andy McCall 1 month ago ↶
977
.collect ( ) ,
978
) ,
979
) ] ) )
980
}
981
pub fn decode_push_response ( bytes : & [u8 ]) -> Result< Vec< RefResult> > {
982
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
983
let m = map ( & v, " push response" ) ? ;
984
let mut out = Vec :: new( ) ;
985
for r in o:: array( field ( m, 0 , " results" ) ? , " results" ) ? {
986
let rm = map ( r, " result" ) ? ;
987
out.push ( RefResult {
988
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
75167672 Andy McCall 1 month ago
989
ok: field ( rm, 1 , " ok" ) ?
990
.as_bool ( )
991
.ok_or_else ( | | schema ( " ok: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
992
reason: o:: text( field ( rm, 2 , " reason" ) ? , " reason" ) ? .to_owned ( ) ,
993
} ) ;
994
}
995
Ok ( out)
996
}
997
}
998
999
# [ cfg ( test ) ]
1000
mod tests {
1001
use super :: * ;
1002
use crate :: attest:: AttestBody;
1003
use crate :: objects:: Hlc;
1004
1005
# [ test ]
1006
fn wire_round_trips ( ) {
1007
let c = crate :: objects:: tests:: sample_change( ) ;
75167672 Andy McCall 1 month ago
1008
let sk = crate :: identity:: SecretKey {
1009
ed_seed: [ 1 ; 32 ] ,
1010
ml_seed: [ 2 ; 32 ] ,
1011
} ;
750251b2 Andy McCall 1 month ago ↶
1012
let id = sk.identity ( " a" , " b" , 0 ) ;
1013
let key = id.hash ( ) ;
1014
let sig = sk.sign ( crate :: identity:: Domain:: Change, & key, & c.hash ( ) ) ;
1015
let seal = Attest:: seal( [ 3 ; 16 ] , vec! [ c.hash ( ) ] ) ;
1016
let seal_sig = sk.sign ( crate :: identity:: Domain:: Attest, & key, & seal.hash ( ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
1017
let policy = Policy:: single_root( [ 3 ; 16 ] , id.clone ( ) , 99 ) ;
1018
let psig = sk.sign ( crate :: identity:: Domain:: Policy, & key, & policy.hash ( ) ) ;
750251b2 Andy McCall 1 month ago ↶
1019
let bundle = Bundle {
1020
changes: vec! [ ( c.clone ( ) , vec! [ sig.clone ( ) ] ) ] ,
75167672 Andy McCall 1 month ago
1021
attests: vec! [ SignedAttest {
1022
attest: seal.clone ( ) ,
1023
sigs: vec! [ seal_sig] ,
1024
} ] ,
1025
objects: vec! [
1026
( ObjectType:: Identity, id.encode ( ) ) ,
1027
( ObjectType:: Chunk, b " xyz" .to_vec ( ) ) ,
1028
] ,
1029
policies: vec! [ SignedPolicy {
1030
policy: policy.clone ( ) ,
1031
sigs: vec! [ psig.clone ( ) ] ,
1032
} ] ,
598009a7 Andy McCall 1 month ago ↶
1033
hashes: vec! [ ] ,
2c75672f Andy McCall 1 month ago ↶
1034
raw_changes: vec! [ ] ,
750251b2 Andy McCall 1 month ago ↶
1035
} ;
1036
let d = wire:: decode_bundle( & wire:: encode_bundle( & bundle) ) .unwrap ( ) ;
598009a7 Andy McCall 1 month ago ↶
1037
assert_eq! (
1038
d.hashes,
1039
vec! [ c.hash ( ) ] ,
1040
" the receiver hashes what it was sent"
1041
) ;
750251b2 Andy McCall 1 month ago ↶
1042
assert_eq! ( d.changes, bundle.changes) ;
1043
assert_eq! ( d.attests, bundle.attests) ;
1044
assert_eq! ( d.objects, bundle.objects) ;
28fc23d7 Andy McCall 1 month ago ↶
1045
assert_eq! ( d.policies, bundle.policies) ;
750251b2 Andy McCall 1 month ago ↶
1046
75167672 Andy McCall 1 month ago
1047
let att = Attest {
1048
repo: [ 3 ; 16 ] ,
1049
hlc: Hlc:: default( ) ,
1050
body: AttestBody:: Ref {
1051
name: " main" .into ( ) ,
1052
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1053
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1054
serial: 4 ,
1055
} ,
1056
} ;
750251b2 Andy McCall 1 month ago ↶
1057
let refs = vec! [
75167672 Andy McCall 1 month ago
1058
RemoteRef {
1059
name: " main" .into ( ) ,
1060
state: ObjectRef( [ 2 ; 32 ] ) ,
1061
heads: vec! [ c.hash ( ) ] ,
1062
attestation: Some ( SignedAttest {
1063
attest: att.clone ( ) ,
1064
sigs: vec! [ sig.clone ( ) ] ,
1065
} ) ,
1066
last_serial: Some ( 4 ) ,
1067
} ,
1068
RemoteRef {
1069
name: " dev" .into ( ) ,
1070
state: ObjectRef( [ 5 ; 32 ] ) ,
1071
heads: vec! [ ] ,
1072
attestation: None ,
1073
last_serial: None ,
1074
} ,
750251b2 Andy McCall 1 month ago ↶
1075
] ;
75167672 Andy McCall 1 month ago
1076
let tag = TagRef {
1077
name: " v1" .into ( ) ,
1078
state: ObjectRef( [ 2 ; 32 ] ) ,
1079
heads: vec! [ c.hash ( ) ] ,
1080
attestation: None ,
1081
} ;
1082
let resp = RefsResponse {
1083
refs,
1084
policy: Some ( SignedPolicy {
1085
policy: policy.clone ( ) ,
1086
sigs: vec! [ psig.clone ( ) ] ,
1087
} ) ,
1088
tags: vec! [ tag.clone ( ) ] ,
1089
history: vec! [ SignedPolicy {
1090
policy: policy.clone ( ) ,
1091
sigs: vec! [ ] ,
1092
} ] ,
1093
} ;
83e0677b Andy McCall 1 month ago ↶
1094
let ( id_back, back) = wire:: decode_refs( & wire:: encode_refs( [ 3 ; 16 ] , & resp) ) .unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1095
assert_eq! ( id_back, [ 3 ; 16 ] ) ;
83e0677b Andy McCall 1 month ago ↶
1096
assert_eq! ( back.refs.len ( ) , 2 ) ;
1097
assert_eq! ( back.refs[ 0 ] .attestation.as_ref ( ) .unwrap ( ) .attest, att) ;
1098
assert_eq! ( back.refs[ 1 ] .last_serial, None ) ;
1099
assert_eq! ( back.policy.as_ref ( ) .unwrap ( ) .policy, policy) ;
28fc23d7 Andy McCall 1 month ago ↶
1100
assert_eq! ( back.history, resp.history) ;
132b884b Andy McCall 1 month ago ↶
1101
assert_eq! ( back.tags, vec! [ tag.clone ( ) ] ) ;
750251b2 Andy McCall 1 month ago ↶
1102
75167672 Andy McCall 1 month ago
1103
let ( h, w) = wire:: decode_negotiate( & wire:: encode_negotiate(
1104
& [ ObjectRef( [ 1 ; 32 ] ) ] ,
1105
& [ ObjectRef( [ 2 ; 32 ] ) ] ,
1106
) )
1107
.unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1108
assert_eq! ( ( h, w) , ( vec! [ ObjectRef( [ 1 ; 32 ] ) ] , vec! [ ObjectRef( [ 2 ; 32 ] ) ] ) ) ;
75167672 Andy McCall 1 month ago
1109
assert_eq! (
1110
wire:: decode_hashes( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1111
vec! [ c.hash ( ) ]
1112
) ;
1113
assert_eq! (
1114
wire:: decode_fetch( & wire:: encode_fetch( & [ c.hash ( ) ] , Some ( 4096 ) ) ) .unwrap ( ) ,
1115
( vec! [ c.hash ( ) ] , Some ( 4096 ) )
1116
) ;
1117
assert_eq! (
1118
wire:: decode_fetch( & wire:: encode_fetch( & [ ] , None ) ) .unwrap ( ) ,
1119
( vec! [ ] , None )
1120
) ;
1121
assert_eq! (
1122
wire:: decode_shallow( & wire:: encode_shallow( & [ c.hash ( ) ] , 3 ) ) .unwrap ( ) ,
1123
( vec! [ c.hash ( ) ] , 3 )
1124
) ;
b3c5a59a Andy McCall 1 month ago ↶
1125
75167672 Andy McCall 1 month ago
1126
assert_eq! (
1127
wire:: decode_fetch( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1128
( vec! [ c.hash ( ) ] , None )
1129
) ;
750251b2 Andy McCall 1 month ago ↶
1130
75167672 Andy McCall 1 month ago
1131
let up = RefUpdate {
1132
name: " main" .into ( ) ,
1133
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1134
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1135
force: true ,
1136
attestation: Some ( SignedAttest {
1137
attest: att,
1138
sigs: vec! [ sig] ,
1139
} ) ,
1140
} ;
1141
let sp = SignedPolicy {
1142
policy,
1143
sigs: vec! [ psig] ,
1144
} ;
1145
let ( b2, ups, pol, tags) = wire:: decode_push( & wire:: encode_push(
1146
& bundle,
1147
std:: slice:: from_ref( & up) ,
1148
Some ( & sp) ,
1149
std:: slice:: from_ref( & tag) ,
1150
) )
1151
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1152
assert_eq! ( pol, Some ( sp) ) ;
132b884b Andy McCall 1 month ago ↶
1153
assert_eq! ( tags, vec! [ tag] ) ;
750251b2 Andy McCall 1 month ago ↶
1154
assert_eq! ( b2.changes.len ( ) , 1 ) ;
1155
assert_eq! ( ups[ 0 ] .name, " main" ) ;
1156
assert! ( ups[ 0 ] .force) ;
1157
assert_eq! ( ups[ 0 ] .attestation, up.attestation) ;
75167672 Andy McCall 1 month ago
1158
let res = vec! [ RefResult {
1159
name: " main" .into ( ) ,
1160
ok: false ,
1161
reason: " stale" .into ( ) ,
1162
} ] ;
1163
assert_eq! (
1164
wire:: decode_push_response( & wire:: encode_push_response( & res) ) .unwrap ( ) ,
1165
res
1166
) ;
1167
assert_eq! (
1168
wire:: decode_capabilities( & wire:: encode_capabilities( [ 7 ; 16 ] ) ) .unwrap ( ) ,
1169
[ 7 ; 16 ]
1170
) ;
750251b2 Andy McCall 1 month ago ↶
1171
}
1172
}