carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3// Proof of work for Keymail federation, ported from keymail
4// internal/pow/pow.go and the nonce rule in federation.go handleRegister.
5// An unknown sender pays it once per recipient mailbox to register its
6// signing key; after that, deliveries from the same key are free.
7//
8// nonce = SHA-256(challenge token)
9// find counter: SHA-256(nonce || sign_pub_raw || counter_be64) has >= bits leading zero bits
10
11import (
12 "context"
13 "crypto/sha256"
14 "encoding/binary"
15 "math/bits"
16)
17
18// maxFederationPowBits is the highest price the console will pay. A mailbox
19// sets its own price, and an invite runs inside an HTTP request: 24 bits is
20// about sixteen million hashes, a few seconds on one core. Keymail's default
21// is 20.
22const maxFederationPowBits = 24
23
24func powLeadingZeroBits(h []byte) int {
25 n := 0
26 for _, b := range h {
27 if b == 0 {
28 n += 8
29 continue
30 }
31 return n + bits.LeadingZeros8(b)
32 }
33 return n
34}
35
36func powVerify(nonce, signPub []byte, counter uint64, difficulty int) bool {
37 if difficulty <= 0 {
38 return true
39 }
40 h := sha256.New()
41 h.Write(nonce)
42 h.Write(signPub)
43 var c [8]byte
44 binary.BigEndian.PutUint64(c[:], counter)
45 h.Write(c[:])
46 return powLeadingZeroBits(h.Sum(nil)) >= difficulty
47}
48
49// powMine searches counters from 0 on one goroutine, checking ctx every
50// 65536 attempts. One core is enough at the prices the console accepts.
51func powMine(ctx context.Context, nonce, signPub []byte, difficulty int) (uint64, error) {
52 if difficulty <= 0 {
53 return 0, nil
54 }
55 prefix := make([]byte, 0, len(nonce)+len(signPub)+8)
56 prefix = append(append(prefix, nonce...), signPub...)
57 buf := append(prefix, make([]byte, 8)...)
58 n := len(prefix)
59 for counter := uint64(0); ; counter++ {
60 if counter&0xffff == 0 {
61 if err := ctx.Err(); err != nil {
62 return 0, err
63 }
64 }
65 binary.BigEndian.PutUint64(buf[n:], counter)
66 sum := sha256.Sum256(buf)
67 if powLeadingZeroBits(sum[:]) >= difficulty {
68 return counter, nil
69 }
70 }
71}
72