Skip to content

Commit

Permalink
Merge pull request #8 from bnb-chain/dev
Browse files Browse the repository at this point in the history
feat: prepare for initial release of sentry
  • Loading branch information
unclezoro authored Apr 2, 2024
2 parents 5d33676 + 44e3838 commit 0c44d2f
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 237 deletions.
4 changes: 0 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ RUN apk add --no-cache build-base git bash linux-headers eudev-dev curl ca-certi
WORKDIR /build
COPY . .

ARG GH_TOKEN=""
RUN go env -w GOPRIVATE="github.com/bnb-chain/*"
RUN git config --global url."https://${GH_TOKEN}@github.com".insteadOf "https://github.com"

RUN go mod tidy
RUN go build -o .build/sentry ./cmd

Expand Down
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,62 @@
# BSC-MEV-Sentry

BSC-MEV-Sentry is a proxy service within the BSC MEV architecture designed to shield the validator network.
It functions to forward requests and pay to builders.
BSC-MEV-Sentry serves as the proxy service for BSC MEV architecture, It has the following features:

1. Forward RPC requests: mev_sendBid, mev_params, mev_running, mev_bestBidGasFee to validators.
2. Forward RPC request: mev_reportIssue to builders.
3. Pay builders on behalf of validators for their bids.
4. Monitor validators' status and health.

See also: https://github.com/bnb-chain/BEPs/pull/322

For the details of mev_params, here are some notices:

1. The builder can call mev_params to obtain the delayLeftOver and bidSimulationLeftOver time settings, and then call
the [BidBetterBefore](https://github.com/bnb-chain/bsc/blob/master/common/bidutil/bidutil.go) to calculate the
deadline for sending the bid.
2. The builder can call mev_params to obtain the gasCeil of the validator, to generate a valid header in the block
building settlement.
3. The builder can call mev_params to obtain the builderFeeCeil of the validator, to help to decide the builder fee.

# Usage

1. `make build`
2. `.build/sentry -config ./configs/config.toml`

Sentry settings are configured in the `config.toml` file. The following is an example of a `config.toml` file:

```
[Service]
HTTPListenAddr = "localhost:8555" # The address to listen on for HTTP requests.
RPCConcurrency = 100 # The maximum number of concurrent requests.
RPCTimeout = "10s" # The timeout for RPC requests.
[[Validators]] # A list of validators to forward requests to.
PrivateURL = "https://bsc-fuji" # The private rpc url of the validator, it can only been accessed in the local network.
PublicHostName = "bsc-fuji" # The domain name of the validator, if a request's HOST info is same with this, it will be forwarded to the validator.
PayAccountMode = "privateKey" # The unlock mode of the pay bid account.
PrivateKey = "59ba8068eb256d520...2bd306e1bd603fdb8c8da10e8" # The private key of the pay bid account.
[[Validators]]
PrivateURL = "https://bsc-mathwallet"
PublicHostName = "bsc-mathwallet"
PayAccountMode = "keystore"
KeystorePath = "./keystore" # The keystore file path of the pay bid account.
PasswordFilePath = "./password.txt" # The path of the pay bid account's password file.
PayAccountAddress = "0x12c86Bf9...845B98F23" # The address of the pay bid account.
[[Validators]]
PrivateURL = "https://bsc-trustwallet"
PublicHostName = "bsc-trustwallet"
PayAccountMode = "privateKey" # The unlock mode of the pay account.
PrivateKey = "59ba8068eb...d306e1bd603fdb8c8da10e8" # The private key of the pay account.
[[Builders]]
Address = "0x45EbEBe8...664D59c12" # The address of the builder.
URL = "http://bsc-builder-1" # The public URL of the builder.
[[Builders]]
Address = "0x980A75eC...fc9b863D5"
URL = "http://bsc-builder-2"
```
94 changes: 46 additions & 48 deletions account/account.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package account

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

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
Expand All @@ -13,7 +14,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"

"github.com/bnb-chain/bsc-mev-sentry/log"
"github.com/bnb-chain/bsc-mev-sentry/node"
)

type Mode string
Expand All @@ -24,17 +24,18 @@ const (
)

type Account interface {
PayBidTx(context.Context, node.FullNode, common.Address, *big.Int) ([]byte, error)
Address() common.Address
SignTx(tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
}

func New(config *Config) (Account, error) {
switch config.Mode {
case privateKeyMode:
return newPrivateKeyAccount(config.PrivateKey)
case keystoreMode:
return newKeystoreAccount(config.KeystorePath, config.Password, config.Address)
return newKeystoreAccount(config.KeystorePath, config.PasswordFilePath, config.Address)
default:
return nil, errors.New("invalid account mode")
return nil, errors.New("invalid pay account mode")
}
}

Expand All @@ -44,60 +45,64 @@ type Config struct {
PrivateKey string
// KeystorePath path of keystore
KeystorePath string
// Password keystore password
Password string
// PasswordFilePath stores keystore password
PasswordFilePath string
// Address public address of sentry wallet
Address string
}

type baseAccount struct {
address common.Address
}

func (a *baseAccount) Address() common.Address {
return a.address
}

type keystoreAccount struct {
keystore *keystore.KeyStore
account accounts.Account
*baseAccount
}

func newKeystoreAccount(keystorePath, password, opAccount string) (*keystoreAccount, error) {
func newKeystoreAccount(keystorePath, passwordFilePath, opAccount string) (*keystoreAccount, error) {
address := common.HexToAddress(opAccount)
ks := keystore.NewKeyStore(keystorePath, keystore.StandardScryptN, keystore.StandardScryptP)
account, err := ks.Find(accounts.Account{Address: common.HexToAddress(opAccount)})
account, err := ks.Find(accounts.Account{Address: address})
if err != nil {
log.Errorw("failed to create key store account", "err", err)
return nil, err
}

password := MakePasswordFromPath(passwordFilePath)

err = ks.Unlock(account, password)
if err != nil {
log.Errorw("failed to unlock account", "err", err)
return nil, err
}

return &keystoreAccount{ks, account}, nil
}

func (k *keystoreAccount) PayBidTx(ctx context.Context, fullNode node.FullNode, receiver common.Address, amount *big.Int) ([]byte, error) {
nonce, err := fetchNonce(ctx, fullNode, k.account.Address)
err = os.Remove(passwordFilePath)
if err != nil {
return nil, err
log.Errorw("failed to remove password file", "err", err)
}

tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
GasPrice: big.NewInt(0),
Gas: 25000,
To: &receiver,
Value: amount,
})
return &keystoreAccount{ks, account, &baseAccount{address: address}}, nil
}

signedTx, err := k.keystore.SignTx(k.account, tx, fullNode.ChainID())
func (k *keystoreAccount) SignTx(tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
signedTx, err := k.keystore.SignTx(k.account, tx, chainID)
if err != nil {
log.Errorw("failed to sign tx", "err", err)
return nil, err
}

return signedTx.MarshalBinary()
return signedTx, nil
}

type privateKeyAccount struct {
key *ecdsa.PrivateKey
address common.Address
key *ecdsa.PrivateKey
*baseAccount
}

func newPrivateKeyAccount(privateKey string) (*privateKeyAccount, error) {
Expand All @@ -116,38 +121,31 @@ func newPrivateKeyAccount(privateKey string) (*privateKeyAccount, error) {

addr := crypto.PubkeyToAddress(*pubKeyECDSA)

return &privateKeyAccount{key, addr}, nil
return &privateKeyAccount{key, &baseAccount{address: addr}}, nil
}

func (p *privateKeyAccount) PayBidTx(ctx context.Context, fullNode node.FullNode, receiver common.Address, amount *big.Int) ([]byte, error) {
nonce, err := fetchNonce(ctx, fullNode, p.address)
if err != nil {
return nil, err
}

tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
GasPrice: big.NewInt(0),
Gas: 25000,
To: &receiver,
Value: amount,
})

signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(fullNode.ChainID()), p.key)
func (p *privateKeyAccount) SignTx(tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), p.key)
if err != nil {
log.Errorw("failed to sign tx", "err", err)
return nil, err
}

return signedTx.MarshalBinary()
return signedTx, nil
}

func fetchNonce(ctx context.Context, fullNode node.FullNode, address common.Address) (uint64, error) {
nonce, err := fullNode.PendingNonceAt(ctx, address)
func MakePasswordFromPath(path string) string {
if path == "" {
return ""
}
text, err := os.ReadFile(path)
if err != nil {
log.Errorw("failed to get nonce", "err", err)
return 0, err
log.Panicw("failed to read password file: %v", err)
}
lines := strings.Split(string(text), "\n")
if len(lines) == 0 {
return ""
}

return nonce, err
return strings.TrimRight(lines[0], "\r")
}
18 changes: 5 additions & 13 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/bnb-chain/bsc-mev-sentry/account"
"github.com/bnb-chain/bsc-mev-sentry/config"
ginutils "github.com/bnb-chain/bsc-mev-sentry/gin"
"github.com/bnb-chain/bsc-mev-sentry/log"
Expand Down Expand Up @@ -40,32 +39,25 @@ func main() {

log.Infow("bsc mev-sentry start", "configPath", *configPath, "config", cfg)

acc, err := account.New(&cfg.Account)
if err != nil {
log.Panicw("failed to create account", "err", err)
}

validators := make(map[string]node.Validator)
for _, v := range cfg.Validators {
validator := node.NewValidator(&v)
validator := node.NewValidator(v)
if validator != nil {
validators[v.PublicHostName] = validator
}
}

builders := make(map[common.Address]node.Builder)
for _, b := range cfg.Builders {
builder := node.NewBuilder(&b)
builder := node.NewBuilder(b)
if builder != nil {
builders[b.Address] = builder
}
}

fullNode := node.NewFullNode(&cfg.FullNode)

rpcServer := rpc.NewServer()
sentryService := service.NewMevSentry(&cfg.Service, acc, validators, builders, fullNode)
if err = rpcServer.RegisterName("mev", sentryService); err != nil {
sentryService := service.NewMevSentry(&cfg.Service, validators, builders)
if err := rpcServer.RegisterName("mev", sentryService); err != nil {
panic(err)
}

Expand All @@ -78,7 +70,7 @@ func main() {

app.POST("/", gin.WrapH(rpcServer))

if err = app.Run(cfg.Service.HTTPListenAddr); err != nil {
if err := app.Run(cfg.Service.HTTPListenAddr); err != nil {
log.Errorf("fail to run rpc server, err:%v", err)
}
}
Expand Down
3 changes: 0 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@ import (

"github.com/naoina/toml"

"github.com/bnb-chain/bsc-mev-sentry/account"
"github.com/bnb-chain/bsc-mev-sentry/node"
"github.com/bnb-chain/bsc-mev-sentry/service"
)

type Config struct {
Service service.Config
Account account.Config
Validators []node.ValidatorConfig
Builders []node.BuilderConfig
FullNode node.FullNodeConfig

Debug DebugConfig
Log LogConfig
Expand Down
38 changes: 17 additions & 21 deletions configs/config-example.toml
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
[Service]
HTTPListenAddr = "localhost:3000"
RPCConcurrency = 10
RPCTimeout = "10s"
HTTPListenAddr = "localhost:8555" # The address to listen on for HTTP requests.
RPCConcurrency = 100 # The maximum number of concurrent requests.
RPCTimeout = "10s" # The timeout for RPC requests.

[Account]
Mode = "keystore"
KeystorePath = "./keystore"
Password = "sentry"
Address = "0x837060bd423eFcDd5B7b6B92aB3CFc74B9CD0df4"
[[Validators]]
PrivateURL = "http://10.200.31.36:8545"
PublicHostName = "bsc-testnet-elbrus.bnbchain.org"
PayAccountMode = "privateKey"
PrivateKey = "b1fed931ad50...34796ddbee68a53cf"

[[Validators]]
PrivateURL = "http://127.0.0.1:8546"
PublicHostName = "127.0.0.1"
PrivateURL = "http://10.200.33.92:8545"
PublicHostName = "bsc-testnet-ararat.bnbchain.org"
PayAccountMode = "privateKey"
PrivateKey = "ce3f1b757384...755f66f647503"

[[Builders]]
Address = "0x837060bd423eFcDd5B7b6B92aB3CFc74B9CD0df4"
URL = "http://localhost:8555"

[FullNode]
URL = "http://localhost:8545"
Address = "0x980A75eCd1309eA12fa2ED87A8744fBfc9b863D5" # The address of the builder.
URL = "http://bsc-builder-1" # The public URL of the builder.

[Debug]
ListenAddr = "localhost:8090"

[Log]
RootDir = "./logs"
Level = "debug"
[[Builders]]
Address = "0x980A75eCd1309eA12fa2ED87A8744fBfc9b863D5"
URL = "http://bsc-builder-2"
Loading

0 comments on commit 0c44d2f

Please sign in to comment.