mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-24 23:55:18 +00:00
Added support for transcript verification and voucher inclusion checks
This commit is contained in:
parent
4d65dff388
commit
32da7a625f
@ -6,4 +6,8 @@ KZG Ceremony client for Quilibrium.
|
||||
|
||||
Run with `go run ./... <voucher_filename>` or omit the filename to write to quil_voucher.hex.
|
||||
|
||||
If you have docker installed you can participate in the ceremony by simply running `make participate`. Your voucher will be written to `vouchers/`.
|
||||
If you have docker installed you can participate in the ceremony by simply running `make participate`. Your voucher will be written to `vouchers/`.
|
||||
|
||||
## Additional Features
|
||||
|
||||
Run with `go run ./... verify-transcript` to verify the latest state of the sequencer, or `go run ./... check-voucher <voucher_filename>` to verify voucher inclusion in the latest state. Please keep in mind voucher inclusions are not immediate after contribution – the current batch must be processed before it will appear, and if there was an error response from the sequencer when contributing the voucher will not be included.
|
||||
|
276
bootstrap.go
276
bootstrap.go
@ -6,6 +6,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -14,6 +15,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/cloudflare/circl/sign/ed448"
|
||||
"golang.org/x/sync/errgroup"
|
||||
bls48581 "source.quilibrium.com/quilibrium/ceremonyclient/ec/bls48581"
|
||||
)
|
||||
|
||||
@ -39,6 +41,18 @@ type PowersOfTau struct {
|
||||
G2Affines []*bls48581.ECP8
|
||||
}
|
||||
|
||||
type CeremonyState struct {
|
||||
PowersOfTau PowersOfTauJson `json:"powersOfTau"`
|
||||
PotPubKey string `json:"potPubKey"`
|
||||
Witness Witness `json:"witness"`
|
||||
VoucherPubKeys []string `json:"voucherPubKeys"`
|
||||
}
|
||||
|
||||
type Witness struct {
|
||||
RunningProducts []string `json:"runningProducts"`
|
||||
PotPubKeys []string `json:"potPubKeys"`
|
||||
}
|
||||
|
||||
type Contribution struct {
|
||||
NumG1Powers int
|
||||
NumG2Powers int
|
||||
@ -249,3 +263,265 @@ func ContributeAndGetVoucher() {
|
||||
fmt.Println(hex.EncodeToString(voucher))
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyState() {
|
||||
csjRes, err := http.DefaultClient.Post(HOST+"current_state", "application/json", bytes.NewBufferString("{}"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer csjRes.Body.Close()
|
||||
|
||||
csjBytes, err := io.ReadAll(csjRes.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
currentStateJson := &CeremonyState{}
|
||||
|
||||
if err := json.Unmarshal(csjBytes, currentStateJson); err != nil {
|
||||
// message is not conformant, we are in validating phase
|
||||
panic(err)
|
||||
}
|
||||
|
||||
verifyState(currentStateJson)
|
||||
}
|
||||
|
||||
func CheckVoucherInclusion(path string) {
|
||||
csjRes, err := http.DefaultClient.Post(HOST+"current_state", "application/json", bytes.NewBufferString("{}"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer csjRes.Body.Close()
|
||||
|
||||
csjBytes, err := io.ReadAll(csjRes.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
currentStateJson := &CeremonyState{}
|
||||
|
||||
if err := json.Unmarshal(csjBytes, currentStateJson); err != nil {
|
||||
// message is not conformant, we are in validating phase
|
||||
panic(err)
|
||||
}
|
||||
|
||||
voucherHex, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
decodedVoucher, err := hex.DecodeString(string(voucherHex))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privKey := ed448.PrivateKey(decodedVoucher)
|
||||
|
||||
verifyPubKey := "0x" + hex.EncodeToString(privKey.Public().(ed448.PublicKey))
|
||||
|
||||
for i, v := range currentStateJson.VoucherPubKeys {
|
||||
if v == verifyPubKey {
|
||||
fmt.Printf("Voucher pubkey found at index %d\n", i)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
panic(errors.New("voucher not found"))
|
||||
}
|
||||
|
||||
func verifyState(currentState *CeremonyState) {
|
||||
wg := &errgroup.Group{}
|
||||
// This limit needs to be low – this check is a very painfully CPU intensive operation
|
||||
wg.SetLimit(8)
|
||||
|
||||
fmt.Println("Checking running products of witnesses...")
|
||||
|
||||
// check the pairings
|
||||
for j := 0; j < len(currentState.Witness.RunningProducts)-1; j++ {
|
||||
j := j
|
||||
wg.Go(func() error {
|
||||
fmt.Printf("Checking witness at %d\n", j)
|
||||
|
||||
currRunningProductHex := strings.TrimPrefix(currentState.Witness.RunningProducts[j], "0x")
|
||||
currRunningProductBytes, err := hex.DecodeString(currRunningProductHex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode G1 at %d", j)
|
||||
}
|
||||
|
||||
currRunningProduct := bls48581.ECP_fromBytes(currRunningProductBytes)
|
||||
if currRunningProduct == nil {
|
||||
return fmt.Errorf("could not convert G1 at %d", j)
|
||||
}
|
||||
|
||||
nextRunningProductHex := strings.TrimPrefix(currentState.Witness.RunningProducts[j+1], "0x")
|
||||
nextRunningProductBytes, err := hex.DecodeString(nextRunningProductHex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode next G1 at %d", j)
|
||||
}
|
||||
|
||||
nextRunningProduct := bls48581.ECP_fromBytes(nextRunningProductBytes)
|
||||
if nextRunningProduct == nil {
|
||||
return fmt.Errorf("could not convert next G1 at %d", j)
|
||||
}
|
||||
|
||||
potPubKeyHex := strings.TrimPrefix(currentState.Witness.PotPubKeys[j+1], "0x")
|
||||
potPubKeyBytes, err := hex.DecodeString(potPubKeyHex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode POT pubkey at %d", j)
|
||||
}
|
||||
|
||||
potPubKey := bls48581.ECP8_fromBytes(potPubKeyBytes)
|
||||
if potPubKey == nil {
|
||||
return fmt.Errorf("could not convert POT pubkey at %d", j)
|
||||
}
|
||||
|
||||
prevPotPubKeyHex := strings.TrimPrefix(currentState.Witness.PotPubKeys[j], "0x")
|
||||
prevPotPubKeyBytes, err := hex.DecodeString(prevPotPubKeyHex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode POT pubkey at %d", j)
|
||||
}
|
||||
|
||||
prevPotPubKey := bls48581.ECP8_fromBytes(prevPotPubKeyBytes)
|
||||
if prevPotPubKey == nil {
|
||||
return fmt.Errorf("could not convert POT pubkey at %d", j)
|
||||
}
|
||||
|
||||
if !pairCheck(potPubKey, currRunningProduct, prevPotPubKey, nextRunningProduct) {
|
||||
return fmt.Errorf("pairing check failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Println("Checking latest witness parity...")
|
||||
|
||||
// Check that the last running product is equal to G1 first power.
|
||||
lastRunningProductIdx := len(currentState.Witness.RunningProducts) - 1
|
||||
lastRunningProduct := currentState.Witness.RunningProducts[lastRunningProductIdx]
|
||||
if lastRunningProduct != currentState.PowersOfTau.G1Affines[1] {
|
||||
panic("mismatched running products for G1")
|
||||
}
|
||||
|
||||
// Check that the first running product is the tau^0 power.
|
||||
firstRunningProduct := currentState.Witness.RunningProducts[0]
|
||||
if firstRunningProduct != currentState.PowersOfTau.G1Affines[0] {
|
||||
panic("mismatched first product for G1")
|
||||
}
|
||||
|
||||
fmt.Println("Checking coherency of G1 powers...")
|
||||
// Check coherency of powers
|
||||
for j := 0; j < 65535; j++ {
|
||||
j := j
|
||||
wg.Go(func() error {
|
||||
fmt.Printf("Checking coherency of G1 at %d\n", j)
|
||||
baseTauG2Hex := strings.TrimPrefix(currentState.PowersOfTau.G2Affines[1], "0x")
|
||||
baseTauG2Bytes, err := hex.DecodeString(baseTauG2Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G2 at %d", j)
|
||||
}
|
||||
|
||||
baseTauG2 := bls48581.ECP8_fromBytes(baseTauG2Bytes)
|
||||
if baseTauG2 == nil {
|
||||
return fmt.Errorf("failed to convert for G2 at %d", j)
|
||||
}
|
||||
|
||||
currG1Hex := strings.TrimPrefix(currentState.PowersOfTau.G1Affines[j], "0x")
|
||||
currG1Bytes, err := hex.DecodeString(currG1Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G1 at %d", j)
|
||||
}
|
||||
|
||||
currG1 := bls48581.ECP_fromBytes(currG1Bytes)
|
||||
if currG1 == nil {
|
||||
return fmt.Errorf("failed to convert for G1 at %d", j)
|
||||
}
|
||||
|
||||
nextG1Hex := strings.TrimPrefix(currentState.PowersOfTau.G1Affines[j+1], "0x")
|
||||
nextG1Bytes, err := hex.DecodeString(nextG1Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G1 at %d", j+1)
|
||||
}
|
||||
|
||||
nextG1 := bls48581.ECP_fromBytes(nextG1Bytes)
|
||||
if nextG1 == nil {
|
||||
return fmt.Errorf("failed to convert for G1 at %d", j+1)
|
||||
}
|
||||
|
||||
if !pairCheck(baseTauG2, currG1, bls48581.ECP8_generator(), nextG1) {
|
||||
return fmt.Errorf("pairing check failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Println("Checking coherency of G2 powers...")
|
||||
|
||||
// Check G2 powers are coherent
|
||||
for j := 0; j < 256; j++ {
|
||||
j := j
|
||||
wg.Go(func() error {
|
||||
fmt.Printf("Checking coherency of G2 at %d\n", j)
|
||||
baseTauG1Hex := strings.TrimPrefix(currentState.PowersOfTau.G1Affines[1], "0x")
|
||||
baseTauG1Bytes, err := hex.DecodeString(baseTauG1Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G1 at %d", j)
|
||||
}
|
||||
|
||||
baseTauG1 := bls48581.ECP_fromBytes(baseTauG1Bytes)
|
||||
if baseTauG1 == nil {
|
||||
return fmt.Errorf("failed to convert for G1 at %d", j)
|
||||
}
|
||||
|
||||
currG2Hex := strings.TrimPrefix(currentState.PowersOfTau.G2Affines[j], "0x")
|
||||
currG2Bytes, err := hex.DecodeString(currG2Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G2 at %d", j)
|
||||
}
|
||||
|
||||
currG2 := bls48581.ECP8_fromBytes(currG2Bytes)
|
||||
if currG2 == nil {
|
||||
return fmt.Errorf("failed to convert for G1 at %d", j)
|
||||
}
|
||||
|
||||
nextG2Hex := strings.TrimPrefix(currentState.PowersOfTau.G2Affines[j+1], "0x")
|
||||
nextG2Bytes, err := hex.DecodeString(nextG2Hex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode for G2 at %d", j+1)
|
||||
}
|
||||
|
||||
nextG2 := bls48581.ECP8_fromBytes(nextG2Bytes)
|
||||
if nextG2 == nil {
|
||||
return fmt.Errorf("failed to convert for G2 at %d", j+1)
|
||||
}
|
||||
|
||||
if !pairCheck(currG2, baseTauG1, nextG2, bls48581.ECP_generator()) {
|
||||
return fmt.Errorf("pairing check failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
panic(fmt.Errorf("error validating transcript: %w", err))
|
||||
}
|
||||
|
||||
fmt.Println("Current state is valid Powers of Tau!")
|
||||
}
|
||||
|
||||
func pairCheck(G21 *bls48581.ECP8, G11 *bls48581.ECP, G22 *bls48581.ECP8, G12 *bls48581.ECP) bool {
|
||||
G12.Neg()
|
||||
v := bls48581.Ate2(G21, G11, G22, G12)
|
||||
v = bls48581.Fexp(v)
|
||||
|
||||
if !v.Isunity() {
|
||||
fmt.Println("pairing check failed")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
5
go.mod
5
go.mod
@ -4,4 +4,7 @@ go 1.18
|
||||
|
||||
require github.com/cloudflare/circl v1.3.2
|
||||
|
||||
require golang.org/x/sys v0.6.0 // indirect
|
||||
require (
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -1,4 +1,6 @@
|
||||
github.com/cloudflare/circl v1.3.2 h1:VWp8dY3yH69fdM7lM6A1+NhhVoDu9vqK0jOgmkQHFWk=
|
||||
github.com/cloudflare/circl v1.3.2/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
23
main.go
23
main.go
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -11,12 +12,32 @@ func main() {
|
||||
PrintLogo()
|
||||
PrintVersion()
|
||||
|
||||
ParseArgs()
|
||||
}
|
||||
|
||||
func ParseArgs() {
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "verify-transcript":
|
||||
VerifyState()
|
||||
os.Exit(0)
|
||||
case "check-voucher":
|
||||
CheckVoucherInclusion(os.Args[2])
|
||||
os.Exit(0)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
WaitForSequencerToBeReady()
|
||||
|
||||
JoinLobby()
|
||||
Bootstrap()
|
||||
fmt.Println("New Pubkey: ")
|
||||
fmt.Println("New PoT Pubkey: ")
|
||||
fmt.Println(bcj.PotPubKey)
|
||||
fmt.Println()
|
||||
fmt.Println("Voucher Pubkey: ")
|
||||
fmt.Println(bcj.VoucherPubKey)
|
||||
ContributeAndGetVoucher()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user