mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
luxfi/warp collapsed a three-level structure into two. The old UnsignedMessage (network ID, source chain, payload) absorbed the PQ lineage that used to live on the v2 envelope and became Message, the single signed subject; the old signed Message became Envelope, holding that Message alongside the BLS Beam and the Corona and ML-DSA lanes. Call sites move UnsignedMessage to Message, NewUnsignedMessage to NewMessage and ParseUnsignedMessage to ParseMessage, and move the formerly-signed Message to Envelope, NewMessage to NewEnvelope and ParseMessage to ParseEnvelope. Envelope.Bytes now returns an error, so the callers that serialize one handle it. The Beam signs a different domain than the old aggregate did. It covers warp.BeamSigningBytes(D) for D the message ID, so the lineage folded into the subject is authenticated rather than carried unsigned beside it; signing the bare canonical bytes now yields a Beam no receiver accepts. The local signer, the aggregator's per-share check and the tests that build signatures by hand all sign and verify that domain. BitSetSignature is a concrete field on the envelope rather than an interface, and its weight and verify methods are no longer exported. The warp precompile supplies its own canonical validator set, with shared-public-key validators aggregated by weight and keyless validators still counted in the quorum denominator, so it cannot route through warp.VerifyEnvelope and carries beamSignedWeight and verifyBeam over the exported digest instead. Bumping warp to v1.24.1 to match evm and coreth carries consensus, crypto, ids and precompile forward with it; nothing outside these call sites needed changing. Co-authored-by: Hanzo Dev <[email protected]>
157 lines
5.1 KiB
Go
157 lines
5.1 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package warp
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/luxfi/evmgpu/precompile/contract"
|
|
"github.com/luxfi/evmgpu/predicate"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/vm"
|
|
log "github.com/luxfi/log"
|
|
commonmath "github.com/luxfi/math"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/warp"
|
|
"github.com/luxfi/warp/payload"
|
|
)
|
|
|
|
var (
|
|
_ messageHandler = addressedPayloadHandler{}
|
|
_ messageHandler = blockHashHandler{}
|
|
)
|
|
|
|
var (
|
|
getVerifiedWarpMessageInvalidOutput []byte
|
|
getVerifiedWarpBlockHashInvalidOutput []byte
|
|
)
|
|
|
|
func init() {
|
|
res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{Valid: false})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
getVerifiedWarpMessageInvalidOutput = res
|
|
|
|
res, err = PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{Valid: false})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
getVerifiedWarpBlockHashInvalidOutput = res
|
|
}
|
|
|
|
type messageHandler interface {
|
|
packFailed() []byte
|
|
handleMessage(msg *warp.Envelope) ([]byte, error)
|
|
}
|
|
|
|
func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, suppliedGas uint64, handler messageHandler) ([]byte, uint64, error) {
|
|
remainingGas, err := contract.DeductGas(suppliedGas, GetVerifiedWarpMessageBaseCost)
|
|
if err != nil {
|
|
return nil, remainingGas, err
|
|
}
|
|
|
|
warpIndexInput, err := UnpackGetVerifiedWarpMessageInput(input)
|
|
if err != nil {
|
|
return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidIndexInput, err)
|
|
}
|
|
if warpIndexInput > math.MaxInt32 {
|
|
return nil, remainingGas, fmt.Errorf("%w: larger than MaxInt32", errInvalidIndexInput)
|
|
}
|
|
warpIndex := int(warpIndexInput) // This conversion is safe even if int is 32 bits because we checked above.
|
|
state := accessibleState.GetStateDB()
|
|
predicateBytes, exists := state.GetPredicateStorageSlots(ContractAddress, warpIndex)
|
|
predicateResults := accessibleState.GetBlockContext().GetPredicateResults(state.GetTxHash(), ContractAddress)
|
|
bitset := set.BitsFromBytes(predicateResults)
|
|
containsIndex := bitset.Contains(warpIndex)
|
|
valid := exists && !containsIndex
|
|
log.Info("DEBUG handleWarpMessage",
|
|
"warpIndex", warpIndex,
|
|
"exists", exists,
|
|
"predicateResultsLen", len(predicateResults),
|
|
"predicateResults", fmt.Sprintf("%x", predicateResults),
|
|
"containsIndex", containsIndex,
|
|
"valid", valid)
|
|
if !valid {
|
|
return handler.packFailed(), remainingGas, nil
|
|
}
|
|
|
|
// Note: we charge for the size of the message during both predicate verification and each time the message is read during
|
|
// EVM execution because each execution incurs an additional read cost.
|
|
msgBytesGas, overflow := commonmath.SafeMul(GasCostPerWarpMessageBytes, uint64(len(predicateBytes)))
|
|
if overflow {
|
|
return nil, 0, vm.ErrOutOfGas
|
|
}
|
|
if remainingGas, err = contract.DeductGas(remainingGas, msgBytesGas); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
// Note: since the predicate is verified in advance of execution, the precompile should not
|
|
// hit an error during execution.
|
|
unpackedPredicateBytes, err := predicate.UnpackPredicate(predicateBytes)
|
|
if err != nil {
|
|
return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidPredicateBytes, err)
|
|
}
|
|
warpMessage, err := warp.ParseEnvelope(unpackedPredicateBytes)
|
|
if err != nil {
|
|
return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidWarpMsg, err)
|
|
}
|
|
res, err := handler.handleMessage(warpMessage)
|
|
if err != nil {
|
|
return nil, remainingGas, err
|
|
}
|
|
return res, remainingGas, nil
|
|
}
|
|
|
|
type addressedPayloadHandler struct{}
|
|
|
|
func (addressedPayloadHandler) packFailed() []byte {
|
|
return getVerifiedWarpMessageInvalidOutput
|
|
}
|
|
|
|
func (addressedPayloadHandler) handleMessage(warpMessage *warp.Envelope) ([]byte, error) {
|
|
addressedPayload, err := payload.ParsePayload(warpMessage.Message.Payload)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %s", errInvalidAddressedPayload, err)
|
|
}
|
|
// Type assert to AddressedCall
|
|
addressedCall, ok := addressedPayload.(*payload.AddressedCall)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%w: payload is not AddressedCall", errInvalidAddressedPayload)
|
|
}
|
|
return PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{
|
|
Message: WarpMessage{
|
|
SourceChainID: common.BytesToHash(warpMessage.Message.SourceChainID[:]),
|
|
OriginSenderAddress: common.BytesToAddress(addressedCall.SourceAddress),
|
|
Payload: addressedCall.Payload,
|
|
},
|
|
Valid: true,
|
|
})
|
|
}
|
|
|
|
type blockHashHandler struct{}
|
|
|
|
func (blockHashHandler) packFailed() []byte {
|
|
return getVerifiedWarpBlockHashInvalidOutput
|
|
}
|
|
|
|
func (blockHashHandler) handleMessage(warpMessage *warp.Envelope) ([]byte, error) {
|
|
parsedPayload, err := payload.ParsePayload(warpMessage.Message.Payload)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %s", errInvalidBlockHashPayload, err)
|
|
}
|
|
// Type assert to Hash
|
|
blockHashPayload, ok := parsedPayload.(*payload.Hash)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%w: payload is not Hash", errInvalidBlockHashPayload)
|
|
}
|
|
return PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{
|
|
WarpBlockHash: WarpBlockHash{
|
|
SourceChainID: common.BytesToHash(warpMessage.Message.SourceChainID[:]),
|
|
BlockHash: common.BytesToHash(blockHashPayload.Hash[:]),
|
|
},
|
|
Valid: true,
|
|
})
|
|
}
|