Sessions
Sessions
A Brollup state transition involves four subsequent sessions;
Entry Signing Session; This session sums individual signatures from msg.senders of the round and results in an aggregate signature to authorize the payload.
Covenant Signing Session This session sums individual signatures from msg.senders of the round and results in an aggregate signature to constrain payable VTXOs to the shared output.
Forfeiting Session This session forfeits VTXOs from msg.senders of the round to cover transaction fees and payable VTXOs.
Entry Signing Session
Accounts that are initiating calls or deploying contracts initially participate in this session to aggregate their signatures in coordination with the operator. This session employs a straightforward, single-round aggregation scheme where each msg.sender signs their own version, with the signature committing to H(m) rather than H(R || P || m). This allows the operator to simply sum all signatures in a single round. The resulting signature is a 64-byte Schnorr signature, which is batch-verified within the Brollup context.
(1) msg.sender creates a entryCtx, and requests to join the round;
let entryCtx = (prevHash, npub, entry, sig), where;
prevHash is the hash of previous Brollup state
nsec is secret key of the msg.sender
npub is the public key of the msg.sender; nsec • G
entry is the non-compact bytecode of the entry that msg.sender intends to transact;
let message = TaggedHash(bytes(prevHash || entry), “SighashEntry”)
let nonce = TaggedHash(bytes(message || nsec), “DeterministicNonce”)
let sig = (nonce • G, (nonce + message • nsec) mod n)
(2) operator responds with an acknowledge message, entryAck.
(3) The operator gathers all entries, sums the signatures, and responds with payloadCtx;
let aggpub = nsec1 • G + nsec2 • G .. nsecN • G
let aggmsg = m1 + m2 .. mN
let aggnonce = 𝑘1 • G + 𝑘2 • G .. 𝑘N • G
let aggcmt = (𝑘1 + m1 • nsec1) + (𝑘2 + m2 • nsec2) … (𝑘N + mN • nsecN)
let aggsig = (aggnonce, aggcmt)
let payloadCtx = (prevHash, npubs[], entries[], fee, aggsig)
At the end of the round, based on the list of entries[] and the fee provided, msg.senders can deterministically see the outcome of their execution and determine the precise amount they need to pay for the execution.
If the outcome of the execution results in an asserted failure in the contract logic, the msg.sender is removed from the round. This prevents failed transactions from being included in blocks, thereby saving space and fees.
Covenant Signing Session
Accounts that pass the entry signing session move into this session to constrain payable VTXOs to the shared output. This session employs a Musig2-based two-round aggregation scheme, where msg.senders aim to produce an aggregate signature from the aggregate key. The resulting signature is a 64-byte valid BIP-340 Schnorr signature.
(0) Given payloadCtx from the earlier session, msg.sender computes the aggregate key aggkey;
let (prevHash, msg.senders[], _, _, _) = payloadCtx
let keyaggCtx = KeyAgg(msg.senders[]) => (Q, gacc, tacc)
Let (aggkey, _, _) = keyaggCtx
(1) msg.sender joins the round with signerCtx;
let signerCtx = (prevHash, npub)
(2) operator returns covenantCtx;
let covenantCtx = (outpoint_self, spks[], values[]), where;
outpoint_self is the prevout that contains the aggregate key, from which msg.senders aim to produce an aggregate signature.
spks[] is an array of scriptPubKeys containing the payable VTXOs
values[] is an array nValues containing the payable VTXO amounts.
(3) msg.sender prepares and responds with pubnonce;
let (secnonce, pubnonce) = NonceGen(nsec, npub)
(4) operator aggregares pubnonces, and responds with aggnonce; NonceAgg(pubnonce1 .. pubnonceN) => aggnonce
(5) msg.sender signs and responds with partial signature partialsig;
let sighash = ReturnSighashCov(covenantCtx)
let sessionCtx = (aggnonce, aggkey, sighash)
let partialsig = Sign(secnonce, nsec, sessionCtx) => partialsig
(6) operator responds with aggregate signature aggsig;
let aggsig = PartialSigAgg(psig1 .. psigN, sessionCtx) => aggsig
Last updated