You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dateKeys-dart/test/locator_support.dart

162 lines
5.3 KiB

// Helpers of the tests of the locator (stage 7a): the vectors of
// tool/locator_go_vectors.go, their edits and their compact addresses, and
// what a reader reads of a locator, as the generator writes it. They read no
// file, so that the tests that run on Node.js can use them.
library;
import 'dart:convert';
import 'dart:typed_data';
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/locator.dart';
import 'package:datekeys/src/profile.dart';
import 'package:datekeys/src/release.dart';
import 'package:datekeys/src/sha256.dart';
typedef Json = Map<String, Object?>;
/// The list [key] of [v], each item a list.
List<List<Object?>> rows(Json v, String key) => [
for (final r in v[key]! as List<Object?>) r! as List<Object?>,
];
/// The text [i] of the table of [v]: `''` for no error.
String textOf(Json v, Object? i) =>
(v['texts']! as List<Object?>)[i! as int]! as String;
/// An address as the generator writes it: the string, or [before, byte,
/// count, after] for one with a run of count copies of a byte.
String uriOf(Object? x) {
if (x is String) return x;
final l = x! as List<Object?>;
return '${l[0]}${(l[1]! as String) * (l[2]! as int)}${l[3]}';
}
/// The edits [edits] of [base] applied in one pass: each [at, delete,
/// insert] replaces delete bytes at the offset at of the base with the bytes
/// of the hexadecimal insert, and [at, delete, byte, count] with count
/// copies of the byte. The offsets are those of the base, in order.
Uint8List applyLocatorEdits(List<int> base, Object? edits) {
final out = BytesBuilder(copy: false);
var pos = 0;
for (final e in (edits! as List<Object?>).cast<List<Object?>>()) {
final at = e[0]! as int;
final delete = e[1]! as int;
if (at < pos || at + delete > base.length) {
throw StateError('edit at $at out of order or beyond the base');
}
out.add(base.sublist(pos, at));
final insert = fromHex(e[2]! as String);
final repeat = e.length > 3 ? e[3]! as int : 1;
for (var i = 0; i < repeat; i++) {
out.add(insert);
}
pos = at + delete;
}
out.add(base.sublist(pos));
return out.takeBytes();
}
/// The lower-case hexadecimal SHA-256 of [b].
String sha256Hex(List<int> b) => toHex(sha256(b));
/// What a reader reads of [l], as the generator writes it: [[uri, offset,
/// host, usable]...], the key, the SHA-256 of the header, the digest of the
/// rest, its size and capsule_digest, with each address in full.
List<Object?> summaryOf(Locator l) => [
[
for (final a in l.addresses)
[a.uri, a.offset, a.host, l.usable.contains(a)],
],
toHex(l.envelopeKey),
sha256Hex(l.envelopeHeader),
toHex(l.restDigest),
l.restSize,
toHex(l.capsuleDigest),
];
/// The summary [s] of the generator, with each address in full.
List<Object?> expandSummary(Object? s) {
final l = s! as List<Object?>;
return [
[
for (final a in (l[0]! as List<Object?>).cast<List<Object?>>())
[uriOf(a[0]), a[1], uriOf(a[2]), a[3]],
],
...l.sublist(1),
];
}
/// The release of [round] of the vectors [v], public data of drand.
Release releaseOf(Json v, int round) {
final r = (v['releases']! as Json)['$round']! as String;
return Release(round, fromHex(r));
}
/// The pinned Quicknet profile, edited as the generator names the edit.
Profile profileOf(String edit) {
final p = quicknet();
switch (edit) {
case '':
return p;
case 'key not on the curve':
final k = Uint8List.fromList(p.publicKey);
k[k.length - 1] ^= 1;
return p.copyWith(publicKey: k);
case 'key at infinity':
return p.copyWith(publicKey: [0xc0, ...List.filled(95, 0)]);
case 'key of another network':
// The generator of G2, compressed.
return p.copyWith(
publicKey: fromHex(
'93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049'
'334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051'
'c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8',
),
);
case 'chain hash of another network':
final h = Uint8List.fromList(p.chainHash);
h[0] ^= 1;
return p.copyWith(chainHash: h);
}
throw ArgumentError(edit);
}
/// The release that an open case names: a round, or an edit of the release
/// of the case.
Release openRelease(Json v, int round, Object? release) {
if (release is int) return releaseOf(v, release);
final r = releaseOf(v, round);
switch (release) {
case 'flipped':
final s = Uint8List.fromList(r.signature);
s[s.length - 1] ^= 1;
return Release(round, s);
case 'short':
return Release(round, r.signature.sublist(0, 47));
case 'other round':
return Release(round, releaseOf(v, 1001).signature);
}
throw ArgumentError('$release');
}
/// The text of the error that [body] throws, `''` when it returns: a
/// [LocatorException] or a DateKeysException by its message.
String errorText(void Function() body) {
try {
body();
return '';
} on LocatorException catch (e) {
return e.message;
} on ArgumentError catch (e) {
// An error of the caller without a normative code, as checkWrite
// reports an extension out of its place.
return '${e.message}';
} on Exception catch (e) {
return '$e';
}
}
/// The JSON of a constant of test/vectors.
Json decodeJson(String s) => jsonDecode(s) as Json;

Powered by TurnKey Linux.