Skip to content

Commit

Permalink
Adding features to mempool to support our pre-ordered txs (#2)
Browse files Browse the repository at this point in the history
* Adding features to mempool to support our pre-ordered txs

* Add framing for gRPC to execute blocks

* Remove public engine API call

* Fix circular dependencies

* Updated to use new Init, and fill out starting attributes

* Add clear astriaordered, cleanup

* readme fix

* add bash and jq to final docker image

* make txpool interface

* no more panics, update DoBlock to also update state + store block

* cleanup

* set post-merge at genesis

* cleanup

* doc update

* remove txpool interface changes

* cleanup

* cleanup

* build and push images wih tags defined by git tags

* build for multiple architectures. use docker-metadata action for semver

* add push: true

* only build arm for git tags/releases

---------

Co-authored-by: Jesse Snyder <[email protected]>
Co-authored-by: elizabeth <[email protected]>
  • Loading branch information
3 people authored Apr 10, 2023
1 parent 5ec91e0 commit f91e86a
Show file tree
Hide file tree
Showing 15 changed files with 607 additions and 143 deletions.
57 changes: 43 additions & 14 deletions .github/workflows/astria-build-and-publish-image.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
name: Build and Publish Docker image

# Trigger on pushes to astria branch, new semantic version tags, and pull request updates
on:
workflow_dispatch:
push:
branches:
- astria # Running this job only for astria branch
- "astria"
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
# trigger on pull request updates when target is `astria` branch
pull_request:
branches:
- "astria"

jobs:
build-and-publish-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2 # Checking out the repo
# Checking out the repo
- uses: actions/checkout@v2
# Setting up Go
- uses: actions/setup-go@v4
with:
go-version: "^1.20.x" # The Go version to download (if necessary) and use.
- run: go version

# https://github.com/docker/setup-qemu-action
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin

# TODO - build for amd64 and arm64?
# FIXME - version needs to be autoincrement, probably from git tags?
- name: Build latest Docker image
run: |
docker build \
--build-arg COMMIT=${GITHUB_SHA} \
--build-arg VERSION=0.1 \
--build-arg BUILDNUM=${GITHUB_RUN_NUMBER} \
--tag ghcr.io/astriaorg/go-ethereum:latest .
- name: Push latest Docker image
run: docker push ghcr.io/astriaorg/go-ethereum:latest
# Generate correct tabs and labels
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/astriaorg/go-ethereum
tags: |
type=ref,event=pr
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=sha
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
# It takes over 30 minutes to build the arm image right now, so we only build it on tags which is what we use for releases.
platforms: ${{ contains(github.ref, 'refs/tags/v') && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
FROM alpine:latest

RUN apk add --no-cache ca-certificates
# Astria - add bash and jq to support start-geth.sh in conductor
RUN apk add bash jq
# Astria - copy genesis.json so it can be used in start-geth.sh
COPY genesis.json /genesis.json
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/

# Astria - add 50051 for GRPC
Expand Down
4 changes: 3 additions & 1 deletion cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/grpc/execution"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/internal/version"
Expand Down Expand Up @@ -174,7 +175,8 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {

// Configure gRPC if requested.
if ctx.IsSet(utils.GRPCEnabledFlag.Name) {
utils.RegisterGRPCService(stack, backend, &cfg.Node)
service := execution.NewExecutionServiceServer(eth)
utils.RegisterGRPCService(stack, service, &cfg.Node)
}

// Add the Ethereum Stats daemon if requested.
Expand Down
9 changes: 4 additions & 5 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb/remotedb"
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/graphql"
executionv1 "github.com/ethereum/go-ethereum/grpc/gen/proto/execution/v1"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/les"
Expand Down Expand Up @@ -2042,9 +2043,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend
Fatalf("Failed to create the LES server: %v", err)
}
}
if err := ethcatalyst.Register(stack, backend); err != nil {
Fatalf("Failed to register the Engine API service: %v", err)
}

stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
return backend.APIBackend, backend
}
Expand All @@ -2066,8 +2065,8 @@ func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, filterSyst

// RegisterGRPCService adds the gRPC API to the node.
// It was done this way so that our grpc execution server can access the ethapi.Backend
func RegisterGRPCService(stack *node.Node, backend ethapi.Backend, cfg *node.Config) {
if err := node.NewGRPCServerHandler(stack, backend, cfg); err != nil {
func RegisterGRPCService(stack *node.Node, execServer executionv1.ExecutionServiceServer, cfg *node.Config) {
if err := node.NewGRPCServerHandler(stack, execServer, cfg); err != nil {
Fatalf("Failed to register the gRPC service: %v", err)
}
}
Expand Down
1 change: 0 additions & 1 deletion consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
}
// Verify the block's difficulty based on its timestamp and parent's difficulty
expected := ethash.CalcDifficulty(chain, header.Time, parent)

if expected.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
}
Expand Down
117 changes: 117 additions & 0 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ type TxPool struct {
pendingNonces *noncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps

astria *astriaOrdered
locals *accountSet // Set of local transaction to exempt from eviction rules
journal *journal // Journal of local transaction to back up to disk

Expand Down Expand Up @@ -926,6 +927,108 @@ func (pool *TxPool) AddLocal(tx *types.Transaction) error {
return errs[0]
}

func (pool *TxPool) SetAstriaOrdered(rawTxs [][]byte) {
txs := []*types.Transaction{}
for idx, rawTx := range rawTxs {
tx := new(types.Transaction)
err := tx.UnmarshalBinary(rawTx)
if err != nil {
log.Warn("failed to unmarshal raw astria tx bytes", rawTx, "at index", idx, "error:", err)
continue
}

err = pool.astriaValidate(tx)
if err != nil {
log.Warn("astria tx failed validation at index", idx, "error:", err)
continue
}

txs = append(txs, tx)
}

pool.astria = newAstriaOrdered(types.Transactions(txs))
}

func (pool *TxPool) ClearAstriaOrdered() {
if pool.astria == nil {
return
}
pool.astria.clear()
}

func (pool *TxPool) AstriaOrdered() *types.Transactions {
// sus but whatever
if pool.astria == nil {
return &types.Transactions{}
}
return &pool.astria.txs
}

// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) astriaValidate(tx *types.Transaction) error {
// Accept only legacy transactions until EIP-2718/2930 activates.
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
return core.ErrTxTypeNotSupported
}
// Reject dynamic fee transactions until EIP-1559 activates.
if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
return core.ErrTxTypeNotSupported
}
// Reject transactions over defined size to prevent DOS attacks
if tx.Size() > txMaxSize {
return ErrOversizedData
}
// Check whether the init code size has been exceeded.
if pool.shanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
return fmt.Errorf("%w: code size %v limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize)
}
// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur if you create a transaction using the RPC.
if tx.Value().Sign() < 0 {
return ErrNegativeValue
}
// Ensure the transaction doesn't exceed the current block limit gas.
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
// Sanity check for extremely large numbers
if tx.GasFeeCap().BitLen() > 256 {
return core.ErrFeeCapVeryHigh
}
if tx.GasTipCap().BitLen() > 256 {
return core.ErrTipVeryHigh
}
// Ensure gasFeeCap is greater than or equal to gasTipCap.
if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 {
return core.ErrTipAboveFeeCap
}
// Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
if err != nil {
return ErrInvalidSender
}
// Ensure the transaction adheres to nonce ordering
if pool.currentState.GetNonce(from) > tx.Nonce() {
return core.ErrNonceTooLow
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
balance := pool.currentState.GetBalance(from)
if balance.Cmp(tx.Cost()) < 0 {
return core.ErrInsufficientFunds
}
// Ensure the transaction has more gas than the basic tx fee.
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
if err != nil {
return err
}
if tx.Gas() < intrGas {
return core.ErrIntrinsicGas
}
return nil
}

// AddRemotes enqueues a batch of transactions into the pool if they are valid. If the
// senders are not among the locally tracked ones, full pricing constraints will apply.
//
Expand Down Expand Up @@ -1647,6 +1750,20 @@ func (a addressesByHeartbeat) Len() int { return len(a) }
func (a addressesByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

type astriaOrdered struct {
txs types.Transactions
}

func newAstriaOrdered(txs types.Transactions) *astriaOrdered {
return &astriaOrdered{
txs: txs,
}
}

func (ao *astriaOrdered) clear() {
ao.txs = *&types.Transactions{}
}

// accountSet is simply a set of addresses to check for existence, and a signer
// capable of deriving addresses from transactions.
type accountSet struct {
Expand Down
39 changes: 20 additions & 19 deletions genesis.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
{
"config": {
"chainId": 1337,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"ethash": {}
},
"difficulty": "10000",
"gasLimit": "8000000",
"alloc": {
"0x46B77EFDFB20979E1C29ec98DcE73e3eCbF64102": { "balance": "300000000000000000000" }
}
}
"chainId": 1337,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"terminalTotalDifficulty": 0,
"ethash": {}
},
"difficulty": "10000000",
"gasLimit": "8000000",
"alloc": {
"0x46B77EFDFB20979E1C29ec98DcE73e3eCbF64102": { "balance": "300000000000000000000" }
}
}
6 changes: 4 additions & 2 deletions grpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ brew install leveldb
# build geth
make geth

# run geth
./build/bin/geth --goerli --grpc --grpc.addr "0.0.0.0" --grpc.port 50051
# generating protobuf files
buf generate buf.build/astria/execution-apis
```

See [private_network.md](../private_network.md) for running a local geth node.

### Running with remote Docker image:
```bash
docker run --rm \
Expand Down
Loading

0 comments on commit f91e86a

Please sign in to comment.