-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Types and refactoring for ACP-77 ValidatorUptime messages #1292
base: master
Are you sure you want to change the base?
Changes from 9 commits
c2da67d
33466ce
964e193
a5cde37
c363886
0bf1de6
dd66fa7
8b3fb1c
008bc37
48f0ab7
991ff46
fe05d33
0747262
c1074a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,14 +30,24 @@ type BlockClient interface { | |
GetAcceptedBlock(ctx context.Context, blockID ids.ID) (snowman.Block, error) | ||
} | ||
|
||
type MessageValidator interface { | ||
// If the validator returns nil, the message is considered valid and the | ||
// backend will sign it. | ||
ValidateMessage(*avalancheWarp.UnsignedMessage) error | ||
} | ||
|
||
// Backend tracks signature-eligible warp messages and provides an interface to fetch them. | ||
// The backend is also used to query for warp message signatures by the signature request handler. | ||
type Backend interface { | ||
// AddMessage signs [unsignedMessage] and adds it to the warp backend database | ||
AddMessage(unsignedMessage *avalancheWarp.UnsignedMessage) error | ||
|
||
// GetMessageSignature returns the signature of the requested message hash. | ||
GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) | ||
// AddMessageValidator adds a validator to the backend. The backend will sign | ||
// messages that pass any of the validators, in addition to those known in the db. | ||
AddMessageValidator(validator MessageValidator) | ||
|
||
// GetMessageSignature returns the signature of the requested message. | ||
GetMessageSignature(message *avalancheWarp.UnsignedMessage) ([bls.SignatureLen]byte, error) | ||
|
||
// GetBlockSignature returns the signature of the requested message hash. | ||
GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) | ||
|
@@ -62,6 +72,7 @@ type backend struct { | |
blockSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte] | ||
messageCache *cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage] | ||
offchainAddressedCallMsgs map[ids.ID]*avalancheWarp.UnsignedMessage | ||
messageValidators []MessageValidator | ||
} | ||
|
||
// NewBackend creates a new Backend, and initializes the signature cache and message tracking database. | ||
|
@@ -88,6 +99,10 @@ func NewBackend( | |
return b, b.initOffChainMessages(offchainMessages) | ||
} | ||
|
||
func (b *backend) AddMessageValidator(validator MessageValidator) { | ||
b.messageValidators = append(b.messageValidators, validator) | ||
} | ||
|
||
func (b *backend) initOffChainMessages(offchainMessages [][]byte) error { | ||
for i, offchainMsg := range offchainMessages { | ||
unsignedMsg, err := avalancheWarp.ParseUnsignedMessage(offchainMsg) | ||
|
@@ -142,15 +157,23 @@ func (b *backend) AddMessage(unsignedMessage *avalancheWarp.UnsignedMessage) err | |
return nil | ||
} | ||
|
||
func (b *backend) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) { | ||
func (b *backend) GetMessageSignature(unsignedMessage *avalancheWarp.UnsignedMessage) ([bls.SignatureLen]byte, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why we replaced messageID with unsignedMessage? it seems we only use it for the messageID. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is so the backend can sign arbitrary messages on the fly (it will need to inspect their contents, such as for validator uptime message, so knowing the ID aka hash is not enough). |
||
messageID := unsignedMessage.ID() | ||
|
||
log.Debug("Getting warp message from backend", "messageID", messageID) | ||
if sig, ok := b.messageSignatureCache.Get(messageID); ok { | ||
return sig, nil | ||
} | ||
|
||
unsignedMessage, err := b.GetMessage(messageID) | ||
var err error | ||
for _, v := range append(b.messageValidators, b) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we expect other interfaces? Rather than adding interfaces via |
||
err = v.ValidateMessage(unsignedMessage) | ||
if err == nil { | ||
break | ||
} | ||
} | ||
if err != nil { | ||
return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err) | ||
return [bls.SignatureLen]byte{}, fmt.Errorf("failed to validate warp message: %w", err) | ||
} | ||
|
||
var signature [bls.SignatureLen]byte | ||
|
@@ -164,6 +187,15 @@ func (b *backend) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, | |
return signature, nil | ||
} | ||
|
||
func (b *backend) ValidateMessage(unsignedMessage *avalancheWarp.UnsignedMessage) error { | ||
messageID := unsignedMessage.ID() | ||
_, err := b.GetMessage(messageID) | ||
if err != nil { | ||
return fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err) | ||
} | ||
return nil | ||
} | ||
|
||
func (b *backend) GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) { | ||
log.Debug("Getting block from backend", "blockID", blockID) | ||
if sig, ok := b.blockSignatureCache.Get(blockID); ok { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// (c) 2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package messages | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/codec/linearcodec" | ||
"github.com/ava-labs/avalanchego/utils/units" | ||
) | ||
|
||
const ( | ||
CodecVersion = 0 | ||
|
||
MaxMessageSize = 24 * units.KiB | ||
) | ||
|
||
var Codec codec.Manager | ||
|
||
func init() { | ||
Codec = codec.NewManager(MaxMessageSize) | ||
lc := linearcodec.NewDefault() | ||
|
||
err := errors.Join( | ||
lc.RegisterType(&ValidatorUptime{}), | ||
Codec.RegisterCodec(CodecVersion, lc), | ||
) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// (c) 2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package messages | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var errWrongType = errors.New("wrong payload type") | ||
|
||
// Payload provides a common interface for all payloads implemented by this | ||
// package. | ||
type Payload interface { | ||
// Bytes returns the binary representation of this payload. | ||
Bytes() []byte | ||
|
||
// initialize the payload with the provided binary representation. | ||
initialize(b []byte) | ||
} | ||
|
||
func Parse(bytes []byte) (Payload, error) { | ||
var payload Payload | ||
if _, err := Codec.Unmarshal(bytes, &payload); err != nil { | ||
return nil, err | ||
} | ||
payload.initialize(bytes) | ||
return payload, nil | ||
} | ||
|
||
func initialize(p Payload) error { | ||
bytes, err := Codec.Marshal(CodecVersion, &p) | ||
if err != nil { | ||
return fmt.Errorf("couldn't marshal %T payload: %w", p, err) | ||
} | ||
p.initialize(bytes) | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO it's kind-of odd to expect message to be validated if "any" of these validators pass. Maybe I did not fully understand the case but shouldn't we just need to have validators on payload types rather than adding them to here?