├── pt-batcher ├── .gitignore ├── compressor │ ├── compressors.go │ └── config.go ├── rpc │ ├── api.go │ └── config.go ├── Dockerfile ├── cmd │ ├── main.go │ └── doc │ │ └── cmd.go ├── metrics │ └── noop.go ├── Makefile └── batcher │ └── tx_data.go ├── pt-chain-ops ├── .gitignore ├── eof │ └── README.md ├── Makefile ├── Dockerfile ├── crossdomain │ ├── testdata │ │ ├── README.md │ │ ├── trace.sh │ │ └── witness.txt │ ├── params.go │ ├── encoding_test.go │ ├── alias.go │ ├── hashing.go │ ├── alias_test.go │ └── types.go ├── ether │ ├── genesis.go │ ├── cli.go │ ├── storage.go │ └── db.go ├── db │ └── util.go ├── genesis │ ├── test_util.go │ ├── layer_two.go │ ├── testdata │ │ └── test-deploy-config-devnet-l1.json │ ├── setters_test.go │ └── config_test.go ├── cmd │ └── eof-crawler │ │ └── main.go ├── state │ └── memory_db_test.go └── README.md ├── pt-proposer ├── .gitignore ├── metrics │ └── noop.go ├── Makefile ├── Dockerfile ├── cmd │ └── main.go └── proposer │ └── utils.go ├── .gitattributes ├── pt-signer ├── .gitignore ├── README.md └── client │ └── config.go ├── pt-node ├── .gitignore ├── version │ └── version.go ├── testutils │ ├── runtime_config.go │ ├── assertions.go │ ├── fuzzerutils │ │ └── fuzzer_functions.go │ ├── block_id.go │ ├── mock_debug_client.go │ ├── mock_l1.go │ ├── deposits.go │ ├── mock_engine.go │ ├── rpc_err_faker.go │ ├── metrics.go │ └── mock_l2.go ├── rollup │ ├── derive │ │ ├── pipeline_test.go │ │ ├── doc.go │ │ ├── test │ │ │ └── random.go │ │ ├── batch_test.go │ │ ├── batch_tob_test.go │ │ ├── deposit_source.go │ │ ├── frame_queue.go │ │ ├── deposits.go │ │ └── params.go │ ├── driver │ │ ├── config.go │ │ └── conf_depth.go │ └── output_root.go ├── node │ ├── node_test.go │ └── comms.go ├── p2p │ ├── store │ │ ├── serialize.go │ │ ├── gc.go │ │ ├── peer_ban_book_test.go │ │ ├── ip_ban_book_test.go │ │ ├── gc_test.go │ │ ├── extended.go │ │ └── serialize_test.go │ ├── peer_scores.go │ ├── mocks │ │ ├── GossipMetricer.go │ │ ├── PeerGater.go │ │ └── Peerstore.go │ ├── cli │ │ └── load_signer.go │ ├── gating │ │ ├── blocking.go │ │ ├── metrics.go │ │ └── scoring.go │ └── notifications.go ├── eth │ ├── output.go │ ├── types_test.go │ ├── label.go │ ├── transactions.go │ ├── receipts.go │ └── status.go ├── testlog │ └── README.md ├── http │ └── http.go ├── Dockerfile ├── flags │ └── flags_test.go ├── metrics │ ├── events.go │ └── caching.go ├── LICENSE ├── sources │ ├── caching │ │ └── cache.go │ ├── rollupclient.go │ ├── debug_client.go │ └── limit.go ├── cmd │ ├── p2p │ │ └── cmd_test.go │ ├── doc │ │ └── cmd.go │ └── stateviz │ │ └── assets │ │ └── index.html ├── heartbeat │ ├── service_test.go │ └── service.go ├── client │ └── rate_limited.go └── Makefile ├── specs ├── assets │ ├── challenger_attestation.png │ ├── challenger_attestation_output_proposed.png │ └── challenger_attestation_dispute_game_created.png ├── meta │ ├── README.md │ ├── versioning.md │ └── markdown-style.md └── batcher.md ├── pt-service ├── Makefile ├── tls │ ├── certman │ │ └── testdata │ │ │ ├── README │ │ │ ├── server.crt │ │ │ ├── server1.crt │ │ │ ├── server2.crt │ │ │ ├── server.key │ │ │ ├── server1.key │ │ │ └── server2.key │ └── tlsinfo.go ├── client │ ├── timeout.go │ ├── ethclient.go │ └── rollup.go ├── log │ ├── defaults.go │ └── http.go ├── metrics │ ├── registry.go │ ├── server.go │ ├── balance_test.go │ ├── node_info.go │ ├── cli.go │ ├── event.go │ └── balance.go ├── backoff │ ├── strategies_test.go │ ├── operation_test.go │ ├── strategies.go │ └── operation.go ├── txmgr │ ├── metrics │ │ └── noop.go │ └── mocks │ │ └── TxManager.go ├── opio │ └── interrupts.go ├── solabi │ └── utils_test.go ├── util_test.go ├── httputil │ ├── server.go │ └── wrapped_response_writer.go ├── enum │ ├── enum.go │ └── enum_test.go ├── pprof │ ├── server.go │ └── cli.go └── rpc │ ├── cli.go │ └── server_test.go ├── ops-bedrock ├── mainnet │ └── rpc-node │ │ ├── Dockerfile.l2 │ │ ├── entrypoint.sh │ │ └── docker-compose.yml └── patex-sepolia │ └── rpc-node │ ├── Dockerfile.l2 │ ├── entrypoint.sh │ └── docker-compose.yml ├── pt-bindings ├── doc.go ├── bindings │ └── registry.go ├── hardhat │ └── utils.go ├── README.md ├── ast │ └── canonicalize_test.go ├── gen_bindings.sh └── predeploys │ └── addresses_test.go ├── .gitmodules ├── .dockerignore ├── CITATION.cff ├── pt-e2e └── e2eutils │ ├── addresses_test.go │ ├── testing.go │ ├── setup_test.go │ ├── waits.go │ └── addresses.go ├── ops └── scripts │ └── update-pt-geth.py ├── LICENSE └── .gitignore /pt-batcher/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /pt-chain-ops/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /pt-proposer/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | -------------------------------------------------------------------------------- /pt-signer/.gitignore: -------------------------------------------------------------------------------- 1 | mocks/ 2 | mock_* 3 | tls/ 4 | -------------------------------------------------------------------------------- /pt-signer/README.md: -------------------------------------------------------------------------------- 1 | # pt-signer 2 | 3 | pt-signer service client 4 | -------------------------------------------------------------------------------- /pt-node/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | # tmp dir 3 | ptnode_discovery_db/ 4 | ptnode_peerstore_db/ -------------------------------------------------------------------------------- /pt-node/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | Version = "v0.10.14" 5 | Meta = "dev" 6 | ) 7 | -------------------------------------------------------------------------------- /specs/assets/challenger_attestation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patex-ecosystem/patex-network/HEAD/specs/assets/challenger_attestation.png -------------------------------------------------------------------------------- /specs/assets/challenger_attestation_output_proposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patex-ecosystem/patex-network/HEAD/specs/assets/challenger_attestation_output_proposed.png -------------------------------------------------------------------------------- /pt-service/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | go test -v ./... 3 | 4 | lint: 5 | golangci-lint run -E asciicheck,goimports,misspell ./... 6 | 7 | .PHONY: \ 8 | test \ 9 | lint 10 | -------------------------------------------------------------------------------- /specs/assets/challenger_attestation_dispute_game_created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patex-ecosystem/patex-network/HEAD/specs/assets/challenger_attestation_dispute_game_created.png -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/README: -------------------------------------------------------------------------------- 1 | Test certificates generated with the following command: 2 | 3 | openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -sha256 -keyout server.key -out server.crt -------------------------------------------------------------------------------- /pt-service/client/timeout.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // DefaultDialTimeout is a default timeout for dialing a client. 8 | const DefaultDialTimeout = 5 * time.Second 9 | -------------------------------------------------------------------------------- /ops-bedrock/mainnet/rpc-node/Dockerfile.l2: -------------------------------------------------------------------------------- 1 | FROM patexhub/pt-geth:latest 2 | 3 | RUN apk add --no-cache jq 4 | 5 | COPY entrypoint.sh /entrypoint.sh 6 | 7 | VOLUME ["/datadir"] 8 | 9 | ENTRYPOINT ["/bin/sh", "/entrypoint.sh"] 10 | -------------------------------------------------------------------------------- /ops-bedrock/patex-sepolia/rpc-node/Dockerfile.l2: -------------------------------------------------------------------------------- 1 | FROM patexhub/pt-geth:latest 2 | 3 | RUN apk add --no-cache jq 4 | 5 | COPY entrypoint.sh /entrypoint.sh 6 | 7 | VOLUME ["/datadir"] 8 | 9 | ENTRYPOINT ["/bin/sh", "/entrypoint.sh"] 10 | -------------------------------------------------------------------------------- /pt-bindings/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The contracts package provides Go bindings for our contracts. 4 | 5 | 6 | To regenerate the bindings, run `make` 7 | The following programs are required: `jq`, `abigen`, and `solc` (at version 0.8.10) 8 | */ 9 | 10 | package pt_bindings 11 | -------------------------------------------------------------------------------- /specs/meta/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Meta Processes 3 | 4 | This directory describes processes that we use to lint/test the specification. 5 | 6 | - [Linting](linting.md): how to lint source files. 7 | - [Markdown Style Guide](markdown-style.md): how to format and structure Markdown files. 8 | 9 | -------------------------------------------------------------------------------- /pt-node/testutils/runtime_config.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import "github.com/ethereum/go-ethereum/common" 4 | 5 | type MockRuntimeConfig struct { 6 | P2PSeqAddress common.Address 7 | } 8 | 9 | func (m *MockRuntimeConfig) P2PSequencerAddress() common.Address { 10 | return m.P2PSeqAddress 11 | } 12 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/pipeline_test.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import "github.com/patex-ecosystem/patex-network/pt-node/testutils" 4 | 5 | var _ Engine = (*testutils.MockEngine)(nil) 6 | 7 | var _ L1Fetcher = (*testutils.MockL1Source)(nil) 8 | 9 | var _ Metrics = (*testutils.TestDerivationMetrics)(nil) 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests"] 2 | path = l2geth/tests/testdata 3 | url = https://github.com/ethereum/tests 4 | [submodule "packages/contracts-periphery/lib/multicall"] 5 | path = packages/contracts-periphery/lib/multicall 6 | url = https://github.com/mds1/multicall 7 | [submodule "lib/multicall"] 8 | branch = v3.1.0 9 | -------------------------------------------------------------------------------- /pt-service/log/defaults.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/ethereum/go-ethereum/log" 7 | ) 8 | 9 | func SetupDefaults() { 10 | log.Root().SetHandler( 11 | log.LvlFilterHandler( 12 | log.LvlInfo, 13 | log.StreamHandler(os.Stdout, log.LogfmtFormat()), 14 | ), 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /pt-node/node/node_test.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestUnixTimeStale(t *testing.T) { 11 | require.True(t, unixTimeStale(1_600_000_000, 1*time.Hour)) 12 | require.False(t, unixTimeStale(uint64(time.Now().Unix()), 1*time.Hour)) 13 | } 14 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .github 2 | .vscode 3 | 4 | node_modules 5 | **/node_modules 6 | .env 7 | **/.env 8 | 9 | test 10 | **/*_test.go 11 | build/_workspace 12 | build/bin 13 | build/_bin 14 | tests/testdata 15 | l2geth/signer/fourbyte 16 | l2geth/cmd/puppeth 17 | l2geth/cmd/clef 18 | go/gas-oracle/gas-oracle 19 | go/batch-submitter/batch-submitter 20 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: If you use this software in your work, please cite it using the following metadata 3 | title: The Patex Network 4 | authors: 5 | - name: The Patex Network 6 | version: 1.0.0 7 | year: 2023 8 | url: https://github.com/patex-ecosystem/patex-network 9 | repository: https://github.com/patex-ecosystem/patex-network 10 | license: MIT 11 | -------------------------------------------------------------------------------- /pt-node/p2p/store/serialize.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import "encoding/json" 4 | 5 | func serializeScoresV0(scores scoreRecord) ([]byte, error) { 6 | // v0 just serializes to JSON. New/unrecognized values default to 0. 7 | return json.Marshal(&scores) 8 | } 9 | 10 | func deserializeScoresV0(data []byte) (scoreRecord, error) { 11 | var out scoreRecord 12 | err := json.Unmarshal(data, &out) 13 | return out, err 14 | } 15 | -------------------------------------------------------------------------------- /pt-service/metrics/registry.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | "github.com/prometheus/client_golang/prometheus/collectors" 6 | ) 7 | 8 | func NewRegistry() *prometheus.Registry { 9 | registry := prometheus.NewRegistry() 10 | registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) 11 | registry.MustRegister(collectors.NewGoCollector()) 12 | return registry 13 | } 14 | -------------------------------------------------------------------------------- /pt-chain-ops/eof/README.md: -------------------------------------------------------------------------------- 1 | # `eof-crawler` 2 | 3 | Simple CLI tool to scan all accounts in a geth LevelDB for contracts that begin with the EOF prefix. 4 | 5 | ## Usage 6 | 7 | 1. Pass the directory of the Geth DB into the tool 8 | ```sh 9 | go run ./cmd/eof-crawler/main.go --db-path [--out ] 10 | ``` 11 | 2. Once the indexing has completed, an array of all EOF-prefixed contracts will be written to `eof_contracts.json` or the designated output file. 12 | -------------------------------------------------------------------------------- /pt-node/testutils/assertions.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | func BigEqual(a, b *big.Int) bool { 9 | if a == nil || b == nil { 10 | return a == b 11 | } else { 12 | return a.Cmp(b) == 0 13 | } 14 | } 15 | 16 | func RequireBigEqual(t *testing.T, exp, actual *big.Int) { 17 | t.Helper() 18 | if !BigEqual(exp, actual) { 19 | t.Fatalf("expected %s to be equal to %s", exp.String(), actual.String()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pt-service/backoff/strategies_test.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestExponential(t *testing.T) { 11 | strategy := &ExponentialStrategy{ 12 | Min: 3000, 13 | Max: 10000, 14 | MaxJitter: 0, 15 | } 16 | 17 | durations := []int{4, 5, 7, 10, 10} 18 | for i, dur := range durations { 19 | require.Equal(t, time.Millisecond*time.Duration(dur*1000), strategy.Duration(i)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pt-node/eth/output.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | ) 6 | 7 | type OutputResponse struct { 8 | Version Bytes32 `json:"version"` 9 | OutputRoot Bytes32 `json:"outputRoot"` 10 | BlockRef L2BlockRef `json:"blockRef"` 11 | WithdrawalStorageRoot common.Hash `json:"withdrawalStorageRoot"` 12 | StateRoot common.Hash `json:"stateRoot"` 13 | Status *SyncStatus `json:"syncStatus"` 14 | } 15 | -------------------------------------------------------------------------------- /pt-node/eth/types_test.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestInputError(t *testing.T) { 11 | err := InputError{ 12 | Inner: errors.New("test error"), 13 | Code: InvalidForkchoiceState, 14 | } 15 | var x InputError 16 | if !errors.As(err, &x) { 17 | t.Fatalf("need InputError to be detected as such") 18 | } 19 | require.ErrorIs(t, err, InputError{}, "need to detect input error with errors.Is") 20 | } 21 | -------------------------------------------------------------------------------- /pt-chain-ops/Makefile: -------------------------------------------------------------------------------- 1 | pt-migrate: 2 | go build -o ./bin/pt-migrate ./cmd/pt-migrate/main.go 3 | 4 | test: 5 | go test ./... 6 | 7 | fuzz: 8 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeWithdrawal ./crossdomain 9 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzEncodeDecodeLegacyWithdrawal ./crossdomain 10 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzAliasing ./crossdomain 11 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzVersionedNonce ./crossdomain 12 | 13 | .PHONY: pt-migrate test 14 | -------------------------------------------------------------------------------- /pt-batcher/compressor/compressors.go: -------------------------------------------------------------------------------- 1 | package compressor 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 5 | ) 6 | 7 | type FactoryFunc func(Config) (derive.Compressor, error) 8 | 9 | const RatioKind = "ratio" 10 | const ShadowKind = "shadow" 11 | 12 | var Kinds = map[string]FactoryFunc{ 13 | RatioKind: NewRatioCompressor, 14 | ShadowKind: NewShadowCompressor, 15 | } 16 | 17 | var KindKeys []string 18 | 19 | func init() { 20 | for k := range Kinds { 21 | KindKeys = append(KindKeys, k) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pt-batcher/rpc/api.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type batcherClient interface { 8 | Start() error 9 | Stop(ctx context.Context) error 10 | } 11 | 12 | type adminAPI struct { 13 | b batcherClient 14 | } 15 | 16 | func NewAdminAPI(dr batcherClient) *adminAPI { 17 | return &adminAPI{ 18 | b: dr, 19 | } 20 | } 21 | 22 | func (a *adminAPI) StartBatcher(_ context.Context) error { 23 | return a.b.Start() 24 | } 25 | 26 | func (a *adminAPI) StopBatcher(ctx context.Context) error { 27 | return a.b.Stop(ctx) 28 | } 29 | -------------------------------------------------------------------------------- /pt-chain-ops/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19.9-alpine3.15 as builder 2 | 3 | RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash 4 | 5 | COPY ./pt-chain-ops /app/pt-chain-ops 6 | COPY ./pt-bindings /app/pt-bindings 7 | COPY ./pt-node /app/pt-node 8 | COPY ./go.mod /app/go.mod 9 | COPY ./go.sum /app/go.sum 10 | COPY ./.git /app/.git 11 | 12 | WORKDIR /app/pt-chain-ops 13 | 14 | RUN make pt-migrate 15 | 16 | FROM alpine:3.15 17 | 18 | COPY --from=builder /app/pt-chain-ops/bin/pt-migrate /usr/local/bin 19 | 20 | ENTRYPOINT ["pt-migrate"] 21 | -------------------------------------------------------------------------------- /pt-node/testlog/README.md: -------------------------------------------------------------------------------- 1 | # testlog 2 | 3 | `github.com/ethereum/go-ethereum/internal/testlog`: a Go-ethereum util for logging in tests. 4 | 5 | Since we use the same logging, but as an external package, we have to move the test utility to our own internal package. 6 | 7 | This fork also made minor modifications: 8 | 9 | - Enable color by default. 10 | - Add `estimateInfoLen` and use this for message padding in `flush()` to align the contents of the log entries, 11 | compensating for the different lengths of the log decoration that the Go library adds. 12 | -------------------------------------------------------------------------------- /pt-e2e/e2eutils/addresses_test.go: -------------------------------------------------------------------------------- 1 | package e2eutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCollectAddresses(t *testing.T) { 10 | tp := &TestParams{ 11 | MaxSequencerDrift: 40, 12 | SequencerWindowSize: 120, 13 | ChannelTimeout: 120, 14 | } 15 | dp := MakeDeployParams(t, tp) 16 | alloc := &AllocParams{PrefundTestUsers: true} 17 | sd := Setup(t, dp, alloc) 18 | addrs := CollectAddresses(sd, dp) 19 | require.NotEmpty(t, addrs) 20 | require.Contains(t, addrs, dp.Addresses.Batcher) 21 | } 22 | -------------------------------------------------------------------------------- /pt-node/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/ethereum/go-ethereum/rpc" 7 | ) 8 | 9 | // Use default timeouts from Geth as battle tested default values 10 | var timeouts = rpc.DefaultHTTPTimeouts 11 | 12 | func NewHttpServer(handler http.Handler) *http.Server { 13 | return &http.Server{ 14 | Handler: handler, 15 | ReadTimeout: timeouts.ReadTimeout, 16 | ReadHeaderTimeout: timeouts.ReadHeaderTimeout, 17 | WriteTimeout: timeouts.WriteTimeout, 18 | IdleTimeout: timeouts.IdleTimeout, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pt-service/txmgr/metrics/noop.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "github.com/ethereum/go-ethereum/core/types" 4 | 5 | type NoopTxMetrics struct{} 6 | 7 | func (*NoopTxMetrics) RecordNonce(uint64) {} 8 | func (*NoopTxMetrics) RecordPendingTx(int64) {} 9 | func (*NoopTxMetrics) RecordGasBumpCount(int) {} 10 | func (*NoopTxMetrics) RecordTxConfirmationLatency(int64) {} 11 | func (*NoopTxMetrics) TxConfirmed(*types.Receipt) {} 12 | func (*NoopTxMetrics) TxPublished(string) {} 13 | func (*NoopTxMetrics) RPCError() {} 14 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/testdata/README.md: -------------------------------------------------------------------------------- 1 | # crossdomain/testdata 2 | 3 | Real world test data is used to generate test vectors for the withdrawal 4 | hashing. The `trace.sh` script will generate artifacts used as part of the 5 | tests. It accepts a single argument, being the transaction hash to fetch 6 | artifacts for. It will fetch a receipt, a call trace and a state diff. 7 | The tests require that a file named after the transaction hash exists 8 | in each of the directories `call-traces`, `receipts` and `state-diffs`. 9 | The `trace.sh` script will ensure that the files are created correctly. 10 | -------------------------------------------------------------------------------- /pt-service/client/ethclient.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/ethclient" 8 | ) 9 | 10 | // DialEthClientWithTimeout attempts to dial the L1 provider using the provided 11 | // URL. If the dial doesn't complete within defaultDialTimeout seconds, this 12 | // method will return an error. 13 | func DialEthClientWithTimeout(ctx context.Context, url string, timeout time.Duration) (*ethclient.Client, error) { 14 | ctxt, cancel := context.WithTimeout(ctx, timeout) 15 | defer cancel() 16 | 17 | return ethclient.DialContext(ctxt, url) 18 | } 19 | -------------------------------------------------------------------------------- /pt-proposer/metrics/noop.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 5 | ptmetrics "github.com/patex-ecosystem/patex-network/pt-service/metrics" 6 | txmetrics "github.com/patex-ecosystem/patex-network/pt-service/txmgr/metrics" 7 | ) 8 | 9 | type noptmetrics struct { 10 | ptmetrics.NoopRefMetrics 11 | txmetrics.NoopTxMetrics 12 | } 13 | 14 | var Noptmetrics Metricer = new(noptmetrics) 15 | 16 | func (*noptmetrics) RecordInfo(version string) {} 17 | func (*noptmetrics) RecordUp() {} 18 | 19 | func (*noptmetrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) {} 20 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/testdata/trace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HASH=$1 4 | 5 | if [[ -z $HASH ]]; then 6 | exit 1 7 | fi 8 | 9 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" 10 | 11 | TRACES=$DIR/call-traces 12 | RECEIPTS=$DIR/receipts 13 | DIFFS=$DIR/state-diffs 14 | 15 | mkdir -p $TRACES 16 | mkdir -p $RECEIPTS 17 | mkdir -p $DIFFS 18 | 19 | cast rpc \ 20 | debug_traceTransaction \ 21 | $HASH \ 22 | '{"tracer": "callTracer"}' | jq > $TRACES/$HASH.json 23 | 24 | cast receipt $HASH --json | jq > $RECEIPTS/$HASH.json 25 | 26 | cast rpc \ 27 | debug_traceTransaction \ 28 | $HASH \ 29 | '{"tracer": "prestateTracer"}' | jq > $DIFFS/$HASH.json 30 | 31 | -------------------------------------------------------------------------------- /pt-service/opio/interrupts.go: -------------------------------------------------------------------------------- 1 | package opio 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | // DefaultInterruptSignals is a set of default interrupt signals. 10 | var DefaultInterruptSignals = []os.Signal{ 11 | os.Interrupt, 12 | os.Kill, 13 | syscall.SIGTERM, 14 | syscall.SIGQUIT, 15 | } 16 | 17 | // BlockOnInterrupts blocks until a SIGTERM is received. 18 | // Passing in signals will override the default signals. 19 | func BlockOnInterrupts(signals ...os.Signal) { 20 | if len(signals) == 0 { 21 | signals = DefaultInterruptSignals 22 | } 23 | interruptChannel := make(chan os.Signal, 1) 24 | signal.Notify(interruptChannel, signals...) 25 | <-interruptChannel 26 | } 27 | -------------------------------------------------------------------------------- /pt-chain-ops/ether/genesis.go: -------------------------------------------------------------------------------- 1 | package ether 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/core" 9 | ) 10 | 11 | // ReadGenesisFromFile reads a genesis object from a file. 12 | func ReadGenesisFromFile(path string) (*core.Genesis, error) { 13 | f, err := os.Open(path) 14 | if err != nil { 15 | return nil, err 16 | } 17 | defer f.Close() 18 | return ReadGenesis(f) 19 | } 20 | 21 | // ReadGenesis reads a genesis object from an io.Reader. 22 | func ReadGenesis(r io.Reader) (*core.Genesis, error) { 23 | genesis := new(core.Genesis) 24 | if err := json.NewDecoder(r).Decode(genesis); err != nil { 25 | return nil, err 26 | } 27 | return genesis, nil 28 | } 29 | -------------------------------------------------------------------------------- /pt-service/log/http.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/log" 8 | "github.com/patex-ecosystem/patex-network/pt-service/httputil" 9 | ) 10 | 11 | func NewLoggingMiddleware(lgr log.Logger, next http.Handler) http.Handler { 12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | ww := httputil.NewWrappedResponseWriter(w) 14 | start := time.Now() 15 | next.ServeHTTP(ww, r) 16 | lgr.Debug( 17 | "served HTTP request", 18 | "status", ww.StatusCode, 19 | "response_len", ww.ResponseLen, 20 | "path", r.URL.EscapedPath(), 21 | "duration", time.Since(start), 22 | "remote_addr", r.RemoteAddr, 23 | ) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /pt-service/metrics/server.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/patex-ecosystem/patex-network/pt-service/httputil" 10 | "github.com/prometheus/client_golang/prometheus" 11 | "github.com/prometheus/client_golang/prometheus/promhttp" 12 | ) 13 | 14 | func ListenAndServe(ctx context.Context, r *prometheus.Registry, hostname string, port int) error { 15 | addr := net.JoinHostPort(hostname, strconv.Itoa(port)) 16 | server := &http.Server{ 17 | Addr: addr, 18 | Handler: promhttp.InstrumentMetricHandler( 19 | r, promhttp.HandlerFor(r, promhttp.HandlerOpts{}), 20 | ), 21 | } 22 | return httputil.ListenAndServeContext(ctx, server) 23 | } 24 | -------------------------------------------------------------------------------- /pt-proposer/Makefile: -------------------------------------------------------------------------------- 1 | GITCOMMIT := $(shell git rev-parse HEAD) 2 | GITDATE := $(shell git show -s --format='%ct') 3 | VERSION := v0.0.0 4 | 5 | LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) 6 | LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) 7 | LDFLAGSSTRING +=-X main.Version=$(VERSION) 8 | LDFLAGS := -ldflags "$(LDFLAGSSTRING)" 9 | 10 | pt-proposer: 11 | env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/pt-proposer ./cmd 12 | 13 | clean: 14 | rm bin/pt-proposer 15 | 16 | test: 17 | go test -v ./... 18 | 19 | lint: 20 | golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" 21 | 22 | .PHONY: \ 23 | clean \ 24 | pt-proposer \ 25 | test \ 26 | lint 27 | -------------------------------------------------------------------------------- /pt-chain-ops/db/util.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/ethereum/go-ethereum/core/rawdb" 7 | "github.com/ethereum/go-ethereum/ethdb" 8 | ) 9 | 10 | func Open(path string, cache int, handles int) (ethdb.Database, error) { 11 | chaindataPath := filepath.Join(path, "geth", "chaindata") 12 | ancientPath := filepath.Join(chaindataPath, "ancient") 13 | ldb, err := rawdb.Open(rawdb.OpenOptions{ 14 | Type: "leveldb", 15 | Directory: chaindataPath, 16 | AncientsDirectory: ancientPath, 17 | Namespace: "", 18 | Cache: cache, 19 | Handles: handles, 20 | ReadOnly: false, 21 | }) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return ldb, nil 26 | } 27 | -------------------------------------------------------------------------------- /ops/scripts/update-pt-geth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | import json 5 | import subprocess 6 | import os 7 | 8 | 9 | GETH_VERSION='v1.11.5' 10 | 11 | 12 | def main(): 13 | for project in ('.', 'indexer'): 14 | print(f'Updating {project}...') 15 | update_mod(project) 16 | 17 | 18 | def update_mod(project): 19 | print('Replacing...') 20 | subprocess.run([ 21 | 'go', 22 | 'mod', 23 | 'edit', 24 | '-replace', 25 | f'github.com/ethereum/go-ethereum@{GETH_VERSION}=github.com/patex-ecosystem/patex-chain@main' 26 | ], cwd=os.path.join(project), check=True) 27 | print('Tidying...') 28 | subprocess.run([ 29 | 'go', 30 | 'mod', 31 | 'tidy' 32 | ], cwd=os.path.join(project), check=True) 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /pt-node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM golang:1.19.9-alpine3.16 as builder 2 | 3 | ARG VERSION=v0.0.0 4 | 5 | RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash 6 | 7 | # build pt-node with the shared go.mod & go.sum files 8 | COPY ./pt-node /app/pt-node 9 | COPY ./pt-chain-ops /app/pt-chain-ops 10 | COPY ./pt-service /app/pt-service 11 | COPY ./pt-bindings /app/pt-bindings 12 | COPY ./go.mod /app/go.mod 13 | COPY ./go.sum /app/go.sum 14 | COPY ./.git /app/.git 15 | 16 | WORKDIR /app/pt-node 17 | 18 | RUN go mod download 19 | 20 | ARG TARGETOS TARGETARCH 21 | 22 | RUN make pt-node VERSION="$VERSION" GOOS=$TARGETOS GOARCH=$TARGETARCH 23 | 24 | FROM alpine:3.16 25 | 26 | COPY --from=builder /app/pt-node/bin/pt-node /usr/local/bin 27 | 28 | CMD ["pt-node"] 29 | -------------------------------------------------------------------------------- /pt-bindings/bindings/registry.go: -------------------------------------------------------------------------------- 1 | package bindings 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/patex-ecosystem/patex-network/pt-bindings/solc" 8 | ) 9 | 10 | var layouts = make(map[string]*solc.StorageLayout) 11 | 12 | var deployedBytecodes = make(map[string]string) 13 | 14 | func GetStorageLayout(name string) (*solc.StorageLayout, error) { 15 | layout := layouts[name] 16 | if layout == nil { 17 | return nil, fmt.Errorf("%s: storage layout not found", name) 18 | } 19 | return layout, nil 20 | } 21 | 22 | func GetDeployedBytecode(name string) ([]byte, error) { 23 | bc := deployedBytecodes[name] 24 | if bc == "" { 25 | return nil, fmt.Errorf("%s: deployed bytecode not found", name) 26 | } 27 | 28 | return common.FromHex(bc), nil 29 | } 30 | -------------------------------------------------------------------------------- /pt-service/solabi/utils_test.go: -------------------------------------------------------------------------------- 1 | package solabi_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/patex-ecosystem/patex-network/pt-service/solabi" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestEmptyReader(t *testing.T) { 12 | t.Run("empty", func(t *testing.T) { 13 | r := new(bytes.Buffer) 14 | require.True(t, solabi.EmptyReader(r)) 15 | }) 16 | t.Run("empty after read", func(t *testing.T) { 17 | r := bytes.NewBufferString("not empty") 18 | tmp := make([]byte, 9) 19 | n, err := r.Read(tmp) 20 | require.Equal(t, 9, n) 21 | require.NoError(t, err) 22 | require.True(t, solabi.EmptyReader(r)) 23 | }) 24 | t.Run("extra bytes", func(t *testing.T) { 25 | r := bytes.NewBufferString("not empty") 26 | require.False(t, solabi.EmptyReader(r)) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /pt-node/p2p/store/gc.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | "github.com/patex-ecosystem/patex-network/pt-service/clock" 10 | ) 11 | 12 | const ( 13 | gcPeriod = 2 * time.Hour 14 | ) 15 | 16 | type gcAction func() error 17 | 18 | func startGc(ctx context.Context, logger log.Logger, clock clock.Clock, bgTasks *sync.WaitGroup, action gcAction) { 19 | bgTasks.Add(1) 20 | go func() { 21 | defer bgTasks.Done() 22 | 23 | gcTimer := clock.NewTicker(gcPeriod) 24 | defer gcTimer.Stop() 25 | 26 | for { 27 | select { 28 | case <-gcTimer.Ch(): 29 | if err := action(); err != nil { 30 | logger.Warn("GC failed", "err", err) 31 | } 32 | 33 | case <-ctx.Done(): 34 | return 35 | } 36 | } 37 | }() 38 | } 39 | -------------------------------------------------------------------------------- /pt-service/backoff/operation_test.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestDo(t *testing.T) { 12 | strategy := Fixed(10 * time.Millisecond) 13 | dummyErr := errors.New("explode") 14 | 15 | start := time.Now() 16 | var i int 17 | require.NoError(t, Do(2, strategy, func() error { 18 | if i == 1 { 19 | return nil 20 | } 21 | 22 | i++ 23 | return dummyErr 24 | })) 25 | require.True(t, time.Since(start) > 10*time.Millisecond) 26 | 27 | start = time.Now() 28 | // add one because the first attempt counts 29 | err := Do(3, strategy, func() error { 30 | return dummyErr 31 | }) 32 | require.Equal(t, dummyErr, err.(*ErrFailedPermanently).LastErr) 33 | require.True(t, time.Since(start) > 20*time.Millisecond) 34 | } 35 | -------------------------------------------------------------------------------- /pt-proposer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder 2 | 3 | ARG VERSION=v0.0.0 4 | 5 | RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash 6 | 7 | # build pt-proposer with the shared go.mod & go.sum files 8 | COPY ./pt-proposer /app/pt-proposer 9 | COPY ./pt-bindings /app/pt-bindings 10 | COPY ./pt-node /app/pt-node 11 | COPY ./pt-service /app/pt-service 12 | COPY ./pt-signer /app/pt-signer 13 | COPY ./go.mod /app/go.mod 14 | COPY ./go.sum /app/go.sum 15 | COPY ./.git /app/.git 16 | 17 | WORKDIR /app/pt-proposer 18 | 19 | RUN go mod download 20 | 21 | ARG TARGETOS TARGETARCH 22 | 23 | RUN make pt-proposer VERSION="$VERSION" GOOS=$TARGETOS GOARCH=$TARGETARCH 24 | 25 | FROM alpine:3.15 26 | 27 | COPY --from=builder /app/pt-proposer/bin/pt-proposer /usr/local/bin 28 | 29 | CMD ["pt-proposer"] 30 | -------------------------------------------------------------------------------- /pt-batcher/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder 2 | 3 | ARG VERSION=v0.0.0 4 | 5 | RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash 6 | 7 | # build pt-batcher with the shared go.mod & go.sum files 8 | COPY ./pt-batcher /app/pt-batcher 9 | COPY ./pt-bindings /app/pt-bindings 10 | COPY ./pt-node /app/pt-node 11 | COPY ./pt-service /app/pt-service 12 | COPY ./pt-signer /app/pt-signer 13 | COPY ./go.mod /app/go.mod 14 | COPY ./go.sum /app/go.sum 15 | 16 | COPY ./.git /app/.git 17 | 18 | WORKDIR /app/pt-batcher 19 | 20 | RUN go mod download 21 | 22 | ARG TARGETOS TARGETARCH 23 | 24 | RUN make pt-batcher VERSION="$VERSION" GOOS=$TARGETOS GOARCH=$TARGETARCH 25 | 26 | FROM alpine:3.15 27 | 28 | COPY --from=builder /app/pt-batcher/bin/pt-batcher /usr/local/bin 29 | 30 | ENTRYPOINT ["pt-batcher"] 31 | -------------------------------------------------------------------------------- /pt-bindings/hardhat/utils.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | import "strings" 4 | 5 | type QualifiedName struct { 6 | SourceName string 7 | ContractName string 8 | } 9 | 10 | func ParseFullyQualifiedName(name string) QualifiedName { 11 | names := strings.Split(name, ":") 12 | if len(names) == 1 { 13 | return QualifiedName{ 14 | SourceName: "", 15 | ContractName: names[0], 16 | } 17 | } 18 | 19 | contractName := names[len(names)-1] 20 | sourceName := strings.Join(names[0:len(names)-1], ":") 21 | 22 | return QualifiedName{ 23 | ContractName: contractName, 24 | SourceName: sourceName, 25 | } 26 | } 27 | 28 | func GetFullyQualifiedName(sourceName, contractName string) string { 29 | return sourceName + ":" + contractName 30 | } 31 | 32 | func IsFullyQualifiedName(name string) bool { 33 | return strings.Contains(name, ":") 34 | } 35 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/doc.go: -------------------------------------------------------------------------------- 1 | // Package derive provides the data transformation functions that take L1 data 2 | // and turn it into L2 blocks and results. Certain L2 data is also able to 3 | // turned back into L1 data. 4 | // 5 | // The flow is data is as follows 6 | // receipts, batches -> eth.PayloadAttributes, by parsing the L1 data and deriving L2 inputs 7 | // l2.PayloadAttributes -> l2.ExecutionPayload, by running the EVM (using an Execution Engine) 8 | // L2 block -> Corresponding L1 block info, by parsing the first deposited transaction 9 | // 10 | // The Payload Attributes derivation stage is a pure function. 11 | // The Execution Payload derivation stage relies on the L2 execution engine to perform the state update. 12 | // The inversion step is a pure function. 13 | // 14 | // The steps should be kept separate to enable easier testing. 15 | package derive 16 | -------------------------------------------------------------------------------- /pt-batcher/rpc/config.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/urfave/cli" 5 | 6 | ptservice "github.com/patex-ecosystem/patex-network/pt-service" 7 | ptrpc "github.com/patex-ecosystem/patex-network/pt-service/rpc" 8 | ) 9 | 10 | const ( 11 | EnableAdminFlagName = "rpc.enable-admin" 12 | ) 13 | 14 | func CLIFlags(envPrefix string) []cli.Flag { 15 | return []cli.Flag{ 16 | cli.BoolFlag{ 17 | Name: EnableAdminFlagName, 18 | Usage: "Enable the admin API (experimental)", 19 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "RPC_ENABLE_ADMIN"), 20 | }, 21 | } 22 | } 23 | 24 | type CLIConfig struct { 25 | ptrpc.CLIConfig 26 | EnableAdmin bool 27 | } 28 | 29 | func ReadCLIConfig(ctx *cli.Context) CLIConfig { 30 | return CLIConfig{ 31 | CLIConfig: ptrpc.ReadCLIConfig(ctx), 32 | EnableAdmin: ctx.GlobalBool(EnableAdminFlagName), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pt-service/client/rollup.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/rpc" 8 | "github.com/patex-ecosystem/patex-network/pt-node/client" 9 | "github.com/patex-ecosystem/patex-network/pt-node/sources" 10 | ) 11 | 12 | // DialRollupClientWithTimeout attempts to dial the RPC provider using the provided 13 | // URL. If the dial doesn't complete within defaultDialTimeout seconds, this 14 | // method will return an error. 15 | func DialRollupClientWithTimeout(ctx context.Context, url string, timeout time.Duration) (*sources.RollupClient, error) { 16 | ctxt, cancel := context.WithTimeout(ctx, timeout) 17 | defer cancel() 18 | 19 | rpcCl, err := rpc.DialContext(ctxt, url) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return sources.NewRollupClient(client.NewBaseRPCClient(rpcCl)), nil 25 | } 26 | -------------------------------------------------------------------------------- /pt-node/eth/label.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | type BlockLabel string 4 | 5 | const ( 6 | // Unsafe is: 7 | // - L1: absolute head of the chain 8 | // - L2: absolute head of the chain, not confirmed on L1 9 | Unsafe = "latest" 10 | // Safe is: 11 | // - L1: Justified checkpoint, beacon chain: 1 epoch of 2/3 of the validators attesting the epoch. 12 | // - L2: Derived chain tip from L1 data 13 | Safe = "safe" 14 | // Finalized is: 15 | // - L1: Finalized checkpoint, beacon chain: 2+ justified epochs with "supermajority link" (see FFG docs). 16 | // More about FFG: https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/gasper/ 17 | // - L2: Derived chain tip from finalized L1 data 18 | Finalized = "finalized" 19 | ) 20 | 21 | func (label BlockLabel) Arg() any { return string(label) } 22 | 23 | func (BlockLabel) CheckID(id BlockID) error { 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /specs/meta/versioning.md: -------------------------------------------------------------------------------- 1 | # Versioning 2 | 3 | 4 | 5 | **Table of Contents** 6 | 7 | - [Go modules](#go-modules) 8 | - [versioning process](#versioning-process) 9 | - [Typescript](#typescript) 10 | 11 | 12 | 13 | ## Go modules 14 | 15 | 16 | ```text 17 | ./pt-service 18 | ./pt-bindings 19 | ./pt-batcher 20 | ./pt-node 21 | ./pt-proposer 22 | ``` 23 | 24 | ### versioning process 25 | 26 | Since changesets versioning is not compatible with Go we are moving away from it. 27 | Starting with new bedrock modules, Go-compatible tags will be used, 28 | formatted as `modulename/vX.Y.Z` where `vX.Y.Z` is semver. 29 | 30 | ## Typescript 31 | 32 | See Changesets. 33 | -------------------------------------------------------------------------------- /pt-node/testutils/fuzzerutils/fuzzer_functions.go: -------------------------------------------------------------------------------- 1 | package fuzzerutils 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | fuzz "github.com/google/gofuzz" 8 | ) 9 | 10 | // AddFuzzerFunctions takes a fuzz.Fuzzer and adds a list of functions to handle different 11 | // data types in a fuzzing campaign. It adds support for commonly used types throughout the 12 | // application. 13 | func AddFuzzerFunctions(fuzzer *fuzz.Fuzzer) { 14 | fuzzer.Funcs( 15 | func(e *big.Int, c fuzz.Continue) { 16 | var temp [32]byte 17 | c.Fuzz(&temp) 18 | e.SetBytes(temp[:]) 19 | }, 20 | func(e *common.Hash, c fuzz.Continue) { 21 | var temp [32]byte 22 | c.Fuzz(&temp) 23 | e.SetBytes(temp[:]) 24 | }, 25 | func(e *common.Address, c fuzz.Continue) { 26 | var temp [20]byte 27 | c.Fuzz(&temp) 28 | e.SetBytes(temp[:]) 29 | }, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /pt-node/node/comms.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/libp2p/go-libp2p/core/peer" 7 | 8 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 9 | ) 10 | 11 | // Tracer configures the PtNode to share events 12 | type Tracer interface { 13 | OnNewL1Head(ctx context.Context, sig eth.L1BlockRef) 14 | OnUnsafeL2Payload(ctx context.Context, from peer.ID, payload *eth.ExecutionPayload) 15 | OnPublishL2Payload(ctx context.Context, payload *eth.ExecutionPayload) 16 | } 17 | 18 | type noOpTracer struct{} 19 | 20 | func (n noOpTracer) OnNewL1Head(ctx context.Context, sig eth.L1BlockRef) {} 21 | 22 | func (n noOpTracer) OnUnsafeL2Payload(ctx context.Context, from peer.ID, payload *eth.ExecutionPayload) { 23 | } 24 | 25 | func (n noOpTracer) OnPublishL2Payload(ctx context.Context, payload *eth.ExecutionPayload) {} 26 | 27 | var _ Tracer = (*noOpTracer)(nil) 28 | -------------------------------------------------------------------------------- /pt-service/util_test.go: -------------------------------------------------------------------------------- 1 | package pt_service 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | func TestCLIFlagsToEnvVars(t *testing.T) { 11 | flags := []cli.Flag{ 12 | cli.StringFlag{ 13 | Name: "test", 14 | EnvVar: "PT_NODE_TEST_VAR", 15 | }, 16 | cli.IntFlag{ 17 | Name: "no env var", 18 | }, 19 | } 20 | res := cliFlagsToEnvVars(flags) 21 | require.Contains(t, res, "PT_NODE_TEST_VAR") 22 | } 23 | 24 | func TestValidateEnvVars(t *testing.T) { 25 | provided := []string{"PT_BATCHER_CONFIG=true", "PT_BATCHER_FAKE=false", "LD_PRELOAD=/lib/fake.so"} 26 | defined := map[string]struct{}{ 27 | "PT_BATCHER_CONFIG": {}, 28 | "PT_BATCHER_OTHER": {}, 29 | } 30 | invalids := validateEnvVars("PT_BATCHER", provided, defined) 31 | require.ElementsMatch(t, invalids, []string{"PT_BATCHER_FAKE=false"}) 32 | } 33 | -------------------------------------------------------------------------------- /pt-service/httputil/server.go: -------------------------------------------------------------------------------- 1 | package httputil 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | func ListenAndServeContext(ctx context.Context, server *http.Server) error { 12 | errCh := make(chan error, 1) 13 | go func() { 14 | errCh <- server.ListenAndServe() 15 | }() 16 | 17 | // verify that the server comes up 18 | tick := time.NewTimer(10 * time.Millisecond) 19 | select { 20 | case err := <-errCh: 21 | return fmt.Errorf("http server failed: %w", err) 22 | case <-tick.C: 23 | break 24 | } 25 | 26 | select { 27 | case err := <-errCh: 28 | if errors.Is(err, http.ErrServerClosed) { 29 | return nil 30 | } 31 | return err 32 | case <-ctx.Done(): 33 | _ = server.Shutdown(context.Background()) 34 | 35 | err := ctx.Err() 36 | if errors.Is(err, context.Canceled) { 37 | return nil 38 | } 39 | return err 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pt-service/httputil/wrapped_response_writer.go: -------------------------------------------------------------------------------- 1 | package httputil 2 | 3 | import "net/http" 4 | 5 | type WrappedResponseWriter struct { 6 | StatusCode int 7 | ResponseLen int 8 | 9 | w http.ResponseWriter 10 | wroteHeader bool 11 | } 12 | 13 | func NewWrappedResponseWriter(w http.ResponseWriter) *WrappedResponseWriter { 14 | return &WrappedResponseWriter{ 15 | StatusCode: 200, 16 | w: w, 17 | } 18 | } 19 | 20 | func (w *WrappedResponseWriter) Header() http.Header { 21 | return w.w.Header() 22 | } 23 | 24 | func (w *WrappedResponseWriter) Write(bytes []byte) (int, error) { 25 | n, err := w.w.Write(bytes) 26 | w.ResponseLen += n 27 | return n, err 28 | } 29 | 30 | func (w *WrappedResponseWriter) WriteHeader(statusCode int) { 31 | if w.wroteHeader { 32 | return 33 | } 34 | 35 | w.wroteHeader = true 36 | w.StatusCode = statusCode 37 | w.w.WriteHeader(statusCode) 38 | } 39 | -------------------------------------------------------------------------------- /pt-service/metrics/balance_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | type weiToEthTestCase struct { 9 | input *big.Int 10 | output float64 11 | } 12 | 13 | func TestWeiToEther(t *testing.T) { 14 | tests := []weiToEthTestCase{ 15 | { 16 | input: big.NewInt(1_000_000_000_000_000_000), 17 | output: 1.0, 18 | }, 19 | { 20 | input: big.NewInt(3_000_000_000_000_000_000), 21 | output: 3.0, 22 | }, 23 | { 24 | input: big.NewInt(3_456_789_000_000_000_000), 25 | output: 3.456789, 26 | }, 27 | { 28 | input: new(big.Int).Mul(big.NewInt(1_000_000), big.NewInt(1_000_000_000_000_000_000)), 29 | output: 1_000_000, 30 | }, 31 | } 32 | 33 | for i, tc := range tests { 34 | out := weiToEther(tc.input) 35 | if out != tc.output { 36 | t.Fatalf("test %v: expected %v but got %v", i, tc.output, out) 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /pt-node/flags/flags_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | // TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set 11 | // the Required field to false. 12 | func TestOptionalFlagsDontSetRequired(t *testing.T) { 13 | for _, flag := range optionalFlags { 14 | reqFlag, ok := flag.(cli.RequiredFlag) 15 | require.True(t, ok) 16 | require.False(t, reqFlag.IsRequired()) 17 | } 18 | } 19 | 20 | // TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags. 21 | func TestUniqueFlags(t *testing.T) { 22 | seenCLI := make(map[string]struct{}) 23 | for _, flag := range Flags { 24 | name := flag.GetName() 25 | if _, ok := seenCLI[name]; ok { 26 | t.Errorf("duplicate flag %s", name) 27 | continue 28 | } 29 | seenCLI[name] = struct{}{} 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pt-service/enum/enum.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Stringered wraps the string type to implement the fmt.Stringer interface. 9 | type Stringered string 10 | 11 | // String returns the string value. 12 | func (s Stringered) String() string { 13 | return string(s) 14 | } 15 | 16 | // StringeredList converts a list of strings to a list of Stringered. 17 | func StringeredList(values []string) []Stringered { 18 | var out []Stringered 19 | for _, v := range values { 20 | out = append(out, Stringered(v)) 21 | } 22 | return out 23 | } 24 | 25 | // EnumString returns a comma-separated string of the enum values. 26 | // This is primarily used to generate a cli flag. 27 | func EnumString[T fmt.Stringer](values []T) string { 28 | var out strings.Builder 29 | for i, v := range values { 30 | out.WriteString(v.String()) 31 | if i+1 < len(values) { 32 | out.WriteString(", ") 33 | } 34 | } 35 | return out.String() 36 | } 37 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/test/random.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/ethereum/go-ethereum/core/types" 7 | "github.com/ethereum/go-ethereum/trie" 8 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 9 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 10 | "github.com/patex-ecosystem/patex-network/pt-node/testutils" 11 | ) 12 | 13 | // RandomL2Block returns a random block whose first transaction is a random 14 | // L1 Info Deposit transaction. 15 | func RandomL2Block(rng *rand.Rand, txCount int) (*types.Block, []*types.Receipt) { 16 | l1Block := types.NewBlock(testutils.RandomHeader(rng), 17 | nil, nil, nil, trie.NewStackTrie(nil)) 18 | l1InfoTx, err := derive.L1InfoDeposit(0, eth.BlockToInfo(l1Block), eth.SystemConfig{}, testutils.RandomBool(rng)) 19 | if err != nil { 20 | panic("L1InfoDeposit: " + err.Error()) 21 | } 22 | return testutils.RandomBlockPrependTxs(rng, txCount, types.NewTx(l1InfoTx)) 23 | } 24 | -------------------------------------------------------------------------------- /pt-node/testutils/block_id.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 9 | ) 10 | 11 | // TestID represents an eth.BlockID as string, and can be shortened for convenience in test definitions. 12 | // 13 | // Format: : where the are 14 | // copied over (i.e. not hex) and is in decimal. 15 | // 16 | // Examples: "foobar:123", or "B:2" 17 | type TestID string 18 | 19 | func (id TestID) ID() eth.BlockID { 20 | parts := strings.Split(string(id), ":") 21 | if len(parts) != 2 { 22 | panic("bad id") 23 | } 24 | if len(parts[0]) > 32 { 25 | panic("test ID hash too long") 26 | } 27 | var h common.Hash 28 | copy(h[:], parts[0]) 29 | v, err := strconv.ParseUint(parts[1], 0, 64) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return eth.BlockID{ 34 | Hash: h, 35 | Number: v, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pt-node/testutils/mock_debug_client.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | type MockDebugClient struct { 11 | mock.Mock 12 | } 13 | 14 | func (m *MockDebugClient) ExpectNodeByHash(hash common.Hash, res []byte, err error) { 15 | m.Mock.On("NodeByHash", hash).Once().Return(res, &err) 16 | } 17 | 18 | func (m *MockDebugClient) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { 19 | out := m.Mock.MethodCalled("NodeByHash", hash) 20 | return out[0].([]byte), *out[1].(*error) 21 | } 22 | 23 | func (m *MockDebugClient) ExpectCodeByHash(hash common.Hash, res []byte, err error) { 24 | m.Mock.On("CodeByHash", hash).Once().Return(res, &err) 25 | } 26 | 27 | func (m *MockDebugClient) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { 28 | out := m.Mock.MethodCalled("CodeByHash", hash) 29 | return out[0].([]byte), *out[1].(*error) 30 | } 31 | -------------------------------------------------------------------------------- /pt-service/pprof/server.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | "net/http/pprof" 8 | "strconv" 9 | 10 | "github.com/patex-ecosystem/patex-network/pt-service/httputil" 11 | ) 12 | 13 | func ListenAndServe(ctx context.Context, hostname string, port int) error { 14 | mux := http.NewServeMux() 15 | 16 | // have to do below to support multiple servers, since the 17 | // pprof import only uses DefaultServeMux 18 | mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) 19 | mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) 20 | mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) 21 | mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) 22 | mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) 23 | 24 | addr := net.JoinHostPort(hostname, strconv.Itoa(port)) 25 | server := &http.Server{ 26 | Addr: addr, 27 | Handler: mux, 28 | } 29 | return httputil.ListenAndServeContext(ctx, server) 30 | } 31 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/params.go: -------------------------------------------------------------------------------- 1 | package crossdomain 2 | 3 | import ( 4 | "math/big" 5 | ) 6 | 7 | // Params contains the configuration parameters used for verifying 8 | // the integrity of the migration. 9 | type Params struct { 10 | // ExpectedSupplyDelta is the expected delta between the total supply of PVM ETH, 11 | // and ETH we were able to migrate. This is used to account for supply bugs in 12 | //previous regenesis events. 13 | ExpectedSupplyDelta *big.Int 14 | } 15 | 16 | var ParamsByChainID = map[int]*Params{ 17 | 1: { 18 | // Regenesis 4 (Nov 11 2021) contained a supply bug such that the total PVM ETH 19 | // supply was 1.628470012 ETH greater than the sum balance of every account migrated 20 | // / during the regenesis. A further 0.0012 ETH was incorrectly not removed from the 21 | // total supply by accidental invocations of the Saurik bug (https://www.saurik.com/patex.html). 22 | new(big.Int).SetUint64(1627270011999999992), 23 | }, 24 | 5: { 25 | new(big.Int), 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /pt-chain-ops/ether/cli.go: -------------------------------------------------------------------------------- 1 | package ether 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/ethereum/go-ethereum/core/state" 9 | ) 10 | 11 | // getPVMETHTotalSupply returns PVM ETH's total supply by reading 12 | // the appropriate storage slot. 13 | func getPVMETHTotalSupply(db *state.StateDB) *big.Int { 14 | key := getPVMETHTotalSupplySlot() 15 | return db.GetState(PVMETHAddress, key).Big() 16 | } 17 | 18 | func getPVMETHTotalSupplySlot() common.Hash { 19 | position := common.Big2 20 | key := common.BytesToHash(common.LeftPadBytes(position.Bytes(), 32)) 21 | return key 22 | } 23 | 24 | func GetPVMETHTotalSupplySlot() common.Hash { 25 | return getPVMETHTotalSupplySlot() 26 | } 27 | 28 | // GetPVMETHBalance gets a user's PVM ETH balance from state by querying the 29 | // appropriate storage slot directly. 30 | func GetPVMETHBalance(db *state.StateDB, addr common.Address) *big.Int { 31 | return db.GetState(PVMETHAddress, CalcPVMETHStorageKey(addr)).Big() 32 | } 33 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/encoding_test.go: -------------------------------------------------------------------------------- 1 | package crossdomain_test 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/crossdomain" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func FuzzVersionedNonce(f *testing.F) { 12 | f.Fuzz(func(t *testing.T, _nonce []byte, _version uint16) { 13 | inputNonce := new(big.Int).SetBytes(_nonce) 14 | 15 | // Clamp nonce to uint240 16 | if inputNonce.Cmp(crossdomain.NonceMask) > 0 { 17 | inputNonce = new(big.Int).Set(crossdomain.NonceMask) 18 | } 19 | // Clamp version to 0 or 1 20 | _version = _version % 2 21 | 22 | inputVersion := new(big.Int).SetUint64(uint64(_version)) 23 | encodedNonce := crossdomain.EncodeVersionedNonce(inputNonce, inputVersion) 24 | 25 | decodedNonce, decodedVersion := crossdomain.DecodeVersionedNonce(encodedNonce) 26 | 27 | require.Equal(t, decodedNonce.Uint64(), inputNonce.Uint64()) 28 | require.Equal(t, decodedVersion.Uint64(), inputVersion.Uint64()) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /pt-node/metrics/events.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/patex-ecosystem/patex-network/pt-service/metrics" 8 | 9 | "github.com/prometheus/client_golang/prometheus" 10 | ) 11 | 12 | type EventMetrics struct { 13 | Total prometheus.Counter 14 | LastTime prometheus.Gauge 15 | } 16 | 17 | func (e *EventMetrics) RecordEvent() { 18 | e.Total.Inc() 19 | e.LastTime.Set(float64(time.Now().Unix())) 20 | } 21 | 22 | func NewEventMetrics(factory metrics.Factory, ns string, name string, displayName string) *EventMetrics { 23 | return &EventMetrics{ 24 | Total: factory.NewCounter(prometheus.CounterOpts{ 25 | Namespace: ns, 26 | Name: fmt.Sprintf("%s_total", name), 27 | Help: fmt.Sprintf("Count of %s events", displayName), 28 | }), 29 | LastTime: factory.NewGauge(prometheus.GaugeOpts{ 30 | Namespace: ns, 31 | Name: fmt.Sprintf("last_%s_unix", name), 32 | Help: fmt.Sprintf("Timestamp of last %s event", displayName), 33 | }), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/batch_test.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/common/hexutil" 10 | ) 11 | 12 | func TestBatchRoundTrip(t *testing.T) { 13 | batches := []*BatchData{ 14 | { 15 | BatchV1: BatchV1{ 16 | ParentHash: common.Hash{}, 17 | EpochNum: 0, 18 | Timestamp: 0, 19 | Transactions: []hexutil.Bytes{}, 20 | }, 21 | }, 22 | { 23 | BatchV1: BatchV1{ 24 | ParentHash: common.Hash{31: 0x42}, 25 | EpochNum: 1, 26 | Timestamp: 1647026951, 27 | Transactions: []hexutil.Bytes{[]byte{0, 0, 0}, []byte{0x76, 0xfd, 0x7c}}, 28 | }, 29 | }, 30 | } 31 | 32 | for i, batch := range batches { 33 | enc, err := batch.MarshalBinary() 34 | assert.NoError(t, err) 35 | var dec BatchData 36 | err = dec.UnmarshalBinary(enc) 37 | assert.NoError(t, err) 38 | assert.Equal(t, batch, &dec, "Batch not equal test case %v", i) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pt-e2e/e2eutils/testing.go: -------------------------------------------------------------------------------- 1 | package e2eutils 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // TestingBase is an interface used for standard Go testing. 10 | // This interface is used for unit tests, benchmarks, and fuzz tests and also emulated in Hive. 11 | // 12 | // The Go testing.TB interface does not allow extensions by embedding the interface, so we repeat it here. 13 | type TestingBase interface { 14 | Cleanup(func()) 15 | Error(args ...any) 16 | Errorf(format string, args ...any) 17 | Fail() 18 | FailNow() 19 | Failed() bool 20 | Fatal(args ...any) 21 | Fatalf(format string, args ...any) 22 | Helper() 23 | Log(args ...any) 24 | Logf(format string, args ...any) 25 | Name() string 26 | Setenv(key, value string) 27 | Skip(args ...any) 28 | SkipNow() 29 | Skipf(format string, args ...any) 30 | Skipped() bool 31 | TempDir() string 32 | Parallel() 33 | } 34 | 35 | func TimeoutCtx(t *testing.T, timeout time.Duration) context.Context { 36 | ctx, cancel := context.WithCancel(context.Background()) 37 | t.Cleanup(cancel) 38 | return ctx 39 | } 40 | -------------------------------------------------------------------------------- /pt-chain-ops/genesis/test_util.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "archive/tar" 5 | "compress/gzip" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func Untar(tarball, target string) error { 12 | f, err := os.Open(tarball) 13 | if err != nil { 14 | return err 15 | } 16 | defer f.Close() 17 | r, err := gzip.NewReader(f) 18 | if err != nil { 19 | return err 20 | } 21 | tarReader := tar.NewReader(r) 22 | 23 | for { 24 | header, err := tarReader.Next() 25 | if err == io.EOF { 26 | break 27 | } else if err != nil { 28 | return err 29 | } 30 | 31 | path := filepath.Join(target, header.Name) 32 | info := header.FileInfo() 33 | if info.IsDir() { 34 | if err = os.MkdirAll(path, info.Mode()); err != nil { 35 | return err 36 | } 37 | continue 38 | } 39 | 40 | file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) 41 | if err != nil { 42 | return err 43 | } 44 | defer file.Close() 45 | _, err = io.Copy(file, tarReader) 46 | if err != nil { 47 | return err 48 | } 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /pt-bindings/README.md: -------------------------------------------------------------------------------- 1 | # pt-bindings 2 | 3 | This package contains built go bindings of the smart contracts. It must be 4 | updated after any changes to the smart contracts to ensure that the bindings are 5 | up to date. 6 | 7 | The bindings include the bytecode for each contract so that go based tests 8 | can deploy the contracts. There are also `more` files that include the deployed 9 | bytecode as well as the storage layout. These are used to dynamically set 10 | bytecode and storage slots in state. 11 | 12 | ## Dependencies 13 | 14 | - `abigen` version 1.10.25 15 | - `make` 16 | 17 | To check the version of `abigen`, run the command `abigen --version`. 18 | 19 | ## abigen 20 | 21 | The `abigen` tool is part of `go-ethereum` and can be used to build go bindings 22 | for smart contracts. It can be installed with go using the commands: 23 | 24 | ```bash 25 | $ go get -u github.com/ethereum/go-ethereum 26 | $ cd $GOPATH/src/github.com/ethereum/go-ethereum/ 27 | $ make devtools 28 | ``` 29 | 30 | The geth docs for `abigen` can be found [here](https://geth.ethereum.org/docs/dapp/native-bindings). 31 | -------------------------------------------------------------------------------- /pt-node/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Patex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright 2020-2023 Patex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /pt-chain-ops/ether/storage.go: -------------------------------------------------------------------------------- 1 | package ether 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "golang.org/x/crypto/sha3" 6 | ) 7 | 8 | // BytesBacked is a re-export of the same interface in Geth, 9 | // which is unfortunately private. 10 | type BytesBacked interface { 11 | Bytes() []byte 12 | } 13 | 14 | // CalcAllowanceStorageKey calculates the storage key of an allowance in PVM ETH. 15 | func CalcAllowanceStorageKey(owner common.Address, spender common.Address) common.Hash { 16 | inner := CalcStorageKey(owner, common.Big1) 17 | return CalcStorageKey(spender, inner) 18 | } 19 | 20 | // CalcPVMETHStorageKey calculates the storage key of an PVM ETH balance. 21 | func CalcPVMETHStorageKey(addr common.Address) common.Hash { 22 | return CalcStorageKey(addr, common.Big0) 23 | } 24 | 25 | // CalcStorageKey is a helper method to calculate storage keys. 26 | func CalcStorageKey(a, b BytesBacked) common.Hash { 27 | hasher := sha3.NewLegacyKeccak256() 28 | hasher.Write(common.LeftPadBytes(a.Bytes(), 32)) 29 | hasher.Write(common.LeftPadBytes(b.Bytes(), 32)) 30 | digest := hasher.Sum(nil) 31 | return common.BytesToHash(digest) 32 | } 33 | -------------------------------------------------------------------------------- /pt-node/p2p/peer_scores.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | log "github.com/ethereum/go-ethereum/log" 5 | pubsub "github.com/libp2p/go-libp2p-pubsub" 6 | ) 7 | 8 | // ConfigurePeerScoring configures the peer scoring parameters for the pubsub 9 | func ConfigurePeerScoring(gossipConf GossipSetupConfigurables, scorer Scorer, log log.Logger) []pubsub.Option { 10 | // If we want to completely disable scoring config here, we can use the [peerScoringParams] 11 | // to return early without returning any [pubsub.Option]. 12 | peerScoreParams := gossipConf.PeerScoringParams() 13 | peerScoreThresholds := NewPeerScoreThresholds() 14 | opts := []pubsub.Option{} 15 | // Check the app specific score since libp2p doesn't export it's [validate] function :/ 16 | if peerScoreParams != nil && peerScoreParams.AppSpecificScore != nil { 17 | opts = []pubsub.Option{ 18 | pubsub.WithPeerScore(peerScoreParams, &peerScoreThresholds), 19 | pubsub.WithPeerScoreInspect(scorer.SnapshotHook(), peerScoreInspectFrequency), 20 | } 21 | } else { 22 | log.Warn("Proceeding with no peer scoring...\nMissing AppSpecificScore in peer scoring params") 23 | } 24 | return opts 25 | } 26 | -------------------------------------------------------------------------------- /pt-node/p2p/mocks/GossipMetricer.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.22.1. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // GossipMetricer is an autogenerated mock type for the GossipMetricer type 8 | type GossipMetricer struct { 9 | mock.Mock 10 | } 11 | 12 | // RecordGossipEvent provides a mock function with given fields: evType 13 | func (_m *GossipMetricer) RecordGossipEvent(evType int32) { 14 | _m.Called(evType) 15 | } 16 | 17 | // SetPeerScores provides a mock function with given fields: _a0 18 | func (_m *GossipMetricer) SetPeerScores(_a0 map[string]float64) { 19 | _m.Called(_a0) 20 | } 21 | 22 | type mockConstructorTestingTNewGossipMetricer interface { 23 | mock.TestingT 24 | Cleanup(func()) 25 | } 26 | 27 | // NewGossipMetricer creates a new instance of GossipMetricer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 28 | func NewGossipMetricer(t mockConstructorTestingTNewGossipMetricer) *GossipMetricer { 29 | mock := &GossipMetricer{} 30 | mock.Mock.Test(t) 31 | 32 | t.Cleanup(func() { mock.AssertExpectations(t) }) 33 | 34 | return mock 35 | } 36 | -------------------------------------------------------------------------------- /pt-node/p2p/cli/load_signer.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | "github.com/urfave/cli" 8 | 9 | "github.com/patex-ecosystem/patex-network/pt-node/flags" 10 | "github.com/patex-ecosystem/patex-network/pt-node/p2p" 11 | ) 12 | 13 | // TODO: implement remote signer setup (config to authenticated endpoint) 14 | // and remote signer itself (e.g. a open http client to make signing requests) 15 | 16 | // LoadSignerSetup loads a configuration for a Signer to be set up later 17 | func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) { 18 | key := ctx.GlobalString(flags.SequencerP2PKeyFlag.Name) 19 | if key != "" { 20 | // Mnemonics are bad because they leak *all* keys when they leak. 21 | // Unencrypted keys from file are bad because they are easy to leak (and we are not checking file permissions). 22 | priv, err := crypto.HexToECDSA(key) 23 | if err != nil { 24 | return nil, fmt.Errorf("failed to read batch submitter key: %w", err) 25 | } 26 | 27 | return &p2p.PreparedSigner{Signer: p2p.NewLocalSigner(priv)}, nil 28 | } 29 | 30 | // TODO: create remote signer 31 | 32 | return nil, nil 33 | } 34 | -------------------------------------------------------------------------------- /pt-chain-ops/ether/db.go: -------------------------------------------------------------------------------- 1 | package ether 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/ethereum/go-ethereum/core/rawdb" 7 | "github.com/ethereum/go-ethereum/ethdb" 8 | "github.com/ethereum/go-ethereum/log" 9 | ) 10 | 11 | // MustOpenDB opens a Geth database, or panics. Note that 12 | // the database must be opened with a freezer in order to 13 | // properly read historical data. 14 | func MustOpenDB(dataDir string) ethdb.Database { 15 | return MustOpenDBWithCacheOpts(dataDir, 0, 0) 16 | } 17 | 18 | // MustOpenDBWithCacheOpts opens a Geth database or panics. Allows 19 | // the caller to pass in LevelDB cache parameters. 20 | func MustOpenDBWithCacheOpts(dataDir string, cacheSize, handles int) ethdb.Database { 21 | dir := filepath.Join(dataDir, "geth", "chaindata") 22 | db, err := rawdb.Open(rawdb.OpenOptions{ 23 | Type: "leveldb", 24 | Directory: dir, 25 | AncientsDirectory: filepath.Join(dir, "ancient"), 26 | Namespace: "", 27 | Cache: cacheSize, 28 | Handles: handles, 29 | ReadOnly: true, 30 | }) 31 | if err != nil { 32 | log.Crit("error opening raw DB", "err", err) 33 | } 34 | return db 35 | } 36 | -------------------------------------------------------------------------------- /pt-e2e/e2eutils/setup_test.go: -------------------------------------------------------------------------------- 1 | package e2eutils 2 | 3 | import ( 4 | "encoding/hex" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/patex-ecosystem/patex-network/pt-bindings/predeploys" 11 | ) 12 | 13 | func TestWriteDefaultJWT(t *testing.T) { 14 | jwtPath := WriteDefaultJWT(t) 15 | data, err := os.ReadFile(jwtPath) 16 | require.NoError(t, err) 17 | require.Equal(t, "0x"+hex.EncodeToString(testingJWTSecret[:]), string(data)) 18 | } 19 | 20 | func TestSetup(t *testing.T) { 21 | tp := &TestParams{ 22 | MaxSequencerDrift: 40, 23 | SequencerWindowSize: 120, 24 | ChannelTimeout: 120, 25 | } 26 | dp := MakeDeployParams(t, tp) 27 | alloc := &AllocParams{PrefundTestUsers: true} 28 | sd := Setup(t, dp, alloc) 29 | require.Contains(t, sd.L1Cfg.Alloc, dp.Addresses.Alice) 30 | require.Equal(t, sd.L1Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12)) 31 | 32 | require.Contains(t, sd.L2Cfg.Alloc, dp.Addresses.Alice) 33 | require.Equal(t, sd.L2Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12)) 34 | 35 | require.Contains(t, sd.L1Cfg.Alloc, predeploys.DevPatexPortalAddr) 36 | require.Contains(t, sd.L2Cfg.Alloc, predeploys.L1BlockAddr) 37 | } 38 | -------------------------------------------------------------------------------- /pt-chain-ops/cmd/eof-crawler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/mattn/go-isatty" 8 | "github.com/urfave/cli/v2" 9 | 10 | "github.com/ethereum/go-ethereum/log" 11 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/eof" 12 | ) 13 | 14 | func main() { 15 | log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd())))) 16 | 17 | app := &cli.App{ 18 | Name: "eof-crawler", 19 | Usage: "Scan a Geth database for EOF-prefixed contracts", 20 | Flags: []cli.Flag{ 21 | &cli.StringFlag{ 22 | Name: "db-path", 23 | Usage: "Path to the geth LevelDB", 24 | }, 25 | &cli.StringFlag{ 26 | Name: "out", 27 | Value: "eof-contracts.json", 28 | Usage: "Path to the output file", 29 | }, 30 | }, 31 | Action: func(ctx *cli.Context) error { 32 | dbPath := ctx.String("db-path") 33 | if len(dbPath) == 0 { 34 | return errors.New("Must specify a db-path") 35 | } 36 | out := ctx.String("out") 37 | 38 | return eof.IndexEOFContracts(dbPath, out) 39 | }, 40 | } 41 | 42 | if err := app.Run(os.Args); err != nil { 43 | log.Crit("error indexing state", "err", err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pt-batcher/compressor/config.go: -------------------------------------------------------------------------------- 1 | package compressor 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 5 | ) 6 | 7 | type Config struct { 8 | // TargetFrameSize to target when creating channel frames. Note that if the 9 | // realized compression ratio is worse than the approximate, more frames may 10 | // actually be created. This also depends on how close the target is to the 11 | // max frame size. 12 | TargetFrameSize uint64 13 | // TargetNumFrames to create in this channel. If the realized compression ratio 14 | // is worse than approxComprRatio, additional leftover frame(s) might get created. 15 | TargetNumFrames int 16 | // ApproxComprRatio to assume. Should be slightly smaller than average from 17 | // experiments to avoid the chances of creating a small additional leftover frame. 18 | ApproxComprRatio float64 19 | // Kind of compressor to use. Must be one of KindKeys. If unset, NewCompressor 20 | // will default to RatioKind. 21 | Kind string 22 | } 23 | 24 | func (c Config) NewCompressor() (derive.Compressor, error) { 25 | if k, ok := Kinds[c.Kind]; ok { 26 | return k(c) 27 | } 28 | // default to RatioCompressor 29 | return Kinds[RatioKind](c) 30 | } 31 | -------------------------------------------------------------------------------- /pt-node/sources/caching/cache.go: -------------------------------------------------------------------------------- 1 | package caching 2 | 3 | import lru "github.com/hashicorp/golang-lru" 4 | 5 | type Metrics interface { 6 | CacheAdd(label string, cacheSize int, evicted bool) 7 | CacheGet(label string, hit bool) 8 | } 9 | 10 | // LRUCache wraps hashicorp *lru.Cache and tracks cache metrics 11 | type LRUCache struct { 12 | m Metrics 13 | label string 14 | inner *lru.Cache 15 | } 16 | 17 | func (c *LRUCache) Get(key any) (value any, ok bool) { 18 | value, ok = c.inner.Get(key) 19 | if c.m != nil { 20 | c.m.CacheGet(c.label, ok) 21 | } 22 | return value, ok 23 | } 24 | 25 | func (c *LRUCache) Add(key, value any) (evicted bool) { 26 | evicted = c.inner.Add(key, value) 27 | if c.m != nil { 28 | c.m.CacheAdd(c.label, c.inner.Len(), evicted) 29 | } 30 | return evicted 31 | } 32 | 33 | // NewLRUCache creates a LRU cache with the given metrics, labeling the cache adds/gets. 34 | // Metrics are optional: no metrics will be tracked if m == nil. 35 | func NewLRUCache(m Metrics, label string, maxSize int) *LRUCache { 36 | // no errors if the size is positive 37 | cache, _ := lru.New(maxSize) 38 | return &LRUCache{ 39 | m: m, 40 | label: label, 41 | inner: cache, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pt-service/rpc/cli.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | 7 | ptservice "github.com/patex-ecosystem/patex-network/pt-service" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | const ( 12 | ListenAddrFlagName = "rpc.addr" 13 | PortFlagName = "rpc.port" 14 | ) 15 | 16 | func CLIFlags(envPrefix string) []cli.Flag { 17 | return []cli.Flag{ 18 | cli.StringFlag{ 19 | Name: ListenAddrFlagName, 20 | Usage: "rpc listening address", 21 | Value: "0.0.0.0", 22 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "RPC_ADDR"), 23 | }, 24 | cli.IntFlag{ 25 | Name: PortFlagName, 26 | Usage: "rpc listening port", 27 | Value: 8545, 28 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "RPC_PORT"), 29 | }, 30 | } 31 | } 32 | 33 | type CLIConfig struct { 34 | ListenAddr string 35 | ListenPort int 36 | } 37 | 38 | func (c CLIConfig) Check() error { 39 | if c.ListenPort < 0 || c.ListenPort > math.MaxUint16 { 40 | return errors.New("invalid RPC port") 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func ReadCLIConfig(ctx *cli.Context) CLIConfig { 47 | return CLIConfig{ 48 | ListenAddr: ctx.GlobalString(ListenAddrFlagName), 49 | ListenPort: ctx.GlobalInt(PortFlagName), 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/alias.go: -------------------------------------------------------------------------------- 1 | package crossdomain 2 | 3 | import ( 4 | "github.com/holiman/uint256" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | var ( 10 | offsetAddr = common.HexToAddress("0x1111000000000000000000000000000000001111") 11 | offsetU256 = new(uint256.Int).SetBytes20(offsetAddr[:]) 12 | ) 13 | 14 | // ApplyL1ToL2Alias will apply the alias applied to L1 to L2 messages when it 15 | // originates from a contract address 16 | func ApplyL1ToL2Alias(address common.Address) common.Address { 17 | var input uint256.Int 18 | input.SetBytes20(address[:]) 19 | input.Add(&input, offsetU256) 20 | // clipping to bytes20 is the same as modulo 160 here, since the modulo is a multiple of 8 bits 21 | return input.Bytes20() 22 | } 23 | 24 | // UndoL1ToL2Alias will remove the alias applied to L1 to L2 messages when it 25 | // originates from a contract address 26 | func UndoL1ToL2Alias(address common.Address) common.Address { 27 | var input uint256.Int 28 | input.SetBytes20(address[:]) 29 | input.Sub(&input, offsetU256) 30 | // clipping to bytes20 is the same as modulo 160 here, since the modulo is a multiple of 8 bits. 31 | // and underflows are not affected either. 32 | return input.Bytes20() 33 | } 34 | -------------------------------------------------------------------------------- /pt-node/rollup/driver/config.go: -------------------------------------------------------------------------------- 1 | package driver 2 | 3 | type Config struct { 4 | // VerifierConfDepth is the distance to keep from the L1 head when reading L1 data for L2 derivation. 5 | VerifierConfDepth uint64 `json:"verifier_conf_depth"` 6 | 7 | // SequencerConfDepth is the distance to keep from the L1 head as origin when sequencing new L2 blocks. 8 | // If this distance is too large, the sequencer may: 9 | // - not adopt a L1 origin within the allowed time (rollup.Config.MaxSequencerDrift) 10 | // - not adopt a L1 origin that can be included on L1 within the allowed range (rollup.Config.SeqWindowSize) 11 | // and thus fail to produce a block with anything more than deposits. 12 | SequencerConfDepth uint64 `json:"sequencer_conf_depth"` 13 | 14 | // SequencerEnabled is true when the driver should sequence new blocks. 15 | SequencerEnabled bool `json:"sequencer_enabled"` 16 | 17 | // SequencerStopped is false when the driver should sequence new blocks. 18 | SequencerStopped bool `json:"sequencer_stopped"` 19 | 20 | // SequencerMaxSafeLag is the maximum number of L2 blocks for restricting the distance between L2 safe and unsafe. 21 | // Disabled if 0. 22 | SequencerMaxSafeLag uint64 `json:"sequencer_max_safe_lag"` 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | results 4 | temp 5 | .nyc_output 6 | coverage.json 7 | *.tsbuildinfo 8 | **/lcov.info 9 | 10 | yarn-error.log 11 | .yarn/* 12 | !.yarn/releases 13 | !.yarn/plugins 14 | .pnp.* 15 | 16 | dist 17 | artifacts 18 | cache 19 | 20 | l2geth/build/bin 21 | packages/contracts/deployments/custom 22 | packages/contracts/coverage* 23 | packages/contracts/@ens* 24 | packages/contracts/@openzeppelin* 25 | packages/contracts-periphery/coverage* 26 | packages/contracts-periphery/@openzeppelin* 27 | packages/contracts-periphery/forge-artifacts* 28 | 29 | packages/data-transport-layer/db 30 | 31 | packages/integration-tests-bedrock/cache 32 | packages/integration-tests-bedrock/artifacts 33 | 34 | packages/contracts-bedrock/deployments/devnetL1 35 | packages/contracts-bedrock/deployments/anvil 36 | 37 | # vim 38 | *.sw* 39 | 40 | # jetbrains 41 | .idea/ 42 | 43 | .env 44 | .env* 45 | !.env.example 46 | *.log 47 | 48 | .devnet 49 | .patex-sepolia 50 | # Ignore local fuzzing results 51 | **/testdata/fuzz/ 52 | 53 | coverage.out 54 | 55 | # Ignore bedrock go bindings local output files 56 | pt-bindings/bin 57 | 58 | 59 | __pycache__ 60 | 61 | # Ignore echidna artifacts 62 | crytic-export 63 | 64 | /.mainnet/ 65 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/hashing.go: -------------------------------------------------------------------------------- 1 | package crossdomain 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/crypto" 8 | ) 9 | 10 | // HashCrossDomainMessageV0 computes the pre bedrock cross domain messaging 11 | // hashing scheme. 12 | func HashCrossDomainMessageV0( 13 | target common.Address, 14 | sender common.Address, 15 | data []byte, 16 | nonce *big.Int, 17 | ) (common.Hash, error) { 18 | encoded, err := EncodeCrossDomainMessageV0(target, sender, data, nonce) 19 | if err != nil { 20 | return common.Hash{}, err 21 | } 22 | hash := crypto.Keccak256(encoded) 23 | return common.BytesToHash(hash), nil 24 | } 25 | 26 | // HashCrossDomainMessageV1 computes the first post bedrock cross domain 27 | // messaging hashing scheme. 28 | func HashCrossDomainMessageV1( 29 | nonce *big.Int, 30 | sender common.Address, 31 | target common.Address, 32 | value *big.Int, 33 | gasLimit *big.Int, 34 | data []byte, 35 | ) (common.Hash, error) { 36 | encoded, err := EncodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data) 37 | if err != nil { 38 | return common.Hash{}, err 39 | } 40 | hash := crypto.Keccak256(encoded) 41 | return common.BytesToHash(hash), nil 42 | } 43 | -------------------------------------------------------------------------------- /pt-node/rollup/output_root.go: -------------------------------------------------------------------------------- 1 | package rollup 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | "github.com/patex-ecosystem/patex-network/pt-bindings/bindings" 8 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 9 | ) 10 | 11 | var NilProof = errors.New("Output root proof is nil") 12 | 13 | // ComputeL2OutputRoot computes the L2 output root by hashing an output root proof. 14 | func ComputeL2OutputRoot(proofElements *bindings.TypesOutputRootProof) (eth.Bytes32, error) { 15 | if proofElements == nil { 16 | return eth.Bytes32{}, NilProof 17 | } 18 | 19 | digest := crypto.Keccak256Hash( 20 | proofElements.Version[:], 21 | proofElements.StateRoot[:], 22 | proofElements.MessagePasserStorageRoot[:], 23 | proofElements.LatestBlockhash[:], 24 | ) 25 | return eth.Bytes32(digest), nil 26 | } 27 | 28 | func ComputeL2OutputRootV0(block eth.BlockInfo, storageRoot [32]byte) (eth.Bytes32, error) { 29 | var l2OutputRootVersion eth.Bytes32 // it's zero for now 30 | return ComputeL2OutputRoot(&bindings.TypesOutputRootProof{ 31 | Version: l2OutputRootVersion, 32 | StateRoot: block.Root(), 33 | MessagePasserStorageRoot: storageRoot, 34 | LatestBlockhash: block.Hash(), 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pt-bindings/ast/canonicalize_test.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "path" 7 | "testing" 8 | 9 | "github.com/patex-ecosystem/patex-network/pt-bindings/solc" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | type astIDTest struct { 14 | In *solc.StorageLayout `json:"in"` 15 | Out *solc.StorageLayout `json:"out"` 16 | } 17 | 18 | func TestCanonicalize(t *testing.T) { 19 | tests := []struct { 20 | name string 21 | filename string 22 | }{ 23 | { 24 | "simple", 25 | "simple.json", 26 | }, 27 | { 28 | "remap public variables", 29 | "public-variables.json", 30 | }, 31 | { 32 | "values in storage", 33 | "values-in-storage.json", 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | f, err := os.Open(path.Join("testdata", tt.filename)) 39 | require.NoError(t, err) 40 | dec := json.NewDecoder(f) 41 | var testData astIDTest 42 | require.NoError(t, dec.Decode(&testData)) 43 | require.NoError(t, f.Close()) 44 | 45 | // Run 100 times to make sure that we aren't relying 46 | // on random map iteration order. 47 | for i := 0; i < 100; i++ { 48 | require.Equal(t, testData.Out, CanonicalizeASTIDs(testData.In)) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pt-proposer/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/urfave/cli" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | "github.com/patex-ecosystem/patex-network/pt-proposer/flags" 11 | "github.com/patex-ecosystem/patex-network/pt-proposer/proposer" 12 | ptlog "github.com/patex-ecosystem/patex-network/pt-service/log" 13 | ) 14 | 15 | var ( 16 | Version = "v0.10.14" 17 | GitCommit = "" 18 | GitDate = "" 19 | ) 20 | 21 | func main() { 22 | ptlog.SetupDefaults() 23 | 24 | app := cli.NewApp() 25 | app.Flags = flags.Flags 26 | app.Version = fmt.Sprintf("%s-%s-%s", Version, GitCommit, GitDate) 27 | app.Name = "pt-proposer" 28 | app.Usage = "L2Output Submitter" 29 | app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract" 30 | 31 | app.Action = curryMain(Version) 32 | err := app.Run(os.Args) 33 | if err != nil { 34 | log.Crit("Application failed", "message", err) 35 | } 36 | } 37 | 38 | // curryMain transforms the proposer.Main function into an app.Action 39 | // This is done to capture the Version of the proposer. 40 | func curryMain(version string) func(ctx *cli.Context) error { 41 | return func(ctx *cli.Context) error { 42 | return proposer.Main(version, ctx) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pt-chain-ops/genesis/layer_two.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/core/types" 5 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/state" 6 | 7 | "github.com/ethereum/go-ethereum/core" 8 | ) 9 | 10 | // BuildL2DeveloperGenesis will build the developer Patex Genesis 11 | // Block. Suitable for devnets. 12 | func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) { 13 | genspec, err := NewL2Genesis(config, l1StartBlock) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | db := state.NewMemoryStateDB(genspec) 19 | 20 | if config.FundDevAccounts { 21 | FundDevAccounts(db) 22 | } 23 | SetPrecompileBalances(db) 24 | 25 | storage, err := NewL2StorageConfig(config, l1StartBlock) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | immutable, err := NewL2ImmutableConfig(config, l1StartBlock) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | if err := SetL2Proxies(db); err != nil { 36 | return nil, err 37 | } 38 | 39 | if err := SetImplementations(db, storage, immutable); err != nil { 40 | return nil, err 41 | } 42 | 43 | if err := SetDevOnlyL2Implementations(db, storage, immutable); err != nil { 44 | return nil, err 45 | } 46 | 47 | return db.Genesis(), nil 48 | } 49 | -------------------------------------------------------------------------------- /pt-service/metrics/node_info.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | "github.com/prometheus/client_golang/prometheus/promauto" 6 | ) 7 | 8 | type NodeRecorder interface { 9 | RecordUp() 10 | RecordInfo(version string) 11 | } 12 | 13 | type noopNodeRecorder struct{} 14 | 15 | var NoopNodeRecorder = new(noopNodeRecorder) 16 | 17 | func (n *noopNodeRecorder) RecordUp() {} 18 | 19 | func (n *noopNodeRecorder) RecordInfo(string) {} 20 | 21 | type PromNodeRecorder struct { 22 | Up prometheus.Gauge 23 | Info *prometheus.GaugeVec 24 | } 25 | 26 | func NewPromNodeRecorder(r *prometheus.Registry, ns string) NodeRecorder { 27 | return &PromNodeRecorder{ 28 | Up: promauto.With(r).NewGauge(prometheus.GaugeOpts{ 29 | Namespace: ns, 30 | Name: "up", 31 | Help: "1 if the node has finished starting up", 32 | }), 33 | Info: promauto.With(r).NewGaugeVec(prometheus.GaugeOpts{ 34 | Namespace: ns, 35 | Name: "info", 36 | Help: "Pseudo-metric tracking version and config info", 37 | }, []string{ 38 | "version", 39 | }), 40 | } 41 | } 42 | 43 | func (p *PromNodeRecorder) RecordUp() { 44 | p.Up.Set(1) 45 | } 46 | 47 | func (p *PromNodeRecorder) RecordInfo(version string) { 48 | p.Info.WithLabelValues(version).Set(1) 49 | } 50 | -------------------------------------------------------------------------------- /pt-node/cmd/p2p/cmd_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | 8 | "github.com/libp2p/go-libp2p/core/crypto" 9 | "github.com/libp2p/go-libp2p/core/peer" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestPrivPub2PeerID(t *testing.T) { 14 | priv, pub, err := crypto.GenerateKeyPair(crypto.Secp256k1, 32) 15 | require.NoError(t, err) 16 | privRaw, err := priv.Raw() 17 | require.NoError(t, err) 18 | pubRaw, err := pub.Raw() 19 | require.NoError(t, err) 20 | 21 | t.Run("with a private key", func(t *testing.T) { 22 | privPidLib, err := peer.IDFromPrivateKey(priv) 23 | require.NoError(t, err) 24 | privPidImpl, err := Priv2PeerID(bytes.NewReader([]byte(hex.EncodeToString(privRaw)))) 25 | require.NoError(t, err) 26 | require.Equal(t, privPidLib.String(), privPidImpl) 27 | }) 28 | t.Run("with a public key", func(t *testing.T) { 29 | pubPidLib, err := peer.IDFromPublicKey(pub) 30 | require.NoError(t, err) 31 | pubPidImpl, err := Pub2PeerID(bytes.NewReader([]byte(hex.EncodeToString(pubRaw)))) 32 | require.NoError(t, err) 33 | require.Equal(t, pubPidLib.String(), pubPidImpl) 34 | }) 35 | t.Run("with bad hex", func(t *testing.T) { 36 | _, err := Priv2PeerID(bytes.NewReader([]byte("I am not hex."))) 37 | require.Error(t, err) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDWjCCAkKgAwIBAgIJAJiO53P0WQTzMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV 3 | BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg 4 | Q29tcGFueSBMdGQwHhcNMTcwODA0MTAzMzIyWhcNMTgwODA0MTAzMzIyWjBCMQsw 5 | CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh 6 | dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 7 | sYk+rsOElxPB5gPH8Vg/RFzdBzDJesI4VCXYcSCl0Ek/lO4AAKjgRssjmZDWWXcZ 8 | 3/Z1UlO4cRZy6zaSllFM07WXX3SQ54iBy3lS6NgBcHiWVfKrtXz/dQrswSPBnGEH 9 | PVBmd+xHcEv4wqYnYYVtJuIcGY1P/i14h1ogpEW22cnCVYJwqmjxrD4UQ8vSeTf9 10 | YwVCbKXZ5T+eiNhfQOkCG0rClqvfvZiMgBBlLYvJrc9bELsqIbNGVD4CrkYjZGEc 11 | ToMrb/wZhqLxlcs/iXKI0/579+9vvc44/pi2MFU0nvhoSnEgopKKPy83SwBogpiH 12 | +mDcVgMiEwbXHUyNAFbz/wIDAQABo1MwUTAdBgNVHQ4EFgQULtTUM8bHdg4jhRT3 13 | 9piSLGyTSsUwHwYDVR0jBBgwFoAULtTUM8bHdg4jhRT39piSLGyTSsUwDwYDVR0T 14 | AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAchsCFqaRslfgR0r0CoOJsX/H 15 | gEfqYHaL8/yJtUUqCRgyN5VtP1Cxzet8GVsAJaIKimeUCXshhaq94JQRZqxMFxJD 16 | sD9nfWVByorkSO9tmq+1FCWRzfau1AFsJxR6J1hpIbRfcfoi9HG4QnoJwULK/2yh 17 | DEXwnmgsCHTcNnj2U9F2vMLydHEKtmtMNe/S0Z7fHw1qlmqXgXmN5a/KTEPVBQm8 18 | eM6AhNlzrMqUPc8IiiZ32eAdgR2eAuhLnTCdnHwnHafpep72hSjwoUXsDjWNA/xW 19 | /lHx4jVL8muLP5G9YHZWzJwMijLqIlnryyyi+IfKAn4ANW/k44Lxm5hmRXh4UA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server1.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDWjCCAkKgAwIBAgIJALkZ2SqlmTT6MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV 3 | BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg 4 | Q29tcGFueSBMdGQwHhcNMTcwODA1MTU1MjIyWhcNMTgwODA1MTU1MjIyWjBCMQsw 5 | CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh 6 | dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 7 | p3vZAGYjBC4kPf6jqh6NYCWMVe8Db5IOHYECtGxBVOhS5DnuXPm0LUT9UCoUfPeB 8 | mU/1LZk41J70tpwl2IHhSIFqbG+fhNlRlUAA9CIHeRa2RVkFxH7I7xt2FhwU2WsX 9 | vi+GEiOKoZ2mC2cuZ5t8zWKinW+Hd8pL270OboQ980gThfF2ugzoXlyYVa+MrE5z 10 | wjLa3lQoKkqzo54/gKwK+KycXYoljmf2Q0++sSsDAqZlFIYknfct4+ST4TvGRqSw 11 | nPxQcJ33MXDpbnGcTBPXqWxXhvocd+QrOF4Rn7fVRLWZqYoFchSnt4R3qsoPuB8q 12 | WdZn+McT6qqgLUUtRy72PQIDAQABo1MwUTAdBgNVHQ4EFgQU83D1oNnMvjHU5q3E 13 | KIJUtrHMh94wHwYDVR0jBBgwFoAU83D1oNnMvjHU5q3EKIJUtrHMh94wDwYDVR0T 14 | AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADByFIBVqK7b2ZJVqWzQlY7pC 15 | DbR4bh+z8hwP/VU/JjlR0IdFj1tWLLPq5hmCWScntsmI8AvThCTbZgwogptvbeuA 16 | n2QRuSb3LMZBtq22XdDTPCdM4CQblinbXR3ePmz2ZpF7xxQMz8/IafeoZaaF2w4l 17 | PfHrf4ID89dMOq13MAhiSFmKeyElx2iGGnhsGQzhcoTbhDCW/HK3Zr/BN+uFyBgr 18 | PcV0H+VLOS7/XVnz5wbIiqTfnx0NhzQzw91zY2dXVOvNadD6QpNN5Abwxo9x16TP 19 | SfqKsjPvxk06wvchNAkopBLsqjLMovgKYMMbolVmFeeZVvNEnY19RoBuneJncA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server2.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDWjCCAkKgAwIBAgIJAJiO53P0WQTzMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV 3 | BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg 4 | Q29tcGFueSBMdGQwHhcNMTcwODA0MTAzMzIyWhcNMTgwODA0MTAzMzIyWjBCMQsw 5 | CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh 6 | dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 7 | sYk+rsOElxPB5gPH8Vg/RFzdBzDJesI4VCXYcSCl0Ek/lO4AAKjgRssjmZDWWXcZ 8 | 3/Z1UlO4cRZy6zaSllFM07WXX3SQ54iBy3lS6NgBcHiWVfKrtXz/dQrswSPBnGEH 9 | PVBmd+xHcEv4wqYnYYVtJuIcGY1P/i14h1ogpEW22cnCVYJwqmjxrD4UQ8vSeTf9 10 | YwVCbKXZ5T+eiNhfQOkCG0rClqvfvZiMgBBlLYvJrc9bELsqIbNGVD4CrkYjZGEc 11 | ToMrb/wZhqLxlcs/iXKI0/579+9vvc44/pi2MFU0nvhoSnEgopKKPy83SwBogpiH 12 | +mDcVgMiEwbXHUyNAFbz/wIDAQABo1MwUTAdBgNVHQ4EFgQULtTUM8bHdg4jhRT3 13 | 9piSLGyTSsUwHwYDVR0jBBgwFoAULtTUM8bHdg4jhRT39piSLGyTSsUwDwYDVR0T 14 | AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAchsCFqaRslfgR0r0CoOJsX/H 15 | gEfqYHaL8/yJtUUqCRgyN5VtP1Cxzet8GVsAJaIKimeUCXshhaq94JQRZqxMFxJD 16 | sD9nfWVByorkSO9tmq+1FCWRzfau1AFsJxR6J1hpIbRfcfoi9HG4QnoJwULK/2yh 17 | DEXwnmgsCHTcNnj2U9F2vMLydHEKtmtMNe/S0Z7fHw1qlmqXgXmN5a/KTEPVBQm8 18 | eM6AhNlzrMqUPc8IiiZ32eAdgR2eAuhLnTCdnHwnHafpep72hSjwoUXsDjWNA/xW 19 | /lHx4jVL8muLP5G9YHZWzJwMijLqIlnryyyi+IfKAn4ANW/k44Lxm5hmRXh4UA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /pt-node/p2p/mocks/PeerGater.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.22.1. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | 8 | peer "github.com/libp2p/go-libp2p/core/peer" 9 | ) 10 | 11 | // PeerGater is an autogenerated mock type for the PeerGater type 12 | type PeerGater struct { 13 | mock.Mock 14 | } 15 | 16 | // IsBlocked provides a mock function with given fields: _a0 17 | func (_m *PeerGater) IsBlocked(_a0 peer.ID) bool { 18 | ret := _m.Called(_a0) 19 | 20 | var r0 bool 21 | if rf, ok := ret.Get(0).(func(peer.ID) bool); ok { 22 | r0 = rf(_a0) 23 | } else { 24 | r0 = ret.Get(0).(bool) 25 | } 26 | 27 | return r0 28 | } 29 | 30 | // Update provides a mock function with given fields: _a0, _a1 31 | func (_m *PeerGater) Update(_a0 peer.ID, _a1 float64) { 32 | _m.Called(_a0, _a1) 33 | } 34 | 35 | type mockConstructorTestingTNewPeerGater interface { 36 | mock.TestingT 37 | Cleanup(func()) 38 | } 39 | 40 | // NewPeerGater creates a new instance of PeerGater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 41 | func NewPeerGater(t mockConstructorTestingTNewPeerGater) *PeerGater { 42 | mock := &PeerGater{} 43 | mock.Mock.Test(t) 44 | 45 | t.Cleanup(func() { mock.AssertExpectations(t) }) 46 | 47 | return mock 48 | } 49 | -------------------------------------------------------------------------------- /pt-bindings/gen_bindings.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | set -eu 3 | 4 | CONTRACTS_PATH="../packages/contracts-bedrock/" 5 | 6 | 7 | if [ "$#" -ne 2 ]; then 8 | echo "This script takes 2 arguments - CONTRACT_NAME PACKAGE" 9 | exit 1 10 | fi 11 | 12 | need_cmd() { 13 | if ! command -v "$1" > /dev/null 2>&1; then 14 | echo "need '$1' (command not found)" 15 | exit 1 16 | fi 17 | } 18 | 19 | need_cmd forge 20 | need_cmd abigen 21 | 22 | NAME=$1 23 | # This can handle both fully qualified syntax or just 24 | # the name of the contract. 25 | # Fully qualified: path-to-contract-file:contract-name 26 | TYPE=$(echo "$NAME" | cut -d ':' -f2) 27 | PACKAGE=$2 28 | 29 | # Convert to lower case to respect golang package naming conventions 30 | TYPE_LOWER=$(echo ${TYPE} | tr '[:upper:]' '[:lower:]') 31 | FILENAME="${TYPE_LOWER}_deployed.go" 32 | 33 | 34 | mkdir -p bin 35 | TEMP=$(mktemp -d) 36 | 37 | CWD=$(pwd) 38 | # Build contracts 39 | cd ${CONTRACTS_PATH} 40 | forge inspect ${NAME} abi > ${TEMP}/${TYPE}.abi 41 | forge inspect ${NAME} bytecode > ${TEMP}/${TYPE}.bin 42 | forge inspect ${NAME} deployedBytecode > ${CWD}/bin/${TYPE_LOWER}_deployed.hex 43 | 44 | # Run ABIGEN 45 | cd ${CWD} 46 | abigen \ 47 | --abi ${TEMP}/${TYPE}.abi \ 48 | --bin ${TEMP}/${TYPE}.bin \ 49 | --pkg ${PACKAGE} \ 50 | --type ${TYPE} \ 51 | --out ./${PACKAGE}/${TYPE_LOWER}.go 52 | -------------------------------------------------------------------------------- /pt-node/heartbeat/service_test.go: -------------------------------------------------------------------------------- 1 | package heartbeat 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/ethereum/go-ethereum/log" 14 | ) 15 | 16 | const expHeartbeat = `{ 17 | "version": "v1.2.3", 18 | "meta": "meta", 19 | "moniker": "yeet", 20 | "peerID": "1UiUfoobar", 21 | "chainID": 1234 22 | }` 23 | 24 | func TestBeat(t *testing.T) { 25 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 26 | defer cancel() 27 | 28 | reqCh := make(chan string, 2) 29 | s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 | w.WriteHeader(204) 31 | body, err := io.ReadAll(r.Body) 32 | require.NoError(t, err) 33 | reqCh <- string(body) 34 | r.Body.Close() 35 | })) 36 | defer s.Close() 37 | 38 | doneCh := make(chan struct{}) 39 | go func() { 40 | _ = Beat(ctx, log.Root(), s.URL, &Payload{ 41 | Version: "v1.2.3", 42 | Meta: "meta", 43 | Moniker: "yeet", 44 | PeerID: "1UiUfoobar", 45 | ChainID: 1234, 46 | }) 47 | doneCh <- struct{}{} 48 | }() 49 | 50 | select { 51 | case hb := <-reqCh: 52 | require.JSONEq(t, expHeartbeat, hb) 53 | cancel() 54 | <-doneCh 55 | case <-ctx.Done(): 56 | t.Fatalf("error: %v", ctx.Err()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/batch_tob_test.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "testing" 5 | 6 | fuzz "github.com/google/gofuzz" 7 | "github.com/patex-ecosystem/patex-network/pt-node/testutils/fuzzerutils" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | // FuzzBatchRoundTrip executes a fuzz test similar to TestBatchRoundTrip, which tests that arbitrary BatchData will be 12 | // encoded and decoded without loss of its original values. 13 | func FuzzBatchRoundTrip(f *testing.F) { 14 | f.Fuzz(func(t *testing.T, fuzzedData []byte) { 15 | // Create our fuzzer wrapper to generate complex values 16 | typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100).AllowUnexportedFields(true) 17 | fuzzerutils.AddFuzzerFunctions(typeProvider) 18 | 19 | // Create our batch data from fuzzed data 20 | var batchData BatchData 21 | typeProvider.Fuzz(&batchData) 22 | 23 | // Encode our batch data 24 | enc, err := batchData.MarshalBinary() 25 | require.NoError(t, err) 26 | 27 | // Decode our encoded batch data 28 | var dec BatchData 29 | err = dec.UnmarshalBinary(enc) 30 | require.NoError(t, err) 31 | 32 | // Ensure the round trip encoding of batch data did not result in data loss 33 | require.Equal(t, &batchData, &dec, "round trip batch encoding/decoding did not match original values") 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /pt-node/p2p/store/peer_ban_book_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | ds "github.com/ipfs/go-datastore" 10 | "github.com/ipfs/go-datastore/sync" 11 | "github.com/patex-ecosystem/patex-network/pt-node/testlog" 12 | "github.com/patex-ecosystem/patex-network/pt-service/clock" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestGetUnknownPeerBan(t *testing.T) { 17 | book := createMemoryPeerBanBook(t) 18 | defer book.Close() 19 | exp, err := book.GetPeerBanExpiration("a") 20 | require.Same(t, UnknownBanErr, err) 21 | require.Equal(t, time.Time{}, exp) 22 | } 23 | 24 | func TestRoundTripPeerBan(t *testing.T) { 25 | book := createMemoryPeerBanBook(t) 26 | defer book.Close() 27 | expiry := time.Unix(2484924, 0) 28 | require.NoError(t, book.SetPeerBanExpiration("a", expiry)) 29 | result, err := book.GetPeerBanExpiration("a") 30 | require.NoError(t, err) 31 | require.Equal(t, result, expiry) 32 | } 33 | 34 | func createMemoryPeerBanBook(t *testing.T) *peerBanBook { 35 | store := sync.MutexWrap(ds.NewMapDatastore()) 36 | logger := testlog.Logger(t, log.LvlInfo) 37 | c := clock.NewDeterministicClock(time.UnixMilli(100)) 38 | book, err := newPeerBanBook(context.Background(), logger, c, store) 39 | require.NoError(t, err) 40 | return book 41 | } 42 | -------------------------------------------------------------------------------- /pt-node/p2p/gating/blocking.go: -------------------------------------------------------------------------------- 1 | package gating 2 | 3 | import ( 4 | "net" 5 | 6 | ds "github.com/ipfs/go-datastore" 7 | "github.com/libp2p/go-libp2p/core/connmgr" 8 | "github.com/libp2p/go-libp2p/core/peer" 9 | "github.com/libp2p/go-libp2p/p2p/net/conngater" 10 | ) 11 | 12 | //go:generate mockery --name BlockingConnectionGater --output mocks/ --with-expecter=true 13 | type BlockingConnectionGater interface { 14 | connmgr.ConnectionGater 15 | 16 | // BlockPeer adds a peer to the set of blocked peers. 17 | // Note: active connections to the peer are not automatically closed. 18 | BlockPeer(p peer.ID) error 19 | UnblockPeer(p peer.ID) error 20 | ListBlockedPeers() []peer.ID 21 | 22 | // BlockAddr adds an IP address to the set of blocked addresses. 23 | // Note: active connections to the IP address are not automatically closed. 24 | BlockAddr(ip net.IP) error 25 | UnblockAddr(ip net.IP) error 26 | ListBlockedAddrs() []net.IP 27 | 28 | // BlockSubnet adds an IP subnet to the set of blocked addresses. 29 | // Note: active connections to the IP subnet are not automatically closed. 30 | BlockSubnet(ipnet *net.IPNet) error 31 | UnblockSubnet(ipnet *net.IPNet) error 32 | ListBlockedSubnets() []*net.IPNet 33 | } 34 | 35 | func NewBlockingConnectionGater(store ds.Batching) (BlockingConnectionGater, error) { 36 | return conngater.NewBasicConnectionGater(store) 37 | } 38 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/testdata/witness.txt: -------------------------------------------------------------------------------- 1 | MSG|0x4200000000000000000000000000000000000007|cafa81dc000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a4cbd4ece900000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1000000000000000000000000420000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000019bd000000000000000000000000000000000000000000000000000000000000000e4a9f9e675000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000000994206dfe8de6ec6920ff4d779b0d950605fb53000000000000000000000000e3a44dd2a8c108be56a78635121ec914074da16d000000000000000000000000e3a44dd2a8c108be56a78635121ec914074da16d0000000000000000000000000000000000000000000001b0ac98ab3858d7547800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 2 | MSG|0x8B1d477410344785ff1DF52500032E6D5f532EE4|cafa81dc000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030420690000000000000000000000000000000000000000000000000000000000 3 | ETH|0x6340d44c5174588B312F545eEC4a42f8a514eF50 -------------------------------------------------------------------------------- /pt-node/p2p/store/ip_ban_book_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "testing" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | ds "github.com/ipfs/go-datastore" 11 | "github.com/ipfs/go-datastore/sync" 12 | "github.com/patex-ecosystem/patex-network/pt-node/testlog" 13 | "github.com/patex-ecosystem/patex-network/pt-service/clock" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestGetUnknownIPBan(t *testing.T) { 18 | book := createMemoryIPBanBook(t) 19 | defer book.Close() 20 | exp, err := book.GetIPBanExpiration(net.IPv4(1, 2, 3, 4)) 21 | require.Same(t, UnknownBanErr, err) 22 | require.Equal(t, time.Time{}, exp) 23 | } 24 | 25 | func TestRoundTripIPBan(t *testing.T) { 26 | book := createMemoryIPBanBook(t) 27 | defer book.Close() 28 | expiry := time.Unix(2484924, 0) 29 | ip := net.IPv4(1, 2, 3, 4) 30 | require.NoError(t, book.SetIPBanExpiration(ip, expiry)) 31 | result, err := book.GetIPBanExpiration(ip) 32 | require.NoError(t, err) 33 | require.Equal(t, result, expiry) 34 | } 35 | 36 | func createMemoryIPBanBook(t *testing.T) *ipBanBook { 37 | store := sync.MutexWrap(ds.NewMapDatastore()) 38 | logger := testlog.Logger(t, log.LvlInfo) 39 | c := clock.NewDeterministicClock(time.UnixMilli(100)) 40 | book, err := newIPBanBook(context.Background(), logger, c, store) 41 | require.NoError(t, err) 42 | return book 43 | } 44 | -------------------------------------------------------------------------------- /pt-chain-ops/genesis/testdata/test-deploy-config-devnet-l1.json: -------------------------------------------------------------------------------- 1 | { 2 | "l1StartingBlockTag": "earliest", 3 | "l1ChainID": 900, 4 | "l2ChainID": 901, 5 | "l2BlockTime": 2, 6 | 7 | "maxSequencerDrift": 100, 8 | "sequencerWindowSize": 4, 9 | "channelTimeout": 40, 10 | "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", 11 | "batchInboxAddress": "0xff00000000000000000000000000000000000000", 12 | "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", 13 | 14 | "l2OutputOracleSubmissionInterval": 20, 15 | "l2OutputOracleStartingTimestamp": -1, 16 | "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 17 | "l2OutputOracleChallenger": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 18 | 19 | "l1BlockTime": 15, 20 | "cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467", 21 | 22 | "baseFeeVaultRecipient": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", 23 | "l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", 24 | "sequencerFeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", 25 | "l1ERC721BridgeProxy": "0xff000000000000000000000000000000000000ff", 26 | "l1StandardBridgeProxy": "0xff000000000000000000000000000000000000fd", 27 | "l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd", 28 | 29 | "deploymentWaitConfirmations": 1, 30 | "fundDevAccounts": true 31 | } 32 | -------------------------------------------------------------------------------- /pt-node/sources/rollupclient.go: -------------------------------------------------------------------------------- 1 | package sources 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | 8 | "github.com/patex-ecosystem/patex-network/pt-node/client" 9 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 10 | "github.com/patex-ecosystem/patex-network/pt-node/rollup" 11 | ) 12 | 13 | type RollupClient struct { 14 | rpc client.RPC 15 | } 16 | 17 | func NewRollupClient(rpc client.RPC) *RollupClient { 18 | return &RollupClient{rpc} 19 | } 20 | 21 | func (r *RollupClient) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) { 22 | var output *eth.OutputResponse 23 | err := r.rpc.CallContext(ctx, &output, "patex_outputAtBlock", hexutil.Uint64(blockNum)) 24 | return output, err 25 | } 26 | 27 | func (r *RollupClient) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) { 28 | var output *eth.SyncStatus 29 | err := r.rpc.CallContext(ctx, &output, "patex_syncStatus") 30 | return output, err 31 | } 32 | 33 | func (r *RollupClient) RollupConfig(ctx context.Context) (*rollup.Config, error) { 34 | var output *rollup.Config 35 | err := r.rpc.CallContext(ctx, &output, "patex_rollupConfig") 36 | return output, err 37 | } 38 | 39 | func (r *RollupClient) Version(ctx context.Context) (string, error) { 40 | var output string 41 | err := r.rpc.CallContext(ctx, &output, "patex_version") 42 | return output, err 43 | } 44 | -------------------------------------------------------------------------------- /pt-node/eth/transactions.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | ) 10 | 11 | // EncodeTransactions encodes a list of transactions into opaque transactions. 12 | func EncodeTransactions(elems []*types.Transaction) ([]hexutil.Bytes, error) { 13 | out := make([]hexutil.Bytes, len(elems)) 14 | for i, el := range elems { 15 | dat, err := el.MarshalBinary() 16 | if err != nil { 17 | return nil, fmt.Errorf("failed to marshal tx %d: %w", i, err) 18 | } 19 | out[i] = dat 20 | } 21 | return out, nil 22 | } 23 | 24 | // DecodeTransactions decodes a list of opaque transactions into transactions. 25 | func DecodeTransactions(data []hexutil.Bytes) ([]*types.Transaction, error) { 26 | dest := make([]*types.Transaction, len(data)) 27 | for i := range dest { 28 | var x types.Transaction 29 | if err := x.UnmarshalBinary(data[i]); err != nil { 30 | return nil, fmt.Errorf("failed to unmarshal tx %d: %w", i, err) 31 | } 32 | dest[i] = &x 33 | } 34 | return dest, nil 35 | } 36 | 37 | // TransactionsToHashes computes the transaction-hash for every transaction in the input. 38 | func TransactionsToHashes(elems []*types.Transaction) []common.Hash { 39 | out := make([]common.Hash, len(elems)) 40 | for i, el := range elems { 41 | out[i] = el.Hash() 42 | } 43 | return out 44 | } 45 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/deposit_source.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/crypto" 8 | ) 9 | 10 | type UserDepositSource struct { 11 | L1BlockHash common.Hash 12 | LogIndex uint64 13 | } 14 | 15 | const ( 16 | UserDepositSourceDomain = 0 17 | L1InfoDepositSourceDomain = 1 18 | ) 19 | 20 | func (dep *UserDepositSource) SourceHash() common.Hash { 21 | var input [32 * 2]byte 22 | copy(input[:32], dep.L1BlockHash[:]) 23 | binary.BigEndian.PutUint64(input[32*2-8:], dep.LogIndex) 24 | depositIDHash := crypto.Keccak256Hash(input[:]) 25 | var domainInput [32 * 2]byte 26 | binary.BigEndian.PutUint64(domainInput[32-8:32], UserDepositSourceDomain) 27 | copy(domainInput[32:], depositIDHash[:]) 28 | return crypto.Keccak256Hash(domainInput[:]) 29 | } 30 | 31 | type L1InfoDepositSource struct { 32 | L1BlockHash common.Hash 33 | SeqNumber uint64 34 | } 35 | 36 | func (dep *L1InfoDepositSource) SourceHash() common.Hash { 37 | var input [32 * 2]byte 38 | copy(input[:32], dep.L1BlockHash[:]) 39 | binary.BigEndian.PutUint64(input[32*2-8:], dep.SeqNumber) 40 | depositIDHash := crypto.Keccak256Hash(input[:]) 41 | 42 | var domainInput [32 * 2]byte 43 | binary.BigEndian.PutUint64(domainInput[32-8:32], L1InfoDepositSourceDomain) 44 | copy(domainInput[32:], depositIDHash[:]) 45 | return crypto.Keccak256Hash(domainInput[:]) 46 | } 47 | -------------------------------------------------------------------------------- /pt-batcher/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/urfave/cli" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | "github.com/patex-ecosystem/patex-network/pt-batcher/batcher" 11 | "github.com/patex-ecosystem/patex-network/pt-batcher/cmd/doc" 12 | "github.com/patex-ecosystem/patex-network/pt-batcher/flags" 13 | ptlog "github.com/patex-ecosystem/patex-network/pt-service/log" 14 | ) 15 | 16 | var ( 17 | Version = "v0.10.14" 18 | GitCommit = "" 19 | GitDate = "" 20 | ) 21 | 22 | func main() { 23 | ptlog.SetupDefaults() 24 | 25 | app := cli.NewApp() 26 | app.Flags = flags.Flags 27 | app.Version = fmt.Sprintf("%s-%s-%s", Version, GitCommit, GitDate) 28 | app.Name = "pt-batcher" 29 | app.Usage = "Batch Submitter Service" 30 | app.Description = "Service for generating and submitting L2 tx batches to L1" 31 | app.Action = curryMain(Version) 32 | app.Commands = []cli.Command{ 33 | { 34 | Name: "doc", 35 | Subcommands: doc.Subcommands, 36 | }, 37 | } 38 | 39 | err := app.Run(os.Args) 40 | if err != nil { 41 | log.Crit("Application failed", "message", err) 42 | } 43 | } 44 | 45 | // curryMain transforms the batcher.Main function into an app.Action 46 | // This is done to capture the Version of the batcher. 47 | func curryMain(version string) func(ctx *cli.Context) error { 48 | return func(ctx *cli.Context) error { 49 | return batcher.Main(version, ctx) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pt-batcher/metrics/noop.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 5 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 6 | ptmetrics "github.com/patex-ecosystem/patex-network/pt-service/metrics" 7 | txmetrics "github.com/patex-ecosystem/patex-network/pt-service/txmgr/metrics" 8 | ) 9 | 10 | type noptmetrics struct { 11 | ptmetrics.NoopRefMetrics 12 | txmetrics.NoopTxMetrics 13 | } 14 | 15 | var Noptmetrics Metricer = new(noptmetrics) 16 | 17 | func (*noptmetrics) Document() []ptmetrics.DocumentedMetric { return nil } 18 | 19 | func (*noptmetrics) RecordInfo(version string) {} 20 | func (*noptmetrics) RecordUp() {} 21 | 22 | func (*noptmetrics) RecordLatestL1Block(l1ref eth.L1BlockRef) {} 23 | func (*noptmetrics) RecordL2BlocksLoaded(eth.L2BlockRef) {} 24 | func (*noptmetrics) RecordChannelOpened(derive.ChannelID, int) {} 25 | func (*noptmetrics) RecordL2BlocksAdded(eth.L2BlockRef, int, int, int, int) {} 26 | 27 | func (*noptmetrics) RecordChannelClosed(derive.ChannelID, int, int, int, int, error) {} 28 | 29 | func (*noptmetrics) RecordChannelFullySubmitted(derive.ChannelID) {} 30 | func (*noptmetrics) RecordChannelTimedOut(derive.ChannelID) {} 31 | 32 | func (*noptmetrics) RecordBatchTxSubmitted() {} 33 | func (*noptmetrics) RecordBatchTxSuccess() {} 34 | func (*noptmetrics) RecordBatchTxFailed() {} 35 | -------------------------------------------------------------------------------- /specs/batcher.md: -------------------------------------------------------------------------------- 1 | 2 | # Batch Submitter 3 | 4 | The batch submitter, also referred to as the batcher, is the entity submitting the L2 sequencer data to L1, 5 | to make it available for verifiers. 6 | 7 | [derivation spec]: derivation.md 8 | 9 | The format of the data transactions is defined in the [derivation spec]: 10 | the data is constructed from L2 blocks in the reverse order as it is derived from data into L2 blocks. 11 | 12 | The timing, operation and transaction signing is implementation-specific: any data can be submitted at any time, 13 | but only the data that matches the [derivation spec] rules will be valid from the verifier perspective. 14 | 15 | The most minimal batcher implementation can be defined as a loop of the following operations: 16 | 17 | 1. See if the `unsafe` L2 block number is past the `safe` block number: `unsafe` data needs to be submitted. 18 | 2. Iterate over all unsafe L2 blocks, skip any that were previously submitted. 19 | 3. Open a channel, buffer all the L2 block data to be submitted, 20 | while applying the encoding and compression as defined in the [derivation spec]. 21 | 4. Pull frames from the channel to fill data transactions with, until the channel is empty. 22 | 5. Submit the data transactions to L1 23 | 24 | The L2 view of safe/unsafe does not instantly update after data is submitted, nor when it gets confirmed on L1, 25 | so special care may have to be taken to not duplicate data submissions. 26 | -------------------------------------------------------------------------------- /pt-service/enum/enum_test.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | // TestEnumString_MultipleInputs tests the EnumString function with multiple inputs. 10 | func TestEnumString_MultipleInputs(t *testing.T) { 11 | require.Equal(t, "a, b, c", EnumString([]Stringered{"a", "b", "c"})) 12 | } 13 | 14 | // TestEnumString_SingleString tests the EnumString function with a single input. 15 | func TestEnumString_SingleString(t *testing.T) { 16 | require.Equal(t, "a", EnumString([]Stringered{"a"})) 17 | } 18 | 19 | // TestEnumString_EmptyString tests the EnumString function with no inputs. 20 | func TestEnumString_EmptyString(t *testing.T) { 21 | require.Equal(t, "", EnumString([]Stringered{})) 22 | } 23 | 24 | // TestStringeredList_MultipleInputs tests the StringeredList function with multiple inputs. 25 | func TestStringeredList_MultipleInputs(t *testing.T) { 26 | require.Equal(t, []Stringered{"a", "b", "c"}, StringeredList([]string{"a", "b", "c"})) 27 | } 28 | 29 | // TestStringeredList_SingleString tests the StringeredList function with a single input. 30 | func TestStringeredList_SingleString(t *testing.T) { 31 | require.Equal(t, []Stringered{"a"}, StringeredList([]string{"a"})) 32 | } 33 | 34 | // TestStringeredList_EmptyString tests the StringeredList function with no inputs. 35 | func TestStringeredList_EmptyString(t *testing.T) { 36 | require.Equal(t, []Stringered(nil), StringeredList([]string{})) 37 | } 38 | -------------------------------------------------------------------------------- /pt-bindings/predeploys/addresses_test.go: -------------------------------------------------------------------------------- 1 | package predeploys 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/patex-ecosystem/patex-network/pt-bindings/bindings" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestGethAddresses(t *testing.T) { 14 | // We test if the addresses in geth match those in pt-bindings, to avoid an import-cycle: 15 | // we import geth in the monorepo, and do not want to import pt-bindings into geth. 16 | require.Equal(t, L1BlockAddr, types.L1BlockAddr) 17 | } 18 | 19 | // TestL1BlockSlots ensures that the storage layout of the L1Block 20 | // contract matches the hardcoded values in `pt-geth`. 21 | func TestL1BlockSlots(t *testing.T) { 22 | layout, err := bindings.GetStorageLayout("L1Block") 23 | require.NoError(t, err) 24 | 25 | var l1BaseFeeSlot, overHeadSlot, scalarSlot common.Hash 26 | for _, entry := range layout.Storage { 27 | switch entry.Label { 28 | case "l1FeeOverhead": 29 | overHeadSlot = common.BigToHash(big.NewInt(int64(entry.Slot))) 30 | case "l1FeeScalar": 31 | scalarSlot = common.BigToHash(big.NewInt(int64(entry.Slot))) 32 | case "basefee": 33 | l1BaseFeeSlot = common.BigToHash(big.NewInt(int64(entry.Slot))) 34 | } 35 | } 36 | 37 | require.Equal(t, types.OverheadSlot, overHeadSlot) 38 | require.Equal(t, types.ScalarSlot, scalarSlot) 39 | require.Equal(t, types.L1BaseFeeSlot, l1BaseFeeSlot) 40 | } 41 | -------------------------------------------------------------------------------- /pt-node/testutils/mock_l1.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 8 | ) 9 | 10 | type MockL1Source struct { 11 | MockEthClient 12 | } 13 | 14 | func (m *MockL1Source) L1BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L1BlockRef, error) { 15 | out := m.Mock.MethodCalled("L1BlockRefByLabel", label) 16 | return out[0].(eth.L1BlockRef), *out[1].(*error) 17 | } 18 | 19 | func (m *MockL1Source) ExpectL1BlockRefByLabel(label eth.BlockLabel, ref eth.L1BlockRef, err error) { 20 | m.Mock.On("L1BlockRefByLabel", label).Once().Return(ref, &err) 21 | } 22 | 23 | func (m *MockL1Source) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) { 24 | out := m.Mock.MethodCalled("L1BlockRefByNumber", num) 25 | return out[0].(eth.L1BlockRef), *out[1].(*error) 26 | } 27 | 28 | func (m *MockL1Source) ExpectL1BlockRefByNumber(num uint64, ref eth.L1BlockRef, err error) { 29 | m.Mock.On("L1BlockRefByNumber", num).Once().Return(ref, &err) 30 | } 31 | 32 | func (m *MockL1Source) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth.L1BlockRef, error) { 33 | out := m.Mock.MethodCalled("L1BlockRefByHash", hash) 34 | return out[0].(eth.L1BlockRef), *out[1].(*error) 35 | } 36 | 37 | func (m *MockL1Source) ExpectL1BlockRefByHash(hash common.Hash, ref eth.L1BlockRef, err error) { 38 | m.Mock.On("L1BlockRefByHash", hash).Once().Return(ref, &err) 39 | } 40 | -------------------------------------------------------------------------------- /pt-batcher/Makefile: -------------------------------------------------------------------------------- 1 | GITCOMMIT := $(shell git rev-parse HEAD) 2 | GITDATE := $(shell git show -s --format='%ct') 3 | VERSION := v0.0.0 4 | 5 | LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) 6 | LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) 7 | LDFLAGSSTRING +=-X main.Version=$(VERSION) 8 | LDFLAGS := -ldflags "$(LDFLAGSSTRING)" 9 | 10 | pt-batcher: 11 | env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/pt-batcher ./cmd 12 | 13 | clean: 14 | rm bin/pt-batcher 15 | 16 | test: 17 | go test -v ./... 18 | 19 | lint: 20 | golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" 21 | 22 | fuzz: 23 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelConfig_CheckTimeout ./batcher 24 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationZero ./batcher 25 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutMaxChannelDuration ./batcher 26 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutZeroMaxChannelDuration ./batcher 27 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelCloseTimeout ./batcher 28 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelZeroCloseTimeout ./batcher 29 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowClose ./batcher 30 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowZeroTimeoutClose ./batcher 31 | 32 | .PHONY: \ 33 | pt-batcher \ 34 | clean \ 35 | test \ 36 | lint \ 37 | fuzz 38 | -------------------------------------------------------------------------------- /pt-node/p2p/gating/metrics.go: -------------------------------------------------------------------------------- 1 | package gating 2 | 3 | import ( 4 | "github.com/libp2p/go-libp2p/core/network" 5 | "github.com/libp2p/go-libp2p/core/peer" 6 | "github.com/multiformats/go-multiaddr" 7 | ) 8 | 9 | type ConnectionGaterMetrics interface { 10 | RecordDial(allow bool) 11 | RecordAccept(allow bool) 12 | } 13 | 14 | type MeteredConnectionGater struct { 15 | BlockingConnectionGater 16 | m ConnectionGaterMetrics 17 | } 18 | 19 | func AddMetering(gater BlockingConnectionGater, m ConnectionGaterMetrics) *MeteredConnectionGater { 20 | return &MeteredConnectionGater{BlockingConnectionGater: gater, m: m} 21 | } 22 | 23 | func (g *MeteredConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) { 24 | allow = g.BlockingConnectionGater.InterceptPeerDial(p) 25 | g.m.RecordDial(allow) 26 | return allow 27 | } 28 | 29 | func (g *MeteredConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) { 30 | allow = g.BlockingConnectionGater.InterceptAddrDial(id, ma) 31 | g.m.RecordDial(allow) 32 | return allow 33 | } 34 | 35 | func (g *MeteredConnectionGater) InterceptAccept(mas network.ConnMultiaddrs) (allow bool) { 36 | allow = g.BlockingConnectionGater.InterceptAccept(mas) 37 | g.m.RecordAccept(allow) 38 | return allow 39 | } 40 | 41 | func (g *MeteredConnectionGater) InterceptSecured(dir network.Direction, id peer.ID, mas network.ConnMultiaddrs) (allow bool) { 42 | allow = g.BlockingConnectionGater.InterceptSecured(dir, id, mas) 43 | g.m.RecordAccept(allow) 44 | return allow 45 | } 46 | -------------------------------------------------------------------------------- /pt-node/testutils/deposits.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "math/big" 5 | "math/rand" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | ) 10 | 11 | // Returns a DepositEvent customized on the basis of the id parameter. 12 | func GenerateDeposit(sourceHash common.Hash, rng *rand.Rand) *types.DepositTx { 13 | dataLen := rng.Int63n(10_000) 14 | data := make([]byte, dataLen) 15 | rng.Read(data) 16 | 17 | var to *common.Address 18 | if rng.Intn(2) == 0 { 19 | x := RandomAddress(rng) 20 | to = &x 21 | } 22 | var mint *big.Int 23 | if rng.Intn(2) == 0 { 24 | mint = RandomETH(rng, 200) 25 | } 26 | 27 | dep := &types.DepositTx{ 28 | SourceHash: sourceHash, 29 | From: RandomAddress(rng), 30 | To: to, 31 | Value: RandomETH(rng, 200), 32 | Gas: uint64(rng.Int63n(10 * 1e6)), // 10 M gas max 33 | Data: data, 34 | Mint: mint, 35 | IsSystemTransaction: false, 36 | } 37 | return dep 38 | } 39 | 40 | // Generates an EVM log entry with the given topics and data. 41 | func GenerateLog(addr common.Address, topics []common.Hash, data []byte) *types.Log { 42 | return &types.Log{ 43 | Address: addr, 44 | Topics: topics, 45 | Data: data, 46 | Removed: false, 47 | 48 | // ignored (zeroed): 49 | BlockNumber: 0, 50 | TxHash: common.Hash{}, 51 | TxIndex: 0, 52 | BlockHash: common.Hash{}, 53 | Index: 0, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pt-service/tls/tlsinfo.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | "context" 5 | "crypto/x509" 6 | "net/http" 7 | ) 8 | 9 | // PeerTLSInfo contains request-scoped peer certificate data 10 | // It can be used by downstream http.Handlers to authorize access for TLS-authenticated clients 11 | type PeerTLSInfo struct { 12 | LeafCertificate *x509.Certificate 13 | } 14 | 15 | type peerTLSInfoContextKey struct{} 16 | 17 | // NewPeerTLSMiddleware returns an http.Handler that extracts the peer's certificate data into PeerTLSInfo and attaches it to the request-scoped context. 18 | // PeerTLSInfo will only be populated if the http.Server is listening with ListenAndServeTLS 19 | // This is useful for ethereum-go/rpc endpoints because the http.Request object isn't accessible in the registered service. 20 | func NewPeerTLSMiddleware(next http.Handler) http.Handler { 21 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | peerTlsInfo := PeerTLSInfo{} 23 | if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { 24 | peerTlsInfo.LeafCertificate = r.TLS.PeerCertificates[0] 25 | } 26 | ctx := context.WithValue(r.Context(), peerTLSInfoContextKey{}, peerTlsInfo) 27 | next.ServeHTTP(w, r.WithContext(ctx)) 28 | }) 29 | } 30 | 31 | // PeerTLSInfoFromContext extracts PeerTLSInfo from the context 32 | // Result will only be populated if NewPeerTLSMiddleware has been added to the handler stack. 33 | func PeerTLSInfoFromContext(ctx context.Context) PeerTLSInfo { 34 | info, _ := ctx.Value(peerTLSInfoContextKey{}).(PeerTLSInfo) 35 | return info 36 | } 37 | -------------------------------------------------------------------------------- /pt-service/pprof/cli.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | 7 | ptservice "github.com/patex-ecosystem/patex-network/pt-service" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | const ( 12 | EnabledFlagName = "pprof.enabled" 13 | ListenAddrFlagName = "pprof.addr" 14 | PortFlagName = "pprof.port" 15 | ) 16 | 17 | func CLIFlags(envPrefix string) []cli.Flag { 18 | return []cli.Flag{ 19 | cli.BoolFlag{ 20 | Name: EnabledFlagName, 21 | Usage: "Enable the pprof server", 22 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "PPROF_ENABLED"), 23 | }, 24 | cli.StringFlag{ 25 | Name: ListenAddrFlagName, 26 | Usage: "pprof listening address", 27 | Value: "0.0.0.0", 28 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "PPROF_ADDR"), 29 | }, 30 | cli.IntFlag{ 31 | Name: PortFlagName, 32 | Usage: "pprof listening port", 33 | Value: 6060, 34 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "PPROF_PORT"), 35 | }, 36 | } 37 | } 38 | 39 | type CLIConfig struct { 40 | Enabled bool 41 | ListenAddr string 42 | ListenPort int 43 | } 44 | 45 | func (m CLIConfig) Check() error { 46 | if !m.Enabled { 47 | return nil 48 | } 49 | 50 | if m.ListenPort < 0 || m.ListenPort > math.MaxUint16 { 51 | return errors.New("invalid pprof port") 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func ReadCLIConfig(ctx *cli.Context) CLIConfig { 58 | return CLIConfig{ 59 | Enabled: ctx.GlobalBool(EnabledFlagName), 60 | ListenAddr: ctx.GlobalString(ListenAddrFlagName), 61 | ListenPort: ctx.GlobalInt(PortFlagName), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pt-node/cmd/doc/cmd.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/olekukonko/tablewriter" 10 | "github.com/patex-ecosystem/patex-network/pt-node/metrics" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | var Subcommands = cli.Commands{ 15 | { 16 | Name: "metrics", 17 | Usage: "Dumps a list of supported metrics to stdout", 18 | Flags: []cli.Flag{ 19 | cli.StringFlag{ 20 | Name: "format", 21 | Value: "markdown", 22 | Usage: "Output format (json|markdown)", 23 | }, 24 | }, 25 | Action: func(ctx *cli.Context) error { 26 | m := metrics.NewMetrics("default") 27 | supportedMetrics := m.Document() 28 | format := ctx.String("format") 29 | 30 | if format != "markdown" && format != "json" { 31 | return fmt.Errorf("invalid format: %s", format) 32 | } 33 | 34 | if format == "json" { 35 | enc := json.NewEncoder(os.Stdout) 36 | return enc.Encode(supportedMetrics) 37 | } 38 | 39 | table := tablewriter.NewWriter(os.Stdout) 40 | table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) 41 | table.SetCenterSeparator("|") 42 | table.SetAutoWrapText(false) 43 | table.SetHeader([]string{"Metric", "Description", "Labels", "Type"}) 44 | var data [][]string 45 | for _, metric := range supportedMetrics { 46 | labels := strings.Join(metric.Labels, ",") 47 | data = append(data, []string{metric.Name, metric.Help, labels, metric.Type}) 48 | } 49 | table.AppendBulk(data) 50 | table.Render() 51 | return nil 52 | }, 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /pt-batcher/cmd/doc/cmd.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/olekukonko/tablewriter" 10 | "github.com/patex-ecosystem/patex-network/pt-batcher/metrics" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | var Subcommands = cli.Commands{ 15 | { 16 | Name: "metrics", 17 | Usage: "Dumps a list of supported metrics to stdout", 18 | Flags: []cli.Flag{ 19 | cli.StringFlag{ 20 | Name: "format", 21 | Value: "markdown", 22 | Usage: "Output format (json|markdown)", 23 | }, 24 | }, 25 | Action: func(ctx *cli.Context) error { 26 | m := metrics.NewMetrics("default") 27 | supportedMetrics := m.Document() 28 | format := ctx.String("format") 29 | 30 | if format != "markdown" && format != "json" { 31 | return fmt.Errorf("invalid format: %s", format) 32 | } 33 | 34 | if format == "json" { 35 | enc := json.NewEncoder(os.Stdout) 36 | return enc.Encode(supportedMetrics) 37 | } 38 | 39 | table := tablewriter.NewWriter(os.Stdout) 40 | table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) 41 | table.SetCenterSeparator("|") 42 | table.SetAutoWrapText(false) 43 | table.SetHeader([]string{"Metric", "Description", "Labels", "Type"}) 44 | var data [][]string 45 | for _, metric := range supportedMetrics { 46 | labels := strings.Join(metric.Labels, ",") 47 | data = append(data, []string{metric.Name, metric.Help, labels, metric.Type}) 48 | } 49 | table.AppendBulk(data) 50 | table.Render() 51 | return nil 52 | }, 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /pt-chain-ops/genesis/setters_test.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/rawdb" 9 | "github.com/ethereum/go-ethereum/core/state" 10 | "github.com/ethereum/go-ethereum/trie" 11 | "github.com/patex-ecosystem/patex-network/pt-bindings/predeploys" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestWipePredeployStorage(t *testing.T) { 16 | rawDB := rawdb.NewMemoryDatabase() 17 | rawStateDB := state.NewDatabaseWithConfig(rawDB, &trie.Config{ 18 | Preimages: true, 19 | Cache: 1024, 20 | }) 21 | stateDB, err := state.New(common.Hash{}, rawStateDB, nil) 22 | require.NoError(t, err) 23 | 24 | storeVal := common.Hash{31: 0xff} 25 | 26 | for _, addr := range predeploys.Predeploys { 27 | a := *addr 28 | stateDB.SetState(a, storeVal, storeVal) 29 | stateDB.SetBalance(a, big.NewInt(99)) 30 | stateDB.SetNonce(a, 99) 31 | } 32 | 33 | root, err := stateDB.Commit(false) 34 | require.NoError(t, err) 35 | 36 | err = stateDB.Database().TrieDB().Commit(root, true) 37 | require.NoError(t, err) 38 | 39 | require.NoError(t, WipePredeployStorage(stateDB)) 40 | 41 | for _, addr := range predeploys.Predeploys { 42 | a := *addr 43 | if FrozenStoragePredeploys[a] { 44 | require.Equal(t, storeVal, stateDB.GetState(a, storeVal)) 45 | } else { 46 | require.Equal(t, common.Hash{}, stateDB.GetState(a, storeVal)) 47 | } 48 | require.Equal(t, big.NewInt(99), stateDB.GetBalance(a)) 49 | require.Equal(t, uint64(99), stateDB.GetNonce(a)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pt-service/rpc/server_test.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "math/rand" 7 | "net/http" 8 | "testing" 9 | 10 | "github.com/ethereum/go-ethereum/rpc" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | type testAPI struct{} 15 | 16 | func (t *testAPI) Frobnicate(n int) int { 17 | return n * 2 18 | } 19 | 20 | func TestBaseServer(t *testing.T) { 21 | appVersion := "test" 22 | server := NewServer( 23 | "127.0.0.1", 24 | 10000+rand.Intn(22768), 25 | appVersion, 26 | WithAPIs([]rpc.API{ 27 | { 28 | Namespace: "test", 29 | Service: new(testAPI), 30 | }, 31 | }), 32 | ) 33 | require.NoError(t, server.Start()) 34 | defer func() { 35 | _ = server.Stop() 36 | }() 37 | 38 | rpcClient, err := rpc.Dial(fmt.Sprintf("http://%s", server.endpoint)) 39 | require.NoError(t, err) 40 | 41 | t.Run("supports GET /healthz", func(t *testing.T) { 42 | res, err := http.Get(fmt.Sprintf("http://%s/healthz", server.endpoint)) 43 | require.NoError(t, err) 44 | defer res.Body.Close() 45 | body, err := io.ReadAll(res.Body) 46 | require.NoError(t, err) 47 | require.EqualValues(t, fmt.Sprintf("{\"version\":\"%s\"}\n", appVersion), string(body)) 48 | }) 49 | 50 | t.Run("supports health_status", func(t *testing.T) { 51 | var res string 52 | require.NoError(t, rpcClient.Call(&res, "health_status")) 53 | require.Equal(t, appVersion, res) 54 | }) 55 | 56 | t.Run("supports additional RPC APIs", func(t *testing.T) { 57 | var res int 58 | require.NoError(t, rpcClient.Call(&res, "test_frobnicate", 2)) 59 | require.Equal(t, 4, res) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /pt-node/testutils/mock_engine.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 7 | ) 8 | 9 | type MockEngine struct { 10 | MockL2Client 11 | } 12 | 13 | func (m *MockEngine) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayload, error) { 14 | out := m.Mock.MethodCalled("GetPayload", payloadId) 15 | return out[0].(*eth.ExecutionPayload), *out[1].(*error) 16 | } 17 | 18 | func (m *MockEngine) ExpectGetPayload(payloadId eth.PayloadID, payload *eth.ExecutionPayload, err error) { 19 | m.Mock.On("GetPayload", payloadId).Once().Return(payload, &err) 20 | } 21 | 22 | func (m *MockEngine) ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) { 23 | out := m.Mock.MethodCalled("ForkchoiceUpdate", state, attr) 24 | return out[0].(*eth.ForkchoiceUpdatedResult), *out[1].(*error) 25 | } 26 | 27 | func (m *MockEngine) ExpectForkchoiceUpdate(state *eth.ForkchoiceState, attr *eth.PayloadAttributes, result *eth.ForkchoiceUpdatedResult, err error) { 28 | m.Mock.On("ForkchoiceUpdate", state, attr).Once().Return(result, &err) 29 | } 30 | 31 | func (m *MockEngine) NewPayload(ctx context.Context, payload *eth.ExecutionPayload) (*eth.PayloadStatusV1, error) { 32 | out := m.Mock.MethodCalled("NewPayload", payload) 33 | return out[0].(*eth.PayloadStatusV1), *out[1].(*error) 34 | } 35 | 36 | func (m *MockEngine) ExpectNewPayload(payload *eth.ExecutionPayload, result *eth.PayloadStatusV1, err error) { 37 | m.Mock.On("NewPayload", payload).Once().Return(result, &err) 38 | } 39 | -------------------------------------------------------------------------------- /pt-node/cmd/stateviz/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /pt-node/p2p/gating/scoring.go: -------------------------------------------------------------------------------- 1 | package gating 2 | 3 | import ( 4 | "github.com/libp2p/go-libp2p/core/network" 5 | "github.com/libp2p/go-libp2p/core/peer" 6 | "github.com/multiformats/go-multiaddr" 7 | ) 8 | 9 | //go:generate mockery --name Scores --output mocks/ --with-expecter=true 10 | type Scores interface { 11 | GetPeerScore(id peer.ID) (float64, error) 12 | } 13 | 14 | // ScoringConnectionGater enhances a ConnectionGater by enforcing a minimum score for peer connections 15 | type ScoringConnectionGater struct { 16 | BlockingConnectionGater 17 | scores Scores 18 | minScore float64 19 | } 20 | 21 | func AddScoring(gater BlockingConnectionGater, scores Scores, minScore float64) *ScoringConnectionGater { 22 | return &ScoringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore} 23 | } 24 | 25 | func (g *ScoringConnectionGater) checkScore(p peer.ID) (allow bool) { 26 | score, err := g.scores.GetPeerScore(p) 27 | if err != nil { 28 | return false 29 | } 30 | return score >= g.minScore 31 | } 32 | 33 | func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) { 34 | return g.BlockingConnectionGater.InterceptPeerDial(p) && g.checkScore(p) 35 | } 36 | 37 | func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) { 38 | return g.BlockingConnectionGater.InterceptAddrDial(id, ma) && g.checkScore(id) 39 | } 40 | 41 | func (g *ScoringConnectionGater) InterceptSecured(dir network.Direction, id peer.ID, mas network.ConnMultiaddrs) (allow bool) { 42 | return g.BlockingConnectionGater.InterceptSecured(dir, id, mas) && g.checkScore(id) 43 | } 44 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/frame_queue.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/ethereum/go-ethereum/log" 8 | 9 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 10 | ) 11 | 12 | var _ NextFrameProvider = &FrameQueue{} 13 | 14 | type NextDataProvider interface { 15 | NextData(context.Context) ([]byte, error) 16 | Origin() eth.L1BlockRef 17 | } 18 | 19 | type FrameQueue struct { 20 | log log.Logger 21 | frames []Frame 22 | prev NextDataProvider 23 | } 24 | 25 | func NewFrameQueue(log log.Logger, prev NextDataProvider) *FrameQueue { 26 | return &FrameQueue{ 27 | log: log, 28 | prev: prev, 29 | } 30 | } 31 | 32 | func (fq *FrameQueue) Origin() eth.L1BlockRef { 33 | return fq.prev.Origin() 34 | } 35 | 36 | func (fq *FrameQueue) NextFrame(ctx context.Context) (Frame, error) { 37 | // Find more frames if we need to 38 | if len(fq.frames) == 0 { 39 | if data, err := fq.prev.NextData(ctx); err != nil { 40 | return Frame{}, err 41 | } else { 42 | if new, err := ParseFrames(data); err == nil { 43 | fq.frames = append(fq.frames, new...) 44 | } else { 45 | fq.log.Warn("Failed to parse frames", "origin", fq.prev.Origin(), "err", err) 46 | } 47 | } 48 | } 49 | // If we did not add more frames but still have more data, retry this function. 50 | if len(fq.frames) == 0 { 51 | return Frame{}, NotEnoughData 52 | } 53 | 54 | ret := fq.frames[0] 55 | fq.frames = fq.frames[1:] 56 | return ret, nil 57 | } 58 | 59 | func (fq *FrameQueue) Reset(_ context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error { 60 | fq.frames = fq.frames[:0] 61 | return io.EOF 62 | } 63 | -------------------------------------------------------------------------------- /pt-node/sources/debug_client.go: -------------------------------------------------------------------------------- 1 | package sources 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/common/hexutil" 9 | "github.com/ethereum/go-ethereum/core/rawdb" 10 | ) 11 | 12 | type DebugClient struct { 13 | callContext CallContextFn 14 | } 15 | 16 | func NewDebugClient(callContext CallContextFn) *DebugClient { 17 | return &DebugClient{callContext} 18 | } 19 | 20 | func (o *DebugClient) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { 21 | // MPT nodes are stored as the hash of the node (with no prefix) 22 | node, err := o.dbGet(ctx, hash[:]) 23 | if err != nil { 24 | return nil, fmt.Errorf("failed to retrieve state MPT node: %w", err) 25 | } 26 | return node, nil 27 | } 28 | 29 | func (o *DebugClient) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { 30 | // First try retrieving with the new code prefix 31 | code, err := o.dbGet(ctx, append(append(make([]byte, 0), rawdb.CodePrefix...), hash[:]...)) 32 | if err != nil { 33 | // Fallback to the legacy un-prefixed version 34 | code, err = o.dbGet(ctx, hash[:]) 35 | if err != nil { 36 | return nil, fmt.Errorf("failed to retrieve contract code, using new and legacy keys, with codehash %s: %w", hash, err) 37 | } 38 | } 39 | return code, nil 40 | } 41 | 42 | func (o *DebugClient) dbGet(ctx context.Context, key []byte) ([]byte, error) { 43 | var node hexutil.Bytes 44 | err := o.callContext(ctx, &node, "debug_dbGet", hexutil.Encode(key)) 45 | if err != nil { 46 | return nil, fmt.Errorf("fetch error %x: %w", key, err) 47 | } 48 | return node, nil 49 | } 50 | -------------------------------------------------------------------------------- /pt-node/testutils/rpc_err_faker.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum" 7 | "github.com/ethereum/go-ethereum/rpc" 8 | 9 | "github.com/patex-ecosystem/patex-network/pt-node/client" 10 | ) 11 | 12 | // RPCErrFaker implements an RPC by wrapping one, but returns an error when prepared with one, to test RPC error handling. 13 | type RPCErrFaker struct { 14 | // RPC to call when no ErrFn is set, or the ErrFn does not return an error 15 | RPC client.RPC 16 | // ErrFn returns an error when the RPC needs to return error upon a call, batch call or subscription. 17 | // The RPC operates without fake errors if the ErrFn is nil, or returns nil. 18 | ErrFn func() error 19 | } 20 | 21 | func (r RPCErrFaker) Close() { 22 | r.RPC.Close() 23 | } 24 | 25 | func (r RPCErrFaker) CallContext(ctx context.Context, result any, method string, args ...any) error { 26 | if r.ErrFn != nil { 27 | if err := r.ErrFn(); err != nil { 28 | return err 29 | } 30 | } 31 | return r.RPC.CallContext(ctx, result, method, args...) 32 | } 33 | 34 | func (r RPCErrFaker) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { 35 | if r.ErrFn != nil { 36 | if err := r.ErrFn(); err != nil { 37 | return err 38 | } 39 | } 40 | return r.RPC.BatchCallContext(ctx, b) 41 | } 42 | 43 | func (r RPCErrFaker) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) { 44 | if r.ErrFn != nil { 45 | if err := r.ErrFn(); err != nil { 46 | return nil, err 47 | } 48 | } 49 | return r.RPC.EthSubscribe(ctx, channel, args...) 50 | } 51 | 52 | var _ client.RPC = (*RPCErrFaker)(nil) 53 | -------------------------------------------------------------------------------- /pt-node/sources/limit.go: -------------------------------------------------------------------------------- 1 | package sources 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/ethereum/go-ethereum" 8 | "github.com/ethereum/go-ethereum/rpc" 9 | "github.com/patex-ecosystem/patex-network/pt-node/client" 10 | ) 11 | 12 | type limitClient struct { 13 | c client.RPC 14 | sema chan struct{} 15 | wg sync.WaitGroup 16 | } 17 | 18 | // LimitRPC limits concurrent RPC requests (excluding subscriptions) to a given number by wrapping the client with a semaphore. 19 | func LimitRPC(c client.RPC, concurrentRequests int) client.RPC { 20 | return &limitClient{ 21 | c: c, 22 | // the capacity of the channel determines how many go-routines can concurrently execute requests with the wrapped client. 23 | sema: make(chan struct{}, concurrentRequests), 24 | } 25 | } 26 | 27 | func (lc *limitClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { 28 | lc.wg.Add(1) 29 | defer lc.wg.Done() 30 | lc.sema <- struct{}{} 31 | defer func() { <-lc.sema }() 32 | return lc.c.BatchCallContext(ctx, b) 33 | } 34 | 35 | func (lc *limitClient) CallContext(ctx context.Context, result any, method string, args ...any) error { 36 | lc.wg.Add(1) 37 | defer lc.wg.Done() 38 | lc.sema <- struct{}{} 39 | defer func() { <-lc.sema }() 40 | return lc.c.CallContext(ctx, result, method, args...) 41 | } 42 | 43 | func (lc *limitClient) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) { 44 | // subscription doesn't count towards request limit 45 | return lc.c.EthSubscribe(ctx, channel, args...) 46 | } 47 | 48 | func (lc *limitClient) Close() { 49 | lc.wg.Wait() 50 | close(lc.sema) 51 | lc.c.Close() 52 | } 53 | -------------------------------------------------------------------------------- /pt-node/p2p/store/gc_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "testing" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | "github.com/patex-ecosystem/patex-network/pt-node/testlog" 11 | "github.com/patex-ecosystem/patex-network/pt-service/clock" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestScheduleGcPeriodically(t *testing.T) { 16 | var bgTasks sync.WaitGroup 17 | ctx, cancel := context.WithCancel(context.Background()) 18 | defer func() { 19 | cancel() 20 | // Wait for the gc background process to complete after cancelling the context 21 | bgTasks.Wait() 22 | }() 23 | logger := testlog.Logger(t, log.LvlInfo) 24 | clock := clock.NewDeterministicClock(time.UnixMilli(5000)) 25 | 26 | called := make(chan struct{}, 10) 27 | action := func() error { 28 | called <- struct{}{} 29 | return nil 30 | } 31 | waitForGc := func(failMsg string) { 32 | timeout, cancel := context.WithTimeout(ctx, 10*time.Second) 33 | defer cancel() 34 | select { 35 | case <-timeout.Done(): 36 | t.Fatal(failMsg) 37 | case <-called: 38 | require.Len(t, called, 0, "should only run once after gc period") 39 | } 40 | } 41 | startGc(ctx, logger, clock, &bgTasks, action) 42 | timeout, tCancel := context.WithTimeout(ctx, 10*time.Second) 43 | defer tCancel() 44 | require.True(t, clock.WaitForNewPendingTask(timeout), "did not schedule pending GC") 45 | 46 | require.Len(t, called, 0, "should not run immediately") 47 | 48 | clock.AdvanceTime(gcPeriod) 49 | waitForGc("should run gc after first time period") 50 | 51 | clock.AdvanceTime(gcPeriod) 52 | waitForGc("should run gc again after second time period") 53 | } 54 | -------------------------------------------------------------------------------- /pt-node/p2p/store/extended.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | ds "github.com/ipfs/go-datastore" 11 | "github.com/libp2p/go-libp2p/core/peerstore" 12 | "github.com/patex-ecosystem/patex-network/pt-service/clock" 13 | ) 14 | 15 | type extendedStore struct { 16 | peerstore.Peerstore 17 | peerstore.CertifiedAddrBook 18 | *scoreBook 19 | *peerBanBook 20 | *ipBanBook 21 | } 22 | 23 | func NewExtendedPeerstore(ctx context.Context, logger log.Logger, clock clock.Clock, ps peerstore.Peerstore, store ds.Batching, scoreRetention time.Duration) (ExtendedPeerstore, error) { 24 | cab, ok := peerstore.GetCertifiedAddrBook(ps) 25 | if !ok { 26 | return nil, errors.New("peerstore should also be a certified address book") 27 | } 28 | sb, err := newScoreBook(ctx, logger, clock, store, scoreRetention) 29 | if err != nil { 30 | return nil, fmt.Errorf("create scorebook: %w", err) 31 | } 32 | sb.startGC() 33 | pb, err := newPeerBanBook(ctx, logger, clock, store) 34 | if err != nil { 35 | return nil, fmt.Errorf("create peer ban book: %w", err) 36 | } 37 | pb.startGC() 38 | ib, err := newIPBanBook(ctx, logger, clock, store) 39 | if err != nil { 40 | return nil, fmt.Errorf("create IP ban book: %w", err) 41 | } 42 | ib.startGC() 43 | return &extendedStore{ 44 | Peerstore: ps, 45 | CertifiedAddrBook: cab, 46 | scoreBook: sb, 47 | peerBanBook: pb, 48 | ipBanBook: ib, 49 | }, nil 50 | } 51 | 52 | func (s *extendedStore) Close() error { 53 | s.scoreBook.Close() 54 | return s.Peerstore.Close() 55 | } 56 | 57 | var _ ExtendedPeerstore = (*extendedStore)(nil) 58 | -------------------------------------------------------------------------------- /pt-service/backoff/strategies.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // Strategy is used to calculate how long a particular Operation 10 | // should wait between attempts. 11 | type Strategy interface { 12 | // Duration returns how long to wait for a given retry attempt. 13 | Duration(attempt int) time.Duration 14 | } 15 | 16 | // ExponentialStrategy performs exponential backoff. The exponential backoff 17 | // function is min(e.Min + (2^attempt * 1000) + randBetween(0, e.MaxJitter), e.Max) 18 | type ExponentialStrategy struct { 19 | // Min is the minimum amount of time to wait between attempts in ms. 20 | Min float64 21 | 22 | // Max is the maximum amount of time to wait between attempts in ms. 23 | Max float64 24 | 25 | // MaxJitter is the maximum amount of random jitter to insert between 26 | // attempts in ms. 27 | MaxJitter int 28 | } 29 | 30 | func (e *ExponentialStrategy) Duration(attempt int) time.Duration { 31 | var jitter int 32 | if e.MaxJitter > 0 { 33 | jitter = rand.Intn(e.MaxJitter) 34 | } 35 | dur := e.Min + (math.Pow(2, float64(attempt)) * 1000) 36 | dur += float64(jitter) 37 | if dur > e.Max { 38 | return time.Millisecond * time.Duration(e.Max) 39 | } 40 | 41 | return time.Millisecond * time.Duration(dur) 42 | } 43 | 44 | func Exponential() Strategy { 45 | return &ExponentialStrategy{ 46 | Max: 10000, 47 | MaxJitter: 250, 48 | } 49 | } 50 | 51 | type FixedStrategy struct { 52 | Dur time.Duration 53 | } 54 | 55 | func (f *FixedStrategy) Duration(attempt int) time.Duration { 56 | return f.Dur 57 | } 58 | 59 | func Fixed(dur time.Duration) Strategy { 60 | return &FixedStrategy{ 61 | Dur: dur, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pt-node/testutils/metrics.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 5 | ) 6 | 7 | // TestDerivationMetrics implements the metrics used in the derivation pipeline as no-op operations. 8 | // Optionally a test may hook into the metrics 9 | type TestDerivationMetrics struct { 10 | FnRecordL1ReorgDepth func(d uint64) 11 | FnRecordL1Ref func(name string, ref eth.L1BlockRef) 12 | FnRecordL2Ref func(name string, ref eth.L2BlockRef) 13 | FnRecordUnsafePayloads func(length uint64, memSize uint64, next eth.BlockID) 14 | FnRecordChannelInputBytes func(inputCompresedBytes int) 15 | } 16 | 17 | func (t *TestDerivationMetrics) RecordL1ReorgDepth(d uint64) { 18 | if t.FnRecordL1ReorgDepth != nil { 19 | t.FnRecordL1ReorgDepth(d) 20 | } 21 | } 22 | 23 | func (t *TestDerivationMetrics) RecordL1Ref(name string, ref eth.L1BlockRef) { 24 | if t.FnRecordL1Ref != nil { 25 | t.FnRecordL1Ref(name, ref) 26 | } 27 | } 28 | 29 | func (t *TestDerivationMetrics) RecordL2Ref(name string, ref eth.L2BlockRef) { 30 | if t.FnRecordL2Ref != nil { 31 | t.FnRecordL2Ref(name, ref) 32 | } 33 | } 34 | 35 | func (t *TestDerivationMetrics) RecordUnsafePayloadsBuffer(length uint64, memSize uint64, next eth.BlockID) { 36 | if t.FnRecordUnsafePayloads != nil { 37 | t.FnRecordUnsafePayloads(length, memSize, next) 38 | } 39 | } 40 | 41 | func (t *TestDerivationMetrics) RecordChannelInputBytes(inputCompresedBytes int) { 42 | if t.FnRecordChannelInputBytes != nil { 43 | t.FnRecordChannelInputBytes(inputCompresedBytes) 44 | } 45 | } 46 | 47 | type TestRPCMetrics struct{} 48 | 49 | func (n *TestRPCMetrics) RecordRPCServerRequest(method string) func() { 50 | return func() {} 51 | } 52 | -------------------------------------------------------------------------------- /pt-service/backoff/operation.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | // Operation represents an operation that will be retried 10 | // based on some backoff strategy if it fails. 11 | type Operation func() error 12 | 13 | // ErrFailedPermanently is an error raised by Do when the 14 | // underlying Operation has been retried maxAttempts times. 15 | type ErrFailedPermanently struct { 16 | attempts int 17 | LastErr error 18 | } 19 | 20 | func (e *ErrFailedPermanently) Error() string { 21 | return fmt.Sprintf("operation failed permanently after %d attempts: %v", e.attempts, e.LastErr) 22 | } 23 | 24 | // Do performs the provided Operation up to maxAttempts times 25 | // with delays in between each retry according to the provided 26 | // Strategy. 27 | func Do(maxAttempts int, strategy Strategy, op Operation) error { 28 | return DoCtx(context.Background(), maxAttempts, strategy, op) 29 | } 30 | 31 | func DoCtx(ctx context.Context, maxAttempts int, strategy Strategy, op Operation) error { 32 | if maxAttempts < 1 { 33 | return fmt.Errorf("need at least 1 attempt to run op, but have %d max attempts", maxAttempts) 34 | } 35 | var attempt int 36 | 37 | reattemptCh := make(chan struct{}, 1) 38 | doReattempt := func() { 39 | reattemptCh <- struct{}{} 40 | } 41 | doReattempt() 42 | 43 | for { 44 | select { 45 | case <-ctx.Done(): 46 | return ctx.Err() 47 | case <-reattemptCh: 48 | attempt++ 49 | err := op() 50 | if err == nil { 51 | return nil 52 | } 53 | 54 | if attempt == maxAttempts { 55 | return &ErrFailedPermanently{ 56 | attempts: maxAttempts, 57 | LastErr: err, 58 | } 59 | } 60 | time.AfterFunc(strategy.Duration(attempt-1), doReattempt) 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pt-chain-ops/state/memory_db_test.go: -------------------------------------------------------------------------------- 1 | package state_test 2 | 3 | import ( 4 | "math/big" 5 | "math/rand" 6 | "testing" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/state" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestAddBalance(t *testing.T) { 16 | t.Parallel() 17 | 18 | rng := rand.New(rand.NewSource(time.Now().UnixNano())) 19 | db := state.NewMemoryStateDB(nil) 20 | 21 | for i := 0; i < 100; i++ { 22 | key, _ := crypto.GenerateKey() 23 | addr := crypto.PubkeyToAddress(key.PublicKey) 24 | value := new(big.Int).Rand(rng, big.NewInt(1000)) 25 | 26 | db.CreateAccount(addr) 27 | db.AddBalance(addr, value) 28 | 29 | account := db.GetAccount(addr) 30 | require.NotNil(t, account) 31 | require.True(t, BigEqual(account.Balance, value)) 32 | } 33 | } 34 | 35 | func TestCode(t *testing.T) { 36 | t.Parallel() 37 | 38 | db := state.NewMemoryStateDB(nil) 39 | 40 | for i := 0; i < 100; i++ { 41 | key, _ := crypto.GenerateKey() 42 | addr := crypto.PubkeyToAddress(key.PublicKey) 43 | 44 | db.CreateAccount(addr) 45 | 46 | pre := db.GetCode(addr) 47 | require.Nil(t, pre) 48 | 49 | code := make([]byte, rand.Intn(1024)) 50 | rand.Read(code) 51 | 52 | db.SetCode(addr, code) 53 | 54 | post := db.GetCode(addr) 55 | require.Equal(t, post, code) 56 | 57 | size := db.GetCodeSize(addr) 58 | require.Equal(t, size, len(code)) 59 | 60 | codeHash := db.GetCodeHash(addr) 61 | require.Equal(t, codeHash, common.BytesToHash(crypto.Keccak256(code))) 62 | } 63 | } 64 | 65 | func BigEqual(a, b *big.Int) bool { 66 | if a == nil || b == nil { 67 | return a == b 68 | } else { 69 | return a.Cmp(b) == 0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ops-bedrock/mainnet/rpc-node/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -exu 3 | 4 | VERBOSITY=${GETH_VERBOSITY:-3} 5 | GETH_DATA_DIR=/datadir 6 | GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata" 7 | GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore" 8 | GENESIS_FILE_PATH="${GENESIS_FILE_PATH:-/genesis.json}" 9 | GETH_SNAPSHOT_FILE_PATH="/mainnet.tar" 10 | 11 | CHAIN_ID=789 12 | RPC_PORT="${RPC_PORT:-8545}" 13 | WS_PORT="${WS_PORT:-8546}" 14 | 15 | if [[ -f "$GETH_SNAPSHOT_FILE_PATH" ]] && [[ ! -d "$GETH_KEYSTORE_DIR" ]]; then 16 | echo "$GETH_SNAPSHOT_FILE_PATH snapshot available, processing..." 17 | tar xvf "$GETH_SNAPSHOT_FILE_PATH" -C ./ ; 18 | fi 19 | 20 | if [ ! -d "$GETH_CHAINDATA_DIR" ]; then 21 | echo "$GETH_CHAINDATA_DIR missing, running init" 22 | echo "Initializing genesis." 23 | geth --verbosity="$VERBOSITY" init \ 24 | --datadir="$GETH_DATA_DIR" \ 25 | "$GENESIS_FILE_PATH" 26 | else 27 | echo "$GETH_CHAINDATA_DIR exists." 28 | fi 29 | 30 | # Warning: Archive mode is required, otherwise old trie nodes will be 31 | # pruned within minutes of starting the mainnet. 32 | 33 | exec geth \ 34 | --ws \ 35 | --ws.port="$WS_PORT" \ 36 | --ws.addr=0.0.0.0 \ 37 | --ws.origins="*" \ 38 | --http \ 39 | --http.port="$RPC_PORT" \ 40 | --http.addr=0.0.0.0 \ 41 | --http.vhosts="*" \ 42 | --http.corsdomain="*" \ 43 | --authrpc.addr="0.0.0.0" \ 44 | --authrpc.port="8551" \ 45 | --authrpc.vhosts="*" \ 46 | --verbosity=3 \ 47 | --rollup.disabletxpoolgossip=true \ 48 | --nodiscover \ 49 | --syncmode=full \ 50 | --gcmode=archive \ 51 | --maxpeers=0 \ 52 | --datadir="$GETH_DATA_DIR" \ 53 | --txlookuplimit=0 \ 54 | --rollup.sequencerhttp="https://mainnet.patex.io:8545" \ 55 | "$@" 56 | -------------------------------------------------------------------------------- /ops-bedrock/patex-sepolia/rpc-node/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -exu 3 | 4 | VERBOSITY=${GETH_VERBOSITY:-3} 5 | GETH_DATA_DIR=/datadir 6 | GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata" 7 | GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore" 8 | GENESIS_FILE_PATH="${GENESIS_FILE_PATH:-/genesis.json}" 9 | GETH_SNAPSHOT_FILE_PATH="/testnet.tar" 10 | 11 | CHAIN_ID=471100 12 | RPC_PORT="${RPC_PORT:-8545}" 13 | WS_PORT="${WS_PORT:-8546}" 14 | 15 | if [[ -f "$GETH_SNAPSHOT_FILE_PATH" ]] && [[ ! -d "$GETH_KEYSTORE_DIR" ]]; then 16 | echo "$GETH_SNAPSHOT_FILE_PATH snapshot available, processing..." 17 | tar xvf "$GETH_SNAPSHOT_FILE_PATH" -C ./ ; 18 | fi 19 | 20 | if [ ! -d "$GETH_CHAINDATA_DIR" ]; then 21 | echo "$GETH_CHAINDATA_DIR missing, running init" 22 | echo "Initializing genesis." 23 | geth --verbosity="$VERBOSITY" init \ 24 | --datadir="$GETH_DATA_DIR" \ 25 | "$GENESIS_FILE_PATH" 26 | else 27 | echo "$GETH_CHAINDATA_DIR exists." 28 | fi 29 | 30 | # Warning: Archive mode is required, otherwise old trie nodes will be 31 | # pruned within minutes of starting the testnet. 32 | 33 | exec geth \ 34 | --ws \ 35 | --ws.port="$WS_PORT" \ 36 | --ws.addr=0.0.0.0 \ 37 | --ws.origins="*" \ 38 | --http \ 39 | --http.port="$RPC_PORT" \ 40 | --http.addr=0.0.0.0 \ 41 | --http.vhosts="*" \ 42 | --http.corsdomain="*" \ 43 | --authrpc.addr="0.0.0.0" \ 44 | --authrpc.port="8551" \ 45 | --authrpc.vhosts="*" \ 46 | --verbosity=3 \ 47 | --rollup.disabletxpoolgossip=true \ 48 | --nodiscover \ 49 | --syncmode=full \ 50 | --gcmode=archive \ 51 | --maxpeers=0 \ 52 | --datadir="$GETH_DATA_DIR" \ 53 | --txlookuplimit=0 \ 54 | --rollup.sequencerhttp="http://testnet.patex.io:8545" \ 55 | "$@" 56 | -------------------------------------------------------------------------------- /pt-chain-ops/genesis/config_test.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | "testing" 9 | 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/common/hexutil" 12 | "github.com/ethereum/go-ethereum/rpc" 13 | 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestConfigMarshalUnmarshal(t *testing.T) { 18 | b, err := os.ReadFile("testdata/test-deploy-config-full.json") 19 | require.NoError(t, err) 20 | dec := json.NewDecoder(bytes.NewReader(b)) 21 | decoded := new(DeployConfig) 22 | require.NoError(t, dec.Decode(decoded)) 23 | encoded, err := json.MarshalIndent(decoded, "", " ") 24 | 25 | require.NoError(t, err) 26 | require.JSONEq(t, string(b), string(encoded)) 27 | } 28 | 29 | func TestUnmarshalL1StartingBlockTag(t *testing.T) { 30 | decoded := new(DeployConfig) 31 | require.NoError(t, json.Unmarshal([]byte(`{"l1StartingBlockTag": "earliest"}`), decoded)) 32 | require.EqualValues(t, rpc.EarliestBlockNumber, *decoded.L1StartingBlockTag.BlockNumber) 33 | h := "0x86c7263d87140ca7cd9bf1bc9e95a435a7a0efc0ae2afaf64920c5b59a6393d4" 34 | require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded)) 35 | require.EqualValues(t, common.HexToHash(h), *decoded.L1StartingBlockTag.BlockHash) 36 | } 37 | 38 | func TestRegolithTimeZero(t *testing.T) { 39 | regolithOffset := hexutil.Uint64(0) 40 | config := &DeployConfig{L2GenesisRegolithTimeOffset: ®olithOffset} 41 | require.Equal(t, uint64(0), *config.RegolithTime(1234)) 42 | } 43 | 44 | func TestRegolithTimeAsOffset(t *testing.T) { 45 | regolithOffset := hexutil.Uint64(1500) 46 | config := &DeployConfig{L2GenesisRegolithTimeOffset: ®olithOffset} 47 | require.Equal(t, uint64(1500+5000), *config.RegolithTime(5000)) 48 | } 49 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/deposits.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/go-multierror" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/common/hexutil" 10 | "github.com/ethereum/go-ethereum/core/types" 11 | ) 12 | 13 | // UserDeposits transforms the L2 block-height and L1 receipts into the transaction inputs for a full L2 block 14 | func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address) ([]*types.DepositTx, error) { 15 | var out []*types.DepositTx 16 | var result error 17 | for i, rec := range receipts { 18 | if rec.Status != types.ReceiptStatusSuccessful { 19 | continue 20 | } 21 | for j, log := range rec.Logs { 22 | if log.Address == depositContractAddr && len(log.Topics) > 0 && log.Topics[0] == DepositEventABIHash { 23 | dep, err := UnmarshalDepositLogEvent(log) 24 | if err != nil { 25 | result = multierror.Append(result, fmt.Errorf("malformatted L1 deposit log in receipt %d, log %d: %w", i, j, err)) 26 | } else { 27 | out = append(out, dep) 28 | } 29 | } 30 | } 31 | } 32 | return out, result 33 | } 34 | 35 | func DeriveDeposits(receipts []*types.Receipt, depositContractAddr common.Address) ([]hexutil.Bytes, error) { 36 | var result error 37 | userDeposits, err := UserDeposits(receipts, depositContractAddr) 38 | if err != nil { 39 | result = multierror.Append(result, err) 40 | } 41 | encodedTxs := make([]hexutil.Bytes, 0, len(userDeposits)) 42 | for i, tx := range userDeposits { 43 | opaqueTx, err := types.NewTx(tx).MarshalBinary() 44 | if err != nil { 45 | result = multierror.Append(result, fmt.Errorf("failed to encode user tx %d", i)) 46 | } else { 47 | encodedTxs = append(encodedTxs, opaqueTx) 48 | } 49 | } 50 | return encodedTxs, result 51 | } 52 | -------------------------------------------------------------------------------- /pt-signer/client/config.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/urfave/cli" 7 | 8 | ptservice "github.com/patex-ecosystem/patex-network/pt-service" 9 | optls "github.com/patex-ecosystem/patex-network/pt-service/tls" 10 | ) 11 | 12 | const ( 13 | EndpointFlagName = "signer.endpoint" 14 | AddressFlagName = "signer.address" 15 | ) 16 | 17 | func CLIFlags(envPrefix string) []cli.Flag { 18 | envPrefix += "_SIGNER" 19 | flags := []cli.Flag{ 20 | cli.StringFlag{ 21 | Name: EndpointFlagName, 22 | Usage: "Signer endpoint the client will connect to", 23 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "ENDPOINT"), 24 | }, 25 | cli.StringFlag{ 26 | Name: AddressFlagName, 27 | Usage: "Address the signer is signing transactions for", 28 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "ADDRESS"), 29 | }, 30 | } 31 | flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer")...) 32 | return flags 33 | } 34 | 35 | type CLIConfig struct { 36 | Endpoint string 37 | Address string 38 | TLSConfig optls.CLIConfig 39 | } 40 | 41 | func (c CLIConfig) Check() error { 42 | if err := c.TLSConfig.Check(); err != nil { 43 | return err 44 | } 45 | if !((c.Endpoint == "" && c.Address == "") || (c.Endpoint != "" && c.Address != "")) { 46 | return errors.New("signer endpoint and address must both be set or not set") 47 | } 48 | return nil 49 | } 50 | 51 | func (c CLIConfig) Enabled() bool { 52 | if c.Endpoint != "" && c.Address != "" { 53 | return true 54 | } 55 | return false 56 | } 57 | 58 | func ReadCLIConfig(ctx *cli.Context) CLIConfig { 59 | cfg := CLIConfig{ 60 | Endpoint: ctx.String(EndpointFlagName), 61 | Address: ctx.String(AddressFlagName), 62 | TLSConfig: optls.ReadCLIConfigWithPrefix(ctx, "signer"), 63 | } 64 | return cfg 65 | } 66 | -------------------------------------------------------------------------------- /pt-node/testutils/mock_l2.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 9 | ) 10 | 11 | type MockL2Client struct { 12 | MockEthClient 13 | } 14 | 15 | func (c *MockL2Client) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) { 16 | return c.Mock.MethodCalled("L2BlockRefByLabel", label).Get(0).(eth.L2BlockRef), nil 17 | } 18 | 19 | func (m *MockL2Client) ExpectL2BlockRefByLabel(label eth.BlockLabel, ref eth.L2BlockRef, err error) { 20 | m.Mock.On("L2BlockRefByLabel", label).Once().Return(ref, &err) 21 | } 22 | 23 | func (c *MockL2Client) L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error) { 24 | return c.Mock.MethodCalled("L2BlockRefByNumber", num).Get(0).(eth.L2BlockRef), nil 25 | } 26 | 27 | func (m *MockL2Client) ExpectL2BlockRefByNumber(num uint64, ref eth.L2BlockRef, err error) { 28 | m.Mock.On("L2BlockRefByNumber", num).Once().Return(ref, &err) 29 | } 30 | 31 | func (c *MockL2Client) L2BlockRefByHash(ctx context.Context, hash common.Hash) (eth.L2BlockRef, error) { 32 | return c.Mock.MethodCalled("L2BlockRefByHash", hash).Get(0).(eth.L2BlockRef), nil 33 | } 34 | 35 | func (m *MockL2Client) ExpectL2BlockRefByHash(hash common.Hash, ref eth.L2BlockRef, err error) { 36 | m.Mock.On("L2BlockRefByHash", hash).Once().Return(ref, &err) 37 | } 38 | 39 | func (m *MockL2Client) SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error) { 40 | return m.Mock.MethodCalled("SystemConfigByL2Hash", hash).Get(0).(eth.SystemConfig), nil 41 | } 42 | 43 | func (m *MockL2Client) ExpectSystemConfigByL2Hash(hash common.Hash, cfg eth.SystemConfig, err error) { 44 | m.Mock.On("SystemConfigByL2Hash", hash).Once().Return(cfg, &err) 45 | } 46 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxiT6uw4SXE8Hm 3 | A8fxWD9EXN0HMMl6wjhUJdhxIKXQST+U7gAAqOBGyyOZkNZZdxnf9nVSU7hxFnLr 4 | NpKWUUzTtZdfdJDniIHLeVLo2AFweJZV8qu1fP91CuzBI8GcYQc9UGZ37EdwS/jC 5 | pidhhW0m4hwZjU/+LXiHWiCkRbbZycJVgnCqaPGsPhRDy9J5N/1jBUJspdnlP56I 6 | 2F9A6QIbSsKWq9+9mIyAEGUti8mtz1sQuyohs0ZUPgKuRiNkYRxOgytv/BmGovGV 7 | yz+JcojT/nv372+9zjj+mLYwVTSe+GhKcSCikoo/LzdLAGiCmIf6YNxWAyITBtcd 8 | TI0AVvP/AgMBAAECggEATZpnau8N+xfozsliUa24YgKRnv4FZAKXqrisRq71q/kI 9 | sOnj2GX5Oxi6o/q6p3q3Nb2+hNERs2UTsJs3MjuxcG1VEKWcXYi+65lJ03vwDSC4 10 | 3jLoObm81IWE/dvKWrfS+Us2rz757y1WPIdyeV9gWfnGPKkXiUyI/ek4kXXjuoiL 11 | WqYeWlhe2mGVxQs5DfxBigvN0th2yfmeQJTLXpoJkCDmSTS3vnvKJfiH/mflLm/B 12 | 51lgKEziqvu53JmgYmhswsgRnLtyZ4yjksMroJYPKCTA3RbDud+J6SO01ok+hiNy 13 | QcO/j+1zlFQlWqAE4BSB1IXbW/6ZQAdszPpX9l318QKBgQDaW+nq0FH7djne5838 14 | sqBO7pHGde2j6To8PuGIoskH86p8X4dgutNiingGnYoqK2qTKVTudkdu2ahwGI3P 15 | S3dflwcyJwAvTbjc3ATSoez+P0oUC62DQciMfqaCR+WYAoAR8cdHtSoMyMvLVhSs 16 | 1h05AoalTybuT+spscV8hFBdlwKBgQDQI9VxKPt9VwhKf0W6K2IKbNxvHcu0UFvZ 17 | pCZKdxQLr7tP3+oo9fWn6R0sSX/aV+km9YQsn6keX0e4ayJHNfU6UwzNSMKWQZvs 18 | JF7OBuZnpZYX/1lSIUDOw8I29kcVtqCHjpgozbFhJSQzJDDcPvRFmkPBI74sgA1z 19 | xuH7xcc52QKBgDPX7snZfB2ADG1oC/gbUQRskB/Wj/2CuljjdRjDzYcdyzSMWdAV 20 | i2qyBZ1MeilY9YzLG2cingMrmlpC+ihleooviX3W1Kxmf6Wwd1SrLWGQFT59J00q 21 | qTryNwZnm5NjxJR+GxpjYQB4DCrS3UXL8FRAzUcia9PZFbRoiMLvh0UxAoGBAIjZ 22 | prL6cTBeEvN4bw4TDCkynlTo0FDELUASL6LyXFm6t3uzC7DW1ygJm8bMpKWY+5FE 23 | CB2W9IkluHBG8IjFr3Ejvd0To+1LQgundjYcT02CkAdDOyVG++d2yrF8iAx8wVuf 24 | o+fgJmprEzwU5ZNKSS2iWj4ZFCcKIs4my9rQlUcxAoGBAMiqpes+Ka3ofb8sxpzj 25 | xAB2oOub0X93a3xQ4xv5VLDysA7+rv1CDwuYKlOKTjGFwg3CwvbnE2EKkKI9BNgh 26 | UHAdpVXWOck5ubD5EB6vp7xWvzu6AA4KyhyRJaRL1GdSLFl9JDg5YvHskVxumt+L 27 | /GgTbnT/P3+JOhCdc0H0XhGq 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCne9kAZiMELiQ9 3 | /qOqHo1gJYxV7wNvkg4dgQK0bEFU6FLkOe5c+bQtRP1QKhR894GZT/UtmTjUnvS2 4 | nCXYgeFIgWpsb5+E2VGVQAD0Igd5FrZFWQXEfsjvG3YWHBTZaxe+L4YSI4qhnaYL 5 | Zy5nm3zNYqKdb4d3ykvbvQ5uhD3zSBOF8Xa6DOheXJhVr4ysTnPCMtreVCgqSrOj 6 | nj+ArAr4rJxdiiWOZ/ZDT76xKwMCpmUUhiSd9y3j5JPhO8ZGpLCc/FBwnfcxcOlu 7 | cZxME9epbFeG+hx35Cs4XhGft9VEtZmpigVyFKe3hHeqyg+4HypZ1mf4xxPqqqAt 8 | RS1HLvY9AgMBAAECggEAGE5R7Mvl0wp7OgAFcn/ilox8dFAumHeC0udRJCv9wzvA 9 | I90Aab/XVSaI+KRSutwUk9JVy5tL8xdqfkHlACnBLwuRDVGZvebn/xf9y3BQ01Ln 10 | euLzglPAB2td1NGYeQEgvfoZo/JCgTfmzAraYjDfiNMCtIRmDY1vOuGSAZnxf6e/ 11 | C7LB7XdKdGijUgjvHOv82DUbdt5RD/RL9chvk+i9esdjJIxS3YaSOHclV7FSjRqe 12 | f5e9MrPKuSdijYHqHOcxX3hztOrLjL7J4gVPRvjGaWbRmdU79m2oFlwfGlLTU+w6 13 | zlzIu5FqKD375rchkOKw3W8uSyPCRsdnmZxT+CZKIQKBgQDPHiXmGWlTBdcBcgaK 14 | 4PIZltAZbA+5MdDDJPDZp9b5gS8/0mXoqOvvC2PeOkrv96l3nrVoTbfOxxutftjO 15 | Xh2OfplpNkxABR8WqY7mKaHR4aphlq1QtxcCcOlmAbmo9Eo03Vhir7q913upgEnY 16 | jzEoX9b41JEJBTR6Mfbfx3b6+QKBgQDPAw2ZuBqiyPmozCDqKnP2yiOJz6wkunqI 17 | rUkZLizN+U9kByiq/NzoDGo6BNchs2XpVhoIt3ixC/xzzhDUkn7V7ZCn8BkEdj/T 18 | jgZyupMJ1wNQK9Du3GaYPRnQ1I3OrfA0oOx1ZVoOOKOEDAEAfhzSW8GWicO3305o 19 | 6/4Tq4gCZQKBgQC18vEuS+KX+chgz6/pryVfz3ou6xyA/786v6gKPYUAGTnN4mJ+ 20 | Wm8xx5rLLgCJANPSbw1EfQndUFMDPizuVgW3GYZhxD6F+znNadVMYwRyYcGRC5Jk 21 | FwPStCiF4TwdrcXG3TB5OZFelv9e74FwCpMPueobHHnxJ65rLpuHCS5/2QKBgBDF 22 | AYwLUvUO7NKUvrHZgI1kcJ6QWTSceqKpzvsgN3b0FE9ZGR1I4KhXoR9UFw1e2Amf 23 | 9PnxyvAktW24KrrdpzKzTP2dwJkQ7zi3D6SpopGwfk83TXScHB+HC5lULqyogIXy 24 | 51TXQgVW50AiLM6aaMFNt4/3VwiFKXfsbievxJPVAoGADlJV011pkxlF2wZxpvDT 25 | RGIWCtT3G3GksNPxTkLDsuBnJ38YE0p95MivBP3Q7kdVhfgOzG1AnWMeOUzcdzNM 26 | hXKW9EcyovPYrHpAcdwMXOj+WuU74l2tHLPJQ5+4zTKAc/4ZJ7TKMjZ21qRjqGHQ 27 | dxddYOr2kCOIFNreAaKD9Po= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /pt-service/tls/certman/testdata/server2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxiT6uw4SXE8Hm 3 | A8fxWD9EXN0HMMl6wjhUJdhxIKXQST+U7gAAqOBGyyOZkNZZdxnf9nVSU7hxFnLr 4 | NpKWUUzTtZdfdJDniIHLeVLo2AFweJZV8qu1fP91CuzBI8GcYQc9UGZ37EdwS/jC 5 | pidhhW0m4hwZjU/+LXiHWiCkRbbZycJVgnCqaPGsPhRDy9J5N/1jBUJspdnlP56I 6 | 2F9A6QIbSsKWq9+9mIyAEGUti8mtz1sQuyohs0ZUPgKuRiNkYRxOgytv/BmGovGV 7 | yz+JcojT/nv372+9zjj+mLYwVTSe+GhKcSCikoo/LzdLAGiCmIf6YNxWAyITBtcd 8 | TI0AVvP/AgMBAAECggEATZpnau8N+xfozsliUa24YgKRnv4FZAKXqrisRq71q/kI 9 | sOnj2GX5Oxi6o/q6p3q3Nb2+hNERs2UTsJs3MjuxcG1VEKWcXYi+65lJ03vwDSC4 10 | 3jLoObm81IWE/dvKWrfS+Us2rz757y1WPIdyeV9gWfnGPKkXiUyI/ek4kXXjuoiL 11 | WqYeWlhe2mGVxQs5DfxBigvN0th2yfmeQJTLXpoJkCDmSTS3vnvKJfiH/mflLm/B 12 | 51lgKEziqvu53JmgYmhswsgRnLtyZ4yjksMroJYPKCTA3RbDud+J6SO01ok+hiNy 13 | QcO/j+1zlFQlWqAE4BSB1IXbW/6ZQAdszPpX9l318QKBgQDaW+nq0FH7djne5838 14 | sqBO7pHGde2j6To8PuGIoskH86p8X4dgutNiingGnYoqK2qTKVTudkdu2ahwGI3P 15 | S3dflwcyJwAvTbjc3ATSoez+P0oUC62DQciMfqaCR+WYAoAR8cdHtSoMyMvLVhSs 16 | 1h05AoalTybuT+spscV8hFBdlwKBgQDQI9VxKPt9VwhKf0W6K2IKbNxvHcu0UFvZ 17 | pCZKdxQLr7tP3+oo9fWn6R0sSX/aV+km9YQsn6keX0e4ayJHNfU6UwzNSMKWQZvs 18 | JF7OBuZnpZYX/1lSIUDOw8I29kcVtqCHjpgozbFhJSQzJDDcPvRFmkPBI74sgA1z 19 | xuH7xcc52QKBgDPX7snZfB2ADG1oC/gbUQRskB/Wj/2CuljjdRjDzYcdyzSMWdAV 20 | i2qyBZ1MeilY9YzLG2cingMrmlpC+ihleooviX3W1Kxmf6Wwd1SrLWGQFT59J00q 21 | qTryNwZnm5NjxJR+GxpjYQB4DCrS3UXL8FRAzUcia9PZFbRoiMLvh0UxAoGBAIjZ 22 | prL6cTBeEvN4bw4TDCkynlTo0FDELUASL6LyXFm6t3uzC7DW1ygJm8bMpKWY+5FE 23 | CB2W9IkluHBG8IjFr3Ejvd0To+1LQgundjYcT02CkAdDOyVG++d2yrF8iAx8wVuf 24 | o+fgJmprEzwU5ZNKSS2iWj4ZFCcKIs4my9rQlUcxAoGBAMiqpes+Ka3ofb8sxpzj 25 | xAB2oOub0X93a3xQ4xv5VLDysA7+rv1CDwuYKlOKTjGFwg3CwvbnE2EKkKI9BNgh 26 | UHAdpVXWOck5ubD5EB6vp7xWvzu6AA4KyhyRJaRL1GdSLFl9JDg5YvHskVxumt+L 27 | /GgTbnT/P3+JOhCdc0H0XhGq 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /pt-proposer/proposer/utils.go: -------------------------------------------------------------------------------- 1 | package proposer 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/ethclient" 10 | "github.com/ethereum/go-ethereum/rpc" 11 | "github.com/patex-ecosystem/patex-network/pt-node/client" 12 | "github.com/patex-ecosystem/patex-network/pt-node/sources" 13 | ) 14 | 15 | var defaultDialTimeout = 5 * time.Second 16 | 17 | // dialEthClientWithTimeout attempts to dial the L1 provider using the provided 18 | // URL. If the dial doesn't complete within defaultDialTimeout seconds, this 19 | // method will return an error. 20 | func dialEthClientWithTimeout(ctx context.Context, url string) (*ethclient.Client, error) { 21 | ctxt, cancel := context.WithTimeout(ctx, defaultDialTimeout) 22 | defer cancel() 23 | 24 | return ethclient.DialContext(ctxt, url) 25 | } 26 | 27 | // dialRollupClientWithTimeout attempts to dial the RPC provider using the provided 28 | // URL. If the dial doesn't complete within defaultDialTimeout seconds, this 29 | // method will return an error. 30 | func dialRollupClientWithTimeout(ctx context.Context, url string) (*sources.RollupClient, error) { 31 | ctxt, cancel := context.WithTimeout(ctx, defaultDialTimeout) 32 | defer cancel() 33 | 34 | rpcCl, err := rpc.DialContext(ctxt, url) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return sources.NewRollupClient(client.NewBaseRPCClient(rpcCl)), nil 40 | } 41 | 42 | // parseAddress parses an ETH address from a hex string. This method will fail if 43 | // the address is not a valid hexadecimal address. 44 | func parseAddress(address string) (common.Address, error) { 45 | if common.IsHexAddress(address) { 46 | return common.HexToAddress(address), nil 47 | } 48 | return common.Address{}, fmt.Errorf("invalid address: %v", address) 49 | } 50 | -------------------------------------------------------------------------------- /pt-chain-ops/README.md: -------------------------------------------------------------------------------- 1 | # pt-chain-ops 2 | 3 | This package performs state surgery. It takes the following input: 4 | 5 | 1. A v0 database 6 | 2. A partial `genesis.json` 7 | 3. A list of addresses that transacted on the network prior to this past regenesis. 8 | 4. A list of addresses that performed approvals on prior versions of the PVM ETH contract. 9 | 10 | It creates an initialized Bedrock Geth database as output. It does this by performing the following steps: 11 | 12 | 1. Iterates over the old state. 13 | 2. For each account in the old state, add that account and its storage to the new state after copying its balance from the PVM_ETH contract. 14 | 3. Iterates over the pre-allocated accounts in the genesis file and adds them to the new state. 15 | 4. Imports any accounts that have PVM ETH balances but aren't in state. 16 | 5. Configures a genesis block in the new state using `genesis.json`. 17 | 18 | It performs the following integrity checks: 19 | 20 | 1. PVM ETH storage slots must be completely accounted for. 21 | 2. The total supply of PVM ETH migrated must match the total supply of the PVM ETH contract. 22 | 23 | This process takes about two hours on mainnet. 24 | 25 | Unlike previous iterations of our state surgery scripts, this one does not write results to a `genesis.json` file. This is for the following reasons: 26 | 27 | 1. **Performance**. It's much faster to write binary to LevelDB than it is to write strings to a JSON file. 28 | 2. **State Size**. There are nearly 1MM accounts on mainnet, which would create a genesis file several gigabytes in size. This is impossible for Geth to import without a large amount of memory, since the entire JSON gets buffered into memory. Importing the entire state database will be much faster, and can be done with fewer resources. 29 | 30 | ## Compilation 31 | 32 | Run `make pt-migrate`. 33 | 34 | -------------------------------------------------------------------------------- /pt-service/metrics/cli.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | 7 | ptservice "github.com/patex-ecosystem/patex-network/pt-service" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | const ( 13 | EnabledFlagName = "metrics.enabled" 14 | ListenAddrFlagName = "metrics.addr" 15 | PortFlagName = "metrics.port" 16 | ) 17 | 18 | func CLIFlags(envPrefix string) []cli.Flag { 19 | return []cli.Flag{ 20 | cli.BoolFlag{ 21 | Name: EnabledFlagName, 22 | Usage: "Enable the metrics server", 23 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "METRICS_ENABLED"), 24 | }, 25 | cli.StringFlag{ 26 | Name: ListenAddrFlagName, 27 | Usage: "Metrics listening address", 28 | Value: "0.0.0.0", 29 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "METRICS_ADDR"), 30 | }, 31 | cli.IntFlag{ 32 | Name: PortFlagName, 33 | Usage: "Metrics listening port", 34 | Value: 7300, 35 | EnvVar: ptservice.PrefixEnvVar(envPrefix, "METRICS_PORT"), 36 | }, 37 | } 38 | } 39 | 40 | type CLIConfig struct { 41 | Enabled bool 42 | ListenAddr string 43 | ListenPort int 44 | } 45 | 46 | func (m CLIConfig) Check() error { 47 | if !m.Enabled { 48 | return nil 49 | } 50 | 51 | if m.ListenPort < 0 || m.ListenPort > math.MaxUint16 { 52 | return errors.New("invalid metrics port") 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func ReadCLIConfig(ctx *cli.Context) CLIConfig { 59 | return CLIConfig{ 60 | Enabled: ctx.GlobalBool(EnabledFlagName), 61 | ListenAddr: ctx.GlobalString(ListenAddrFlagName), 62 | ListenPort: ctx.GlobalInt(PortFlagName), 63 | } 64 | } 65 | 66 | func ReadLocalCLIConfig(ctx *cli.Context) CLIConfig { 67 | return CLIConfig{ 68 | Enabled: ctx.Bool(EnabledFlagName), 69 | ListenAddr: ctx.String(ListenAddrFlagName), 70 | ListenPort: ctx.Int(PortFlagName), 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pt-node/client/rate_limited.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum" 8 | "github.com/ethereum/go-ethereum/rpc" 9 | "golang.org/x/time/rate" 10 | ) 11 | 12 | // RateLimitingClient is a wrapper around a pure RPC that implements a global rate-limit on requests. 13 | type RateLimitingClient struct { 14 | c RPC 15 | rl *rate.Limiter 16 | } 17 | 18 | // NewRateLimitingClient implements a global rate-limit for all RPC requests. 19 | // A limit of N will ensure that over a long enough time-frame the given number of tokens per second is targeted. 20 | // Burst limits how far off we can be from the target, by specifying how many requests are allowed at once. 21 | func NewRateLimitingClient(c RPC, limit rate.Limit, burst int) *RateLimitingClient { 22 | return &RateLimitingClient{c: c, rl: rate.NewLimiter(limit, burst)} 23 | } 24 | 25 | func (b *RateLimitingClient) Close() { 26 | b.c.Close() 27 | } 28 | 29 | func (b *RateLimitingClient) CallContext(ctx context.Context, result any, method string, args ...any) error { 30 | if err := b.rl.Wait(ctx); err != nil { 31 | return err 32 | } 33 | cCtx, cancel := context.WithTimeout(ctx, 10*time.Second) 34 | defer cancel() 35 | return b.c.CallContext(cCtx, result, method, args...) 36 | } 37 | 38 | func (b *RateLimitingClient) BatchCallContext(ctx context.Context, batch []rpc.BatchElem) error { 39 | if err := b.rl.WaitN(ctx, len(batch)); err != nil { 40 | return err 41 | } 42 | cCtx, cancel := context.WithTimeout(ctx, 20*time.Second) 43 | defer cancel() 44 | return b.c.BatchCallContext(cCtx, batch) 45 | } 46 | 47 | func (b *RateLimitingClient) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) { 48 | if err := b.rl.Wait(ctx); err != nil { 49 | return nil, err 50 | } 51 | return b.c.EthSubscribe(ctx, channel, args...) 52 | } 53 | -------------------------------------------------------------------------------- /pt-service/metrics/event.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | ) 8 | 9 | type Event struct { 10 | Total prometheus.Counter 11 | LastTime prometheus.Gauge 12 | } 13 | 14 | func (e *Event) Record() { 15 | e.Total.Inc() 16 | e.LastTime.SetToCurrentTime() 17 | } 18 | 19 | func NewEvent(factory Factory, ns string, subsystem string, name string, displayName string) Event { 20 | return Event{ 21 | Total: factory.NewCounter(prometheus.CounterOpts{ 22 | Namespace: ns, 23 | Name: fmt.Sprintf("%s_total", name), 24 | Help: fmt.Sprintf("Count of %s events", displayName), 25 | Subsystem: subsystem, 26 | }), 27 | LastTime: factory.NewGauge(prometheus.GaugeOpts{ 28 | Namespace: ns, 29 | Name: fmt.Sprintf("last_%s_unix", name), 30 | Help: fmt.Sprintf("Timestamp of last %s event", displayName), 31 | Subsystem: subsystem, 32 | }), 33 | } 34 | } 35 | 36 | type EventVec struct { 37 | Total prometheus.CounterVec 38 | LastTime prometheus.GaugeVec 39 | } 40 | 41 | func (e *EventVec) Record(lvs ...string) { 42 | e.Total.WithLabelValues(lvs...).Inc() 43 | e.LastTime.WithLabelValues(lvs...).SetToCurrentTime() 44 | } 45 | 46 | func NewEventVec(factory Factory, ns string, subsystem string, name string, displayName string, labelNames []string) EventVec { 47 | return EventVec{ 48 | Total: *factory.NewCounterVec(prometheus.CounterOpts{ 49 | Namespace: ns, 50 | Name: fmt.Sprintf("%s_total", name), 51 | Help: fmt.Sprintf("Count of %s events", displayName), 52 | Subsystem: subsystem, 53 | }, labelNames), 54 | LastTime: *factory.NewGaugeVec(prometheus.GaugeOpts{ 55 | Namespace: ns, 56 | Name: fmt.Sprintf("last_%s_unix", name), 57 | Help: fmt.Sprintf("Timestamp of last %s event", displayName), 58 | Subsystem: subsystem, 59 | }, labelNames), 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pt-node/eth/receipts.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/common/hexutil" 9 | "github.com/ethereum/go-ethereum/core/types" 10 | ) 11 | 12 | // EncodeReceipts encodes a list of receipts into raw receipts. Some non-consensus meta-data may be lost. 13 | func EncodeReceipts(elems []*types.Receipt) ([]hexutil.Bytes, error) { 14 | out := make([]hexutil.Bytes, len(elems)) 15 | for i, el := range elems { 16 | dat, err := el.MarshalBinary() 17 | if err != nil { 18 | return nil, fmt.Errorf("failed to marshal receipt %d: %w", i, err) 19 | } 20 | out[i] = dat 21 | } 22 | return out, nil 23 | } 24 | 25 | // DecodeRawReceipts decodes receipts and adds additional blocks metadata. 26 | // The contract-deployment addresses are not set however (high cost, depends on nonce values, unused by pt-node). 27 | func DecodeRawReceipts(block BlockID, rawReceipts []hexutil.Bytes, txHashes []common.Hash) ([]*types.Receipt, error) { 28 | result := make([]*types.Receipt, len(rawReceipts)) 29 | totalIndex := uint(0) 30 | prevCumulativeGasUsed := uint64(0) 31 | for i, r := range rawReceipts { 32 | var x types.Receipt 33 | if err := x.UnmarshalBinary(r); err != nil { 34 | return nil, fmt.Errorf("failed to decode receipt %d: %w", i, err) 35 | } 36 | x.TxHash = txHashes[i] 37 | x.BlockHash = block.Hash 38 | x.BlockNumber = new(big.Int).SetUint64(block.Number) 39 | x.TransactionIndex = uint(i) 40 | x.GasUsed = x.CumulativeGasUsed - prevCumulativeGasUsed 41 | // contract address meta-data is not computed. 42 | prevCumulativeGasUsed = x.CumulativeGasUsed 43 | for _, l := range x.Logs { 44 | l.BlockNumber = block.Number 45 | l.TxHash = x.TxHash 46 | l.TxIndex = uint(i) 47 | l.BlockHash = block.Hash 48 | l.Index = totalIndex 49 | totalIndex += 1 50 | } 51 | result[i] = &x 52 | } 53 | return result, nil 54 | } 55 | -------------------------------------------------------------------------------- /pt-node/p2p/notifications.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "github.com/libp2p/go-libp2p/core/network" 5 | ma "github.com/multiformats/go-multiaddr" 6 | 7 | "github.com/patex-ecosystem/patex-network/pt-node/metrics" 8 | 9 | "github.com/ethereum/go-ethereum/log" 10 | ) 11 | 12 | type NotificationsMetricer interface { 13 | IncPeerCount() 14 | DecPeerCount() 15 | IncStreamCount() 16 | DecStreamCount() 17 | } 18 | 19 | type notifications struct { 20 | log log.Logger 21 | m NotificationsMetricer 22 | } 23 | 24 | func (notif *notifications) Listen(n network.Network, a ma.Multiaddr) { 25 | notif.log.Info("started listening network address", "addr", a) 26 | } 27 | func (notif *notifications) ListenClose(n network.Network, a ma.Multiaddr) { 28 | notif.log.Info("stopped listening network address", "addr", a) 29 | } 30 | func (notif *notifications) Connected(n network.Network, v network.Conn) { 31 | notif.m.IncPeerCount() 32 | notif.log.Info("connected to peer", "peer", v.RemotePeer(), "addr", v.RemoteMultiaddr()) 33 | } 34 | func (notif *notifications) Disconnected(n network.Network, v network.Conn) { 35 | notif.m.DecPeerCount() 36 | notif.log.Info("disconnected from peer", "peer", v.RemotePeer(), "addr", v.RemoteMultiaddr()) 37 | } 38 | func (notif *notifications) OpenedStream(n network.Network, v network.Stream) { 39 | notif.m.IncStreamCount() 40 | c := v.Conn() 41 | notif.log.Trace("opened stream", "protocol", v.Protocol(), "peer", c.RemotePeer(), "addr", c.RemoteMultiaddr()) 42 | } 43 | func (notif *notifications) ClosedStream(n network.Network, v network.Stream) { 44 | notif.m.DecStreamCount() 45 | c := v.Conn() 46 | notif.log.Trace("opened stream", "protocol", v.Protocol(), "peer", c.RemotePeer(), "addr", c.RemoteMultiaddr()) 47 | } 48 | 49 | func NewNetworkNotifier(log log.Logger, m metrics.Metricer) network.Notifiee { 50 | if m == nil { 51 | m = metrics.Noptmetrics 52 | } 53 | return ¬ifications{log: log, m: m} 54 | } 55 | -------------------------------------------------------------------------------- /pt-node/rollup/driver/conf_depth.go: -------------------------------------------------------------------------------- 1 | package driver 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum" 7 | "github.com/patex-ecosystem/patex-network/pt-node/eth" 8 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 9 | ) 10 | 11 | // confDepth is an util that wraps the L1 input fetcher used in the pipeline, 12 | // and hides the part of the L1 chain with insufficient confirmations. 13 | // 14 | // At 0 depth the l1 head is completely ignored. 15 | type confDepth struct { 16 | // everything fetched by hash is trusted already, so we implement those by embedding the fetcher 17 | derive.L1Fetcher 18 | l1Head func() eth.L1BlockRef 19 | depth uint64 20 | } 21 | 22 | func NewConfDepth(depth uint64, l1Head func() eth.L1BlockRef, fetcher derive.L1Fetcher) *confDepth { 23 | return &confDepth{L1Fetcher: fetcher, l1Head: l1Head, depth: depth} 24 | } 25 | 26 | // L1BlockRefByNumber is used for L1 traversal and for finding a safe common point between the L2 engine and L1 chain. 27 | // Any block numbers that are within confirmation depth of the L1 head are mocked to be "not found", 28 | // effectively hiding the uncertain part of the L1 chain. 29 | func (c *confDepth) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) { 30 | // TODO: performance optimization: buffer the l1Unsafe, invalidate any reorged previous buffer content, 31 | // and instantly return the origin by number from the buffer if we can. 32 | 33 | // Don't apply the conf depth is l1Head is empty (as it is during the startup case before the l1State is initialized). 34 | l1Head := c.l1Head() 35 | if l1Head == (eth.L1BlockRef{}) { 36 | return c.L1Fetcher.L1BlockRefByNumber(ctx, num) 37 | } 38 | if num == 0 || c.depth == 0 || num+c.depth <= l1Head.Number { 39 | return c.L1Fetcher.L1BlockRefByNumber(ctx, num) 40 | } 41 | return eth.L1BlockRef{}, ethereum.NotFound 42 | } 43 | 44 | var _ derive.L1Fetcher = (*confDepth)(nil) 45 | -------------------------------------------------------------------------------- /pt-node/eth/status.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ForkchoiceUpdateErr(payloadStatus PayloadStatusV1) error { 8 | switch payloadStatus.Status { 9 | case ExecutionSyncing: 10 | return fmt.Errorf("updated forkchoice, but node is syncing") 11 | case ExecutionAccepted, ExecutionInvalidTerminalBlock, ExecutionInvalidBlockHash: 12 | // ACCEPTED, INVALID_TERMINAL_BLOCK, INVALID_BLOCK_HASH are only for execution 13 | return fmt.Errorf("unexpected %s status, could not update forkchoice", payloadStatus.Status) 14 | case ExecutionInvalid: 15 | return fmt.Errorf("cannot update forkchoice, block is invalid") 16 | case ExecutionValid: 17 | return nil 18 | default: 19 | return fmt.Errorf("unknown forkchoice status: %q", string(payloadStatus.Status)) 20 | } 21 | } 22 | 23 | func NewPayloadErr(payload *ExecutionPayload, payloadStatus *PayloadStatusV1) error { 24 | switch payloadStatus.Status { 25 | case ExecutionValid: 26 | return nil 27 | case ExecutionSyncing: 28 | return fmt.Errorf("failed to execute payload %s, node is syncing", payload.ID()) 29 | case ExecutionInvalid: 30 | return fmt.Errorf("execution payload %s was INVALID! Latest valid hash is %s, ignoring bad block: %v", payload.ID(), payloadStatus.LatestValidHash, payloadStatus.ValidationError) 31 | case ExecutionInvalidBlockHash: 32 | return fmt.Errorf("execution payload %s has INVALID BLOCKHASH! %v", payload.BlockHash, payloadStatus.ValidationError) 33 | case ExecutionInvalidTerminalBlock: 34 | return fmt.Errorf("engine is misconfigured. Received invalid-terminal-block error while engine API should be active at genesis. err: %v", payloadStatus.ValidationError) 35 | case ExecutionAccepted: 36 | return fmt.Errorf("execution payload cannot be validated yet, latest valid hash is %s", payloadStatus.LatestValidHash) 37 | default: 38 | return fmt.Errorf("unknown execution status on %s: %q, ", payload.ID(), string(payloadStatus.Status)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pt-service/metrics/balance.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/ethclient" 10 | "github.com/ethereum/go-ethereum/log" 11 | "github.com/ethereum/go-ethereum/params" 12 | "github.com/prometheus/client_golang/prometheus" 13 | "github.com/prometheus/client_golang/prometheus/promauto" 14 | ) 15 | 16 | // weiToEther divides the wei value by 10^18 to get a number in ether as a float64 17 | func weiToEther(wei *big.Int) float64 { 18 | num := new(big.Rat).SetInt(wei) 19 | denom := big.NewRat(params.Ether, 1) 20 | num = num.Quo(num, denom) 21 | f, _ := num.Float64() 22 | return f 23 | } 24 | 25 | // LaunchBalanceMetrics fires off a go rountine that queries the balance of the supplied account & periodically records it 26 | // to the balance metric of the namespace. The balance of the account is recorded in Ether (not Wei). 27 | // Cancel the supplied context to shut down the go routine 28 | func LaunchBalanceMetrics(ctx context.Context, log log.Logger, r *prometheus.Registry, ns string, client *ethclient.Client, account common.Address) { 29 | go func() { 30 | balanceGuage := promauto.With(r).NewGauge(prometheus.GaugeOpts{ 31 | Namespace: ns, 32 | Name: "balance", 33 | Help: "balance (in ether) of account " + account.String(), 34 | }) 35 | 36 | ticker := time.NewTicker(10 * time.Second) 37 | defer ticker.Stop() 38 | 39 | for { 40 | select { 41 | case <-ticker.C: 42 | ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) 43 | bigBal, err := client.BalanceAt(ctx, account, nil) 44 | if err != nil { 45 | log.Warn("failed to get balance of account", "err", err, "address", account) 46 | cancel() 47 | continue 48 | } 49 | bal := weiToEther(bigBal) 50 | balanceGuage.Set(bal) 51 | cancel() 52 | case <-ctx.Done(): 53 | log.Info("balance metrics shutting down") 54 | return 55 | } 56 | } 57 | 58 | }() 59 | } 60 | -------------------------------------------------------------------------------- /pt-node/p2p/mocks/Peerstore.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.28.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | 8 | peer "github.com/libp2p/go-libp2p/core/peer" 9 | 10 | store "github.com/patex-ecosystem/patex-network/pt-node/p2p/store" 11 | ) 12 | 13 | // Peerstore is an autogenerated mock type for the Peerstore type 14 | type Peerstore struct { 15 | mock.Mock 16 | } 17 | 18 | // PeerInfo provides a mock function with given fields: _a0 19 | func (_m *Peerstore) PeerInfo(_a0 peer.ID) peer.AddrInfo { 20 | ret := _m.Called(_a0) 21 | 22 | var r0 peer.AddrInfo 23 | if rf, ok := ret.Get(0).(func(peer.ID) peer.AddrInfo); ok { 24 | r0 = rf(_a0) 25 | } else { 26 | r0 = ret.Get(0).(peer.AddrInfo) 27 | } 28 | 29 | return r0 30 | } 31 | 32 | // Peers provides a mock function with given fields: 33 | func (_m *Peerstore) Peers() peer.IDSlice { 34 | ret := _m.Called() 35 | 36 | var r0 peer.IDSlice 37 | if rf, ok := ret.Get(0).(func() peer.IDSlice); ok { 38 | r0 = rf() 39 | } else { 40 | if ret.Get(0) != nil { 41 | r0 = ret.Get(0).(peer.IDSlice) 42 | } 43 | } 44 | 45 | return r0 46 | } 47 | 48 | // SetScore provides a mock function with given fields: id, diff 49 | func (_m *Peerstore) SetScore(id peer.ID, diff store.ScoreDiff) error { 50 | ret := _m.Called(id, diff) 51 | 52 | var r0 error 53 | if rf, ok := ret.Get(0).(func(peer.ID, store.ScoreDiff) error); ok { 54 | r0 = rf(id, diff) 55 | } else { 56 | r0 = ret.Error(0) 57 | } 58 | 59 | return r0 60 | } 61 | 62 | type mockConstructorTestingTNewPeerstore interface { 63 | mock.TestingT 64 | Cleanup(func()) 65 | } 66 | 67 | // NewPeerstore creates a new instance of Peerstore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 68 | func NewPeerstore(t mockConstructorTestingTNewPeerstore) *Peerstore { 69 | mock := &Peerstore{} 70 | mock.Mock.Test(t) 71 | 72 | t.Cleanup(func() { mock.AssertExpectations(t) }) 73 | 74 | return mock 75 | } 76 | -------------------------------------------------------------------------------- /pt-batcher/batcher/tx_data.go: -------------------------------------------------------------------------------- 1 | package batcher 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/patex-ecosystem/patex-network/pt-node/rollup/derive" 7 | ) 8 | 9 | // txData represents the data for a single transaction. 10 | // 11 | // Note: The batcher currently sends exactly one frame per transaction. This 12 | // might change in the future to allow for multiple frames from possibly 13 | // different channels. 14 | type txData struct { 15 | frame frameData 16 | } 17 | 18 | // ID returns the id for this transaction data. It can be used as a map key. 19 | func (td *txData) ID() txID { 20 | return td.frame.id 21 | } 22 | 23 | // Bytes returns the transaction data. It's a version byte (0) followed by the 24 | // concatenated frames for this transaction. 25 | func (td *txData) Bytes() []byte { 26 | return append([]byte{derive.DerivationVersion0}, td.frame.data...) 27 | } 28 | 29 | func (td *txData) Len() int { 30 | return 1 + len(td.frame.data) 31 | } 32 | 33 | // Frame returns the single frame of this tx data. 34 | // 35 | // Note: when the batcher is changed to possibly send multiple frames per tx, 36 | // this should be changed to a func Frames() []frameData. 37 | func (td *txData) Frame() frameData { 38 | return td.frame 39 | } 40 | 41 | // txID is an opaque identifier for a transaction. 42 | // It's internal fields should not be inspected after creation & are subject to change. 43 | // This ID must be trivially comparable & work as a map key. 44 | // 45 | // Note: transactions currently only hold a single frame, so it can be 46 | // identified by the frame. This needs to be changed once the batcher is changed 47 | // to send multiple frames per tx. 48 | type txID = frameID 49 | 50 | func (id txID) String() string { 51 | return fmt.Sprintf("%s:%d", id.chID.String(), id.frameNumber) 52 | } 53 | 54 | // TerminalString implements log.TerminalStringer, formatting a string for console 55 | // output during logging. 56 | func (id txID) TerminalString() string { 57 | return fmt.Sprintf("%s:%d", id.chID.TerminalString(), id.frameNumber) 58 | } 59 | -------------------------------------------------------------------------------- /pt-node/rollup/derive/params.go: -------------------------------------------------------------------------------- 1 | package derive 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | // count the tagging info as 200 in terms of buffer size. 10 | const frameOverhead = 200 11 | 12 | // frameSize calculates the size of the frame + overhead for 13 | // storing the frame. The sum of the frame size of each frame in 14 | // a channel determines the channel's size. The sum of the channel 15 | // sizes is used for pruning & compared against `MaxChannelBankSize` 16 | func frameSize(frame Frame) uint64 { 17 | return uint64(len(frame.Data)) + frameOverhead 18 | } 19 | 20 | const DerivationVersion0 = 0 21 | 22 | // MaxChannelBankSize is the amount of memory space, in number of bytes, 23 | // till the bank is pruned by removing channels, 24 | // starting with the oldest channel. 25 | const MaxChannelBankSize = 100_000_000 26 | 27 | // MaxRLPBytesPerChannel is the maximum amount of bytes that will be read from 28 | // a channel. This limit is set when decoding the RLP. 29 | const MaxRLPBytesPerChannel = 10_000_000 30 | 31 | // DuplicateErr is returned when a newly read frame is already known 32 | var DuplicateErr = errors.New("duplicate frame") 33 | 34 | // ChannelIDLength defines the length of the channel IDs 35 | const ChannelIDLength = 16 36 | 37 | // ChannelID is an opaque identifier for a channel. It is 128 bits to be globally unique. 38 | type ChannelID [ChannelIDLength]byte 39 | 40 | func (id ChannelID) String() string { 41 | return fmt.Sprintf("%x", id[:]) 42 | } 43 | 44 | // TerminalString implements log.TerminalStringer, formatting a string for console output during logging. 45 | func (id ChannelID) TerminalString() string { 46 | return fmt.Sprintf("%x..%x", id[:3], id[13:]) 47 | } 48 | 49 | func (id ChannelID) MarshalText() ([]byte, error) { 50 | return []byte(id.String()), nil 51 | } 52 | 53 | func (id *ChannelID) UnmarshalText(text []byte) error { 54 | h, err := hex.DecodeString(string(text)) 55 | if err != nil { 56 | return err 57 | } 58 | if len(h) != ChannelIDLength { 59 | return errors.New("invalid length") 60 | } 61 | copy(id[:], h) 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /pt-e2e/e2eutils/waits.go: -------------------------------------------------------------------------------- 1 | package e2eutils 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum" 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/core/types" 12 | "github.com/ethereum/go-ethereum/ethclient" 13 | ) 14 | 15 | func WaitReceiptOK(ctx context.Context, client *ethclient.Client, hash common.Hash) (*types.Receipt, error) { 16 | return WaitReceipt(ctx, client, hash, types.ReceiptStatusSuccessful) 17 | } 18 | 19 | func WaitReceiptFail(ctx context.Context, client *ethclient.Client, hash common.Hash) (*types.Receipt, error) { 20 | return WaitReceipt(ctx, client, hash, types.ReceiptStatusFailed) 21 | } 22 | 23 | func WaitReceipt(ctx context.Context, client *ethclient.Client, hash common.Hash, status uint64) (*types.Receipt, error) { 24 | ticker := time.NewTicker(100 * time.Millisecond) 25 | defer ticker.Stop() 26 | for { 27 | receipt, err := client.TransactionReceipt(ctx, hash) 28 | if errors.Is(err, ethereum.NotFound) { 29 | select { 30 | case <-ctx.Done(): 31 | return nil, ctx.Err() 32 | case <-ticker.C: 33 | continue 34 | } 35 | } 36 | if err != nil { 37 | return nil, err 38 | } 39 | if receipt.Status != status { 40 | return receipt, fmt.Errorf("expected status %d, but got %d", status, receipt.Status) 41 | } 42 | return receipt, nil 43 | } 44 | } 45 | 46 | func WaitBlock(ctx context.Context, client *ethclient.Client, n uint64) error { 47 | for { 48 | height, err := client.BlockNumber(ctx) 49 | if err != nil { 50 | return err 51 | } 52 | if height < n { 53 | time.Sleep(500 * time.Millisecond) 54 | continue 55 | } 56 | break 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func WaitFor(ctx context.Context, rate time.Duration, cb func() (bool, error)) error { 63 | tick := time.NewTicker(rate) 64 | defer tick.Stop() 65 | 66 | for { 67 | select { 68 | case <-ctx.Done(): 69 | return ctx.Err() 70 | case <-tick.C: 71 | done, err := cb() 72 | if err != nil { 73 | return err 74 | } 75 | if done { 76 | return nil 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/alias_test.go: -------------------------------------------------------------------------------- 1 | package crossdomain_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/crossdomain" 11 | ) 12 | 13 | func FuzzAliasing(f *testing.F) { 14 | f.Fuzz(func(t *testing.T, address []byte) { 15 | addr := common.BytesToAddress(address) 16 | aliased := crossdomain.ApplyL1ToL2Alias(addr) 17 | unaliased := crossdomain.UndoL1ToL2Alias(aliased) 18 | require.Equal(t, addr, unaliased) 19 | }) 20 | } 21 | 22 | func TestAliasing(t *testing.T) { 23 | cases := []struct { 24 | Input common.Address 25 | Output common.Address 26 | }{ 27 | { 28 | Input: common.HexToAddress("0x24eb0f74a434b2f4f07744652630ce90367aab71"), 29 | Output: common.HexToAddress("0x35fc0f74a434b2f4f07744652630ce90367abc82"), 30 | }, 31 | { 32 | Input: common.HexToAddress("0xd3f11e293c353bd07ce8fd89e911180e12a7eb77"), 33 | Output: common.HexToAddress("0xe5021e293c353bd07ce8fd89e911180e12a7fc88"), 34 | }, 35 | { 36 | Input: common.HexToAddress("0xa900b52694dfa5de7255e9b0b6161ec3cc522fed"), 37 | Output: common.HexToAddress("0xba11b52694dfa5de7255e9b0b6161ec3cc5240fe"), 38 | }, 39 | { 40 | Input: common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"), 41 | Output: common.HexToAddress("0x1111000000000000000000000000000000001110"), 42 | }, 43 | { 44 | Input: common.HexToAddress("0x0000000000000000000000000000000000000041"), 45 | Output: common.HexToAddress("0x1111000000000000000000000000000000001152"), 46 | }, 47 | { 48 | Input: common.HexToAddress("0x4c0aa49c57716406043f97c087a72fe96397959b"), 49 | Output: common.HexToAddress("0x5d1ba49c57716406043f97c087a72fe96397a6ac"), 50 | }, 51 | } 52 | 53 | for i, test := range cases { 54 | t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) { 55 | aliased := crossdomain.ApplyL1ToL2Alias(test.Input) 56 | require.Equal(t, test.Output, aliased) 57 | unaliased := crossdomain.UndoL1ToL2Alias(aliased) 58 | require.Equal(t, test.Input, unaliased) 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /specs/meta/markdown-style.md: -------------------------------------------------------------------------------- 1 | # Markdown Style Guide 2 | 3 | 4 | 5 | **Table of Contents** 6 | 7 | - [Linting](#linting) 8 | - [Links](#links) 9 | - [Glossary](#glossary) 10 | - [Internal (In-File) Links](#internal-in-file-links) 11 | 12 | 13 | 14 | ## Linting 15 | 16 | Respect the [linting rules] (you can run the linter with `yarn lint`). 17 | 18 | Notably: 19 | 20 | - lines should be < 120 characters long 21 | - in practice, some of our files are justified at 100 characters, some at 120 22 | 23 | [linting rules]: linting.md#markdown 24 | 25 | ## Links 26 | 27 | In general: 28 | 29 | - Use link references preferentially. 30 | - e.g. `[my text][link-ref]` and then on its own line `[link-ref]: https://mylink.com` 31 | - e.g. `[my text]` and then on its own line: `[my text]: https://mylink.com` 32 | - exceptions: where it fits neatly on a single line, in particular in lists of links 33 | - Excepted for internal and glossary links (see below), add the link reference definition directly 34 | after the paragraph where the link first appears. 35 | 36 | ### Glossary 37 | 38 | - Use links to the [glossary] liberally. 39 | - Include the references to all the glossary links at the top of the file, under the top-level 40 | title. 41 | - A glossary link reference should start with the `g-` prefix. This enables to see what links to the 42 | glossary at a glance when editing the specification. 43 | - e.g. `[g-block]: glossary.md#block` 44 | - Example: [Rollup Node Specification source][rollup-node] 45 | 46 | [glossary]: ../glossary.md 47 | [rollup-node]: https://raw.githubusercontent.com/ethereum-Patex/patex-specs/main/specs/rollup-node.md 48 | 49 | ## Internal (In-File) Links 50 | 51 | If linking to another heading to the same file, add the link reference directly under that heading. 52 | This makes it easier to keep the heading and the link in-sync, and signals that the heading is being 53 | linked to from elsewhere. 54 | -------------------------------------------------------------------------------- /pt-node/metrics/caching.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/patex-ecosystem/patex-network/pt-service/metrics" 5 | "github.com/prometheus/client_golang/prometheus" 6 | ) 7 | 8 | // CacheMetrics implements the Metrics interface in the caching package, 9 | // implementing reusable metrics for different caches. 10 | type CacheMetrics struct { 11 | SizeVec *prometheus.GaugeVec 12 | GetVec *prometheus.CounterVec 13 | AddVec *prometheus.CounterVec 14 | } 15 | 16 | // CacheAdd meters the addition of an item with a given type to the cache, 17 | // metering the change of the cache size of that type, and indicating a corresponding eviction if any. 18 | func (m *CacheMetrics) CacheAdd(typeLabel string, typeCacheSize int, evicted bool) { 19 | m.SizeVec.WithLabelValues(typeLabel).Set(float64(typeCacheSize)) 20 | if evicted { 21 | m.AddVec.WithLabelValues(typeLabel, "true").Inc() 22 | } else { 23 | m.AddVec.WithLabelValues(typeLabel, "false").Inc() 24 | } 25 | } 26 | 27 | // CacheGet meters a lookup of an item with a given type to the cache 28 | // and indicating if the lookup was a hit. 29 | func (m *CacheMetrics) CacheGet(typeLabel string, hit bool) { 30 | if hit { 31 | m.GetVec.WithLabelValues(typeLabel, "true").Inc() 32 | } else { 33 | m.GetVec.WithLabelValues(typeLabel, "false").Inc() 34 | } 35 | } 36 | 37 | func NewCacheMetrics(factory metrics.Factory, ns string, name string, displayName string) *CacheMetrics { 38 | return &CacheMetrics{ 39 | SizeVec: factory.NewGaugeVec(prometheus.GaugeOpts{ 40 | Namespace: ns, 41 | Name: name + "_size", 42 | Help: displayName + " cache size", 43 | }, []string{ 44 | "type", 45 | }), 46 | GetVec: factory.NewCounterVec(prometheus.CounterOpts{ 47 | Namespace: ns, 48 | Name: name + "_get", 49 | Help: displayName + " lookups, hitting or not", 50 | }, []string{ 51 | "type", 52 | "hit", 53 | }), 54 | AddVec: factory.NewCounterVec(prometheus.CounterOpts{ 55 | Namespace: ns, 56 | Name: name + "_add", 57 | Help: displayName + " additions, evicting previous values or not", 58 | }, []string{ 59 | "type", 60 | "evicted", 61 | }), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ops-bedrock/patex-sepolia/rpc-node/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | # This Compose file is expected to be used with the testnet-up.sh script. 4 | # The volumes below mount the configs generated by the script into each 5 | # service. 6 | 7 | volumes: 8 | tl2_data: 9 | tpt_log: 10 | 11 | 12 | services: 13 | t_l2: 14 | image: patex-testnet-l2 15 | build: 16 | context: . 17 | dockerfile: Dockerfile.l2 18 | ports: 19 | - "19545:8545" 20 | - "18060:6060" 21 | - "19546:8546" 22 | volumes: 23 | - "tl2_data:/datadir" 24 | - "${PWD}/../../../.patex-sepolia/genesis.json:/genesis.json" 25 | - "${PWD}/../../../.patex-sepolia/jwt.txt:/config/jwt.txt" 26 | - "${PWD}/../../../.patex-sepolia/testnet.tar:/testnet.tar" 27 | entrypoint: # pass the L2 specific flags by overriding the entry-point and adding extra arguments 28 | - "/bin/sh" 29 | - "/entrypoint.sh" 30 | - "--authrpc.jwtsecret=/config/jwt.txt" 31 | 32 | t_pt-node: 33 | image: patex-testnet-pt-node 34 | depends_on: 35 | - t_l2 36 | build: 37 | context: ../../../ 38 | dockerfile: ./pt-node/Dockerfile 39 | command: > 40 | pt-node 41 | --l2=http://t_l2:8551 42 | --l2.jwt-secret=/config/jwt.txt 43 | --rpc.addr=0.0.0.0 44 | --rpc.port=8545 45 | --p2p.listen.ip=0.0.0.0 46 | --p2p.listen.tcp=9003 47 | --p2p.listen.udp=9003 48 | --p2p.static=/dns4/testnet.patex.io/tcp/9003/p2p/16Uiu2HAmL1aH6b7hvrx3BHPdRqk7d8ynf5p9F1Rhc5fTKBUu696F 49 | --p2p.scoring.peers=light 50 | --p2p.ban.peers=true 51 | --p2p.priv.path=/ptnode_p2p_priv.txt 52 | --rollup.config=/rollup.json 53 | --l1=https://ethereum-sepolia-archive.allthatnode.com 54 | --l1.rpckind=basic 55 | ports: 56 | - "17545:8545" 57 | - "19003:9003" 58 | - "17300:7300" 59 | - "16060:6060" 60 | volumes: 61 | - "${PWD}/../../../.patex-sepolia/jwt.txt:/config/jwt.txt" 62 | - "${PWD}/../../../.patex-sepolia/rollup.json:/rollup.json" 63 | - "${PWD}/../../../.patex-sepolia/ptnode_p2p_priv.txt:/ptnode_p2p_priv.txt" 64 | - tpt_log:/tpt_log 65 | -------------------------------------------------------------------------------- /pt-node/heartbeat/service.go: -------------------------------------------------------------------------------- 1 | // Package heartbeat provides a service for sending heartbeats to a server. 2 | package heartbeat 3 | 4 | import ( 5 | "bytes" 6 | "context" 7 | "encoding/json" 8 | "fmt" 9 | "net/http" 10 | "time" 11 | 12 | "github.com/ethereum/go-ethereum/log" 13 | ) 14 | 15 | // SendInterval determines the delay between requests. This must be larger than the MinHeartbeatInterval in the server. 16 | const SendInterval = 10 * time.Minute 17 | 18 | type Payload struct { 19 | Version string `json:"version"` 20 | Meta string `json:"meta"` 21 | Moniker string `json:"moniker"` 22 | PeerID string `json:"peerID"` 23 | ChainID uint64 `json:"chainID"` 24 | } 25 | 26 | // Beat sends a heartbeat to the server at the given URL. It will send a heartbeat immediately, and then every SendInterval. 27 | // Beat spawns a goroutine that will send heartbeats until the context is canceled. 28 | func Beat( 29 | ctx context.Context, 30 | log log.Logger, 31 | url string, 32 | payload *Payload, 33 | ) error { 34 | payloadJSON, err := json.Marshal(payload) 35 | if err != nil { 36 | return fmt.Errorf("telemetry crashed: %w", err) 37 | } 38 | 39 | client := &http.Client{ 40 | Timeout: 10 * time.Second, 41 | } 42 | 43 | send := func() { 44 | req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(payloadJSON)) 45 | req.Header.Set("User-Agent", fmt.Sprintf("pt-node/%s", payload.Version)) 46 | req.Header.Set("Content-Type", "application/json") 47 | if err != nil { 48 | log.Error("error creating heartbeat HTTP request", "err", err) 49 | return 50 | } 51 | res, err := client.Do(req) 52 | if err != nil { 53 | log.Warn("error sending heartbeat", "err", err) 54 | return 55 | } 56 | res.Body.Close() 57 | 58 | if res.StatusCode < 200 || res.StatusCode > 204 { 59 | log.Warn("heartbeat server returned non-200 status code", "status", res.StatusCode) 60 | return 61 | } 62 | 63 | log.Info("sent heartbeat") 64 | } 65 | 66 | send() 67 | tick := time.NewTicker(SendInterval) 68 | defer tick.Stop() 69 | for { 70 | select { 71 | case <-tick.C: 72 | send() 73 | case <-ctx.Done(): 74 | return nil 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pt-node/p2p/store/serialize_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestRoundtripScoresV0(t *testing.T) { 12 | scores := scoreRecord{ 13 | PeerScores: PeerScores{Gossip: GossipScores{Total: 1234.52382}}, 14 | LastUpdate: 1923841, 15 | } 16 | data, err := serializeScoresV0(scores) 17 | require.NoError(t, err) 18 | 19 | result, err := deserializeScoresV0(data) 20 | require.NoError(t, err) 21 | require.Equal(t, scores, result) 22 | } 23 | 24 | // TestParseHistoricSerializations checks that existing data can still be deserialized 25 | // Adding new fields should not require bumping the version. Removing fields may require bumping. 26 | // Scores should always default to 0. 27 | // A new entry should be added to this test each time any fields are changed to ensure it can always be deserialized 28 | func TestParseHistoricSerializationsV0(t *testing.T) { 29 | tests := []struct { 30 | data string 31 | expected scoreRecord 32 | }{ 33 | { 34 | data: `{"peerScores":{"gossip":{"total":1234.52382,"blocks":{"timeInMesh":1234,"firstMessageDeliveries":12,"meshMessageDeliveries":34,"invalidMessageDeliveries":56},"IPColocationFactor":12.34,"behavioralPenalty":56.78},"reqRespSync":123456},"lastUpdate":1923841}`, 35 | expected: scoreRecord{ 36 | PeerScores: PeerScores{ 37 | Gossip: GossipScores{ 38 | Total: 1234.52382, 39 | Blocks: TopicScores{ 40 | TimeInMesh: 1234, 41 | FirstMessageDeliveries: 12, 42 | MeshMessageDeliveries: 34, 43 | InvalidMessageDeliveries: 56, 44 | }, 45 | IPColocationFactor: 12.34, 46 | BehavioralPenalty: 56.78, 47 | }, 48 | ReqRespSync: 123456, 49 | }, 50 | LastUpdate: 1923841, 51 | }, 52 | }, 53 | } 54 | for idx, test := range tests { 55 | test := test 56 | out, _ := json.Marshal(&test.expected) 57 | t.Log(string(out)) 58 | t.Run(strconv.Itoa(idx), func(t *testing.T) { 59 | result, err := deserializeScoresV0([]byte(test.data)) 60 | require.NoError(t, err) 61 | require.Equal(t, test.expected, result) 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pt-e2e/e2eutils/addresses.go: -------------------------------------------------------------------------------- 1 | package e2eutils 2 | 3 | import ( 4 | "bytes" 5 | "sort" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core" 9 | 10 | "github.com/patex-ecosystem/patex-network/pt-bindings/predeploys" 11 | "github.com/patex-ecosystem/patex-network/pt-chain-ops/crossdomain" 12 | ) 13 | 14 | func collectAllocAddrs(alloc core.GenesisAlloc) []common.Address { 15 | var out []common.Address 16 | for addr := range alloc { 17 | out = append(out, addr) 18 | } 19 | // make output deterministic 20 | sort.Slice(out, func(i, j int) bool { 21 | return bytes.Compare(out[i][:], out[j][:]) < 0 22 | }) 23 | return out 24 | } 25 | 26 | // CollectAddresses constructs a lists of addresses that may be used as fuzzing corpora 27 | // or random address selection. 28 | func CollectAddresses(sd *SetupData, dp *DeployParams) (out []common.Address) { 29 | // This should be seeded with: 30 | // - reserve 0 for selecting nil (contract creation) 31 | out = append(out, common.Address{}) 32 | // - zero address 33 | out = append(out, common.Address{}) 34 | // - addresses of signing accounts 35 | out = append(out, dp.Addresses.All()...) 36 | // prefunded L1/L2 accounts for testing 37 | out = append(out, collectAllocAddrs(sd.L1Cfg.Alloc)...) 38 | out = append(out, collectAllocAddrs(sd.L2Cfg.Alloc)...) 39 | 40 | // - addresses of system contracts 41 | out = append(out, 42 | sd.L1Cfg.Coinbase, 43 | sd.L2Cfg.Coinbase, 44 | dp.Addresses.SequencerP2P, 45 | predeploys.SequencerFeeVaultAddr, 46 | sd.RollupCfg.BatchInboxAddress, 47 | sd.RollupCfg.Genesis.SystemConfig.BatcherAddr, 48 | sd.RollupCfg.DepositContractAddress, 49 | ) 50 | // - precompiles 51 | for i := 0; i <= 0xff; i++ { 52 | out = append(out, common.Address{19: byte(i)}) 53 | } 54 | // - masked L2 version of all the original addrs 55 | original := out[:] 56 | for _, addr := range original { 57 | masked := crossdomain.ApplyL1ToL2Alias(addr) 58 | out = append(out, masked) 59 | } 60 | // - unmasked L1 version of all the original addrs 61 | for _, addr := range original { 62 | unmasked := crossdomain.UndoL1ToL2Alias(addr) 63 | out = append(out, unmasked) 64 | } 65 | return out 66 | } 67 | -------------------------------------------------------------------------------- /pt-service/txmgr/mocks/TxManager.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.23.1. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | common "github.com/ethereum/go-ethereum/common" 9 | 10 | mock "github.com/stretchr/testify/mock" 11 | 12 | txmgr "github.com/patex-ecosystem/patex-network/pt-service/txmgr" 13 | 14 | types "github.com/ethereum/go-ethereum/core/types" 15 | ) 16 | 17 | // TxManager is an autogenerated mock type for the TxManager type 18 | type TxManager struct { 19 | mock.Mock 20 | } 21 | 22 | // From provides a mock function with given fields: 23 | func (_m *TxManager) From() common.Address { 24 | ret := _m.Called() 25 | 26 | var r0 common.Address 27 | if rf, ok := ret.Get(0).(func() common.Address); ok { 28 | r0 = rf() 29 | } else { 30 | if ret.Get(0) != nil { 31 | r0 = ret.Get(0).(common.Address) 32 | } 33 | } 34 | 35 | return r0 36 | } 37 | 38 | // Send provides a mock function with given fields: ctx, candidate 39 | func (_m *TxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (*types.Receipt, error) { 40 | ret := _m.Called(ctx, candidate) 41 | 42 | var r0 *types.Receipt 43 | var r1 error 44 | if rf, ok := ret.Get(0).(func(context.Context, txmgr.TxCandidate) (*types.Receipt, error)); ok { 45 | return rf(ctx, candidate) 46 | } 47 | if rf, ok := ret.Get(0).(func(context.Context, txmgr.TxCandidate) *types.Receipt); ok { 48 | r0 = rf(ctx, candidate) 49 | } else { 50 | if ret.Get(0) != nil { 51 | r0 = ret.Get(0).(*types.Receipt) 52 | } 53 | } 54 | 55 | if rf, ok := ret.Get(1).(func(context.Context, txmgr.TxCandidate) error); ok { 56 | r1 = rf(ctx, candidate) 57 | } else { 58 | r1 = ret.Error(1) 59 | } 60 | 61 | return r0, r1 62 | } 63 | 64 | type mockConstructorTestingTNewTxManager interface { 65 | mock.TestingT 66 | Cleanup(func()) 67 | } 68 | 69 | // NewTxManager creates a new instance of TxManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 70 | func NewTxManager(t mockConstructorTestingTNewTxManager) *TxManager { 71 | mock := &TxManager{} 72 | mock.Mock.Test(t) 73 | 74 | t.Cleanup(func() { mock.AssertExpectations(t) }) 75 | 76 | return mock 77 | } 78 | -------------------------------------------------------------------------------- /pt-chain-ops/crossdomain/types.go: -------------------------------------------------------------------------------- 1 | package crossdomain 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/accounts/abi" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | ) 10 | 11 | // DangerousUnfilteredWithdrawals is a list of raw withdrawal witness 12 | // data. It has not been filtered for messages from sources other than 13 | // the 14 | type DangerousUnfilteredWithdrawals []*LegacyWithdrawal 15 | 16 | // SafeFilteredWithdrawals is a list of withdrawals that have been filtered to only include 17 | // withdrawals that were from the L2XDM. 18 | type SafeFilteredWithdrawals []*LegacyWithdrawal 19 | 20 | var ( 21 | // Standard ABI types 22 | Uint256Type, _ = abi.NewType("uint256", "", nil) 23 | BytesType, _ = abi.NewType("bytes", "", nil) 24 | AddressType, _ = abi.NewType("address", "", nil) 25 | Bytes32Type, _ = abi.NewType("bytes32", "", nil) 26 | ) 27 | 28 | // WithdrawalMessage represents a Withdrawal. The Withdrawal 29 | // and LegacyWithdrawal types must implement this interface. 30 | type WithdrawalMessage interface { 31 | Encode() ([]byte, error) 32 | Decode([]byte) error 33 | Hash() (common.Hash, error) 34 | StorageSlot() (common.Hash, error) 35 | } 36 | 37 | // InvalidMessage represents a message to the L1 message passer that 38 | // cannot be decoded as a withdrawal. They are defined as a separate 39 | // type in order to completely disambiguate them from any other 40 | // message. 41 | type InvalidMessage SentMessage 42 | 43 | func (msg *InvalidMessage) Encode() ([]byte, error) { 44 | out := make([]byte, len(msg.Msg)+20) 45 | copy(out, msg.Msg) 46 | copy(out[len(msg.Msg):], msg.Who.Bytes()) 47 | return out, nil 48 | } 49 | 50 | func (msg *InvalidMessage) Hash() (common.Hash, error) { 51 | bytes, err := msg.Encode() 52 | if err != nil { 53 | return common.Hash{}, fmt.Errorf("cannot hash: %w", err) 54 | } 55 | return crypto.Keccak256Hash(bytes), nil 56 | } 57 | 58 | func (msg *InvalidMessage) StorageSlot() (common.Hash, error) { 59 | hash, err := msg.Hash() 60 | if err != nil { 61 | return common.Hash{}, fmt.Errorf("cannot compute storage slot: %w", err) 62 | } 63 | preimage := make([]byte, 64) 64 | copy(preimage, hash.Bytes()) 65 | 66 | return crypto.Keccak256Hash(preimage), nil 67 | } 68 | -------------------------------------------------------------------------------- /ops-bedrock/mainnet/rpc-node/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | # This Compose file is expected to be used with the mainnet-up.sh script. 4 | # The volumes below mount the configs generated by the script into each 5 | # service. 6 | 7 | volumes: 8 | l2_data: 9 | pt_log: 10 | 11 | 12 | services: 13 | m_l2: 14 | image: patex-mainnet-l2 15 | build: 16 | context: . 17 | dockerfile: Dockerfile.l2 18 | ports: 19 | - "9545:8545" 20 | - "8060:6060" 21 | - "9546:8546" 22 | volumes: 23 | - "l2_data:/datadir" 24 | - "${PWD}/../../../.mainnet/genesis.json:/genesis.json" 25 | - "${PWD}/../../../.mainnet/jwt.txt:/config/jwt.txt" 26 | - "${PWD}/../../../.mainnet/mainnet.tar:/mainnet.tar" 27 | entrypoint: # pass the L2 specific flags by overriding the entry-point and adding extra arguments 28 | - "/bin/sh" 29 | - "/entrypoint.sh" 30 | - "--authrpc.jwtsecret=/config/jwt.txt" 31 | 32 | m_pt-node: 33 | image: patex-mainnet-pt-node 34 | depends_on: 35 | - m_l2 36 | build: 37 | context: ../../../ 38 | dockerfile: ./pt-node/Dockerfile 39 | command: > 40 | pt-node 41 | --l2=http://m_l2:8551 42 | --l2.jwt-secret=/config/jwt.txt 43 | --rpc.addr=0.0.0.0 44 | --rpc.port=8545 45 | --p2p.listen.ip=0.0.0.0 46 | --p2p.listen.tcp=9003 47 | --p2p.listen.udp=9003 48 | --p2p.static=/dns4/p2p.patex.io/tcp/9003/p2p/16Uiu2HAmAFAZiabLuxib2DtF4PQYpURgVXviW4XurBMC1cmAfGKf,/dns4/p2p.patex.io/tcp/9003/p2p/16Uiu2HAmQdiuEfCPd81hMddzFpU4zrjv8yEYatPw6JA2uqpBgYeQ,/dns4/p2p.patex.io/tcp/9003/p2p/16Uiu2HAm1h26yjHVpZzAt4isjkXHRiaVp4YoxP2URjnqVaoyrieS 49 | --p2p.scoring.peers=light 50 | --p2p.ban.peers=true 51 | --p2p.priv.path=/ptnode_p2p_priv.txt 52 | --rollup.config=/rollup.json 53 | --l1=https://eth-mainnet.g.alchemy.com/v2/iueHs2ZoprYdchPkt7_EHXNG9RX0wfLr 54 | --l1.rpckind=alchemy 55 | ports: 56 | - "7545:8545" 57 | - "9003:9003" 58 | - "7300:7300" 59 | - "6060:6060" 60 | volumes: 61 | - "${PWD}/../../../.mainnet/jwt.txt:/config/jwt.txt" 62 | - "${PWD}/../../../.mainnet/rollup.json:/rollup.json" 63 | - "${PWD}/../../../.mainnet/ptnode_p2p_priv.txt:/ptnode_p2p_priv.txt" 64 | - pt_log:/pt_log 65 | -------------------------------------------------------------------------------- /pt-node/Makefile: -------------------------------------------------------------------------------- 1 | GITCOMMIT := $(shell git rev-parse HEAD) 2 | GITDATE := $(shell git show -s --format='%ct') 3 | VERSION := v0.0.0 4 | 5 | LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) 6 | LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) 7 | LDFLAGSSTRING +=-X github.com/patex-ecosystem/patex-network/pt-node/version.Version=$(VERSION) 8 | LDFLAGSSTRING +=-X github.com/patex-ecosystem/patex-network/pt-node/version.Meta=$(VERSION_META) 9 | LDFLAGS := -ldflags "$(LDFLAGSSTRING)" 10 | 11 | pt-node: 12 | env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/pt-node ./cmd/main.go 13 | 14 | clean: 15 | rm bin/pt-node 16 | 17 | test: 18 | go test -v ./... 19 | 20 | lint: 21 | golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" 22 | 23 | fuzz: 24 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadUnmarshal ./eth 25 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzExecutionPayloadMarshalUnmarshal ./eth 26 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzOBP01 ./eth 27 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoRoundTrip ./rollup/derive 28 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoAgainstContract ./rollup/derive 29 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzUnmarshallLogEvent ./rollup/derive 30 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseFrames ./rollup/derive 31 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFrameUnmarshalBinary ./rollup/derive 32 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzBatchRoundTrip ./rollup/derive 33 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsRoundTrip ./rollup/derive 34 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsBadVersion ./rollup/derive 35 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataValid ./rollup/derive 36 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataBadLength ./rollup/derive 37 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzRejectCreateBlockBadTimestamp ./rollup/driver 38 | go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDecodeDepositTxDataToL1Info ./rollup/driver 39 | 40 | 41 | .PHONY: \ 42 | pt-node \ 43 | clean \ 44 | test \ 45 | lint \ 46 | fuzz 47 | --------------------------------------------------------------------------------