Skip to content

Commit

Permalink
feat(galois): more comments
Browse files Browse the repository at this point in the history
  • Loading branch information
hussein-aitlahcen committed Oct 10, 2023
1 parent 4057a62 commit 1b1c978
Showing 1 changed file with 37 additions and 14 deletions.
51 changes: 37 additions & 14 deletions galoisd/pkg/lightclient/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import (
"github.com/consensys/gnark/std/math/emulated"
)

// NOTE: this circuit is compatible with the bn254 backend ONLY as we assume
// that the scalar field is the one from this curve for many public inputs.

/*
This format is highly unlikely not to change as this would break the next validator hash of current blocks for a given cosmos chain.
Each variables is guarantee to fit in a bn254 field element as long as the public key is a G1 point:
- 4 bytes PK meta
- PK X/Y coordinates as bn254 field element
- 1 byte for power meta
- 10 bytes for the power
This protobuf format is highly unlikely to change as this would break the next
validator hash of current blocks for a given cosmos chain. Each variable is
guaranteed to fit in a bn254 field element as long as the public key is a G1
point:
- 4 bytes PK meta
- PK X/Y coordinates as bn254 field elements
- 1 byte for power meta
- max of 10 bytes (varint) for the power
*/
const ValProtoElems = 4
const ValProtoSize = 4 + 32 + 1 + 10
Expand All @@ -26,12 +32,6 @@ const ValProtoPKX = 4
const ValProtoPowerMeta = 4 + 32
const ValProtoPower = 4 + 32 + 1

/* [10, 34, 26, 32,
195, 219, 47, 93, 175, 207, 17, 53, 249, 199, 195, 220, 162, 241, 238, 159, 218, 217, 73, 232, 88, 243, 210, 203, 63, 164, 253, 6, 23, 62, 229, 73,
16,
128, 128, 154, 166, 234, 175, 227, 1]
*/
// Max number of validators this lc can handle
const MaxVal = 16

Expand All @@ -52,6 +52,7 @@ func NewTendermintLightClientAPI(api frontend.API, input *TendermintLightClientI
return &TendermintLightClientAPI{api: api, input: input}
}

// Given the X coordinate and it's compression bit, we can reconstruct the compressed point.
func ToG1AffineCompressed(api frontend.API, x frontend.Variable, compressionMask []frontend.Variable) []frontend.Variable {
// TODO: Make this public in Gnark
// mMask byte = 0b11 << 6
Expand All @@ -66,6 +67,7 @@ func ToG1AffineCompressed(api frontend.API, x frontend.Variable, compressionMask
return bytes
}

// Given a variable of size N and limbs of size M, split the variable in N/M limbs.
func Unpack(api frontend.API, packed frontend.Variable, sizeOfInput int, sizeOfElem int) []frontend.Variable {
nbOfElems := sizeOfInput / sizeOfElem
if sizeOfElem == 1 {
Expand All @@ -80,6 +82,7 @@ func Unpack(api frontend.API, packed frontend.Variable, sizeOfInput int, sizeOfE
}
}

// Reconstruct a value from it's limbs.
func Repack(api frontend.API, unpacked []frontend.Variable, sizeOfInput int, sizeOfElem int) []frontend.Variable {
nbOfElems := sizeOfInput / sizeOfElem
elems := make([]frontend.Variable, nbOfElems)
Expand All @@ -94,7 +97,12 @@ func (lc *TendermintLightClientAPI) Verify(message *gadget.G2Affine, expectedVal
lc.api.AssertIsLessOrEqual(lc.input.NbOfSignature, lc.input.NbOfVal)
bitmap := lc.api.ToBinary(lc.input.Bitmap, MaxVal)

// Facility to iterate over the validators in the lc, this function will do the necessary decoding/marshalling for the caller.
// Facility to iterate over the validators in the lc, this function will
// do the necessary decoding/marshalling for the caller.
//
// This function will reconstruct each validator protobuf payload from the secret inputs by:
// - re-compressing the given public key with the given mask
// - decoding the validator power that is a varint64
forEachVal := func(f func(i int, signed frontend.Variable, power frontend.Variable, PK *gadget.G1Affine, rawProto [ValProtoSize]frontend.Variable, size frontend.Variable)) {
for i, signed := range bitmap {
validatorData := lc.input.ProtoValidators[i]
Expand All @@ -115,6 +123,9 @@ func (lc *TendermintLightClientAPI) Verify(message *gadget.G2Affine, expectedVal
powerBytes := Unpack(lc.api, validatorPowerProto, proto.MaxVarintSize*8, 8)

rawProto := [ValProtoSize]frontend.Variable{}
// This values are hardcoded protobuf tags that will
// never change for the network as changing this would
// break consensus.
rawProto[ValProtoPKMeta+0] = 10
rawProto[ValProtoPKMeta+1] = 34
rawProto[ValProtoPKMeta+2] = 26
Expand All @@ -127,7 +138,10 @@ func (lc *TendermintLightClientAPI) Verify(message *gadget.G2Affine, expectedVal
rawProto[j+ValProtoPower] = powerBytes[j]
}

// The lc.api expect 4*64bits limbs, we unpack
// Gnark G1Affine coordinates are expressed in 64 bit
// limbs, we must unpack the inputs. Note that we know
// that PKX/PKY can fit in a single scalar field as we
// ALWAYS use the bn254 backend (bn254 public keys).
var PK gadget.G1Affine
PK.X.Limbs = Unpack(lc.api, validatorPKXBytes, 256, 64)
PK.Y.Limbs = Unpack(lc.api, validatorPKYBytes, 256, 64)
Expand Down Expand Up @@ -172,18 +186,26 @@ func (lc *TendermintLightClientAPI) Verify(message *gadget.G2Affine, expectedVal
leafHashes[i] = merkle.LeafHash(rawProto[:], protoSize)
})

// Compute validator set merkle root
rootHash := merkle.RootHash(leafHashes, lc.input.NbOfVal)

// Decompose the given sha256 that can't fit in a single scalar field (hence, 2 public inputs)
// TODO: can be optimizing by dropping the 2 msb of the hash to fit in the field
expectedRootHash0 := Unpack(lc.api, expectedValRoot[0], 128, 8)
expectedRootHash1 := Unpack(lc.api, expectedValRoot[1], 128, 8)

// Verify that the merkle root is equal to the given root (public input)
for i := 0; i < 16; i++ {
lc.api.AssertIsEqual(expectedRootHash0[i], rootHash[15-i])
}
for i := 0; i < 16; i++ {
lc.api.AssertIsEqual(expectedRootHash1[i], rootHash[31-i])
}

// Ensure that we actually aggregated the correct number of signatures
lc.api.AssertIsEqual(aggregatedKeys, lc.input.NbOfSignature)

// Ensure that the current sum of voting power exceed the expected threshold
// x > ay/b <=> ay < bx
votingPowerNeeded := lc.api.Mul(totalVotingPower, powerNumerator)
currentVotingPowerScaled := lc.api.Mul(currentVotingPower, powerDenominator)
Expand All @@ -194,6 +216,7 @@ func (lc *TendermintLightClientAPI) Verify(message *gadget.G2Affine, expectedVal
return fmt.Errorf("new pairing: %w", err)
}

// Verify that the aggregated signature is correct
var g1AffGenNeg curve.G1Affine
g1AffGenNeg.Neg(&g1AffGen)
negG1 := gadget.NewG1Affine(g1AffGenNeg)
Expand Down

0 comments on commit 1b1c978

Please sign in to comment.