Skip to content

Commit

Permalink
Verify chunk signature (#1862)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsachiherman authored Jan 15, 2025
1 parent 1699a51 commit d194b71
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 24 deletions.
34 changes: 34 additions & 0 deletions x/dsmr/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
package dsmr

import (
"errors"
"fmt"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/wrappers"
Expand All @@ -16,6 +19,8 @@ import (

const InitialChunkSize = 250 * 1024

var ErrFailedChunkSigVerification = errors.New("failed to verify bls chunk signature")

type Tx interface {
GetID() ids.ID
GetExpiry() int64
Expand Down Expand Up @@ -82,6 +87,35 @@ func signChunk[T Tx](
return newChunk(chunk, pkBytes, signature)
}

func (c *Chunk[T]) Verify(networkID uint32, chainID ids.ID) error {
signature, err := bls.SignatureFromBytes(c.Signature[:])
if err != nil {
return err
}

pk, err := bls.PublicKeyFromCompressedBytes(c.Signer[:])
if err != nil {
return err
}

// Construct the unsigned message from the UnsignedChunk (stripping the signature fields)
packer := wrappers.Packer{Bytes: make([]byte, 0, InitialChunkSize), MaxSize: consts.NetworkSizeLimit}
if err := codec.LinearCodec.MarshalInto(c.UnsignedChunk, &packer); err != nil {
return err
}

msg, err := warp.NewUnsignedMessage(networkID, chainID, packer.Bytes)
if err != nil {
return fmt.Errorf("failed to create unsigned warp message from chunk: %w", err)
}

if !bls.Verify(pk, signature, msg.Bytes()) {
return ErrFailedChunkSigVerification
}

return nil
}

// newChunk signs a chunk
func newChunk[T Tx](
unsignedChunk UnsignedChunk[T],
Expand Down
43 changes: 23 additions & 20 deletions x/dsmr/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,11 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) {
wantErr: ErrInvalidChunk,
},
{
name: "valid chunk",
verifier: NoVerifier[dsmrtest.Tx]{},
name: "valid chunk",
verifier: ChunkVerifier[dsmrtest.Tx]{
networkID: networkID,
chainID: chainID,
},
},
}

Expand Down Expand Up @@ -514,22 +517,19 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) {
)
r.NoError(err)

chunk, err := newChunk[dsmrtest.Tx](
UnsignedChunk[dsmrtest.Tx]{
Producer: ids.GenerateTestNodeID(),
Beneficiary: codec.Address{123},
Expiry: 123,
Txs: []dsmrtest.Tx{
{
ID: ids.GenerateTestID(),
Expiry: 456,
Sponsor: codec.Address{4, 5, 6},
},
unsignedChunk := UnsignedChunk[dsmrtest.Tx]{
Producer: ids.GenerateTestNodeID(),
Beneficiary: codec.Address{123},
Expiry: 123,
Txs: []dsmrtest.Tx{
{
ID: ids.GenerateTestID(),
Expiry: 456,
Sponsor: codec.Address{4, 5, 6},
},
},
[48]byte{},
[96]byte{},
)
}
chunk, err := signChunk[dsmrtest.Tx](unsignedChunk, networkID, chainID, pk, signer)
r.NoError(err)

packer := wrappers.Packer{MaxSize: MaxMessageSize}
Expand Down Expand Up @@ -1261,16 +1261,19 @@ func newTestNodes(t *testing.T, n int) []*Node[dsmrtest.Tx] {
require.NoError(t, err)
pk := bls.PublicFromSecretKey(sk)
signer := warp.NewSigner(sk, networkID, chainID)

chunkStorage, err := NewChunkStorage[dsmrtest.Tx](NoVerifier[dsmrtest.Tx]{}, memdb.New())
verifier := ChunkVerifier[dsmrtest.Tx]{networkID: networkID, chainID: chainID}
chunkStorage, err := NewChunkStorage[dsmrtest.Tx](verifier, memdb.New())
require.NoError(t, err)

getChunkHandler := &GetChunkHandler[dsmrtest.Tx]{
storage: chunkStorage,
}
chunkSignatureRequestHandler := acp118.NewHandler(ChunkSignatureRequestVerifier[dsmrtest.Tx]{
verifier: NoVerifier[dsmrtest.Tx]{},
storage: chunkStorage,
verifier: ChunkVerifier[dsmrtest.Tx]{
networkID: networkID,
chainID: chainID,
},
storage: chunkStorage,
}, signer)
chunkCertificateGossipHandler := ChunkCertificateGossipHandler[dsmrtest.Tx]{
storage: chunkStorage,
Expand Down
1 change: 1 addition & 0 deletions x/dsmr/p2p.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ func (c ChunkSignatureRequestVerifier[T]) Verify(
return ErrInvalidChunk
}

// check to see if this chunk was already accepted.
_, accepted, err := c.storage.GetChunkBytes(chunk.Expiry, chunk.id)
if err != nil && !errors.Is(err, database.ErrNotFound) {
return &common.AppError{
Expand Down
19 changes: 15 additions & 4 deletions x/dsmr/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ type Verifier[T Tx] interface {
Verify(chunk Chunk[T]) error
}

var _ Verifier[Tx] = (*NoVerifier[Tx])(nil)
var _ Verifier[Tx] = (*ChunkVerifier[Tx])(nil)

type NoVerifier[T Tx] struct{}
type ChunkVerifier[T Tx] struct {
networkID uint32
chainID ids.ID
}

func (NoVerifier[T]) Verify(Chunk[T]) error {
return nil
func (c ChunkVerifier[T]) Verify(chunk Chunk[T]) error {
// TODO:
// check if the expiry of this chunk isn't in the past or too far into the future.

// TODO:
// check if the producer was expected to produce this chunk.

// TODO:
// add rate limiting for a given producer.
return chunk.Verify(c.networkID, c.chainID)
}

type StoredChunkSignature[T Tx] struct {
Expand Down

0 comments on commit d194b71

Please sign in to comment.