Skip to content

Commit

Permalink
Merge pull request #52 from chainbound/feat/bolt-boost-constraints
Browse files Browse the repository at this point in the history
feat(mev-boost): updated constraints API
  • Loading branch information
merklefruit authored Jun 4, 2024
2 parents 832986e + 0bb16cb commit e26ef2d
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 166 deletions.
1 change: 1 addition & 0 deletions mev-boost/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/DataDog/zstd v1.5.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/chainbound/shardmap v0.0.3-0.20240604113309-43d9072efe8c // indirect
github.com/cockroachdb/errors v1.9.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect
Expand Down
4 changes: 4 additions & 0 deletions mev-boost/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chainbound/shardmap v0.0.2 h1:yB1weccdm2vC6dnqzzLwPIvyAnRj7815mJWbkPybiYw=
github.com/chainbound/shardmap v0.0.2/go.mod h1:TBvIzhHyFUbt+oa3UzbijobTUh221st6xIbuki7WzPc=
github.com/chainbound/shardmap v0.0.3-0.20240604113309-43d9072efe8c h1:7GkJDinsgr9Wcr5yJZGGtYFcCAxS9WSdOBr4DXBaM7c=
github.com/chainbound/shardmap v0.0.3-0.20240604113309-43d9072efe8c/go.mod h1:TBvIzhHyFUbt+oa3UzbijobTUh221st6xIbuki7WzPc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
Expand Down
170 changes: 60 additions & 110 deletions mev-boost/server/constraints.go
Original file line number Diff line number Diff line change
@@ -1,146 +1,96 @@
package server

import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"

"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/chainbound/shardmap"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

type SignedConstraintSubmission struct {
Message *ConstraintSubmission
Signature phase0.BLSSignature `ssz-size:"96"`
}
type BatchedSignedConstraints = []*SignedConstraints

type signedConstraintSubmissionJSON struct {
Message *ConstraintSubmission `json:"message"`
Signature string `json:"signature"`
type SignedConstraints struct {
Message ConstraintsMessage `json:"message"`
Signature HexBytes `json:"signature"`
}

func (s *SignedConstraintSubmission) MarshalJSON() ([]byte, error) {
return json.Marshal(&signedConstraintSubmissionJSON{
Message: s.Message,
Signature: fmt.Sprintf("%#x", s.Signature),
})
type ConstraintsMessage struct {
ValidatorIndex uint64
Slot uint64
Constraints []*Constraint
}

func (s *SignedConstraintSubmission) UnmarshalJSON(input []byte) error {
var data signedConstraintSubmissionJSON
if err := json.Unmarshal(input, &data); err != nil {
return errors.Wrap(err, "invalid JSON")
}

if data.Message == nil {
return errors.New("message missing")
}

s.Message = data.Message

if data.Signature == "" {
return errors.New("signature missing")
}

signature, err := hex.DecodeString(strings.TrimPrefix(data.Signature, "0x"))
if err != nil {
return errors.Wrap(err, "invalid signature")
}

if len(signature) != phase0.SignatureLength {
return errors.New("incorrect length for signature")
}
copy(s.Signature[:], signature)

return nil
}

type ConstraintSubmission struct {
Slot uint64
TxHash phase0.Hash32 `ssz-size:"32"`
RawTx Transaction `ssz-max:"1073741824"`
type Constraint struct {
Tx Transaction
Index *uint64
}

type constraintSubmissionJSON struct {
Slot uint64 `json:"slot"`
TxHash string `json:"txHash"`
RawTx string `json:"rawTx"`
// ConstraintCache is a cache for constraints.
type ConstraintCache struct {
// map of slots to all constraints for that slot
constraints shardmap.FIFOMap[uint64, map[common.Hash]*Constraint]
}

func (c *ConstraintSubmission) MarshalJSON() ([]byte, error) {
return json.Marshal(&constraintSubmissionJSON{
Slot: c.Slot,
TxHash: c.TxHash.String(),
RawTx: fmt.Sprintf("%#x", c.RawTx),
})
// NewConstraintCache creates a new constraint cache.
// cap is the maximum number of slots to store constraints for.
func NewConstraintCache(cap int) *ConstraintCache {
return &ConstraintCache{
constraints: *shardmap.NewFIFOMap[uint64, map[common.Hash]*Constraint](int(cap), 1, shardmap.HashUint64),
}
}

func (c *ConstraintSubmission) UnmarshalJSON(input []byte) error {
var data constraintSubmissionJSON
if err := json.Unmarshal(input, &data); err != nil {
return err
// AddInclusionConstraint adds an inclusion constraint to the cache at the given slot for the given transaction.
func (c *ConstraintCache) AddInclusionConstraint(slot uint64, tx Transaction, index *uint64) error {
if _, exists := c.constraints.Get(slot); !exists {
c.constraints.Put(slot, make(map[common.Hash]*Constraint))
}

c.Slot = data.Slot

txHash, err := hex.DecodeString((strings.TrimPrefix(data.TxHash, "0x")))
// parse transaction to get its hash and store it in the cache
// for constant time lookup later
var parsedTx = new(types.Transaction)
err := parsedTx.UnmarshalBinary(tx)
if err != nil {
return errors.Wrap(err, "invalid tx hash")
return err
}

copy(c.TxHash[:], txHash)

rawTx, err := hex.DecodeString((strings.TrimPrefix(data.RawTx, "0x")))
if err != nil {
return errors.Wrap(err, "invalid raw tx")
m, _ := c.constraints.Get(slot)
m[parsedTx.Hash()] = &Constraint{
Tx: tx,
Index: index,
}

c.RawTx = rawTx

return nil
}

// Constraints is a map of constraints for a block.
type Constraints = map[phase0.Hash32]*Constraint

// Constraint is a constraint on a block. For now just preconfirmations
// or inclusion constraints.
type Constraint struct {
RawTx Transaction `json:"rawTx"`
}

// ConstraintCache is a cache for constraints.
type ConstraintCache struct {
// map of slots to constraints
constraints map[uint64]Constraints
}

// NewConstraintCache creates a new constraint cache.
func NewConstraintCache() *ConstraintCache {
return &ConstraintCache{
// TODO: there should be a maximum length here that we can pre-allocate (probably the lookahead window size)
constraints: make(map[uint64]Constraints),
// AddInclusionConstraints adds multiple inclusion constraints to the cache at the given slot
func (c *ConstraintCache) AddInclusionConstraints(slot uint64, constraints []*Constraint) error {
if _, exists := c.constraints.Get(slot); !exists {
c.constraints.Put(slot, make(map[common.Hash]*Constraint))
}
}

// AddInclusionConstraint adds an inclusion constraint to the cache at the given slot for the given transaction.
func (c *ConstraintCache) AddInclusionConstraint(slot uint64, txHash phase0.Hash32, rawTx Transaction) {
if _, exists := c.constraints[slot]; !exists {
c.constraints[slot] = make(map[phase0.Hash32]*Constraint)
m, _ := c.constraints.Get(slot)
for _, constraint := range constraints {
var parsedTx = new(types.Transaction)
err := parsedTx.UnmarshalBinary(constraint.Tx)
if err != nil {
return err
}
m[parsedTx.Hash()] = constraint
}

c.constraints[slot][txHash] = &Constraint{
RawTx: rawTx,
}
return nil
}

// Get gets the constraints at the given slot.
func (c *ConstraintCache) Get(slot uint64) Constraints {
return c.constraints[slot]
func (c *ConstraintCache) Get(slot uint64) (map[common.Hash]*Constraint, bool) {
return c.constraints.Get(slot)
}

// Delete deletes the constraints at the given slot.
func (c *ConstraintCache) Delete(slot uint64) {
delete(c.constraints, slot)
// FindTransactionByHash finds the constraint for the given transaction hash and returns it.
func (c *ConstraintCache) FindTransactionByHash(txHash common.Hash) (*Constraint, bool) {
for kv := range c.constraints.Iter() {
if constraint, exists := kv.Value[txHash]; exists {
return constraint, true
}
}
return nil, false
}
2 changes: 1 addition & 1 deletion mev-boost/server/mock_relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func (m *mockRelay) handleSubmitConstraint(w http.ResponseWriter, req *http.Requ
}

func (m *mockRelay) defaultHandleSubmitConstraint(w http.ResponseWriter, req *http.Request) {
payload := []SignedConstraintSubmission{}
payload := BatchedSignedConstraints{}
if err := DecodeJSON(req.Body, &payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
Expand Down
6 changes: 5 additions & 1 deletion mev-boost/server/proofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ func (p *InclusionProof) String() string {

type HexBytes []byte

func (h HexBytes) Equal(other HexBytes) bool {
return bytes.Equal(h, other)
}

// MarshalJSON implements json.Marshaler.
func (h HexBytes) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%#x"`, h)), nil
return []byte(fmt.Sprintf(`"%#x"`, []byte(h))), nil
}

// UnmarshalJSON implements json.Unmarshaler.
Expand Down
Loading

0 comments on commit e26ef2d

Please sign in to comment.