-
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 13 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 |
---|---|---|
|
@@ -15,6 +15,7 @@ import ( | |
"github.com/ava-labs/avalanchego/utils/crypto/bls" | ||
avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" | ||
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" | ||
"github.com/ava-labs/subnet-evm/warp/messages" | ||
"github.com/ethereum/go-ethereum/ethdb" | ||
"github.com/ethereum/go-ethereum/log" | ||
) | ||
|
@@ -36,8 +37,8 @@ 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) | ||
// 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) | ||
|
@@ -142,15 +143,16 @@ 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) { | ||
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) | ||
if err != nil { | ||
return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err) | ||
if err := b.ValidateMessage(unsignedMessage); err != nil { | ||
return [bls.SignatureLen]byte{}, fmt.Errorf("failed to validate warp message: %w", err) | ||
} | ||
|
||
var signature [bls.SignatureLen]byte | ||
|
@@ -164,6 +166,37 @@ func (b *backend) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, | |
return signature, nil | ||
} | ||
|
||
func (b *backend) ValidateMessage(unsignedMessage *avalancheWarp.UnsignedMessage) error { | ||
// Known on-chain messages should be signed | ||
if _, err := b.GetMessage(unsignedMessage.ID()); err == nil { | ||
return nil | ||
} | ||
|
||
// Try to parse the payload as an AddressedCall | ||
addressedCall, err := payload.ParseAddressedCall(unsignedMessage.Payload) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse unknown message as AddressedCall: %w", err) | ||
} | ||
Comment on lines
+176
to
+179
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. what happens if that message type is not supposed to comfort any types. i.e a supposed to be a pure addressedcall without any specific type? (like current teleporter messages) |
||
|
||
// Further, parse the payload to see if it is a known type. | ||
parsed, err := messages.Parse(addressedCall.Payload) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse unknown message: %w", err) | ||
} | ||
|
||
// Check if the message is a known type that can be signed on demand | ||
signable, ok := parsed.(messages.Signable) | ||
if !ok { | ||
return fmt.Errorf("parsed message is not Signable: %T", signable) | ||
} | ||
|
||
// Check if the message should be signed according to its type | ||
if err := signable.VerifyMesssage(addressedCall.SourceAddress); err != nil { | ||
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. we don't need to sort it out right not but seems we might need a handler to take different interfaces (VM, providers etc). One example could be the uptime request needs the uptime manager (and/or the state) to verify the message. |
||
return fmt.Errorf("failed to verify Signable message: %w", 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,45 @@ | ||
// (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) | ||
} | ||
|
||
// Signable is an optional interface that payloads can implement to allow | ||
// on-the-fly signing of incoming messages by the warp backend. | ||
type Signable interface { | ||
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. I've made this an optional interface but we can make it required and have non-implementers always return an error if there is a preference for that 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. how do we make it required? |
||
VerifyMesssage(sourceAddress []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. 🚀 |
||
} | ||
|
||
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.
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 comment
The 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).
I didn't want to make another struct that has access to the bls signer key.
Do you suggest a different code organization here?