Files
evm/precompile/contracts/warp/contract_warp_handler.go

134 lines
4.4 KiB
Go

// (c) 2023, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"fmt"
"math"
"github.com/luxfi/evm/utils/set"
"github.com/luxfi/evm/iface"
"github.com/luxfi/evm/precompile/contract"
"github.com/luxfi/evm/predicate"
"github.com/luxfi/geth/common"
"github.com/luxfi/evm/vmerrs"
)
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 *iface.WarpSignedMessage) ([]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 > uint32(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)
valid := exists && !set.BitsFromBytes(predicateResults).Contains(warpIndex)
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 := iface.SafeMul(GasCostPerWarpMessageBytes, uint64(len(predicateBytes)))
if overflow {
return nil, 0, vmerrs.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 := iface.ParseMessage(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 *iface.WarpSignedMessage) ([]byte, error) {
addressedPayload, err := iface.ParseAddressedCall(warpMessage.UnsignedMessage.Payload)
if err != nil {
return nil, fmt.Errorf("%w: %s", errInvalidAddressedPayload, err)
}
return PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{
Message: WarpMessage{
SourceChainID: common.Hash(warpMessage.SourceChainID),
OriginSenderAddress: common.BytesToAddress(addressedPayload.SourceAddress),
Payload: addressedPayload.Payload,
},
Valid: true,
})
}
type blockHashHandler struct{}
func (blockHashHandler) packFailed() []byte {
return getVerifiedWarpBlockHashInvalidOutput
}
func (blockHashHandler) handleMessage(warpMessage *iface.WarpSignedMessage) ([]byte, error) {
blockHashPayload, err := iface.ParseHash(warpMessage.UnsignedMessage.Payload)
if err != nil {
return nil, fmt.Errorf("%w: %s", errInvalidBlockHashPayload, err)
}
return PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{
WarpBlockHash: WarpBlockHash{
SourceChainID: common.Hash(warpMessage.SourceChainID),
BlockHash: common.BytesToHash(blockHashPayload.Hash[:]),
},
Valid: true,
})
}