Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
Fix parsing of bitfields (paritytech#635)
Browse files Browse the repository at this point in the history
Co-authored-by: Alistair Singh <alistair.singh7@gmail.com>
  • Loading branch information
vgeddes and alistair-singh authored Jun 1, 2022
1 parent 1b5d8d5 commit e342ca3
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 36 deletions.
43 changes: 43 additions & 0 deletions relayer/relays/beefy/bitfield/bitfield.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package bitfield

import (
"math/big"
)

type Bitfield []byte

func reverse(thing []byte) {
for i, j := 0, len(thing)-1; i < j; i, j = i+1, j-1 {
thing[i], thing[j] = thing[j], thing[i]
}
}

// New returns a Bitfield initialized from the Solidity representation of a Bitfield (an array of uint256). See below:
// https://github.com/Snowfork/snowbridge/blob/18c6225b21782170156729d54a35404d876a2c7b/ethereum/contracts/utils/Bitfield.sol
func New(input []*big.Int) Bitfield {
const length = 256 / 8
result := make(Bitfield, length*len(input))
for i, chunk := range input {
k := i * length
j := (i + 1) * length
chunk.FillBytes(result[k:j])
reverse(result[k:j])
}
return result
}

// Members returns the set bits in the bitfield
func (b Bitfield) Members() []uint64 {
results := []uint64{}
for idx, bits := range b {
if bits == 0 {
continue
}
for i := 0; i < 8; i++ {
if bits&byte(1<<i) > 0 {
results = append(results, uint64((idx*8)+i))
}
}
}
return results
}
57 changes: 57 additions & 0 deletions relayer/relays/beefy/bitfield/bitfield_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package bitfield

import (
"fmt"
"math/big"
"testing"

"github.com/stretchr/testify/assert"
)

func TestBitfieldMembers(t *testing.T) {
x := big.NewInt(1)
y := big.NewInt(1)
z := big.NewInt(1)

u := New([]*big.Int{x, y, z})
fmt.Printf("%v\n", u)
fmt.Printf("%v\n", u.Members())

assert.Equal(t, u.Members(), []uint64{0, 256, 512})
}

func TestBitfieldMembers2(t *testing.T) {
foo := make([]byte, 32)
foo[0] = 128
foo[31] = 1

x := big.NewInt(1)
x.SetBytes(foo)

u := New([]*big.Int{x})
fmt.Printf("%v\n", u)
fmt.Printf("%v\n", u.Members())

assert.Equal(t, u.Members(), []uint64{0, 255})
}

func TestBitfiledMembers3(t *testing.T) {
var x, y, z, w big.Int

// Four uint256 with first and last bit set
x.SetString("8000000000000000000000000000000000000000000000000000000000000001", 16)
y.SetString("8000000000000000000000000000000000000000000000000000000000000001", 16)
z.SetString("8000000000000000000000000000000000000000000000000000000000000001", 16)
w.SetString("8000000000000000000000000000000000000000000000000000000000000001", 16)

u := New([]*big.Int{&x, &y, &z, &w})
fmt.Printf("%v\n", u)
fmt.Printf("%v\n", u.Members())

assert.Equal(t, u.Members(), []uint64{
/* x */ 0, 255,
/* y */ 256, 511,
/* z */ 512, 767,
/* w */ 768, 1023,
})
}
24 changes: 4 additions & 20 deletions relayer/relays/beefy/ethereum-writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"math/big"
"strconv"
"time"

"golang.org/x/sync/errgroup"
Expand All @@ -20,6 +19,7 @@ import (

"github.com/snowfork/snowbridge/relayer/chain/ethereum"
"github.com/snowfork/snowbridge/relayer/contracts/beefyclient"
"github.com/snowfork/snowbridge/relayer/relays/beefy/bitfield"

log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -268,35 +268,19 @@ func (wr *EthereumWriter) doSubmitInitial(ctx context.Context, task *Request) (*
return tx, nil
}

func bitfieldToString(bitfield []*big.Int) string {
bitfieldString := ""
for _, bitfieldInt := range bitfield {
bits := strconv.FormatInt(bitfieldInt.Int64(), 2)

// add bits from this int at leftmost position
bitfieldString = bits + bitfieldString

// pad to 256 bits to include missing validators
for bitsLength := len(bits); bitsLength < 256; bitsLength++ {
bitfieldString = "0" + bitfieldString
}
}
return bitfieldString
}

// doFinalSubmit sends a SubmitFinal tx to the BeefyClient contract
func (wr *EthereumWriter) doSubmitFinal(ctx context.Context, validationID int64, task *Request) (*types.Transaction, error) {
randomBitfield, err := wr.contract.CreateFinalBitfield(
finalBitfield, err := wr.contract.CreateFinalBitfield(
&bind.CallOpts{Pending: true},
big.NewInt(validationID),
)
if err != nil {
return nil, fmt.Errorf("create validator bitfield: %w", err)
}

bitfield := bitfieldToString(randomBitfield)
validatorIndices := bitfield.New(finalBitfield).Members()

params, err := task.MakeSubmitFinalParams(validationID, bitfield)
params, err := task.MakeSubmitFinalParams(validationID, validatorIndices)
if err != nil {
return nil, err
}
Expand Down
26 changes: 10 additions & 16 deletions relayer/relays/beefy/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,32 +94,21 @@ func (r *Request) generateValidatorAddressProof(validatorIndex int64) ([][32]byt
return proof, nil
}

func (r *Request) MakeSubmitFinalParams(validationID int64, bitfield string) (*FinalRequestParams, error) {
func (r *Request) MakeSubmitFinalParams(validationID int64, validatorIndices []uint64) (*FinalRequestParams, error) {
validationDataID := big.NewInt(validationID)

validatorIndices := []*big.Int{}

// bitfield is right to left order, so loop backwards
for i := len(bitfield) - 1; i >= 0; i-- {
bit := bitfield[i : i+1]
if bit == "1" {
position := len(bitfield) - 1 - i // positions start from 0 and increase to len(bitfield) - 1
validatorIndices = append(validatorIndices, big.NewInt(int64(position)))
}
}

signatures := [][]byte{}
validatorAddresses := []common.Address{}
validatorAddressProofs := [][][32]byte{}
for _, validatorIndex := range validatorIndices {

ok, beefySig := r.SignedCommitment.Signatures[validatorIndex.Int64()].Unwrap()
ok, beefySig := r.SignedCommitment.Signatures[validatorIndex].Unwrap()
if !ok {
return nil, fmt.Errorf("signature is empty")
}

signatures = append(signatures, cleanSignature(beefySig))
pubKey := r.Validators[validatorIndex.Int64()]
pubKey := r.Validators[validatorIndex]

address, err := pubKey.IntoEthereumAddress()
if err != nil {
Expand All @@ -128,7 +117,7 @@ func (r *Request) MakeSubmitFinalParams(validationID int64, bitfield string) (*F

validatorAddresses = append(validatorAddresses, address)

merkleProof, err := r.generateValidatorAddressProof(validatorIndex.Int64())
merkleProof, err := r.generateValidatorAddressProof(int64(validatorIndex))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -167,12 +156,17 @@ func (r *Request) MakeSubmitFinalParams(validationID int64, bitfield string) (*F
Order: r.Proof.MerkleProofOrder,
}

validatorIndicesBigInt := []*big.Int{}
for _, index := range validatorIndices {
validatorIndicesBigInt = append(validatorIndicesBigInt, new(big.Int).SetUint64(index))
}

msg := FinalRequestParams{
ID: validationDataID,
Commitment: commitment,
Proof: beefyclient.BeefyClientValidatorMultiProof{
Signatures: signatures,
Indices: validatorIndices,
Indices: validatorIndicesBigInt,
Addrs: validatorAddresses,
MerkleProofs: validatorAddressProofs,
},
Expand Down

0 comments on commit e342ca3

Please sign in to comment.