diff --git a/.github/workflows/astria-build-and-publish-image.yml b/.github/workflows/astria-build-and-publish-image.yml index 24d0c582139b..374702c85229 100644 --- a/.github/workflows/astria-build-and-publish-image.yml +++ b/.github/workflows/astria-build-and-publish-image.yml @@ -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 }} diff --git a/Dockerfile b/Dockerfile index 0b51035e3669..ce3414df97db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 75becbf59028..a45ab6602ef5 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -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" @@ -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. diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2b16231d1ea3..b61a9b78634b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -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" @@ -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 } @@ -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) } } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c3c06c541cab..481fe28b7dec 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -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) } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index ac4486c6cba8..8597f6190f65 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -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 @@ -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. // @@ -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 { diff --git a/genesis.json b/genesis.json index 0669d58c454d..57fca8b3f88f 100644 --- a/genesis.json +++ b/genesis.json @@ -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" } - } - } \ No newline at end of file + "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" } + } +} diff --git a/grpc/README.md b/grpc/README.md index eb91757bc3db..666fe69c5083 100644 --- a/grpc/README.md +++ b/grpc/README.md @@ -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 \ diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 5c32c51280ef..18f413d8c848 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -6,10 +6,16 @@ package execution import ( "context" + "fmt" + "time" + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" executionv1 "github.com/ethereum/go-ethereum/grpc/gen/proto/execution/v1" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" ) @@ -20,45 +26,96 @@ type ExecutionServiceServer struct { // for forward compatibility executionv1.UnimplementedExecutionServiceServer - // TODO - will need access to the consensus api to call functions for building a block - // e.g. getPayload, newPayload, forkchoiceUpdated + consensus *catalyst.ConsensusAPI + eth *eth.Ethereum - Backend ethapi.Backend - - // TODO - will need access to forkchoice on first run. - // this will probably be passed in when calling NewServer + bc *core.BlockChain } -// FIXME - how do we know which hash to start with? will probably need another api function like -// GetHeadHash() to get the head hash of the forkchoice +func NewExecutionServiceServer(eth *eth.Ethereum) *ExecutionServiceServer { + consensus := catalyst.NewConsensusAPI(eth) + + bc := eth.BlockChain() + + return &ExecutionServiceServer{ + eth: eth, + consensus: consensus, + bc: bc, + } +} func (s *ExecutionServiceServer) DoBlock(ctx context.Context, req *executionv1.DoBlockRequest) (*executionv1.DoBlockResponse, error) { log.Info("DoBlock called request", "request", req) + prevHeadHash := common.BytesToHash(req.PrevStateRoot) - // NOTE - Request.Header.ParentHash needs to match forkchoice head hash - // ParentHash should be the forkchoice head of the last block + // The Engine API has been modified to use transactions from this mempool and abide by it's ordering. + s.eth.TxPool().SetAstriaOrdered(req.Transactions) - // TODO - need to call consensus api to build a block + // Do the whole Engine API in a single loop + startForkChoice := &engine.ForkchoiceStateV1{ + HeadBlockHash: prevHeadHash, + SafeBlockHash: prevHeadHash, + FinalizedBlockHash: prevHeadHash, + } + payloadAttributes := &engine.PayloadAttributes{ + Timestamp: uint64(req.GetTimestamp().GetSeconds()), + Random: common.Hash{}, + SuggestedFeeRecipient: common.Address{}, + } + fcStartResp, err := s.consensus.ForkchoiceUpdatedV1(*startForkChoice, payloadAttributes) + if err != nil { + return nil, err + } - // txs := bytesToTransactions(req.Transactions) - // for _, tx := range txs { - // s.Backend.SendTx(ctx, tx) - // } + // super janky but this is what the payload builder requires :/ (miner.worker.buildPayload()) + // we should probably just execute + store the block directly instead of using the engine api. + time.Sleep(time.Second) + payloadResp, err := s.consensus.GetPayloadV1(*fcStartResp.PayloadID) + if err != nil { + log.Error("failed to call GetPayloadV1", "err", err) + return nil, err + } + + // call blockchain.InsertChain to actually execute and write the blocks to state + block, err := engine.ExecutableDataToBlock(*payloadResp) + if err != nil { + return nil, err + } + blocks := types.Blocks{ + block, + } + n, err := s.bc.InsertChain(blocks) + if err != nil { + return nil, err + } + if n != 1 { + return nil, fmt.Errorf("failed to insert block into blockchain (n=%d)", n) + } + + newForkChoice := &engine.ForkchoiceStateV1{ + HeadBlockHash: block.Hash(), + SafeBlockHash: block.Hash(), + FinalizedBlockHash: block.Hash(), + } + fcEndResp, err := s.consensus.ForkchoiceUpdatedV1(*newForkChoice, nil) + if err != nil { + log.Error("failed to call ForkchoiceUpdatedV1", "err", err) + return nil, err + } res := &executionv1.DoBlockResponse{ - // TODO - get state root from last block - StateRoot: []byte{0x00}, + // TODO: RENAME THIS - this is not the state root!! it's the block hash + StateRoot: fcEndResp.PayloadStatus.LatestValidHash.Bytes(), } return res, nil } -// convert bytes to transactions -func bytesToTransactions(b [][]byte) []*types.Transaction { - txs := []*types.Transaction{} - for _, txBytes := range b { - tx := &types.Transaction{} - tx.UnmarshalBinary(txBytes) - txs = append(txs, tx) +func (s *ExecutionServiceServer) InitState(ctx context.Context, req *executionv1.InitStateRequest) (*executionv1.InitStateResponse, error) { + currHead := s.eth.BlockChain().CurrentHeader() + res := &executionv1.InitStateResponse{ + // TODO: RENAME THIS - this is not the state root!! it's the block hash + StateRoot: currHead.Hash().Bytes(), } - return txs + + return res, nil } diff --git a/grpc/gen/proto/execution/v1/execution.pb.go b/grpc/gen/proto/execution/v1/execution.pb.go index aca5c123480f..2382493e64f0 100644 --- a/grpc/gen/proto/execution/v1/execution.pb.go +++ b/grpc/gen/proto/execution/v1/execution.pb.go @@ -9,6 +9,7 @@ package executionv1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -25,8 +26,9 @@ type DoBlockRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Header []byte `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Transactions [][]byte `protobuf:"bytes,2,rep,name=transactions,proto3" json:"transactions,omitempty"` + PrevStateRoot []byte `protobuf:"bytes,1,opt,name=prev_state_root,json=prevStateRoot,proto3" json:"prev_state_root,omitempty"` + Transactions [][]byte `protobuf:"bytes,2,rep,name=transactions,proto3" json:"transactions,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *DoBlockRequest) Reset() { @@ -61,9 +63,9 @@ func (*DoBlockRequest) Descriptor() ([]byte, []int) { return file_proto_execution_v1_execution_proto_rawDescGZIP(), []int{0} } -func (x *DoBlockRequest) GetHeader() []byte { +func (x *DoBlockRequest) GetPrevStateRoot() []byte { if x != nil { - return x.Header + return x.PrevStateRoot } return nil } @@ -75,6 +77,13 @@ func (x *DoBlockRequest) GetTransactions() [][]byte { return nil } +func (x *DoBlockRequest) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + type DoBlockResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -122,39 +131,140 @@ func (x *DoBlockResponse) GetStateRoot() []byte { return nil } +type InitStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *InitStateRequest) Reset() { + *x = InitStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_execution_v1_execution_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitStateRequest) ProtoMessage() {} + +func (x *InitStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_execution_v1_execution_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitStateRequest.ProtoReflect.Descriptor instead. +func (*InitStateRequest) Descriptor() ([]byte, []int) { + return file_proto_execution_v1_execution_proto_rawDescGZIP(), []int{2} +} + +type InitStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StateRoot []byte `protobuf:"bytes,1,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` +} + +func (x *InitStateResponse) Reset() { + *x = InitStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_execution_v1_execution_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitStateResponse) ProtoMessage() {} + +func (x *InitStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_execution_v1_execution_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitStateResponse.ProtoReflect.Descriptor instead. +func (*InitStateResponse) Descriptor() ([]byte, []int) { + return file_proto_execution_v1_execution_proto_rawDescGZIP(), []int{3} +} + +func (x *InitStateResponse) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + var File_proto_execution_v1_execution_proto protoreflect.FileDescriptor var file_proto_execution_v1_execution_proto_rawDesc = []byte{ 0x0a, 0x22, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x76, 0x31, 0x22, 0x4c, 0x0a, 0x0e, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x22, 0x30, 0x0a, 0x0f, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, - 0x6f, 0x74, 0x32, 0x5a, 0x0a, 0x10, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x1c, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xb8, - 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x3b, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x58, 0x58, - 0xaa, 0x02, 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x31, 0xca, - 0x02, 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0xe2, 0x02, - 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, - 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0d, 0x70, 0x72, 0x65, 0x76, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x22, + 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x30, 0x0a, 0x0f, + 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x12, + 0x0a, 0x10, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x32, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x32, 0xa8, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x44, + 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x6f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x1e, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0xb8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x6f, + 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, + 0x31, 0x3b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x31, 0xa2, 0x02, 0x03, + 0x45, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x56, 0x31, 0xca, 0x02, 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -169,19 +279,25 @@ func file_proto_execution_v1_execution_proto_rawDescGZIP() []byte { return file_proto_execution_v1_execution_proto_rawDescData } -var file_proto_execution_v1_execution_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_execution_v1_execution_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proto_execution_v1_execution_proto_goTypes = []interface{}{ - (*DoBlockRequest)(nil), // 0: execution.v1.DoBlockRequest - (*DoBlockResponse)(nil), // 1: execution.v1.DoBlockResponse + (*DoBlockRequest)(nil), // 0: execution.v1.DoBlockRequest + (*DoBlockResponse)(nil), // 1: execution.v1.DoBlockResponse + (*InitStateRequest)(nil), // 2: execution.v1.InitStateRequest + (*InitStateResponse)(nil), // 3: execution.v1.InitStateResponse + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_proto_execution_v1_execution_proto_depIdxs = []int32{ - 0, // 0: execution.v1.ExecutionService.DoBlock:input_type -> execution.v1.DoBlockRequest - 1, // 1: execution.v1.ExecutionService.DoBlock:output_type -> execution.v1.DoBlockResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 4, // 0: execution.v1.DoBlockRequest.timestamp:type_name -> google.protobuf.Timestamp + 0, // 1: execution.v1.ExecutionService.DoBlock:input_type -> execution.v1.DoBlockRequest + 2, // 2: execution.v1.ExecutionService.InitState:input_type -> execution.v1.InitStateRequest + 1, // 3: execution.v1.ExecutionService.DoBlock:output_type -> execution.v1.DoBlockResponse + 3, // 4: execution.v1.ExecutionService.InitState:output_type -> execution.v1.InitStateResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_proto_execution_v1_execution_proto_init() } @@ -214,6 +330,30 @@ func file_proto_execution_v1_execution_proto_init() { return nil } } + file_proto_execution_v1_execution_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_execution_v1_execution_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -221,7 +361,7 @@ func file_proto_execution_v1_execution_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_execution_v1_execution_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/grpc/gen/proto/execution/v1/execution_grpc.pb.go b/grpc/gen/proto/execution/v1/execution_grpc.pb.go index 63995f7e874d..1faf8aeb74dd 100644 --- a/grpc/gen/proto/execution/v1/execution_grpc.pb.go +++ b/grpc/gen/proto/execution/v1/execution_grpc.pb.go @@ -19,7 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - ExecutionService_DoBlock_FullMethodName = "/execution.v1.ExecutionService/DoBlock" + ExecutionService_DoBlock_FullMethodName = "/execution.v1.ExecutionService/DoBlock" + ExecutionService_InitState_FullMethodName = "/execution.v1.ExecutionService/InitState" ) // ExecutionServiceClient is the client API for ExecutionService service. @@ -27,6 +28,7 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ExecutionServiceClient interface { DoBlock(ctx context.Context, in *DoBlockRequest, opts ...grpc.CallOption) (*DoBlockResponse, error) + InitState(ctx context.Context, in *InitStateRequest, opts ...grpc.CallOption) (*InitStateResponse, error) } type executionServiceClient struct { @@ -46,11 +48,21 @@ func (c *executionServiceClient) DoBlock(ctx context.Context, in *DoBlockRequest return out, nil } +func (c *executionServiceClient) InitState(ctx context.Context, in *InitStateRequest, opts ...grpc.CallOption) (*InitStateResponse, error) { + out := new(InitStateResponse) + err := c.cc.Invoke(ctx, ExecutionService_InitState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ExecutionServiceServer is the server API for ExecutionService service. // All implementations must embed UnimplementedExecutionServiceServer // for forward compatibility type ExecutionServiceServer interface { DoBlock(context.Context, *DoBlockRequest) (*DoBlockResponse, error) + InitState(context.Context, *InitStateRequest) (*InitStateResponse, error) mustEmbedUnimplementedExecutionServiceServer() } @@ -61,6 +73,9 @@ type UnimplementedExecutionServiceServer struct { func (UnimplementedExecutionServiceServer) DoBlock(context.Context, *DoBlockRequest) (*DoBlockResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DoBlock not implemented") } +func (UnimplementedExecutionServiceServer) InitState(context.Context, *InitStateRequest) (*InitStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InitState not implemented") +} func (UnimplementedExecutionServiceServer) mustEmbedUnimplementedExecutionServiceServer() {} // UnsafeExecutionServiceServer may be embedded to opt out of forward compatibility for this service. @@ -92,6 +107,24 @@ func _ExecutionService_DoBlock_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _ExecutionService_InitState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExecutionServiceServer).InitState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ExecutionService_InitState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExecutionServiceServer).InitState(ctx, req.(*InitStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ExecutionService_ServiceDesc is the grpc.ServiceDesc for ExecutionService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -103,6 +136,10 @@ var ExecutionService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DoBlock", Handler: _ExecutionService_DoBlock_Handler, }, + { + MethodName: "InitState", + Handler: _ExecutionService_InitState_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/execution/v1/execution.proto", diff --git a/miner/worker.go b/miner/worker.go index 67a5842d23e0..5baf69aeae54 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -877,6 +877,88 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]* return receipt.Logs, nil } +// This is a copy of commitTransactions, but updated to take a list of txs instead of using heap +func (w *worker) commitAstriaTransactions(env *environment, txs *types.Transactions, interrupt *int32) error { + gasLimit := env.header.GasLimit + if env.gasPool == nil { + env.gasPool = new(core.GasPool).AddGas(gasLimit) + } + var coalescedLogs []*types.Log + + for _, tx := range *txs { + // Check interruption signal and abort building if it's fired. + if interrupt != nil { + if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { + return signalToErr(signal) + } + } + // If we don't have enough gas for any further transactions then we're done. + if env.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + break + } + + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + from, _ := types.Sender(env.signer, tx) + + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + + continue + } + // Start executing the transaction + env.state.SetTxContext(tx.Hash(), env.tcount) + + logs, err := w.commitTransaction(env, tx) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + + case errors.Is(err, types.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + } + } + if !w.isRunning() && len(coalescedLogs) > 0 { + // We don't push the pendingLogsEvent while we are sealing. The reason is that + // when we are sealing, the worker will regenerate a sealing block every 3 seconds. + // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. + + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make([]*types.Log, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(types.Log) + *cpy[i] = *l + } + w.pendingLogsFeed.Send(cpy) + } + return nil +} + func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) error { gasLimit := env.header.GasLimit if env.gasPool == nil { @@ -1068,28 +1150,14 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { // into the given sealing block. The transaction selection and ordering strategy can // be customized with the plugin in the future. func (w *worker) fillTransactions(interrupt *int32, env *environment) error { - // Split the pending transactions into locals and remotes - // Fill the block with all available pending transactions. - pending := w.eth.TxPool().Pending(true) - localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending - for _, account := range w.eth.TxPool().Locals() { - if txs := remoteTxs[account]; len(txs) > 0 { - delete(remoteTxs, account) - localTxs[account] = txs - } - } - if len(localTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) - if err := w.commitTransactions(env, txs, interrupt); err != nil { - return err - } - } - if len(remoteTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) - if err := w.commitTransactions(env, txs, interrupt); err != nil { + // Use pre ordered array of txs + astriaTxs := w.eth.TxPool().AstriaOrdered() + if len(*astriaTxs) > 0 { + if err := w.commitAstriaTransactions(env, astriaTxs, interrupt); err != nil { return err } } + w.eth.TxPool().ClearAstriaOrdered() return nil } diff --git a/node/grpcstack.go b/node/grpcstack.go index 1c0c3f4768a4..4ebb22e013ae 100644 --- a/node/grpcstack.go +++ b/node/grpcstack.go @@ -4,9 +4,7 @@ import ( "net" "sync" - "github.com/ethereum/go-ethereum/grpc/execution" executionv1 "github.com/ethereum/go-ethereum/grpc/gen/proto/execution/v1" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "google.golang.org/grpc" ) @@ -18,28 +16,24 @@ type GRPCServerHandler struct { endpoint string server *grpc.Server - executionServiceServer *execution.ExecutionServiceServer + executionServiceServer *executionv1.ExecutionServiceServer } // NewServer creates a new gRPC server. // It registers the execution service server. // It registers the gRPC server with the node so it can be stopped on shutdown. -func NewGRPCServerHandler(node *Node, backend ethapi.Backend, cfg *Config) error { +func NewGRPCServerHandler(node *Node, execService executionv1.ExecutionServiceServer, cfg *Config) error { server := grpc.NewServer() - executionServiceServer := &execution.ExecutionServiceServer{ - Backend: backend, - } - log.Info("gRPC server enabled", "endpoint", cfg.GRPCEndpoint()) serverHandler := &GRPCServerHandler{ endpoint: cfg.GRPCEndpoint(), server: server, - executionServiceServer: executionServiceServer, + executionServiceServer: &execService, } - executionv1.RegisterExecutionServiceServer(server, executionServiceServer) + executionv1.RegisterExecutionServiceServer(server, execService) node.RegisterGRPCServer(serverHandler) return nil diff --git a/node/node.go b/node/node.go index fe1b17592bcd..2e8efac30408 100644 --- a/node/node.go +++ b/node/node.go @@ -274,12 +274,14 @@ func (n *Node) openEndpoints() error { // start RPC endpoints err := n.startRPC() if err != nil { + n.log.Error("failed to start RPC endpoints", "err", err) n.stopRPC() n.server.Stop() } // start GRPC endpoints err = n.startGRPC() if err != nil { + n.log.Error("failed to start gRPC endpoints", "err", err) n.stopGRPC() } return err diff --git a/private_network.md b/private_network.md index 1b9bb03e72e3..7da7a2768f61 100644 --- a/private_network.md +++ b/private_network.md @@ -1,12 +1,25 @@ # private network +### astria 1. Make a new account in Metamask (or whichever method you prefer). Copy paste the address into `genesis.json`'s `alloc` field. This account will be allocated 300 ETH at startup. -2. Replace the etherbase in the following with your account (it doesn't really matter though, since mining doesn't require signing). Then, run: +2. To build and initialize Geth: ```bash make geth ./build/bin/geth --datadir ~/.astriageth/ init genesis.json -./build/bin/geth --datadir ~/.astriageth/ --http --http.port=8545 --ws --ws.port=8545 --networkid=1337 --http.corsdomain='*' --ws.origins='*' --mine --miner.threads 1 --miner.etherbase=0xDB8c0F738639Da247bC67251548a186b2107bf4d ``` -4. Open up Metamask and go to the Localhost 8545 network. You should see your account has 300 ETH. You can now transfer this to other accounts. \ No newline at end of file +To run without mining (ie. using the conductor): +```bash +./build/bin/geth --datadir ~/.astriageth/ --http --http.port=8545 --ws --ws.port=8545 --networkid=1337 --http.corsdomain='*' --ws.origins='*' --grpc --grpc.addr=localhost --grpc.port 50051 +``` + +4. Open up Metamask and go to the Localhost 8545 network. You should see your account has 300 ETH. You can now transfer this to other accounts. + +### ethash +To run with mining (which you don't want if running Astria): +1. Remove the `"terminalTotalDifficulty": 0,` line in `genesis.json`. Then run steps 1-2 as above. +2. Replace the etherbase in the following with your account (it doesn't really matter though, since mining doesn't require signing). Then, +```bash +./build/bin/geth --datadir ~/.astriageth/ --http --http.port=8545 --ws --ws.port=8545 --networkid=1337 --http.corsdomain='*' --ws.origins='*' --mine --miner.threads 1 --miner.etherbase=0x46B77EFDFB20979E1C29ec98DcE73e3eCbF64102 --grpc --grpc.addr=localhost --grpc.port 50051 +```