Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,472 changes: 794 additions & 678 deletions p2p/gen/go/bidderapi/v1/bidderapi.pb.go

Large diffs are not rendered by default.

1,228 changes: 672 additions & 556 deletions p2p/gen/go/providerapi/v1/providerapi.pb.go

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions p2p/gen/openapi/bidderapi/v1/bidderapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,9 @@ definitions:
positionConstraint:
$ref: '#/definitions/bidderapiv1PositionConstraint'
description: Position constraint for the transaction in the block.
shutterisedBidOption:
$ref: '#/definitions/bidderapiv1ShutterisedBidOption'
description: Shutterised bid option for the transaction in the block.
bidderapiv1BidOptions:
type: object
properties:
Expand Down Expand Up @@ -556,6 +559,17 @@ definitions:
- BASIS_PERCENTILE: Position is a percentile of the block size
- BASIS_ABSOLUTE: Position is an absolute position in the block
- BASIS_GAS_PERCENTILE: Position is a percentile of the gas used in the block
bidderapiv1ShutterisedBidOption:
type: object
properties:
identityPrefix:
type: string
description: Hex string encoding of the identity prefix of the transaction that the bidder wants to include in the block.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should standardize all hex-encoded fields to consistently include the 0x prefix. This follows Ethereum conventions and the approach we use in the Shutter API, and it also makes it immediately clear from the string representation which encoding we’re dealing with.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The standard followed in mev-commit repo, is to not include 0x prefix to the hex encoded strings, which is also followed here!

pattern: '[a-fA-F0-9]{64}'
encryptedTx:
type: string
description: Hex string encoding of the encrypted transaction that the bidder wants to include in the block.
pattern: '[a-fA-F0-9]{64}'
googlerpcStatus:
type: object
properties:
Expand Down
2 changes: 1 addition & 1 deletion p2p/gen/openapi/debugapi/v1/debugapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ definitions:
`NullValue` is a singleton enumeration to represent the null value for the
`Value` type union.

The JSON representation for `NullValue` is JSON `null`.
The JSON representation for `NullValue` is JSON `null`.
v1CancelTransactionResponse:
type: object
example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ definitions:
`NullValue` is a singleton enumeration to represent the null value for the
`Value` type union.

The JSON representation for `NullValue` is JSON `null`.
The JSON representation for `NullValue` is JSON `null`.
v1Notification:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions p2p/gen/openapi/providerapi/v1/providerapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ definitions:
positionConstraint:
$ref: '#/definitions/providerapiv1PositionConstraint'
description: Position constraint for the transaction in the block.
shutterisedBidOption:
$ref: '#/definitions/providerapiv1ShutterisedBidOption'
description: Shutterised bid option for the transaction in the block.
providerapiv1BidOptions:
type: object
properties:
Expand Down Expand Up @@ -365,6 +368,17 @@ definitions:
- BASIS_PERCENTILE: Position is a percentile of the block size
- BASIS_ABSOLUTE: Position is an absolute position in the block
- BASIS_GAS_PERCENTILE: Position is a percentile of the gas used in the block
providerapiv1ShutterisedBidOption:
type: object
properties:
identityPrefix:
type: string
description: Hex string encoding of the identity prefix of the transaction that the bidder wants to include in the block.
pattern: '[a-fA-F0-9]{64}'
encryptedTx:
type: string
description: Hex string encoding of the encrypted transaction that the bidder wants to include in the block.
pattern: '[a-fA-F0-9]{64}'
v1BidResponse:
type: object
example:
Expand Down
12 changes: 12 additions & 0 deletions p2p/pkg/rpc/bidder/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,22 @@ func (s *Service) SendBid(
)
switch {
case len(bid.TxHashes) > 0:
if bid.BidOptions != nil && len(bid.BidOptions.Options) > 0 {
for _, option := range bid.BidOptions.Options {
if option.GetShutterisedBidOption() != nil {
c := option.GetShutterisedBidOption()
if len(c.GetIdentityPrefix()) != 64 || len(c.GetEncryptedTx()) == 0 {
s.logger.Error("shutterised bid option identity prefix or encrypted tx is invalid", "option", option)
return status.Errorf(codes.InvalidArgument, "shutterised bid option identity prefix or encrypted tx is invalid")
}
}
}
}
txnsStr = strings.Join(stripPrefix(bid.TxHashes), ",")
case len(bid.RawTransactions) > 0:
strBuilder := new(strings.Builder)
for i, rawTx := range bid.RawTransactions {

rawTxnBytes, err := hex.DecodeString(strings.TrimPrefix(rawTx, "0x"))
if err != nil {
s.logger.Error("decoding raw transaction", "error", err)
Expand Down
255 changes: 255 additions & 0 deletions p2p/pkg/rpc/bidder/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,3 +797,258 @@ func TestGetBidInfo(t *testing.T) {
}
})
}

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

client := startServer(t)

t.Run("valid shutterised bid options", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{common.HexToHash("0x0000ab").Hex()[2:]},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
EncryptedTx: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid with valid shutterised options: %v", err)
}

count := 0
for {
_, err := rcv.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
t.Fatalf("error receiving preconfs: %v", err)
}
count++
}
if count != 2 {
t.Fatalf("expected 2 preconfs, got %v", count)
}
})

t.Run("nil shutterised bid option", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: nil,
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid: %v", err)
}

_, err = rcv.Recv()
if err == nil || !strings.Contains(err.Error(), "shutterised bid option identity prefix or encrypted tx is invalid") {
t.Fatalf("expected error about nil shutterised bid option, got %v", err)
}
})

t.Run("empty identity prefix", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "",
EncryptedTx: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid: %v", err)
}

_, err = rcv.Recv()
if err == nil || !strings.Contains(err.Error(), "shutterised bid option identity prefix or encrypted tx is invalid") {
t.Fatalf("expected error about empty identity prefix, got %v", err)
}
})

t.Run("empty encrypted tx", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
EncryptedTx: "",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid: %v", err)
}

_, err = rcv.Recv()
if err == nil || !strings.Contains(err.Error(), "shutterised bid option identity prefix or encrypted tx is invalid") {
t.Fatalf("expected error about empty encrypted tx, got %v", err)
}
})

t.Run("both identity prefix and encrypted tx empty", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "",
EncryptedTx: "",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid: %v", err)
}

_, err = rcv.Recv()
if err == nil || !strings.Contains(err.Error(), "shutterised bid option identity prefix or encrypted tx is invalid") {
t.Fatalf("expected error about empty fields, got %v", err)
}
})

t.Run("multiple transactions with shutterised options", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{
common.HexToHash("0x0000ab").Hex()[2:],
common.HexToHash("0x0000cd").Hex()[2:],
},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
EncryptedTx: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
},
},
},
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321",
EncryptedTx: "0987654321fedcba0987654321fedcba0987654321fedcba0987654321fedcba",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid with multiple shutterised options: %v", err)
}

count := 0
for {
_, err := rcv.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
t.Fatalf("error receiving preconfs: %v", err)
}
count++
}
if count != 2 {
t.Fatalf("expected 2 preconfs, got %v", count)
}
})

t.Run("mixed bid options with one invalid shutterised option", func(t *testing.T) {
rcv, err := client.SendBid(context.Background(), &bidderapiv1.Bid{
TxHashes: []string{
common.HexToHash("0x0000ab").Hex()[2:],
common.HexToHash("0x0000cd").Hex()[2:],
},
Amount: "1000000000000000000",
BlockNumber: 1,
DecayStartTimestamp: 10,
DecayEndTimestamp: 20,
BidOptions: &bidderapiv1.BidOptions{
Options: []*bidderapiv1.BidOption{
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
EncryptedTx: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
},
},
},
{
Opt: &bidderapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &bidderapiv1.ShutterisedBidOption{
IdentityPrefix: "",
EncryptedTx: "0987654321fedcba0987654321fedcba0987654321fedcba0987654321fedcba",
},
},
},
},
},
})
if err != nil {
t.Fatalf("error sending bid: %v", err)
}

_, err = rcv.Recv()
if err == nil || !strings.Contains(err.Error(), "shutterised bid option identity prefix or encrypted tx is invalid") {
t.Fatalf("expected error about invalid shutterised option, got %v", err)
}
})
}
11 changes: 11 additions & 0 deletions p2p/pkg/rpc/provider/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ func (s *Service) ProcessBid(
},
}
opts.Options = append(opts.Options, opt)
case bOpt.GetShutterisedBidOption() != nil:
c := bOpt.GetShutterisedBidOption()
opt := &providerapiv1.BidOption{
Opt: &providerapiv1.BidOption_ShutterisedBidOption{
ShutterisedBidOption: &providerapiv1.ShutterisedBidOption{
IdentityPrefix: c.GetIdentityPrefix(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't EncryptedTransaction missing here?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, nice catch

EncryptedTx: c.GetEncryptedTx(),
},
},
}
opts.Options = append(opts.Options, opt)
}
}
}
Expand Down
Loading