I forgot what was the meaning of this acronym.
the signature algorithm is implemented roughly as described in the FROST paper, and most of it was copied from https://github.com/bytemare/frost and adapted to use github.com/btcsuite/btcd/btcec
, but it has the following substantial changes:
-
for BIP-340 compatibility, the key dealing algorithm negates the secret key before sharding if its public key’s
y
is odd; -
for BIP-340 compatibility, when creating partial signatures, signers have to compute the group commitment, and if it’s
y
is odd then all the public nonces and their own private nonce is negated; -
for BIP-340 compatibility, then signing challenge is computed with
taggedhash("BIP0340/challenge", group-commitment-x || user-pubkey-x || event_id)
; -
because it felt appropriate, other parts of the algorithm that would use hashes also use
taggedhash()
with different tags, the code will speak better than I can.
-
client generates a
user-secret-key
locally — or the user inputs a preexisting key; -
client shards the key into as
n
shards, using theTrustedKeyDeal()
function underfrost/key_dealer.go
ortrustedKeyDeal()
underjs-dealer/shardkey.ts
, it also has to pick a numberm
, which will be the minimum threshold for generating signatures using FROST; -
if it doesn’t have one already, client signs and publishes a
kind:10002
relay list with some inbox relays; -
client picks a number
n
of signers, identified by their public keys, each of which will receive a shard; -
client fetches
kind:10002
relays for each of the signers; -
client picks a coordinator that acts as a relay;
-
client builds a
kind:26428
"shard event" for each signer, as follows:{ "kind": 26428, "pubkey": "<user-pubkey>", "tags": [ ["p", "<signer-pubkey>"], ["coordinator", "<coordinator-url>"], ], "content": nip44_encrypt("<hex-encoded-secret-key-shard>") }
where `<hex-encoded-secret-key-shard>` is given by the hex-encoded of the concatenation of - [encoded-public-shard]: given by: - [public-shard-id]: 2-bytes (little-endian) - [number-of-vss-commits]: 4-bytes (little-endian) - [shard-public-key]: 33-bytes (compressed) - <number-of-vss-commits> * [vss-commit]: 33-bytes (compressed) each - [shard-secret-key]: 32-bytes (big-endian) - [user-pubkey]: 33-bytes (compressed)
-
client builds NIP-13 proof-of-work into that event of at least 20 bits;
-
client sends the signed "shard event" to the each desired signer in their "read" relays as given by their
kind:10002
; -
client starts listening on their own "read" relays for replies from signer;
-
signer receives the event from client, checks the proof-of-work, decrypts and validates the
<encoded-secret-key-shard>
, then saves that information locally somehow; -
signer builds a
kind:26429
"shard ack event", as follows:{ "kind": 26429, "pubkey": "<signer-pubkey>", "tags": [ ["e", "<shard-event-id>"], ["p", "<user-pubkey>"] ] }
-
signer fetches
kind:10002
relays for<user-pubkey>
and sends the "shard ack event" to the "read" relays; -
upon receiving the "shard ack event" from all the signers, client builds a
kind:16430
"account registration event" as follows:{ "kind": 16430, "pubkey": "<user-pubkey>", "tags": [ ["handlersecret", "<random-private-key>"], ["h", "<public-key-corresponding-to-handlersecret>"], ["threshold", "<m>"], ["p", "<signer-pubkey>", "<hex-encoded-public-shard>"] * n ] }
in which the `"p"` tag is repeated once for each signer, and "<hex-encoded-public-shard>" is encoded just as above.
-
upon receiving the "account registration event", coordinator stores it and keeps it secret;
-
coordinator should now listen for NIP-46 calls directed at its own relay, targeting
<public-key-corresponding-to-handlersecret>
.
-
coordinator listens for all NIP-46 events targeting
<public-key-corresponding-to-handlersecret>
; -
upon receiving a NIP-46 request, coordinator matches it against its
p
tag with a storedkind:16430
, useshandlersecret
to decrypt (and later sign and encrypt the response); -
for
get_public_key
coordinator can just answer immediately; -
for
sign_event
coordinator then finds out what signers are online and connected, choosesm
of them and initiates a signing session; -
coordinator sends a
kind:26430
"configuration event" to each chosen signer in the form:{ "kind": 26430, "pubkey": "<coordinator-pubkey>", "tags": [ ["p", "<signer-pubkey>"] ], "content": "<hex-encoded-configuration-object>" }
where <hex-encoded-configuration-object> is given by the hex-encoded concatenation of - [m]: 2-bytes (little-endian) - [n]: 2-bytes (little-endian) - [number-of-signers]: 2-bytes (little-endian) - [user-pubkey]: 33-bytes (compressed) - <number-of-signers> * [encoded-public-shard] (as above)
-
upon receiving this, signer generates its local commitments, or a pair of public and private nonces, and sends the public parts to coordinator in a
kind:26431
"commit event", as follows:{ "kind": 26431, "pubkey": "<signer-pubkey>", "tags": [ ["e", "<configuration-event-id>"], ["p", "<user-pubkey>"] ], "content": "<hex-encoded-commit>" }
where <hex-encoded-commit> is given by the hex-encoded concatenation of - [commit-id]: 8-bytes (little-endian) - [signer-id]: 2-bytes (little-endian) - [binding-nonce-point]: 33-bytes (compressed) - [hiding-nonce-point]: 33-bytes (compressed)
-
upon receiving commits from all signers, coordinator then aggregates the commits into a group commit and sends it back to all the signers:
{ "kind": 26432, "pubkey": "<coordinator-pubkey>", "tags": [ ["e", "<configuration-event-id>"], ["p", "<signer-pubkey>"] ], "content": "<hex-encoded-group-commit>" }
where <hex-encoded-group-commit> is given by the hex-encoded concatenation of - [first-nonce]: 33-bytes (compressed) - [second-nonce]: 33-bytes (compressed)
-
then coordinator sends the event that is to be signed to all signers in a
kind:26432
event, in the form:{ "kind": 26433, "pubkey": "<coordinator-pubkey>", "tags": [ ["e", "<configuration-event-id>"], ["p", "<signer-pubkey>"] ], "content": "<json-encoded-event-to-be-signed>" }
-
finally, each signer groups together all commits and uses these together with their secret nonces and the hash of the event to be signed to produce a
<partial-signature>
and sends that back to coordinator in akind:26433
event, as follows:{ "kind": 26434, "pubkey": "<signer-pubkey>", "tags": [ ["e", "<configuration-event-id>"], ["p", "<user-pubkey>"] ], "content": "<hex-encoded-partial-signature>" }
where <hex-encoded-partial-signature> is given by the hex-encoded concatenation of: - [signer-id]: 2-bytes (little-endian) - [partial-signature-scalar]: 32-bytes (big-endian)
-
coordinator assembles all the partial signatures and builds the aggregated signature which can then be put into the event and sent as a response to the
sign_event
NIP-46 request.