Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from primevprotocol/ckartik/preconf-protocol-impl
Browse files Browse the repository at this point in the history
Rough Implementation of Pre-Confirmations
  • Loading branch information
ckartik authored Oct 2, 2023
2 parents d2a5646 + fb3daf0 commit db47916
Show file tree
Hide file tree
Showing 8 changed files with 565 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
vendor
**/.idea
**/.vscode
**/.vscode
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ require (
require (
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.5.0 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.10.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/ethereum/c-kzg-4844 v0.3.1 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand Down Expand Up @@ -61,6 +67,7 @@ require (
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
Expand All @@ -86,6 +93,7 @@ require (
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/dig v1.17.0 // indirect
Expand All @@ -100,4 +108,5 @@ require (
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
google.golang.org/protobuf v1.31.0 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
58 changes: 58 additions & 0 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pkg/discovery/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestDiscovery(t *testing.T) {
}

svc := p2ptest.New(
&client,
p2ptest.WithConnectFunc(func(addr []byte) (p2p.Peer, error) {
if string(addr) != "test" {
return p2p.Peer{}, errors.New("invalid address")
Expand Down
6 changes: 4 additions & 2 deletions pkg/p2p/testing/p2p.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func pipe(a, b *testStream) {
type Option func(*P2PTest)

type P2PTest struct {
self *p2p.Peer
handlers map[string]p2p.ProtocolSpec
connectFunc func([]byte) (p2p.Peer, error)
addressbookFunc func(p2p.Peer) ([]byte, error)
Expand All @@ -87,9 +88,10 @@ func WithAddressbookFunc(fn func(p p2p.Peer) ([]byte, error)) Option {
}
}

func New(opts ...Option) *P2PTest {
func New(selfNode *p2p.Peer, opts ...Option) *P2PTest {
p := &P2PTest{
handlers: make(map[string]p2p.ProtocolSpec),
self: selfNode,
}

for _, opt := range opts {
Expand Down Expand Up @@ -146,7 +148,7 @@ func (p *P2PTest) NewStream(
go func() {
defer in.Close()

err := handler(context.Background(), peer, in)
err := handler(context.Background(), *p.self, in)
if err != nil {
panic(err)
}
Expand Down
158 changes: 158 additions & 0 deletions pkg/preconfirmation/preconfirmation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package preconfirmation

import (
"context"
"crypto/ecdsa"
"errors"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/primevprotocol/mev-commit/pkg/p2p"
"github.com/primevprotocol/mev-commit/pkg/p2p/msgpack"
"github.com/primevprotocol/mev-commit/pkg/structures/preconf"
"github.com/primevprotocol/mev-commit/pkg/topology"
)

const (
ProtocolName = "preconfirmation"
ProtocolVersion = "1.0.0"
)

type Preconfirmation struct {
signer preconf.Signer
topo Topology
streamer p2p.Streamer
cs CommitmentsStore
us UserStore
}

type Topology interface {
GetPeers(topology.Query) []p2p.Peer
}

func New(topo Topology, streamer p2p.Streamer, key *ecdsa.PrivateKey, us UserStore, cs CommitmentsStore) *Preconfirmation {
return &Preconfirmation{
topo: topo,
streamer: streamer,
signer: preconf.PrivateKeySigner{PrivKey: key},
us: us,
cs: cs,
}
}

func (p *Preconfirmation) Protocol() p2p.ProtocolSpec {
return p2p.ProtocolSpec{
Name: ProtocolName,
Version: ProtocolVersion,
StreamSpecs: []p2p.StreamSpec{
{
Name: "bid",
Handler: p.handleBid,
},
},
}
}

// BidHash -> map of preconfs
// Key: BidHash
// Value: List of preconfs
// var commitments map[string][]preconf.PreconfCommitment
type CommitmentsStore interface {
GetCommitments(bidHash []byte) ([]preconf.PreconfCommitment, error)
AddCommitment(bidHash []byte, commitment *preconf.PreconfCommitment) error
}

type UserStore interface {
CheckUserRegistred(*common.Address) bool
}

// SendBid is meant to be called by the searcher to construct and send bids to the builder.
// It takes the txnHash, the bid amount in wei and the maximum valid block number.
// It waits for commitments from all builders and then returns.
// TODO(@ckartik): construct seperate go-routine to wait for commitments, with a context that cancels if a timeout is reached.
//
// It returns an error if the bid is not valid.
func (p *Preconfirmation) SendBid(ctx context.Context, txnHash string, bidamt *big.Int, blockNumber *big.Int) error {
signedBid, err := preconf.ConstructSignedBid(bidamt, txnHash, blockNumber, p.signer)
if err != nil {
return err
}

builders := p.topo.GetPeers(topology.Query{Type: p2p.PeerTypeBuilder})

// TODO(@ckartik): Push into a channel and process in parallel
for _, builder := range builders {
// Create a new connection
builderStream, err := p.streamer.NewStream(ctx, builder, ProtocolName, ProtocolVersion, "bid")
if err != nil {
return err
}

r, w := msgpack.NewReaderWriter[preconf.PreconfCommitment, preconf.PreConfBid](builderStream)
err = w.WriteMsg(ctx, signedBid)
if err != nil {
return err
}

commitment, err := r.ReadMsg(ctx)
if err != nil {
return err
}

// Process commitment as a searcher
_, err = commitment.VerifyBuilderSignature()
if err != nil {
return err
}
_, err = commitment.VerifySearcherSignature()
if err != nil {
return err
}

// Verify the bid details correspond.
err = p.cs.AddCommitment(signedBid.BidHash, commitment)
if err != nil {
return err
}
}

return nil
}

var ErrInvalidSearcherTypeForBid = errors.New("invalid searcher type for bid")

// handlebid is the function that is called when a bid is received
// It is meant to be used by the builder exclusively to read the bid value from the searcher.
func (p *Preconfirmation) handleBid(
ctx context.Context,
peer p2p.Peer,
stream p2p.Stream,
) error {
if peer.Type != p2p.PeerTypeSearcher {
return ErrInvalidSearcherTypeForBid
}

// TODO(@ckartik): Change to reader only once availble
r, w := msgpack.NewReaderWriter[preconf.PreConfBid, preconf.PreconfCommitment](stream)
bid, err := r.ReadMsg(ctx)
if err != nil {
return err
}

ethAddress, err := bid.VerifySearcherSignature()
if err != nil {
return err
}

if p.us.CheckUserRegistred(ethAddress) {
// More conditional Logic to determine signing of bid
commitment, err := preconf.ConstructCommitment(*bid, p.signer)
if err != nil {
return err
}

return w.WriteMsg(ctx, commitment)
}

return nil
}
93 changes: 93 additions & 0 deletions pkg/preconfirmation/preconfirmation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package preconfirmation_test

import (
"context"
"math/big"
"sync"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/primevprotocol/mev-commit/pkg/p2p"
p2ptest "github.com/primevprotocol/mev-commit/pkg/p2p/testing"
"github.com/primevprotocol/mev-commit/pkg/preconfirmation"
"github.com/primevprotocol/mev-commit/pkg/structures/preconf"
"github.com/primevprotocol/mev-commit/pkg/topology"
)

type testTopo struct {
mu sync.Mutex
peers []p2p.Peer
}

func (t *testTopo) AddPeers(peers ...p2p.Peer) {
t.mu.Lock()
defer t.mu.Unlock()

t.peers = append(t.peers, peers...)
}

func (t *testTopo) GetPeers(q topology.Query) []p2p.Peer {
t.mu.Lock()
defer t.mu.Unlock()

return []p2p.Peer{{EthAddress: common.HexToAddress("0x2"), Type: p2p.PeerTypeBuilder}}
}

func (t *testTopo) Connected(p2p.Peer) {
}

func (t *testTopo) Disconnected(p2p.Peer) {
}

type testCommitmentStore struct {
}

func (t *testCommitmentStore) GetCommitments(bidHash []byte) ([]preconf.PreconfCommitment, error) {
return []preconf.PreconfCommitment{}, nil
}

func (t *testCommitmentStore) AddCommitment(bidHash []byte, commitment *preconf.PreconfCommitment) error {
return nil
}

type testUserStore struct {
}

func (t *testUserStore) CheckUserRegistred(_ *common.Address) bool {
return true
}

func TestPreconfBidSubmission(t *testing.T) {
t.Parallel()

t.Run("ok", func(t *testing.T) {
client := p2p.Peer{
EthAddress: common.HexToAddress("0x1"),
Type: p2p.PeerTypeSearcher,
}
server := p2p.Peer{
EthAddress: common.HexToAddress("0x2"),
Type: p2p.PeerTypeBuilder,
}

svc := p2ptest.New(
&client,
)

topo := &testTopo{}
us := &testUserStore{}
cs := &testCommitmentStore{}
key, _ := crypto.GenerateKey()
p := preconfirmation.New(topo, svc, key, us, cs)

// svc.SetPeerHandler(client, p.Protocol())
svc.SetPeerHandler(server, p.Protocol())

err := p.SendBid(context.Background(), "0x4c03a845396b770ad41b975d6bd3bf8c2bd5cca36867a3301f9598f2e3e9518d", big.NewInt(10), big.NewInt(10))
if err != nil {
t.Fatal(err)
}

})
}
Loading

0 comments on commit db47916

Please sign in to comment.