tool/bls12381_bench.dart times, on the VM or compiled to JavaScript, the decoding of a point of G1 and of G2, a pairing, the verification of a Quicknet round signature, the IBE decryption and encryption, and steps 10 and 11 of the opening together, as the plan asks in «Rendimiento». It reads no file, so that a phone can run it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>v0.11
parent
719a703d84
commit
2bfd6aa2bc
@ -0,0 +1,122 @@
|
||||
// Times the BLS12-381 and tlock code of stage 3 (docs/PLAN_dart.md,
|
||||
// «Rendimiento»): decoding a point of G1 and of G2, one pairing, the
|
||||
// verification of a Quicknet round signature (hash to G1 and the pairing
|
||||
// check), the IBE decryption of a tlock stanza, the IBE encryption, and
|
||||
// steps 10 and 11 of the opening together. On the VM:
|
||||
//
|
||||
// dart run tool/bls12381_bench.dart
|
||||
//
|
||||
// and compiled to JavaScript, on Node.js, where dart2js reaches the random
|
||||
// generator through self:
|
||||
//
|
||||
// dart compile js -O2 -o bench.js tool/bls12381_bench.dart
|
||||
// node -e "globalThis.self = globalThis; require('./bench.js')"
|
||||
//
|
||||
// Each figure is the median of several runs after one to warm up. The
|
||||
// program reads no file, so that it runs on the web too.
|
||||
// ignore_for_file: avoid_print
|
||||
library;
|
||||
|
||||
import 'package:datekeys/datekeys.dart' show fromHex;
|
||||
import 'package:datekeys/src/bls12381_curve.dart';
|
||||
import 'package:datekeys/src/bls12381_pairing.dart';
|
||||
import 'package:datekeys/src/ibe.dart';
|
||||
import 'package:datekeys/src/release.dart';
|
||||
import 'package:datekeys/src/tlock.dart';
|
||||
|
||||
// The pinned Quicknet profile, as testdata/vectors/profile_quicknet.json.
|
||||
final class _Quicknet implements PinnedProfile {
|
||||
@override
|
||||
String get id => 'datekeys:quicknet:v1';
|
||||
|
||||
@override
|
||||
String get scheme => quicknetScheme;
|
||||
|
||||
@override
|
||||
final publicKey = fromHex(
|
||||
'83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b'
|
||||
'6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809'
|
||||
'bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a',
|
||||
);
|
||||
|
||||
@override
|
||||
final chainHash = fromHex(
|
||||
'52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971',
|
||||
);
|
||||
|
||||
@override
|
||||
int get maxRound => 83903165811;
|
||||
}
|
||||
|
||||
// The releases of rounds 1000 and 1001 (testdata/fixtures/time_only.json and
|
||||
// empty_payload.json). The runs alternate between them: the last release
|
||||
// verified is kept (release.dart), and verifying it again costs no pairing.
|
||||
final _signatures = {
|
||||
1000: fromHex(
|
||||
'b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a'
|
||||
'8dd2bacbe47e4b6b63ed5e39',
|
||||
),
|
||||
1001: fromHex(
|
||||
'b33bf3667cbd5a82de3a24b4e0e9fe5513cc1a0e840368c6e31f5fcfa79bea03f73896b2'
|
||||
'5883abf2853d10337fb8fa41',
|
||||
),
|
||||
};
|
||||
|
||||
double _median(List<double> xs) => (xs..sort())[xs.length ~/ 2];
|
||||
|
||||
void _time(String name, int runs, void Function(int round) body) {
|
||||
body(1001);
|
||||
final ms = <double>[];
|
||||
for (var i = 0; i < runs; i++) {
|
||||
final round = i.isEven ? 1000 : 1001;
|
||||
final sw = Stopwatch()..start();
|
||||
body(round);
|
||||
ms.add(sw.elapsedMicroseconds / 1000);
|
||||
}
|
||||
print('${name.padRight(48)} ${_median(ms).toStringAsFixed(1)} ms');
|
||||
}
|
||||
|
||||
void main(List<String> args) {
|
||||
final runs = args.isEmpty ? 7 : int.parse(args.first);
|
||||
final p = _Quicknet();
|
||||
Release release(int round) => Release(round, _signatures[round]!);
|
||||
final sig = G1Point.decode(_signatures[1000]!)!;
|
||||
final key = G2Point.decode(p.publicKey)!;
|
||||
final message = fromHex('00112233445566778899aabbccddeeff');
|
||||
final ct = {
|
||||
for (final round in [1000, 1001])
|
||||
round: encryptOnG2(p.publicKey, roundIdentity(round), message),
|
||||
};
|
||||
final stanza = {
|
||||
for (final round in [1000, 1001]) round: wrapTlockStanza(p, round, message),
|
||||
};
|
||||
_time('decode a point of G1 (the signature)', runs, (round) {
|
||||
G1Point.decode(_signatures[round]!);
|
||||
});
|
||||
_time('decode a point of G2 (U)', runs, (round) {
|
||||
G2Point.decode(ct[round]!.u);
|
||||
});
|
||||
_time('one pairing', runs, (_) => pairing(sig, key));
|
||||
// The pinned key is decoded once and kept (pinnedKey of release.dart): the
|
||||
// first verification pays for it.
|
||||
_time('verify a Quicknet round signature, key kept', runs, (round) {
|
||||
verifyRelease(p, round, release(round));
|
||||
});
|
||||
_time('the same, decoding the pinned key', runs, (round) {
|
||||
G2Point.decode(p.publicKey);
|
||||
verifyRelease(p, round, release(round));
|
||||
});
|
||||
_time('IBE decryption of a tlock stanza', runs, (round) {
|
||||
decryptOnG2(_signatures[round]!, ct[round]!);
|
||||
});
|
||||
_time('IBE encryption', runs, (round) {
|
||||
encryptOnG2(p.publicKey, roundIdentity(round), message);
|
||||
});
|
||||
// Steps 10 and 11 of the opening: the release verified, then the stanza
|
||||
// unwrapped, which verifies the same release again, as Go does.
|
||||
_time('steps 10 and 11: verify, then unwrap the stanza', runs, (round) {
|
||||
verifyRelease(p, round, release(round));
|
||||
final (args, body) = stanza[round]!;
|
||||
unwrapTlockStanza(p, round, release(round), args, body);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in new issue