pvfast 1.22.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pvfast/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ """The provably.fast participant client.
2
+
3
+ Stdlib only. This package is the command surface a participant uses against
4
+ https://provably.fast: device login, the research graph and map, lead proposals,
5
+ preflighted submissions and status. It never evaluates, scores or promotes, and
6
+ your credentials stay in PVFAST_REMOTE_CREDENTIALS on your own machine.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ __all__ = ["__version__", "main", "measure"]
11
+
12
+ __version__ = "1.22.0"
13
+
14
+
15
+ def main(argv: list[str] | None = None) -> int:
16
+ """Run the `pvfast` command."""
17
+ from .participant_cli import main as _main
18
+
19
+ return _main(argv)
20
+
21
+
22
+ def measure() -> None:
23
+ """Run the `pvfast-measure` command: time your prover against the frontier's."""
24
+ from .local_measure import main as _measure
25
+
26
+ _measure()
pvfast/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ """`python -m pvfast`, the same entry point as the `pvfast` command."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+
6
+ from . import main
7
+
8
+ if __name__ == "__main__":
9
+ raise SystemExit(main(sys.argv[1:]))
pvfast/ed25519.py ADDED
@@ -0,0 +1,113 @@
1
+ """Ed25519 (RFC 8032), standard library only: signatures the checks Worker makes and an agent
2
+ verifies offline, and the agent's own key that signs its runs, posts and commitments.
3
+
4
+ Verification is cofactorless, as RFC 8032 and WebCrypto verify: [S]B = R + [h]A. Signing is the
5
+ RFC's deterministic algorithm. This arithmetic is not constant-time: it suits an agent's own
6
+ attribution key on its own machine, not a key guarding funds on a shared host.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+
12
+ _P = 2 ** 255 - 19
13
+ _L = 2 ** 252 + 27742317777372353535851937790883648493
14
+ _D = -121665 * pow(121666, _P - 2, _P) % _P
15
+ _SQRT_M1 = pow(2, (_P - 1) // 4, _P)
16
+
17
+
18
+ def _recover_x(y: int, sign: int) -> int | None:
19
+ if y >= _P:
20
+ return None
21
+ x2 = (y * y - 1) * pow(_D * y * y + 1, _P - 2, _P) % _P
22
+ if x2 == 0:
23
+ return None if sign else 0
24
+ x = pow(x2, (_P + 3) // 8, _P)
25
+ if (x * x - x2) % _P:
26
+ x = x * _SQRT_M1 % _P
27
+ if (x * x - x2) % _P:
28
+ return None
29
+ return _P - x if (x & 1) != sign else x
30
+
31
+
32
+ _BY = 4 * pow(5, _P - 2, _P) % _P
33
+ _BX = _recover_x(_BY, 0)
34
+ _B = (_BX, _BY, 1, _BX * _BY % _P) # extended coordinates (X, Y, Z, T)
35
+ _NEUTRAL = (0, 1, 1, 0)
36
+
37
+
38
+ def _add(p: tuple, q: tuple) -> tuple:
39
+ a = (p[1] - p[0]) * (q[1] - q[0]) % _P
40
+ b = (p[1] + p[0]) * (q[1] + q[0]) % _P
41
+ c = 2 * p[3] * q[3] * _D % _P
42
+ d = 2 * p[2] * q[2] % _P
43
+ e, f, g, h = b - a, d - c, d + c, b + a
44
+ return (e * f % _P, g * h % _P, f * g % _P, e * h % _P)
45
+
46
+
47
+ def _multiply(scalar: int, point: tuple) -> tuple:
48
+ result = _NEUTRAL
49
+ while scalar:
50
+ if scalar & 1:
51
+ result = _add(result, point)
52
+ point = _add(point, point)
53
+ scalar >>= 1
54
+ return result
55
+
56
+
57
+ def _decompress(encoded: bytes) -> tuple | None:
58
+ if len(encoded) != 32:
59
+ return None
60
+ y = int.from_bytes(encoded, "little")
61
+ x = _recover_x(y & ((1 << 255) - 1), y >> 255)
62
+ if x is None:
63
+ return None
64
+ y &= (1 << 255) - 1
65
+ return (x, y, 1, x * y % _P)
66
+
67
+
68
+ def _same(p: tuple, q: tuple) -> bool:
69
+ return (p[0] * q[2] - q[0] * p[2]) % _P == 0 and (p[1] * q[2] - q[1] * p[2]) % _P == 0
70
+
71
+
72
+ def verify(public_key: bytes, message: bytes, signature: bytes) -> bool:
73
+ """Whether `signature` is public_key's Ed25519 signature of `message`."""
74
+ if len(public_key) != 32 or len(signature) != 64:
75
+ return False
76
+ a = _decompress(public_key)
77
+ r = _decompress(signature[:32])
78
+ s = int.from_bytes(signature[32:], "little")
79
+ if a is None or r is None or s >= _L:
80
+ return False
81
+ h = int.from_bytes(hashlib.sha512(signature[:32] + public_key + message).digest(), "little") % _L
82
+ return _same(_multiply(s, _B), _add(r, _multiply(h, a)))
83
+
84
+
85
+ def _compress(point: tuple) -> bytes:
86
+ z_inverse = pow(point[2], _P - 2, _P)
87
+ x, y = point[0] * z_inverse % _P, point[1] * z_inverse % _P
88
+ return (y | ((x & 1) << 255)).to_bytes(32, "little")
89
+
90
+
91
+ def _expand(seed: bytes) -> tuple[int, bytes]:
92
+ if len(seed) != 32:
93
+ raise ValueError("an Ed25519 seed is 32 bytes")
94
+ digest = hashlib.sha512(seed).digest()
95
+ scalar = int.from_bytes(digest[:32], "little")
96
+ scalar &= (1 << 254) - 8
97
+ scalar |= 1 << 254
98
+ return scalar, digest[32:]
99
+
100
+
101
+ def public_key(seed: bytes) -> bytes:
102
+ """The public key of a 32-byte private seed."""
103
+ return _compress(_multiply(_expand(seed)[0], _B))
104
+
105
+
106
+ def sign(seed: bytes, message: bytes) -> bytes:
107
+ """The Ed25519 signature of `message` by the key with this 32-byte private seed."""
108
+ scalar, prefix = _expand(seed)
109
+ public = _compress(_multiply(scalar, _B))
110
+ r = int.from_bytes(hashlib.sha512(prefix + message).digest(), "little") % _L
111
+ encoded_r = _compress(_multiply(r, _B))
112
+ h = int.from_bytes(hashlib.sha512(encoded_r + public + message).digest(), "little") % _L
113
+ return encoded_r + ((r + h * scalar) % _L).to_bytes(32, "little")