Files
evmgpu/warp/verifier_backend.go
zeekayandHanzo Dev 0c11cf1960 warp: name the message for what it now is
The identifiers still said unsigned, but warp no longer has an unsigned
message: the Message that the previous commit migrated to is the signed
subject, and the thing that carries a signature beside it is the
Envelope. Locals and parameters that held one drop the prefix, the
LocalSigner method that signs one becomes SignMessage, and the two
places where a test builds a deliberately malformed message or reads
one back out of an event name it invalidMsg and unpacked rather than
shadowing the valid message in the enclosing test.

PackSendWarpMessageEvent keeps unsignedMessageID, which mirrors the
field name in the SendWarpMessage ABI event rather than a Go type.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-06 03:52:57 -07:00

110 lines
3.2 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"context"
"errors"
"fmt"
"github.com/luxfi/evmgpu/warp/messages"
"github.com/luxfi/crypto"
"github.com/luxfi/database"
"github.com/luxfi/ids"
"github.com/luxfi/warp"
"github.com/luxfi/warp/payload"
)
const (
ParseErrCode = iota + 1
VerifyErrCode
)
// Verify verifies the signature of the message
// It implements the warp.Verifier interface
func (b *backend) Verify(ctx context.Context, msg *warp.Message, _ []byte) error {
messageIDBytes := msg.ID()
messageID := ids.ID(crypto.Keccak256Hash(messageIDBytes[:]))
// Known on-chain messages should be signed
if _, err := b.GetMessage(messageID); err == nil {
return nil
} else if err != database.ErrNotFound {
return fmt.Errorf("failed to get message %s: %w", messageID, err)
}
parsed, err := payload.ParsePayload(msg.Payload)
if err != nil {
b.stats.IncMessageParseFail()
return fmt.Errorf("failed to parse payload: %w", err)
}
switch p := parsed.(type) {
case *payload.AddressedCall:
return b.verifyOffchainAddressedCall(p)
case *payload.Hash:
return b.verifyBlockMessage(ctx, p)
default:
b.stats.IncMessageParseFail()
return fmt.Errorf("unknown payload type: %T", p)
}
}
// verifyBlockMessage returns nil if blockHashPayload contains the ID
// of an accepted block indicating it should be signed by the VM.
func (b *backend) verifyBlockMessage(ctx context.Context, blockHashPayload *payload.Hash) error {
// Convert []byte to ids.ID
var blockID ids.ID
copy(blockID[:], blockHashPayload.Hash)
_, err := b.blockClient.GetAcceptedBlock(ctx, blockID)
if err != nil {
b.stats.IncBlockValidationFail()
return fmt.Errorf("failed to get block %s: %w", blockID, err)
}
return nil
}
// verifyOffchainAddressedCall verifies the addressed call message
func (b *backend) verifyOffchainAddressedCall(addressedCall *payload.AddressedCall) error {
// Further, parse the payload to see if it is a known type.
parsed, err := messages.Parse(addressedCall.Payload)
if err != nil {
b.stats.IncMessageParseFail()
return fmt.Errorf("failed to parse addressed call message: %w", err)
}
if len(addressedCall.SourceAddress) != 0 {
return errors.New("source address should be empty for offchain addressed messages")
}
switch p := parsed.(type) {
case *messages.ValidatorUptime:
if err := b.verifyUptimeMessage(p); err != nil {
b.stats.IncUptimeValidationFail()
return err
}
default:
b.stats.IncMessageParseFail()
return fmt.Errorf("unknown message type: %T", p)
}
return nil
}
func (b *backend) verifyUptimeMessage(uptimeMsg *messages.ValidatorUptime) error {
vdr, currentUptime, _, err := b.validatorReader.GetValidatorAndUptime(uptimeMsg.ValidationID)
if err != nil {
return fmt.Errorf("failed to get uptime for validationID %s: %w", uptimeMsg.ValidationID, err)
}
currentUptimeSeconds := uint64(currentUptime.Seconds())
// verify the current uptime against the total uptime in the message
if currentUptimeSeconds < uptimeMsg.TotalUptime {
return fmt.Errorf("current uptime %d is less than queried uptime %d for nodeID %s", currentUptimeSeconds, uptimeMsg.TotalUptime, vdr.NodeID)
}
return nil
}