├── .codacy.yml ├── .dockerignore ├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── release.yml │ └── test_build.yml ├── .gitignore ├── .goreleaser.yml ├── .run └── testnet.run.xml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYING.LESSER ├── Dockerfile.release ├── FUZZING.md ├── Makefile ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── _config.yml ├── appveyor.yml ├── cmd ├── cmdtest │ └── test_cmd.go └── opera │ ├── launcher │ ├── accountcmd.go │ ├── accountcmd_test.go │ ├── chaincmd.go │ ├── check.go │ ├── config.go │ ├── config_custom.go │ ├── config_custom_test.go │ ├── consolecmd.go │ ├── consolecmd_test.go │ ├── db-transform.go │ ├── dbcmd.go │ ├── defaults.go │ ├── export.go │ ├── fake.go │ ├── fake_test.go │ ├── fixdirty.go │ ├── genesiscmd.go │ ├── import.go │ ├── launcher.go │ ├── metrics │ │ └── metrics.go │ ├── misccmd.go │ ├── params.go │ ├── run_test.go │ ├── snapshotcmd.go │ ├── testdata │ │ ├── dupes │ │ │ ├── 1 │ │ │ ├── 2 │ │ │ └── foo │ │ ├── empty.js │ │ ├── guswallet.json │ │ ├── keystore │ │ │ ├── .hiddenfile │ │ │ ├── README │ │ │ ├── UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 │ │ │ ├── aaa │ │ │ ├── empty │ │ │ ├── foo │ │ │ │ └── fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e │ │ │ ├── garbage │ │ │ ├── no-address │ │ │ ├── zero │ │ │ └── zzz │ │ ├── passwords.txt │ │ └── wrong-passwords.txt │ ├── testnet.go │ ├── tracing │ │ └── tracing.go │ ├── usage.go │ ├── validator.go │ ├── validatorcmd.go │ ├── valkeystore.go │ ├── x1testnet.go │ └── xenblocks.go │ └── main.go ├── debug ├── api.go ├── flags.go ├── loudpanic.go ├── loudpanic_fallback.go ├── trace.go └── trace_fallback.go ├── demo ├── .gitignore ├── README.md ├── _params.sh ├── clean.sh ├── start.sh └── stop.sh ├── docker ├── Dockerfile.x1 └── networksimulator │ ├── README.md │ ├── generate.py │ ├── grafana │ ├── etc │ │ └── grafana │ │ │ └── provisioning │ │ │ ├── dashboards │ │ │ └── main.yaml │ │ │ └── datasources │ │ │ └── datasource.yml │ └── var │ │ └── lib │ │ └── grafana │ │ └── dashboards │ │ ├── home.json │ │ └── host.json │ ├── prometheus │ └── .gitignore │ ├── requirements.txt │ ├── templates │ ├── docker-compose.yaml.j2 │ ├── grafana.conf.j2 │ └── prometheus.yml.j2 │ ├── txblaster.py │ └── update_network_version.py ├── docs └── validators │ ├── README.md │ ├── connect-wallet.png │ └── create-validator.png ├── ethapi ├── README.md ├── abft_api.go ├── addrlock.go ├── api.go ├── backend.go ├── dag_api.go └── transaction_args.go ├── eventcheck ├── all.go ├── ban.go ├── basiccheck │ └── basic_check.go ├── bvallcheck │ └── all_check.go ├── epochcheck │ └── epoch_check.go ├── evallcheck │ └── evallcheck.go ├── gaspowercheck │ └── gas_power_check.go ├── heavycheck │ ├── adapters.go │ ├── config.go │ └── heavy_check.go ├── parentlesscheck │ └── parentless_check.go └── parentscheck │ └── parents_check.go ├── evmcore ├── .gitignore ├── apply_fake_genesis.go ├── bench_test.go ├── blocks.go ├── chain_makers.go ├── dummy_block.go ├── error.go ├── evm.go ├── gaspool.go ├── helper_test.go ├── notify.go ├── state_prefetcher.go ├── state_processor.go ├── state_transition.go ├── tx_cacher.go ├── tx_journal.go ├── tx_list.go ├── tx_list_test.go ├── tx_noncer.go ├── tx_pool.go ├── tx_pool_test.go └── types.go ├── flags └── helpers.go ├── ftmclient ├── dag_api.go └── ethclient.go ├── go.mod ├── go.sum ├── gossip ├── api.go ├── apply_genesis.go ├── basiccheck_test.go ├── blockproc │ ├── drivermodule │ │ └── driver_txs.go │ ├── eventmodule │ │ └── confirmed_events_processor.go │ ├── evmmodule │ │ └── evm.go │ ├── interface.go │ ├── sealmodule │ │ └── sealer.go │ └── verwatcher │ │ ├── store.go │ │ ├── store_network_version.go │ │ └── version_watcher.go ├── c_block_callbacks.go ├── c_block_callbacks_test.go ├── c_event_callbacks.go ├── c_llr_callbacks.go ├── c_llr_callbacks_test.go ├── checker_helpers.go ├── common_test.go ├── config.go ├── contract │ ├── ballot │ │ ├── Ballot.go │ │ ├── Ballot.sol │ │ ├── ballot.abi │ │ └── ballot.bin │ ├── contract.go │ ├── driver100 │ │ └── contract.go │ ├── driverauth100 │ │ └── contract.go │ ├── netinit100 │ │ └── contract.go │ ├── sfc100 │ │ └── contract.go │ ├── sfclib100 │ │ └── contract.go │ └── solc │ │ └── .gitignore ├── discover.go ├── dummy_tx_pool.go ├── emitter │ ├── config.go │ ├── control.go │ ├── emitter.go │ ├── emitter_llr.go │ ├── emitter_test.go │ ├── hooks.go │ ├── mock │ │ └── world.go │ ├── originatedtxs │ │ └── txs_ring_buffer.go │ ├── parents.go │ ├── piecefuncs.go │ ├── prev_action_files.go │ ├── sync.go │ ├── txs.go │ ├── validators.go │ └── world.go ├── emitter_world.go ├── enr_entry.go ├── eth_state_adapter.go ├── ethapi_backend.go ├── evm_state_reader.go ├── evm_test.go ├── evmstore │ ├── apply_genesis.go │ ├── config.go │ ├── evmpruner │ │ ├── bloom.go │ │ ├── exact.go │ │ └── pruner.go │ ├── store.go │ ├── store_block_cache.go │ ├── store_receipts.go │ ├── store_receipts_test.go │ ├── store_test.go │ ├── store_tx.go │ ├── store_tx_position.go │ └── utils.go ├── execqueue.go ├── filters │ ├── api.go │ ├── api_test.go │ ├── filter.go │ ├── filter_system.go │ ├── filter_system_test.go │ └── filter_test.go ├── gasprice │ ├── constructive.go │ ├── gasprice.go │ ├── gasprice_test.go │ └── reactive.go ├── gpo_backend.go ├── handler.go ├── handler_fuzz.go ├── handler_snap.go ├── heavycheck_test.go ├── mps_test.go ├── peer.go ├── peerset.go ├── proclogger │ ├── dag_logger.go │ └── llr_logger.go ├── protocol.go ├── protocols │ ├── blockrecords │ │ ├── brprocessor │ │ │ ├── config.go │ │ │ └── processor.go │ │ └── brstream │ │ │ ├── brstreamleecher │ │ │ ├── config.go │ │ │ └── leecher.go │ │ │ ├── brstreamseeder │ │ │ ├── config.go │ │ │ └── seeder.go │ │ │ └── types.go │ ├── blockvotes │ │ ├── bvprocessor │ │ │ ├── config.go │ │ │ └── processor.go │ │ └── bvstream │ │ │ ├── bvstreamleecher │ │ │ ├── config.go │ │ │ └── leecher.go │ │ │ ├── bvstreamseeder │ │ │ ├── config.go │ │ │ └── seeder.go │ │ │ └── types.go │ ├── dag │ │ └── dagstream │ │ │ ├── dagstreamleecher │ │ │ ├── config.go │ │ │ ├── leecher.go │ │ │ └── leecher_test.go │ │ │ ├── dagstreamseeder │ │ │ ├── config.go │ │ │ └── seeder.go │ │ │ └── types.go │ ├── epochpacks │ │ ├── epprocessor │ │ │ ├── config.go │ │ │ └── processor.go │ │ └── epstream │ │ │ ├── epstreamleecher │ │ │ ├── config.go │ │ │ └── leecher.go │ │ │ ├── epstreamseeder │ │ │ ├── config.go │ │ │ └── seeder.go │ │ │ └── types.go │ └── snap │ │ ├── api.go │ │ ├── events.go │ │ └── snapstream │ │ └── snapleecher │ │ ├── leecher.go │ │ ├── peer.go │ │ ├── statesync.go │ │ └── types.go ├── randselect.go ├── service.go ├── sfc_test.go ├── store.go ├── store_activation_heights.go ├── store_block.go ├── store_decided_state.go ├── store_epoch.go ├── store_event.go ├── store_heads.go ├── store_last_bv.go ├── store_last_ev.go ├── store_last_events.go ├── store_llr_block.go ├── store_llr_epoch.go ├── store_llr_state.go ├── store_migration.go ├── sync.go └── tflusher.go ├── integration ├── assembly.go ├── bench_db_flush_test.go ├── db.go ├── diskusage.go ├── diskusage_openbsd.go ├── diskusage_windows.go ├── legacy_migrate.go ├── makefakegenesis │ └── genesis.go ├── makegenesis │ └── genesis.go ├── makeoriginaltestnetgenesis │ ├── genesis.go │ └── rules.go ├── maketestnetgenesis │ └── genesis.go ├── metric.go ├── metric_test.go ├── presets.go ├── routing.go ├── status.go └── xenblocks │ └── reporter │ ├── stack.go │ └── xenblocks.go ├── inter ├── block.go ├── drivertype │ └── driver_type.go ├── event.go ├── event_serializer.go ├── event_serializer_test.go ├── event_test.go ├── events.go ├── gas_power_left.go ├── gas_power_left_test.go ├── iblockproc │ ├── decided_state.go │ ├── legacy.go │ └── profiles.go ├── ibr │ └── inter_block_records.go ├── iep │ └── inter_epoch_packs.go ├── ier │ └── inter_epoch_records.go ├── inter_llr.go ├── inter_llr_test.go ├── inter_mps.go ├── signature.go ├── time.go ├── transaction_serializer.go ├── transaction_serializer_test.go └── validatorpk │ ├── pubkey.go │ └── pubkey_test.go ├── logger ├── instance.go ├── logger.go ├── logrus.go ├── periodic_log.go └── test_output.go ├── opera ├── contracts │ ├── driver │ │ ├── driver_predeploy.go │ │ ├── drivercall │ │ │ └── driver_calls.go │ │ └── driverpos │ │ │ └── positions.go │ ├── driverauth │ │ └── driverauth.go │ ├── emitterdriver │ │ └── emitterdriver.go │ ├── evmwriter │ │ ├── evm_writer.go │ │ └── evm_writer_test.go │ ├── netinit │ │ ├── netinitcalls │ │ │ └── network_initializer_calls.go │ │ └── network_initializer.go │ ├── sfc │ │ └── sfc_predeploy.go │ └── sfclib │ │ └── sfclib_predeploy.go ├── genesis │ ├── gpos │ │ └── validators.go │ └── types.go ├── genesisstore │ ├── disk.go │ ├── filelog │ │ └── filelog.go │ ├── fileshash │ │ ├── filehash_test.go │ │ ├── reader_file.go │ │ ├── reader_map.go │ │ └── write_file.go │ ├── readersmap │ │ └── reader_map.go │ ├── store.go │ └── store_genesis.go ├── legacy_serialization.go ├── marshal.go ├── marshal_test.go └── rules.go ├── sonar-project.properties ├── system ├── etc │ ├── default │ │ └── x1 │ └── x1 │ │ └── config.toml ├── lib │ └── systemd │ │ └── system │ │ └── x1.service ├── usr │ └── share │ │ └── x1 │ │ └── configs │ │ └── testnet │ │ ├── api-node.toml │ │ ├── archive-node.toml │ │ ├── full-node.toml │ │ └── validator-node.toml ├── x1-post-install.sh └── x1-pre-install.sh ├── topicsdb ├── index.go ├── key.go ├── key_test.go ├── record.go ├── search_parallel.go ├── search_test.go ├── sync.go ├── thread_pool.go ├── topicsdb.go └── topicsdb_test.go ├── tracing └── tx-tracing.go ├── utils ├── adapters │ ├── ethdb2kvdb │ │ └── adapter.go │ ├── kvdb2ethdb │ │ └── adapter.go │ ├── snap2kvdb │ │ └── adapter.go │ └── vecmt2dagidx │ │ └── vecmt2lachesis.go ├── bitmap │ └── bitset.go ├── bits │ ├── bits.go │ └── bits_test.go ├── concurrent │ ├── ValidatorBlocksSet.go │ ├── ValidatorEpochsSet.go │ ├── ValidatorEventsSet.go │ └── events.go ├── cser │ ├── binary.go │ ├── binary_test.go │ ├── read_writer.go │ └── read_writer_test.go ├── dbutil │ ├── asyncflushproducer │ │ ├── producer.go │ │ └── store.go │ ├── autocompact │ │ ├── store.go │ │ └── strategy.go │ ├── compactdb │ │ ├── compactdb.go │ │ ├── keys.go │ │ ├── keys_test.go │ │ └── log.go │ ├── dbcounter │ │ └── dbcounter.go │ ├── itergc │ │ └── iterator_gc.go │ ├── switchable │ │ ├── snapshot.go │ │ └── snapshot_test.go │ └── threads │ │ ├── counted.go │ │ ├── pool.go │ │ └── pool_test.go ├── devnullfile │ └── devnull.go ├── errlock │ └── errlock.go ├── eventid │ └── eventid.go ├── fast │ ├── buffer.go │ └── buffer_test.go ├── file.go ├── iodb │ └── io_db.go ├── ioread │ └── ioread.go ├── memory │ ├── LICENSE │ ├── doc.go │ ├── memory_bsd.go │ ├── memory_darwin.go │ ├── memory_linux.go │ ├── memory_test.go │ ├── memory_windows.go │ ├── memsysctl.go │ └── stub.go ├── migration │ ├── id_store.go │ ├── kvdb_id_store.go │ ├── migration.go │ └── migration_test.go ├── nameof.go ├── num_queue.go ├── num_queue_test.go ├── pretty_duration.go ├── pretty_duration_test.go ├── randat │ └── rand_at.go ├── rate │ ├── gauge.go │ └── gauge_test.go ├── rlpstore │ └── rlpstore.go ├── self-table.go ├── signers │ ├── gsignercache │ │ └── global_cache.go │ └── internaltx │ │ ├── internaltx.go │ │ └── internaltx_test.go ├── spin_lock.go ├── to_ftm.go ├── txtime │ └── txtime.go ├── uint2hash.go ├── weighted_shuffle.go ├── weighted_shuffle_test.go └── wgmutex │ └── wg_mutex.go ├── valkeystore ├── cache.go ├── common_test.go ├── default.go ├── encryption │ ├── encryption.go │ ├── io.go │ └── migration.go ├── files.go ├── files_test.go ├── keystore.go ├── mem.go ├── mem_test.go ├── signer.go └── sync.go ├── vecmt ├── index.go ├── index_test.go ├── median_time.go ├── median_time_test.go ├── no_cheaters.go ├── store_vectors.go ├── vector.go └── vector_ops.go └── version ├── version.go └── version_test.go /.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | rubocop: 4 | enabled: true 5 | exclude_paths: 6 | - config/engines.yml 7 | duplication: 8 | enabled: true 9 | exclude_paths: 10 | - config/engines.yml 11 | metrics: 12 | enabled: true 13 | exclude_paths: 14 | - config/engines.yml 15 | coverage: 16 | enabled: true 17 | exclude_paths: 18 | - config/engines.yml 19 | languages: 20 | css: 21 | extensions: 22 | - '-css.resource' 23 | exclude_paths: 24 | - '.bundle/**' 25 | - 'spec/**/*' 26 | - 'benchmarks/**/*' 27 | - '*.min.js' 28 | - '**/*.pb.go' 29 | - '**/tests/**' 30 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | 8 | [*.go] 9 | indent_style = tab 10 | tab_width = 4 11 | 12 | [Makefile] 13 | indent_style = tab 14 | 15 | [*.yml] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.proto] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Golang files: 2 | *.go @andrecronje 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/test_build.yml: -------------------------------------------------------------------------------- 1 | name: Check build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: 7 | - develop 8 | - master 9 | 10 | jobs: 11 | check-build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Golang dependency 18 | uses: actions/setup-go@v3 19 | with: 20 | go-version: '^1.18' 21 | 22 | - name: Run unit tests 23 | run: go test ./... 24 | 25 | - name: Build 26 | run: make x1 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS 2 | .DS_Store 3 | 4 | # IDE 5 | .vscode 6 | .idea/ 7 | 8 | # Go prof output 9 | *.prof 10 | 11 | # Test files 12 | *.rlp 13 | 14 | # Go test output 15 | */*.test 16 | */*.out 17 | fuzzing/ 18 | 19 | # Build output 20 | build 21 | dist 22 | 23 | # Dependencies 24 | vendor 25 | glide.lock 26 | 27 | # patches and diff files 28 | *.patch 29 | *.diff 30 | 31 | # gomobile compilation output 32 | *.aar 33 | *.jar 34 | 35 | # archives 36 | *.tar.gz 37 | *.tgz 38 | *.zip 39 | 40 | # opera project specific 41 | scripts/certs 42 | scripts/nodes 43 | scripts/peers.json 44 | scripts/docker/upx 45 | nodes 46 | peers.json 47 | certs 48 | lachesis_* 49 | !*.go 50 | *.log 51 | *.graph 52 | *.gv 53 | *.finality 54 | testnet.g 55 | 56 | # Backup copies 57 | *~ 58 | *.bak 59 | 60 | .#* 61 | 62 | **/coverage.txt 63 | 64 | dist/ 65 | /docker/networksimulator/docker-compose.yaml 66 | /docker/networksimulator/prometheus/prometheus.yml 67 | -------------------------------------------------------------------------------- /.run/testnet.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.14.x 5 | 6 | go_import_path: github.com/Fantom-foundation/go-opera 7 | 8 | cache: 9 | directories: 10 | - $GOPATH/pkg 11 | 12 | env: 13 | global: 14 | - GO111MODULE=on 15 | 16 | # install: skip 17 | 18 | script: 19 | - go build -v ./... 20 | 21 | after_success: 22 | - | 23 | if [ -n "$GO_TEST" ]; then 24 | go test -coverprofile=coverage.txt -covermode=atomic ./... 25 | elif [ -n "$GO_INTEGRATION_TEST" ]; then 26 | go test ./integration/ 27 | fi 28 | 29 | after_script: 30 | - | 31 | if [ -n "$GO_TEST" ]; then 32 | bash <(curl -s https://codecov.io/bash) 33 | fi 34 | 35 | matrix: 36 | include: 37 | - name: go test 38 | env: 39 | - GO_TEST=1 40 | 41 | - name: testnet 42 | env: 43 | - GO_INTEGRATION_TEST=1 44 | -------------------------------------------------------------------------------- /Dockerfile.release: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | RUN set -x \ 4 | && apk add --update --no-cache \ 5 | ca-certificates \ 6 | && rm -rf /var/cache/apk/* 7 | COPY x1 /usr/local/bin/ 8 | 9 | EXPOSE 18545 18546 5050 10 | ENTRYPOINT ["x1"] 11 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please check if what you want to add to `go-opera` list meets [quality standards](https://github.com/Fantom-foundation/go-opera/blob/master/CONTRIBUTING.md#quality-standard) before sending pull request. 2 | 3 | **Please provide package links to:** 4 | 5 | - github.com repo: 6 | - godoc.org: 7 | - goreportcard.com: 8 | - coverage service link ([cover.run](https://cover.run/), [gocover](http://gocover.io/), [coveralls](https://coveralls.io/) etc.), example: `[![cover.run](https://cover.run/go/github.com/user/repository.svg?style=flat&tag=golang-1.10)](https://cover.run/go?tag=golang-1.10&repo=github.com%2Fuser%2Frepository)` 9 | 10 | **Make sure that you've checked the boxes below before you submit PR:** 11 | - [ ] I have added my package in alphabetical order. 12 | - [ ] I have an appropriate description with correct grammar. 13 | - [ ] I know that this package was not listed before. 14 | - [ ] I have added godoc link to the repo and to my pull request. 15 | - [ ] I have added coverage service link to the repo and to my pull request. 16 | - [ ] I have added goreportcard link to the repo and to my pull request. 17 | - [ ] I have read [Contribution guidelines](https://github.com/Fantom-foundation/go-opera/blob/master/CONTRIBUTING.md#contribution-guidelines), [maintainers note](https://github.com/Fantom-foundation/go-opera/blob/master/CONTRIBUTING.md#maintainers) and [Quality standard](https://github.com/Fantom-foundation/go-opera/blob/master/CONTRIBUTING.md#quality-standard). 18 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: Xen Crypto Documentation 2 | description: XEN is a cryptocurrency for the masses. It is designed and built to deliver on the First Principles of Crypto. 3 | url: https://docs.xen.network/ 4 | 5 | author: 6 | name: Fair Crypto Foundation 7 | url: https://faircrypto.org 8 | 9 | plugins: 10 | - jekyll-remote-theme 11 | - jekyll-titles-from-headings 12 | remote_theme: FairCrypto/minima 13 | minima: 14 | skin: auto 15 | social_links: 16 | - { platform: github, user_url: "https://github.com/FairCrypto/go-x1" } 17 | - { platform: twitter, user_url: "https://twitter.com/XEN_Crypto" } 18 | - { platform: telegram, user_url: "https://t.me/XENCryptoTalk" } 19 | - { platform: youtube, user_url: "https://m.youtube.com/channel/UCiw5nyHHt9BPHvoRbcGNehA/playlists" } 20 | show_excerpts: false 21 | 22 | titles_from_headings: 23 | enabled: true 24 | strip_title: true 25 | collections: false 26 | 27 | header_pages: 28 | - index.md 29 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.4.5-rc1#{build} 2 | image: Visual Studio 2017 3 | 4 | build: off 5 | 6 | clone_folder: c:\gopath\src\github.com\Fantom-foundation\go-opera 7 | 8 | environment: 9 | PATH: C:\gopath\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin;C:\PROGRA~1\Git\bin;C:\PROGRA~1\Git\usr\bin\;C:\ProgramData\chocolatey\bin;$(PATH) 10 | GOPATH: c:\gopath 11 | GO111MODULE: on 12 | 13 | stack: go 1.14 14 | 15 | init: 16 | - choco install make mingw 17 | 18 | cache: 19 | - C:\usr 20 | - C:\ProgramData\chocolatey 21 | 22 | before_test: 23 | - ps: Set-NetFirewallProfile -All -Enabled False 24 | - ps: Disable-NetFirewallRule -All 25 | 26 | test_script: 27 | - go test -v ./... 28 | -------------------------------------------------------------------------------- /cmd/opera/launcher/fake.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 10 | cli "gopkg.in/urfave/cli.v1" 11 | 12 | "github.com/Fantom-foundation/go-opera/integration/makefakegenesis" 13 | ) 14 | 15 | // FakeNetFlag enables special testnet, where validators are automatically created 16 | var FakeNetFlag = cli.StringFlag{ 17 | Name: "fakenet", 18 | Usage: "'n/N' - sets coinbase as fake n-th key from genesis of N validators.", 19 | } 20 | 21 | func getFakeValidatorKey(ctx *cli.Context) *ecdsa.PrivateKey { 22 | id, _, err := parseFakeGen(ctx.GlobalString(FakeNetFlag.Name)) 23 | if err != nil || id == 0 { 24 | return nil 25 | } 26 | return makefakegenesis.FakeKey(id) 27 | } 28 | 29 | func parseFakeGen(s string) (id idx.ValidatorID, num idx.Validator, err error) { 30 | parts := strings.SplitN(s, "/", 2) 31 | if len(parts) != 2 { 32 | err = fmt.Errorf("use %%d/%%d format") 33 | return 34 | } 35 | 36 | var u32 uint64 37 | u32, err = strconv.ParseUint(parts[0], 10, 32) 38 | if err != nil { 39 | return 40 | } 41 | id = idx.ValidatorID(u32) 42 | 43 | u32, err = strconv.ParseUint(parts[1], 10, 32) 44 | num = idx.Validator(u32) 45 | if num < 0 || idx.Validator(id) > num { 46 | err = fmt.Errorf("key-num should be in range from 1 to validators (/), or should be zero for non-validator node") 47 | return 48 | } 49 | 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /cmd/opera/launcher/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum/log" 11 | "github.com/ethereum/go-ethereum/metrics" 12 | ) 13 | 14 | var once sync.Once 15 | 16 | func SetDataDir(datadir string) { 17 | once.Do(func() { 18 | go measureDbDir("db_size", datadir) 19 | }) 20 | } 21 | 22 | func measureDbDir(name, datadir string) { 23 | var ( 24 | dbSize int64 25 | gauge metrics.Gauge 26 | rescan = (len(datadir) > 0 && datadir != "inmemory") 27 | ) 28 | for { 29 | time.Sleep(10 * time.Second) 30 | 31 | if rescan { 32 | size := sizeOfDir(datadir, new(int)) 33 | atomic.StoreInt64(&dbSize, size) 34 | } 35 | 36 | if gauge == nil { 37 | gauge = metrics.NewRegisteredFunctionalGauge(name, nil, func() int64 { 38 | return atomic.LoadInt64(&dbSize) 39 | }) 40 | } 41 | 42 | if !rescan { 43 | break 44 | } 45 | } 46 | } 47 | 48 | func sizeOfDir(dir string, counter *int) (size int64) { 49 | err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 50 | *counter++ 51 | if *counter % 100 == 0 { 52 | time.Sleep(100 * time.Millisecond) 53 | } 54 | if err != nil { 55 | log.Debug("datadir walk", "path", path, "err", err) 56 | return filepath.SkipDir 57 | } 58 | 59 | if info.IsDir() { 60 | return nil 61 | } 62 | 63 | dst, err := filepath.EvalSymlinks(path) 64 | if err == nil && dst != path { 65 | size += sizeOfDir(dst, counter) 66 | } else { 67 | size += info.Size() 68 | } 69 | 70 | return nil 71 | }) 72 | 73 | if err != nil { 74 | log.Debug("datadir walk", "path", dir, "err", err) 75 | } 76 | 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /cmd/opera/launcher/misccmd.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "strings" 8 | 9 | "github.com/ethereum/go-ethereum/cmd/utils" 10 | "github.com/ethereum/go-ethereum/params" 11 | "gopkg.in/urfave/cli.v1" 12 | 13 | "github.com/Fantom-foundation/go-opera/gossip" 14 | ) 15 | 16 | var ( 17 | versionCommand = cli.Command{ 18 | Action: utils.MigrateFlags(version), 19 | Name: "version", 20 | Usage: "Print version numbers", 21 | ArgsUsage: " ", 22 | Category: "MISCELLANEOUS COMMANDS", 23 | Description: ` 24 | The output of this command is supposed to be machine-readable. 25 | `, 26 | } 27 | 28 | licenseCommand = cli.Command{ 29 | Action: utils.MigrateFlags(license), 30 | Name: "license", 31 | Usage: "Display license information", 32 | ArgsUsage: " ", 33 | Category: "MISCELLANEOUS COMMANDS", 34 | } 35 | ) 36 | 37 | func version(ctx *cli.Context) error { 38 | fmt.Println(strings.Title(clientIdentifier)) 39 | fmt.Println("Version:", params.VersionWithMeta()) 40 | if gitCommit != "" { 41 | fmt.Println("Git Commit:", gitCommit) 42 | } 43 | if gitDate != "" { 44 | fmt.Println("Git Commit Date:", gitDate) 45 | } 46 | fmt.Println("Architecture:", runtime.GOARCH) 47 | fmt.Println("Protocol Versions:", []uint{gossip.ProtocolVersion}) 48 | fmt.Println("Go Version:", runtime.Version()) 49 | fmt.Println("Operating System:", runtime.GOOS) 50 | fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) 51 | fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) 52 | return nil 53 | } 54 | 55 | func license(_ *cli.Context) error { 56 | // TODO: license text 57 | fmt.Println(``) 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/dupes/1: -------------------------------------------------------------------------------- 1 | {"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/dupes/2: -------------------------------------------------------------------------------- 1 | {"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/dupes/foo: -------------------------------------------------------------------------------- 1 | {"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/empty.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/guswallet.json: -------------------------------------------------------------------------------- 1 | { 2 | "encseed": "26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba", 3 | "ethaddr": "d4584b5f6229b7be90727b0fc8c6b91bb427821f", 4 | "email": "gustav.simonsson@gmail.com", 5 | "btcaddr": "1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx" 6 | } 7 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/.hiddenfile: -------------------------------------------------------------------------------- 1 | {"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} 2 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/README: -------------------------------------------------------------------------------- 1 | This directory contains accounts for testing. 2 | The passphrase that unlocks them is "foobar". 3 | 4 | The "good" key files which are supposed to be loadable are: 5 | 6 | - File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 7 | Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8 8 | - File: aaa 9 | Address: 0xf466859ead1932d743d622cb74fc058882e8648a 10 | - File: zzz 11 | Address: 0x289d485d9771714cce91d3393d764e1311907acc 12 | 13 | The other files (including this README) are broken in various ways 14 | and should not be picked up by package accounts: 15 | 16 | - File: no-address (missing address field, otherwise same as "aaa") 17 | - File: garbage (file with random data) 18 | - File: empty (file with no content) 19 | - File: swapfile~ (should be skipped) 20 | - File: .hiddenfile (should be skipped) 21 | - File: foo/... (should be skipped because it is a directory) 22 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8: -------------------------------------------------------------------------------- 1 | {"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/aaa: -------------------------------------------------------------------------------- 1 | {"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FairCrypto/go-x1/dd692b02e6947b8a5177dff553b05f7d5d66b670/cmd/opera/launcher/testdata/keystore/empty -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e: -------------------------------------------------------------------------------- 1 | {"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/garbage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FairCrypto/go-x1/dd692b02e6947b8a5177dff553b05f7d5d66b670/cmd/opera/launcher/testdata/keystore/garbage -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/no-address: -------------------------------------------------------------------------------- 1 | {"crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/zero: -------------------------------------------------------------------------------- 1 | {"address":"0000000000000000000000000000000000000000","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/keystore/zzz: -------------------------------------------------------------------------------- 1 | {"address":"289d485d9771714cce91d3393d764e1311907acc","crypto":{"cipher":"aes-128-ctr","ciphertext":"faf32ca89d286b107f5e6d842802e05263c49b78d46eac74e6109e9a963378ab","cipherparams":{"iv":"558833eec4a665a8c55608d7d503407d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"d571fff447ffb24314f9513f5160246f09997b857ac71348b73e785aab40dc04"},"mac":"21edb85ff7d0dab1767b9bf498f2c3cb7be7609490756bd32300bb213b59effe"},"id":"3279afcf-55ba-43ff-8997-02dcc46a6525","version":3} -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/passwords.txt: -------------------------------------------------------------------------------- 1 | foobar 2 | foobar 3 | foobar 4 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testdata/wrong-passwords.txt: -------------------------------------------------------------------------------- 1 | wrong 2 | wrong 3 | wrong 4 | -------------------------------------------------------------------------------- /cmd/opera/launcher/testnet.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | cli "gopkg.in/urfave/cli.v1" 5 | ) 6 | 7 | // TestnetFlag enables special testnet, where validators are automatically created 8 | var TestnetFlag = cli.BoolFlag{ 9 | Name: "testnet", 10 | Usage: "Generates X1 testnet genesis and starts the chain", 11 | } 12 | -------------------------------------------------------------------------------- /cmd/opera/launcher/tracing/tracing.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | opentracing "github.com/opentracing/opentracing-go" 5 | jaegercfg "github.com/uber/jaeger-client-go/config" 6 | jaegerlog "github.com/uber/jaeger-client-go/log" 7 | "github.com/uber/jaeger-lib/metrics" 8 | "gopkg.in/urfave/cli.v1" 9 | 10 | "github.com/Fantom-foundation/go-opera/tracing" 11 | ) 12 | 13 | var EnableFlag = cli.BoolFlag{ 14 | Name: "tracing", 15 | Usage: "Enable traces collection and reporting", 16 | } 17 | 18 | func Start(ctx *cli.Context) (stop func(), err error) { 19 | stop = func() {} 20 | 21 | if !ctx.Bool(EnableFlag.Name) { 22 | return 23 | } 24 | 25 | var cfg *jaegercfg.Configuration 26 | cfg, err = jaegercfg.FromEnv() 27 | if err != nil { 28 | return 29 | } 30 | 31 | cfg.ServiceName = "opera" 32 | 33 | tracer, closer, err := cfg.NewTracer( 34 | jaegercfg.Logger(jaegerlog.StdLogger), 35 | jaegercfg.Metrics(metrics.NullFactory), 36 | ) 37 | if err != nil { 38 | return 39 | } 40 | stop = func() { 41 | closer.Close() 42 | } 43 | 44 | opentracing.SetGlobalTracer(tracer) 45 | tracing.SetEnabled(true) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /cmd/opera/launcher/x1testnet.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "github.com/Fantom-foundation/go-opera/integration/makefakegenesis" 6 | cli "gopkg.in/urfave/cli.v1" 7 | ) 8 | 9 | // X1TestnetFlag enables special testnet, where validators are automatically created 10 | var X1TestnetFlag = cli.BoolFlag{ 11 | Name: "x1-testnet", 12 | Usage: "Generates X1 testnet genesis and starts the chain", 13 | } 14 | 15 | func getX1ValidatorKey(ctx *cli.Context) *ecdsa.PrivateKey { 16 | return makefakegenesis.FakeKey(1) 17 | } 18 | -------------------------------------------------------------------------------- /cmd/opera/launcher/xenblocks.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "fmt" 5 | cli "gopkg.in/urfave/cli.v1" 6 | "strings" 7 | ) 8 | 9 | var XenBlocksEndpointFlag = cli.StringFlag{ 10 | Name: "xenblocks-endpoint", 11 | Usage: "Sets the Xenblocks reporter endpoint.", 12 | } 13 | 14 | func parseXenBlocksEndpoint(s string) (url string, err error) { 15 | if !strings.HasPrefix(s, "ws://") && !strings.HasPrefix(s, "wss://") { 16 | err = fmt.Errorf("use ws:// or wss:// prefix") 17 | } 18 | return s, err 19 | } 20 | -------------------------------------------------------------------------------- /cmd/opera/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/Fantom-foundation/go-opera/cmd/opera/launcher" 8 | ) 9 | 10 | func main() { 11 | if err := launcher.Launch(os.Args); err != nil { 12 | fmt.Fprintln(os.Stderr, err) 13 | os.Exit(1) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /debug/loudpanic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | //go:build go1.6 18 | // +build go1.6 19 | 20 | package debug 21 | 22 | import "runtime/debug" 23 | 24 | // LoudPanic panics in a way that gets all goroutine stacks printed on stderr. 25 | func LoudPanic(x interface{}) { 26 | debug.SetTraceback("all") 27 | panic(x) 28 | } 29 | -------------------------------------------------------------------------------- /debug/loudpanic_fallback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | //go:build !go1.6 18 | // +build !go1.6 19 | 20 | package debug 21 | 22 | // LoudPanic panics in a way that gets all goroutine stacks printed on stderr. 23 | func LoudPanic(x interface{}) { 24 | panic(x) 25 | } 26 | -------------------------------------------------------------------------------- /debug/trace_fallback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | //go:build !go1.5 18 | // +build !go1.5 19 | 20 | // no-op implementation of tracing methods for Go < 1.5. 21 | 22 | package debug 23 | 24 | import "errors" 25 | 26 | func (*HandlerT) StartGoTrace(string) error { 27 | return errors.New("tracing is not supported on Go < 1.5") 28 | } 29 | 30 | func (*HandlerT) StopGoTrace() error { 31 | return errors.New("tracing is not supported on Go < 1.5") 32 | } 33 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | opera*.datadir 2 | *.log 3 | -------------------------------------------------------------------------------- /demo/_params.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | declare -ri N="${N:-3}" 4 | declare -ri M="${M:-2}" 5 | declare -r TAG="${TAG:-latest}" 6 | 7 | PORT_BASE=3000 8 | RPCP_BASE=4000 9 | WSP_BASE=4500 10 | 11 | attach_and_exec() { 12 | local i=$1 13 | local CMD=$2 14 | local RPCP=$(($RPCP_BASE+$i)) 15 | 16 | for attempt in $(seq 40) 17 | do 18 | if (( attempt > 5 )) 19 | then 20 | echo " - attempt ${attempt}: " >&2 21 | fi 22 | 23 | res=$(../build/demo_opera --exec "${CMD}" attach http://127.0.0.1:${RPCP} 2> /dev/null) 24 | if [ $? -eq 0 ] 25 | then 26 | #echo "success" >&2 27 | echo $res 28 | return 0 29 | else 30 | #echo "wait" >&2 31 | sleep 1 32 | fi 33 | done 34 | echo "failed RPC connection to ${NAME}" >&2 35 | return 1 36 | } 37 | -------------------------------------------------------------------------------- /demo/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | 4 | 5 | rm -fr opera*.datadir 6 | rm *.log 7 | rm ../build/demo_opera 8 | -------------------------------------------------------------------------------- /demo/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | . ./_params.sh 4 | 5 | set -e 6 | 7 | echo -e "\nStart $N nodes:\n" 8 | 9 | go build -o ../build/demo_opera ../cmd/opera 10 | 11 | rm -f ./transactions.rlp 12 | for ((i=0;i<$N;i+=1)) 13 | do 14 | DATADIR="${PWD}/opera$i.datadir" 15 | mkdir -p ${DATADIR} 16 | 17 | PORT=$(($PORT_BASE+$i)) 18 | RPCP=$(($RPCP_BASE+$i)) 19 | WSP=$(($WSP_BASE+$i)) 20 | ACC=$(($i+1)) 21 | (../build/demo_opera \ 22 | --datadir=${DATADIR} \ 23 | --fakenet=${ACC}/$N \ 24 | --port=${PORT} \ 25 | --nat extip:127.0.0.1 \ 26 | --http --http.addr="127.0.0.1" --http.port=${RPCP} --http.corsdomain="*" --http.api="eth,debug,net,admin,web3,personal,txpool,ftm,dag" \ 27 | --ws --ws.addr="127.0.0.1" --ws.port=${WSP} --ws.origins="*" --ws.api="eth,debug,net,admin,web3,personal,txpool,ftm,dag" \ 28 | --metrics --metrics.addr=127.0.0.1 --metrics.port=$(($RPCP+1100)) \ 29 | --verbosity=3 --tracing >> opera$i.log 2>&1)& 30 | 31 | echo -e "\tnode$i ok" 32 | done 33 | 34 | echo -e "\nConnect nodes to ring:\n" 35 | for ((i=0;i<$N;i+=1)) 36 | do 37 | for ((n=0;n<$M;n+=1)) 38 | do 39 | j=$(((i+n+1) % N)) 40 | 41 | enode=$(attach_and_exec $j 'admin.nodeInfo.enode') 42 | echo " p2p address = ${enode}" 43 | 44 | echo " connecting node-$i to node-$j:" 45 | res=$(attach_and_exec $i "admin.addPeer(${enode})") 46 | echo " result = ${res}" 47 | done 48 | done 49 | -------------------------------------------------------------------------------- /demo/stop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | killall demo_opera 4 | -------------------------------------------------------------------------------- /docker/Dockerfile.x1: -------------------------------------------------------------------------------- 1 | FROM golang:alpine3.19 as builder 2 | 3 | RUN apk add --no-cache make gcc musl-dev linux-headers git 4 | 5 | WORKDIR /go/go-x1 6 | COPY . . 7 | 8 | ARG GOPROXY 9 | RUN go mod download 10 | RUN make x1 11 | 12 | 13 | 14 | FROM alpine:latest 15 | 16 | RUN apk add --no-cache ca-certificates 17 | 18 | COPY --from=builder /go/go-x1/build/x1 / 19 | 20 | EXPOSE 5050 8545 8546 21 | 22 | ENTRYPOINT ["/x1"] 23 | -------------------------------------------------------------------------------- /docker/networksimulator/grafana/etc/grafana/provisioning/dashboards/main.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "Dashboard provider" 5 | orgId: 1 6 | type: file 7 | disableDeletion: false 8 | updateIntervalSeconds: 10 9 | allowUiUpdates: false 10 | editable: true 11 | options: 12 | path: /var/lib/grafana/dashboards 13 | foldersFromFilesStructure: true 14 | -------------------------------------------------------------------------------- /docker/networksimulator/grafana/etc/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | url: http://prometheus:9090 7 | isDefault: true 8 | access: proxy 9 | editable: true 10 | -------------------------------------------------------------------------------- /docker/networksimulator/prometheus/.gitignore: -------------------------------------------------------------------------------- 1 | prometheus.yml 2 | -------------------------------------------------------------------------------- /docker/networksimulator/requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | web3 3 | -------------------------------------------------------------------------------- /docker/networksimulator/templates/grafana.conf.j2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FairCrypto/go-x1/dd692b02e6947b8a5177dff553b05f7d5d66b670/docker/networksimulator/templates/grafana.conf.j2 -------------------------------------------------------------------------------- /docker/networksimulator/templates/prometheus.yml.j2: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | scrape_timeout: 10s 4 | evaluation_interval: 15s 5 | 6 | scrape_configs: 7 | - job_name: x1 8 | honor_timestamps: true 9 | scrape_interval: 15s 10 | scrape_timeout: 10s 11 | metrics_path: /debug/metrics/prometheus 12 | scheme: http 13 | static_configs: 14 | - targets: 15 | {% for node in nodes %} 16 | - node{{ node.id }}:6060{% endfor %} 17 | 18 | - job_name: prometheus 19 | honor_timestamps: true 20 | scrape_interval: 21 | static_configs: 22 | - targets: 23 | - node-exporter:9100 24 | -------------------------------------------------------------------------------- /docs/validators/connect-wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FairCrypto/go-x1/dd692b02e6947b8a5177dff553b05f7d5d66b670/docs/validators/connect-wallet.png -------------------------------------------------------------------------------- /docs/validators/create-validator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FairCrypto/go-x1/dd692b02e6947b8a5177dff553b05f7d5d66b670/docs/validators/create-validator.png -------------------------------------------------------------------------------- /ethapi/README.md: -------------------------------------------------------------------------------- 1 | Package is a full copy of github.com/ethereum/go-ethereum/internal/ethapi -------------------------------------------------------------------------------- /ethapi/addrlock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package ethapi 18 | 19 | import ( 20 | "sync" 21 | 22 | "github.com/ethereum/go-ethereum/common" 23 | ) 24 | 25 | type AddrLocker struct { 26 | mu sync.Mutex 27 | locks map[common.Address]*sync.Mutex 28 | } 29 | 30 | // lock returns the lock of the given address. 31 | func (l *AddrLocker) lock(address common.Address) *sync.Mutex { 32 | l.mu.Lock() 33 | defer l.mu.Unlock() 34 | if l.locks == nil { 35 | l.locks = make(map[common.Address]*sync.Mutex) 36 | } 37 | if _, ok := l.locks[address]; !ok { 38 | l.locks[address] = new(sync.Mutex) 39 | } 40 | return l.locks[address] 41 | } 42 | 43 | // LockAddr locks an account's mutex. This is used to prevent another tx getting the 44 | // same nonce until the lock is released. The mutex prevents the (an identical nonce) from 45 | // being read again during the time that the first transaction is being signed. 46 | func (l *AddrLocker) LockAddr(address common.Address) { 47 | l.lock(address).Lock() 48 | } 49 | 50 | // UnlockAddr unlocks the mutex of the given account. 51 | func (l *AddrLocker) UnlockAddr(address common.Address) { 52 | l.lock(address).Unlock() 53 | } 54 | -------------------------------------------------------------------------------- /eventcheck/all.go: -------------------------------------------------------------------------------- 1 | package eventcheck 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/eventcheck/basiccheck" 5 | "github.com/Fantom-foundation/go-opera/eventcheck/epochcheck" 6 | "github.com/Fantom-foundation/go-opera/eventcheck/gaspowercheck" 7 | "github.com/Fantom-foundation/go-opera/eventcheck/heavycheck" 8 | "github.com/Fantom-foundation/go-opera/eventcheck/parentscheck" 9 | "github.com/Fantom-foundation/go-opera/inter" 10 | ) 11 | 12 | // Checkers is collection of all the checkers 13 | type Checkers struct { 14 | Basiccheck *basiccheck.Checker 15 | Epochcheck *epochcheck.Checker 16 | Parentscheck *parentscheck.Checker 17 | Gaspowercheck *gaspowercheck.Checker 18 | Heavycheck *heavycheck.Checker 19 | } 20 | 21 | // Validate runs all the checks except Poset-related 22 | func (v *Checkers) Validate(e inter.EventPayloadI, parents inter.EventIs) error { 23 | if err := v.Basiccheck.Validate(e); err != nil { 24 | return err 25 | } 26 | if err := v.Epochcheck.Validate(e); err != nil { 27 | return err 28 | } 29 | if err := v.Parentscheck.Validate(e, parents); err != nil { 30 | return err 31 | } 32 | var selfParent inter.EventI 33 | if e.SelfParent() != nil { 34 | selfParent = parents[0] 35 | } 36 | if err := v.Gaspowercheck.Validate(e, selfParent); err != nil { 37 | return err 38 | } 39 | if err := v.Heavycheck.ValidateEvent(e); err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /eventcheck/ban.go: -------------------------------------------------------------------------------- 1 | package eventcheck 2 | 3 | import ( 4 | "errors" 5 | 6 | base "github.com/Fantom-foundation/lachesis-base/eventcheck" 7 | 8 | "github.com/Fantom-foundation/go-opera/eventcheck/epochcheck" 9 | "github.com/Fantom-foundation/go-opera/eventcheck/heavycheck" 10 | ) 11 | 12 | var ( 13 | ErrAlreadyProcessedBVs = errors.New("BVs is processed already") 14 | ErrAlreadyProcessedBR = errors.New("BR is processed already") 15 | ErrAlreadyProcessedEV = errors.New("EV is processed already") 16 | ErrAlreadyProcessedER = errors.New("ER is processed already") 17 | ErrUnknownEpochBVs = heavycheck.ErrUnknownEpochBVs 18 | ErrUnknownEpochEV = heavycheck.ErrUnknownEpochEV 19 | ErrUndecidedBR = errors.New("BR is unprocessable yet") 20 | ErrUndecidedER = errors.New("ER is unprocessable yet") 21 | ErrAlreadyConnectedEvent = base.ErrAlreadyConnectedEvent 22 | ErrSpilledEvent = base.ErrSpilledEvent 23 | ErrDuplicateEvent = base.ErrDuplicateEvent 24 | ) 25 | 26 | func IsBan(err error) bool { 27 | if err == epochcheck.ErrNotRelevant || 28 | err == ErrAlreadyConnectedEvent || 29 | err == ErrAlreadyProcessedBVs || 30 | err == ErrAlreadyProcessedBR || 31 | err == ErrAlreadyProcessedEV || 32 | err == ErrAlreadyProcessedER || 33 | err == ErrUnknownEpochBVs || 34 | err == ErrUndecidedBR || 35 | err == ErrUnknownEpochEV || 36 | err == ErrUndecidedER || 37 | err == ErrSpilledEvent || 38 | err == ErrDuplicateEvent { 39 | return false 40 | } 41 | return err != nil 42 | } 43 | -------------------------------------------------------------------------------- /eventcheck/bvallcheck/all_check.go: -------------------------------------------------------------------------------- 1 | package bvallcheck 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/inter" 5 | ) 6 | 7 | type Checker struct { 8 | HeavyCheck HeavyCheck 9 | LightCheck LightCheck 10 | } 11 | 12 | type LightCheck func(bvs inter.LlrSignedBlockVotes) error 13 | 14 | type HeavyCheck interface { 15 | Enqueue(bvs inter.LlrSignedBlockVotes, checked func(error)) error 16 | } 17 | 18 | type Callback struct { 19 | HeavyCheck HeavyCheck 20 | LightCheck LightCheck 21 | } 22 | 23 | // Enqueue tries to fill gaps the fetcher's future import queue. 24 | func (c *Checker) Enqueue(bvs inter.LlrSignedBlockVotes, checked func(error)) { 25 | // Run light checks right away 26 | err := c.LightCheck(bvs) 27 | if err != nil { 28 | checked(err) 29 | return 30 | } 31 | 32 | // Run heavy check in parallel 33 | _ = c.HeavyCheck.Enqueue(bvs, checked) 34 | } 35 | -------------------------------------------------------------------------------- /eventcheck/evallcheck/evallcheck.go: -------------------------------------------------------------------------------- 1 | package evallcheck 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/inter" 5 | ) 6 | 7 | type Checker struct { 8 | HeavyCheck HeavyCheck 9 | LightCheck LightCheck 10 | } 11 | 12 | type LightCheck func(evs inter.LlrSignedEpochVote) error 13 | 14 | type HeavyCheck interface { 15 | Enqueue(evs inter.LlrSignedEpochVote, checked func(error)) error 16 | } 17 | 18 | type Callback struct { 19 | HeavyCheck HeavyCheck 20 | LightCheck LightCheck 21 | } 22 | 23 | // Enqueue tries to fill gaps the fetcher's future import queue. 24 | func (c *Checker) Enqueue(evs inter.LlrSignedEpochVote, checked func(error)) { 25 | // Run light checks right away 26 | err := c.LightCheck(evs) 27 | if err != nil { 28 | checked(err) 29 | return 30 | } 31 | 32 | // Run heavy check in parallel 33 | _ = c.HeavyCheck.Enqueue(evs, checked) 34 | } 35 | -------------------------------------------------------------------------------- /eventcheck/heavycheck/adapters.go: -------------------------------------------------------------------------------- 1 | package heavycheck 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 5 | 6 | "github.com/Fantom-foundation/go-opera/inter" 7 | ) 8 | 9 | type EventsOnly struct { 10 | *Checker 11 | } 12 | 13 | func (c *EventsOnly) Enqueue(e dag.Event, onValidated func(error)) error { 14 | return c.Checker.EnqueueEvent(e.(inter.EventPayloadI), onValidated) 15 | } 16 | 17 | type BVsOnly struct { 18 | *Checker 19 | } 20 | 21 | func (c *BVsOnly) Enqueue(bvs inter.LlrSignedBlockVotes, onValidated func(error)) error { 22 | return c.Checker.EnqueueBVs(bvs, onValidated) 23 | } 24 | 25 | type EVOnly struct { 26 | *Checker 27 | } 28 | 29 | func (c *EVOnly) Enqueue(ers inter.LlrSignedEpochVote, onValidated func(error)) error { 30 | return c.Checker.EnqueueEV(ers, onValidated) 31 | } 32 | -------------------------------------------------------------------------------- /eventcheck/heavycheck/config.go: -------------------------------------------------------------------------------- 1 | package heavycheck 2 | 3 | type Config struct { 4 | MaxQueuedTasks int // the maximum number of tasks to queue up 5 | Threads int 6 | } 7 | 8 | func DefaultConfig() Config { 9 | return Config{ 10 | MaxQueuedTasks: 1024, 11 | Threads: 0, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /eventcheck/parentlesscheck/parentless_check.go: -------------------------------------------------------------------------------- 1 | package parentlesscheck 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 5 | ) 6 | 7 | type Checker struct { 8 | HeavyCheck HeavyCheck 9 | LightCheck LightCheck 10 | } 11 | 12 | type LightCheck func(dag.Event) error 13 | 14 | type HeavyCheck interface { 15 | Enqueue(e dag.Event, checked func(error)) error 16 | } 17 | 18 | // Enqueue tries to fill gaps the fetcher's future import queue. 19 | func (c *Checker) Enqueue(e dag.Event, checked func(error)) { 20 | // Run light checks right away 21 | err := c.LightCheck(e) 22 | if err != nil { 23 | checked(err) 24 | return 25 | } 26 | 27 | // Run heavy check in parallel 28 | _ = c.HeavyCheck.Enqueue(e, checked) 29 | } 30 | -------------------------------------------------------------------------------- /eventcheck/parentscheck/parents_check.go: -------------------------------------------------------------------------------- 1 | package parentscheck 2 | 3 | import ( 4 | "errors" 5 | 6 | base "github.com/Fantom-foundation/lachesis-base/eventcheck/parentscheck" 7 | 8 | "github.com/Fantom-foundation/go-opera/inter" 9 | ) 10 | 11 | var ( 12 | ErrPastTime = errors.New("event has lower claimed time than self-parent") 13 | ) 14 | 15 | // Checker which require only parents list + current epoch info 16 | type Checker struct { 17 | base *base.Checker 18 | } 19 | 20 | // New validator which performs checks, which require known the parents 21 | func New() *Checker { 22 | return &Checker{ 23 | base: &base.Checker{}, 24 | } 25 | } 26 | 27 | // Validate event 28 | func (v *Checker) Validate(e inter.EventI, parents inter.EventIs) error { 29 | if err := v.base.Validate(e, parents.Bases()); err != nil { 30 | return err 31 | } 32 | 33 | if e.SelfParent() != nil { 34 | selfParent := parents[0] 35 | if !e.IsSelfParent(selfParent.ID()) { 36 | // sanity check, self-parent is always first, it's how it's stored 37 | return base.ErrWrongSelfParent 38 | } 39 | // selfParent time 40 | if e.CreationTime() <= selfParent.CreationTime() { 41 | return ErrPastTime 42 | } 43 | } 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /evmcore/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | /tmp 8 | */**/*un~ 9 | *un~ 10 | .DS_Store 11 | */**/.DS_Store 12 | 13 | -------------------------------------------------------------------------------- /evmcore/blocks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package evmcore 18 | 19 | import ( 20 | "github.com/ethereum/go-ethereum/common" 21 | ) 22 | 23 | // BadHashes represent a set of manually tracked bad hashes (usually hard forks) 24 | var BadHashes = map[common.Hash]bool{} 25 | -------------------------------------------------------------------------------- /evmcore/gaspool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package evmcore 18 | 19 | import ( 20 | "fmt" 21 | "math" 22 | ) 23 | 24 | // GasPool tracks the amount of gas available during execution of the transactions 25 | // in a block. The zero value is a pool with zero gas available. 26 | type GasPool uint64 27 | 28 | // AddGas makes gas available for execution. 29 | func (gp *GasPool) AddGas(amount uint64) *GasPool { 30 | if uint64(*gp) > math.MaxUint64-amount { 31 | panic("gas pool pushed above uint64") 32 | } 33 | *(*uint64)(gp) += amount 34 | return gp 35 | } 36 | 37 | // SubGas deducts the given amount from the pool if enough gas is 38 | // available and returns an error otherwise. 39 | func (gp *GasPool) SubGas(amount uint64) error { 40 | if uint64(*gp) < amount { 41 | return ErrGasLimitReached 42 | } 43 | *(*uint64)(gp) -= amount 44 | return nil 45 | } 46 | 47 | // Gas returns the amount of gas remaining in the pool. 48 | func (gp *GasPool) Gas() uint64 { 49 | return uint64(*gp) 50 | } 51 | 52 | func (gp *GasPool) String() string { 53 | return fmt.Sprintf("%d", *gp) 54 | } 55 | -------------------------------------------------------------------------------- /evmcore/notify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package evmcore 18 | 19 | import ( 20 | "github.com/ethereum/go-ethereum/common" 21 | "github.com/ethereum/go-ethereum/core/types" 22 | ) 23 | 24 | // NewTxsNotify is posted when a batch of transactions enter the transaction pool. 25 | type NewTxsNotify struct{ Txs []*types.Transaction } 26 | 27 | // PendingLogsNotify is posted pre mining and notifies of pending logs. 28 | type PendingLogsNotify struct { 29 | Logs []*types.Log 30 | } 31 | 32 | // NewMinedBlockNotify is posted when a block has been imported. 33 | type NewMinedBlockNotify struct{ Block *EvmBlock } 34 | 35 | // RemovedLogsNotify is posted when a reorg happens 36 | type RemovedLogsNotify struct{ Logs []*types.Log } 37 | 38 | type ChainNotify struct { 39 | Block *EvmBlock 40 | Hash common.Hash 41 | Logs []*types.Log 42 | } 43 | 44 | type ChainSideNotify struct { 45 | Block *EvmBlock 46 | } 47 | 48 | type ChainHeadNotify struct{ Block *EvmBlock } 49 | -------------------------------------------------------------------------------- /ftmclient/ethclient.go: -------------------------------------------------------------------------------- 1 | package ftmclient 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/ethclient" 7 | "github.com/ethereum/go-ethereum/rpc" 8 | ) 9 | 10 | // Client extends Ethereum API client with typed wrappers for the FTM API. 11 | type Client struct { 12 | ethclient.Client 13 | c *rpc.Client 14 | } 15 | 16 | // Dial connects a client to the given URL. 17 | func Dial(rawurl string) (*Client, error) { 18 | return DialContext(context.Background(), rawurl) 19 | } 20 | 21 | func DialContext(ctx context.Context, rawurl string) (*Client, error) { 22 | c, err := rpc.DialContext(ctx, rawurl) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return NewClient(c), nil 27 | } 28 | 29 | // NewClient creates a client that uses the given RPC client. 30 | func NewClient(c *rpc.Client) *Client { 31 | return &Client{ 32 | Client: *ethclient.NewClient(c), 33 | c: c, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gossip/api.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | ) 7 | 8 | // PublicEthereumAPI provides an API to access Ethereum-like information. 9 | // It is a github.com/ethereum/go-ethereum/eth simulation for console. 10 | type PublicEthereumAPI struct { 11 | s *Service 12 | } 13 | 14 | // NewPublicEthereumAPI creates a new Ethereum protocol API for gossip. 15 | func NewPublicEthereumAPI(s *Service) *PublicEthereumAPI { 16 | return &PublicEthereumAPI{s} 17 | } 18 | 19 | // Etherbase returns the zero address for web3 compatibility 20 | func (api *PublicEthereumAPI) Etherbase() (common.Address, error) { 21 | return common.Address{}, nil 22 | } 23 | 24 | // Coinbase returns the zero address for web3 compatibility 25 | func (api *PublicEthereumAPI) Coinbase() (common.Address, error) { 26 | return common.Address{}, nil 27 | } 28 | 29 | // Hashrate returns the zero POW hashrate for web3 compatibility 30 | func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { 31 | return hexutil.Uint64(0) 32 | } 33 | 34 | // ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. 35 | func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { 36 | return hexutil.Uint64(api.s.store.GetRules().NetworkID) 37 | } 38 | -------------------------------------------------------------------------------- /gossip/blockproc/verwatcher/store.go: -------------------------------------------------------------------------------- 1 | package verwatcher 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/kvdb" 7 | 8 | "github.com/Fantom-foundation/go-opera/logger" 9 | ) 10 | 11 | // Store is a node persistent storage working over physical key-value database. 12 | type Store struct { 13 | mainDB kvdb.Store 14 | 15 | cache struct { 16 | networkVersion atomic.Value 17 | missedVersion atomic.Value 18 | } 19 | 20 | logger.Instance 21 | } 22 | 23 | // NewStore creates store over key-value db. 24 | func NewStore(mainDB kvdb.Store) *Store { 25 | s := &Store{ 26 | mainDB: mainDB, 27 | Instance: logger.New("verwatcher-store"), 28 | } 29 | 30 | return s 31 | } 32 | -------------------------------------------------------------------------------- /gossip/blockproc/verwatcher/store_network_version.go: -------------------------------------------------------------------------------- 1 | package verwatcher 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/common/bigendian" 5 | ) 6 | 7 | const ( 8 | nvKey = "v" 9 | mvKey = "m" 10 | ) 11 | 12 | // SetNetworkVersion stores network version. 13 | func (s *Store) SetNetworkVersion(v uint64) { 14 | s.cache.networkVersion.Store(v) 15 | err := s.mainDB.Put([]byte(nvKey), bigendian.Uint64ToBytes(v)) 16 | if err != nil { 17 | s.Log.Crit("Failed to put key", "err", err) 18 | } 19 | } 20 | 21 | // GetNetworkVersion returns stored network version. 22 | func (s *Store) GetNetworkVersion() uint64 { 23 | if v := s.cache.networkVersion.Load(); v != nil { 24 | return v.(uint64) 25 | } 26 | valBytes, err := s.mainDB.Get([]byte(nvKey)) 27 | if err != nil { 28 | s.Log.Crit("Failed to get key", "err", err) 29 | } 30 | v := uint64(0) 31 | if valBytes != nil { 32 | v = bigendian.BytesToUint64(valBytes) 33 | } 34 | s.cache.networkVersion.Store(v) 35 | return v 36 | } 37 | 38 | // SetMissedVersion stores non-supported network upgrade. 39 | func (s *Store) SetMissedVersion(v uint64) { 40 | s.cache.missedVersion.Store(v) 41 | err := s.mainDB.Put([]byte(mvKey), bigendian.Uint64ToBytes(v)) 42 | if err != nil { 43 | s.Log.Crit("Failed to put key", "err", err) 44 | } 45 | } 46 | 47 | // GetMissedVersion returns stored non-supported network upgrade. 48 | func (s *Store) GetMissedVersion() uint64 { 49 | if v := s.cache.missedVersion.Load(); v != nil { 50 | return v.(uint64) 51 | } 52 | valBytes, err := s.mainDB.Get([]byte(mvKey)) 53 | if err != nil { 54 | s.Log.Crit("Failed to get key", "err", err) 55 | } 56 | v := uint64(0) 57 | if valBytes != nil { 58 | v = bigendian.BytesToUint64(valBytes) 59 | } 60 | s.cache.missedVersion.Store(v) 61 | return v 62 | } 63 | -------------------------------------------------------------------------------- /gossip/c_block_callbacks_test.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 9 | "github.com/ethereum/go-ethereum/core/types" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/Fantom-foundation/go-opera/logger" 13 | "github.com/Fantom-foundation/go-opera/utils" 14 | ) 15 | 16 | func TestConsensusCallback(t *testing.T) { 17 | logger.SetTestMode(t) 18 | require := require.New(t) 19 | 20 | const rounds = 30 21 | 22 | const validatorsNum = 3 23 | 24 | env := newTestEnv(2, validatorsNum) 25 | defer env.Close() 26 | 27 | // save start balances 28 | balances := make([]*big.Int, validatorsNum) 29 | for i := range balances { 30 | balances[i] = env.State().GetBalance(env.Address(idx.ValidatorID(i + 1))) 31 | } 32 | 33 | for n := uint64(0); n < rounds; n++ { 34 | // transfers 35 | txs := make([]*types.Transaction, validatorsNum) 36 | for i := idx.Validator(0); i < validatorsNum; i++ { 37 | from := i % validatorsNum 38 | to := 0 39 | txs[i] = env.Transfer(idx.ValidatorID(from+1), idx.ValidatorID(to+1), utils.ToFtm(100)) 40 | } 41 | tm := sameEpoch 42 | if n%10 == 0 { 43 | tm = nextEpoch 44 | } 45 | rr, err := env.ApplyTxs(tm, txs...) 46 | require.NoError(err) 47 | // subtract fees 48 | for i, r := range rr { 49 | fee := big.NewInt(0).Mul(new(big.Int).SetUint64(r.GasUsed), txs[i].GasPrice()) 50 | balances[i] = big.NewInt(0).Sub(balances[i], fee) 51 | } 52 | // balance movements 53 | balances[0].Add(balances[0], utils.ToFtm(200)) 54 | balances[1].Sub(balances[1], utils.ToFtm(100)) 55 | balances[2].Sub(balances[2], utils.ToFtm(100)) 56 | } 57 | 58 | // check balances 59 | for i := range balances { 60 | require.Equal( 61 | balances[i], 62 | env.State().GetBalance(env.Address(idx.ValidatorID(i+1))), 63 | fmt.Sprintf("account%d", i), 64 | ) 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /gossip/contract/ballot/ballot.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[{"internalType":"bytes32[]","name":"proposalNames","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"voteCount","type":"uint256"}],"name":"NewProposal","type":"event"},{"inputs":[],"name":"chairperson","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposals","outputs":[{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposal","type":"uint256"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voters","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"bool","name":"voted","type":"bool"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"vote","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"winnerName","outputs":[{"internalType":"bytes32","name":"winnerName_","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"winningProposal","outputs":[{"internalType":"uint256","name":"winningProposal_","type":"uint256"}],"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /gossip/contract/solc/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.abi 3 | *.bin 4 | *.bin-runtime 5 | -------------------------------------------------------------------------------- /gossip/emitter/originatedtxs/txs_ring_buffer.go: -------------------------------------------------------------------------------- 1 | package originatedtxs 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/hashicorp/golang-lru/simplelru" 6 | ) 7 | 8 | type Buffer struct { 9 | senderCount *simplelru.LRU // sender address -> number of transactions 10 | } 11 | 12 | func New(maxAddresses int) *Buffer { 13 | ring := &Buffer{} 14 | ring.senderCount, _ = simplelru.NewLRU(maxAddresses, nil) 15 | return ring 16 | } 17 | 18 | // Inc is not safe for concurrent use 19 | func (ring *Buffer) Inc(sender common.Address) { 20 | cur, ok := ring.senderCount.Peek(sender) 21 | if ok { 22 | ring.senderCount.Add(sender, cur.(int)+1) 23 | } else { 24 | ring.senderCount.Add(sender, int(1)) 25 | } 26 | } 27 | 28 | // Dec is not safe for concurrent use 29 | func (ring *Buffer) Dec(sender common.Address) { 30 | cur, ok := ring.senderCount.Peek(sender) 31 | if !ok { 32 | return 33 | } 34 | if cur.(int) <= 1 { 35 | ring.senderCount.Remove(sender) 36 | } else { 37 | ring.senderCount.Add(sender, cur.(int)-1) 38 | } 39 | } 40 | 41 | // Clear is not safe for concurrent use 42 | func (ring *Buffer) Clear() { 43 | ring.senderCount.Purge() 44 | } 45 | 46 | // TotalOf is not safe for concurrent use 47 | func (ring *Buffer) TotalOf(sender common.Address) int { 48 | cur, ok := ring.senderCount.Get(sender) 49 | if !ok { 50 | return 0 51 | } 52 | return cur.(int) 53 | } 54 | 55 | // Empty is not safe for concurrent use 56 | func (ring *Buffer) Empty() bool { 57 | return ring.senderCount.Len() == 0 58 | } 59 | -------------------------------------------------------------------------------- /gossip/enr_entry.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/core/forkid" 5 | "github.com/ethereum/go-ethereum/rlp" 6 | ) 7 | 8 | // Enr is ENR entry which advertises eth protocol 9 | // on the discovery network. 10 | type Enr struct { 11 | ForkID forkid.ID 12 | // Ignore additional fields (for forward compatibility). 13 | Rest []rlp.RawValue `rlp:"tail"` 14 | } 15 | 16 | // ENRKey implements enr.Entry. 17 | func (e Enr) ENRKey() string { 18 | return "opera" 19 | } 20 | 21 | func (s *Service) currentEnr() *Enr { 22 | return &Enr{} 23 | } 24 | -------------------------------------------------------------------------------- /gossip/eth_state_adapter.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/core/state" 6 | "github.com/ethereum/go-ethereum/core/state/snapshot" 7 | ) 8 | 9 | // ethBlockChain wraps store to implement eth/protocols/snap.BlockChain interface. 10 | type ethBlockChain struct { 11 | store *Store 12 | } 13 | 14 | func newEthBlockChain(s *Store) (*ethBlockChain, error) { 15 | bc := ðBlockChain{ 16 | store: s, 17 | } 18 | 19 | return bc, nil 20 | } 21 | 22 | // StateCache returns the caching database underpinning the blockchain instance. 23 | func (bc *ethBlockChain) StateCache() state.Database { 24 | return bc.store.LastKvdbEvmSnapshot().EvmState 25 | } 26 | 27 | // ContractCode retrieves a blob of data associated with a contract hash 28 | // either from ephemeral in-memory cache, or from persistent storage. 29 | func (bc *ethBlockChain) ContractCode(hash common.Hash) ([]byte, error) { 30 | return bc.store.LastKvdbEvmSnapshot().EvmState.ContractCode(common.Hash{}, hash) 31 | } 32 | 33 | // Snapshots returns the blockchain snapshot tree to paused it during sync. 34 | func (bc *ethBlockChain) Snapshots() *snapshot.Tree { 35 | return bc.store.LastKvdbEvmSnapshot().Snapshots() 36 | } 37 | -------------------------------------------------------------------------------- /gossip/evmstore/apply_genesis.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb/batched" 5 | "github.com/syndtr/goleveldb/leveldb/opt" 6 | 7 | "github.com/Fantom-foundation/go-opera/opera/genesis" 8 | "github.com/Fantom-foundation/go-opera/utils/adapters/ethdb2kvdb" 9 | "github.com/Fantom-foundation/go-opera/utils/dbutil/autocompact" 10 | ) 11 | 12 | // ApplyGenesis writes initial state. 13 | func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) { 14 | db := batched.Wrap(autocompact.Wrap2M(ethdb2kvdb.Wrap(s.EvmDb), opt.GiB, 16*opt.GiB, true, "evm")) 15 | g.RawEvmItems.ForEach(func(key, value []byte) bool { 16 | err = db.Put(key, value) 17 | if err != nil { 18 | return false 19 | } 20 | return true 21 | }) 22 | if err != nil { 23 | return err 24 | } 25 | return db.Write() 26 | } 27 | 28 | func (s *Store) WrapTablesAsBatched() (unwrap func()) { 29 | origTables := s.table 30 | 31 | batchedTxs := batched.Wrap(s.table.Txs) 32 | s.table.Txs = batchedTxs 33 | 34 | batchedTxPositions := batched.Wrap(s.table.TxPositions) 35 | s.table.TxPositions = batchedTxPositions 36 | 37 | unwrapLogs := s.EvmLogs.WrapTablesAsBatched() 38 | 39 | batchedReceipts := batched.Wrap(autocompact.Wrap2M(s.table.Receipts, opt.GiB, 16*opt.GiB, false, "receipts")) 40 | s.table.Receipts = batchedReceipts 41 | return func() { 42 | _ = batchedTxs.Flush() 43 | _ = batchedTxPositions.Flush() 44 | _ = batchedReceipts.Flush() 45 | unwrapLogs() 46 | s.table = origTables 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /gossip/evmstore/evmpruner/exact.go: -------------------------------------------------------------------------------- 1 | package evmpruner 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/kvdb" 8 | "github.com/Fantom-foundation/lachesis-base/kvdb/leveldb" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/core/rawdb" 11 | "github.com/syndtr/goleveldb/leveldb/opt" 12 | ) 13 | 14 | type exactSetStore struct { 15 | db kvdb.Store 16 | } 17 | 18 | func NewLevelDBSet(name string) (*exactSetStore, io.Closer, error) { 19 | db, err := leveldb.New(name, 256*opt.MiB, 0, nil, nil) 20 | if err != nil { 21 | return nil, nil, err 22 | } 23 | return &exactSetStore{db}, db, nil 24 | } 25 | 26 | func (set *exactSetStore) Put(key []byte, _ []byte) error { 27 | // If the key length is not 32bytes, ensure it's contract code 28 | // entry with new scheme. 29 | if len(key) != common.HashLength { 30 | isCode, codeKey := rawdb.IsCodeKey(key) 31 | if !isCode { 32 | return errors.New("invalid entry") 33 | } 34 | return set.db.Put(codeKey, []byte{}) 35 | } 36 | return set.db.Put(key, []byte{}) 37 | } 38 | 39 | func (set *exactSetStore) Delete(key []byte) error { panic("not supported") } 40 | 41 | func (set *exactSetStore) Contain(key []byte) (bool, error) { 42 | return set.db.Has(key) 43 | } 44 | 45 | func (set *exactSetStore) Commit(filename, tempname string) error { 46 | // No need in manual writing 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /gossip/evmstore/store_block_cache.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 5 | "github.com/ethereum/go-ethereum/common" 6 | 7 | "github.com/Fantom-foundation/go-opera/evmcore" 8 | ) 9 | 10 | func (s *Store) GetCachedEvmBlock(n idx.Block) *evmcore.EvmBlock { 11 | c, ok := s.cache.EvmBlocks.Get(n) 12 | if !ok { 13 | return nil 14 | } 15 | 16 | return c.(*evmcore.EvmBlock) 17 | } 18 | 19 | func (s *Store) SetCachedEvmBlock(n idx.Block, b *evmcore.EvmBlock) { 20 | var empty = common.Hash{} 21 | if b.EvmHeader.TxHash == empty { 22 | panic("You have to cache only completed blocks (with txs)") 23 | } 24 | s.cache.EvmBlocks.Add(n, b, uint(b.EstimateSize())) 25 | } 26 | -------------------------------------------------------------------------------- /gossip/evmstore/store_test.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb/memorydb" 5 | ) 6 | 7 | func cachedStore() *Store { 8 | cfg := LiteStoreConfig() 9 | 10 | return NewStore(memorydb.NewProducer(""), cfg) 11 | } 12 | 13 | func nonCachedStore() *Store { 14 | cfg := StoreConfig{} 15 | 16 | return NewStore(memorydb.NewProducer(""), cfg) 17 | } 18 | -------------------------------------------------------------------------------- /gossip/evmstore/store_tx.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "github.com/ethereum/go-ethereum/log" 9 | 10 | "github.com/Fantom-foundation/go-opera/inter" 11 | ) 12 | 13 | // SetTx stores non-event transaction. 14 | func (s *Store) SetTx(txid common.Hash, tx *types.Transaction) { 15 | s.rlp.Set(s.table.Txs, txid.Bytes(), tx) 16 | } 17 | 18 | // GetTx returns stored non-event transaction. 19 | func (s *Store) GetTx(txid common.Hash) *types.Transaction { 20 | tx, _ := s.rlp.Get(s.table.Txs, txid.Bytes(), &types.Transaction{}).(*types.Transaction) 21 | 22 | return tx 23 | } 24 | 25 | func (s *Store) GetBlockTxs(n idx.Block, block inter.Block, getEventPayload func(hash.Event) *inter.EventPayload) types.Transactions { 26 | if cached := s.GetCachedEvmBlock(n); cached != nil { 27 | return cached.Transactions 28 | } 29 | 30 | transactions := make(types.Transactions, 0, len(block.Txs)+len(block.InternalTxs)+len(block.Events)*10) 31 | for _, txid := range block.InternalTxs { 32 | tx := s.GetTx(txid) 33 | if tx == nil { 34 | log.Crit("Internal tx not found", "tx", txid.String()) 35 | continue 36 | } 37 | transactions = append(transactions, tx) 38 | } 39 | for _, txid := range block.Txs { 40 | tx := s.GetTx(txid) 41 | if tx == nil { 42 | log.Crit("Tx not found", "tx", txid.String()) 43 | continue 44 | } 45 | transactions = append(transactions, tx) 46 | } 47 | for _, id := range block.Events { 48 | e := getEventPayload(id) 49 | if e == nil { 50 | log.Crit("Block event not found", "event", id.String()) 51 | continue 52 | } 53 | transactions = append(transactions, e.Txs()...) 54 | } 55 | 56 | transactions = inter.FilterSkippedTxs(transactions, block.SkippedTxs) 57 | 58 | return transactions 59 | } 60 | -------------------------------------------------------------------------------- /gossip/evmstore/store_tx_position.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | /* 4 | In LRU cache data stored like pointer 5 | */ 6 | 7 | import ( 8 | "github.com/Fantom-foundation/lachesis-base/hash" 9 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 10 | "github.com/ethereum/go-ethereum/common" 11 | ) 12 | 13 | type TxPosition struct { 14 | Block idx.Block 15 | Event hash.Event 16 | EventOffset uint32 17 | BlockOffset uint32 18 | } 19 | 20 | // SetTxPosition stores transaction block and position. 21 | func (s *Store) SetTxPosition(txid common.Hash, position TxPosition) { 22 | s.rlp.Set(s.table.TxPositions, txid.Bytes(), &position) 23 | 24 | // Add to LRU cache. 25 | s.cache.TxPositions.Add(txid.String(), &position, nominalSize) 26 | } 27 | 28 | // GetTxPosition returns stored transaction block and position. 29 | func (s *Store) GetTxPosition(txid common.Hash) *TxPosition { 30 | // Get data from LRU cache first. 31 | if c, ok := s.cache.TxPositions.Get(txid.String()); ok { 32 | if b, ok := c.(*TxPosition); ok { 33 | return b 34 | } 35 | } 36 | 37 | txPosition, _ := s.rlp.Get(s.table.TxPositions, txid.Bytes(), &TxPosition{}).(*TxPosition) 38 | 39 | // Add to LRU cache. 40 | if txPosition != nil { 41 | s.cache.TxPositions.Add(txid.String(), txPosition, nominalSize) 42 | } 43 | 44 | return txPosition 45 | } 46 | -------------------------------------------------------------------------------- /gossip/proclogger/dag_logger.go: -------------------------------------------------------------------------------- 1 | package proclogger 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/go-opera/inter" 7 | "github.com/Fantom-foundation/go-opera/logger" 8 | "github.com/Fantom-foundation/go-opera/utils" 9 | ) 10 | 11 | func NewLogger() *Logger { 12 | return &Logger{ 13 | Instance: logger.New(), 14 | } 15 | } 16 | 17 | // EventConnectionStarted starts the event logging 18 | // Not safe for concurrent use 19 | func (l *Logger) EventConnectionStarted(e inter.EventPayloadI, emitted bool) func() { 20 | l.dagSum.connected++ 21 | 22 | start := time.Now() 23 | l.emitting = emitted 24 | l.noSummary = true // print summary after the whole event is processed 25 | l.lastID = e.ID() 26 | l.lastEventTime = e.CreationTime() 27 | 28 | return func() { 29 | now := time.Now() 30 | // logging for the individual item 31 | msg := "New event" 32 | logType := l.Log.Debug 33 | if emitted { 34 | msg = "New event emitted" 35 | logType = l.Log.Info 36 | } 37 | logType(msg, "id", e.ID(), "parents", len(e.Parents()), "by", e.Creator(), 38 | "frame", e.Frame(), "txs", e.Txs().Len(), 39 | "age", utils.PrettyDuration(now.Sub(e.CreationTime().Time())), "t", utils.PrettyDuration(now.Sub(start))) 40 | // logging for the summary 41 | l.dagSum.totalProcessing += now.Sub(start) 42 | l.emitting = false 43 | l.noSummary = false 44 | l.summary(now) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /gossip/protocols/blockrecords/brprocessor/config.go: -------------------------------------------------------------------------------- 1 | package brprocessor 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/syndtr/goleveldb/leveldb/opt" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 9 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 10 | ) 11 | 12 | type Config struct { 13 | BufferLimit dag.Metric 14 | 15 | SemaphoreTimeout time.Duration 16 | 17 | MaxTasks int 18 | } 19 | 20 | func DefaultConfig(scale cachescale.Func) Config { 21 | return Config{ 22 | BufferLimit: dag.Metric{ 23 | Num: 10000, 24 | Size: scale.U64(15 * opt.MiB), 25 | }, 26 | SemaphoreTimeout: 10 * time.Second, 27 | MaxTasks: 512, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gossip/protocols/blockrecords/brstream/brstreamleecher/config.go: -------------------------------------------------------------------------------- 1 | package brstreamleecher 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamleecher/basepeerleecher" 7 | ) 8 | 9 | type Config struct { 10 | Session basepeerleecher.EpochDownloaderConfig 11 | RecheckInterval time.Duration 12 | BaseProgressWatchdog time.Duration 13 | BaseSessionWatchdog time.Duration 14 | MinSessionRestart time.Duration 15 | MaxSessionRestart time.Duration 16 | } 17 | 18 | // DefaultConfig returns default leecher config 19 | func DefaultConfig() Config { 20 | return Config{ 21 | Session: basepeerleecher.EpochDownloaderConfig{ 22 | DefaultChunkItemsNum: 500, 23 | DefaultChunkItemsSize: 512 * 1024, 24 | ParallelChunksDownload: 6, 25 | RecheckInterval: 10 * time.Millisecond, 26 | }, 27 | RecheckInterval: time.Second, 28 | BaseProgressWatchdog: time.Second * 5, 29 | BaseSessionWatchdog: time.Second * 30 * 5, 30 | MinSessionRestart: time.Second * 5, 31 | MaxSessionRestart: time.Minute * 5, 32 | } 33 | } 34 | 35 | // LiteConfig returns default leecher config for tests 36 | func LiteConfig() Config { 37 | cfg := DefaultConfig() 38 | cfg.Session.DefaultChunkItemsSize /= 10 39 | cfg.Session.DefaultChunkItemsNum /= 10 40 | cfg.Session.ParallelChunksDownload = cfg.Session.ParallelChunksDownload/2 + 1 41 | return cfg 42 | } 43 | -------------------------------------------------------------------------------- /gossip/protocols/blockrecords/brstream/brstreamseeder/config.go: -------------------------------------------------------------------------------- 1 | package brstreamseeder 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamseeder" 5 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 6 | ) 7 | 8 | type Config basestreamseeder.Config 9 | 10 | func DefaultConfig(scale cachescale.Func) Config { 11 | return Config{ 12 | SenderThreads: 2, 13 | MaxSenderTasks: 64, 14 | MaxPendingResponsesSize: scale.I64(32 * 1024 * 1024), 15 | MaxResponsePayloadNum: 4096, 16 | MaxResponsePayloadSize: 8 * 1024 * 1024, 17 | MaxResponseChunks: 12, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gossip/protocols/blockrecords/brstream/types.go: -------------------------------------------------------------------------------- 1 | package brstream 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | "github.com/ethereum/go-ethereum/rlp" 8 | 9 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream" 10 | ) 11 | 12 | type Request struct { 13 | Session Session 14 | Limit Metric 15 | Type basestream.RequestType 16 | MaxChunks uint32 17 | } 18 | 19 | type Response struct { 20 | SessionID uint32 21 | Done bool 22 | Payload []rlp.RawValue 23 | } 24 | 25 | type Session struct { 26 | ID uint32 27 | Start Locator 28 | Stop Locator 29 | } 30 | 31 | type Locator idx.Block 32 | 33 | func (l Locator) Compare(b basestream.Locator) int { 34 | if l == b.(Locator) { 35 | return 0 36 | } 37 | if l < b.(Locator) { 38 | return -1 39 | } 40 | return 1 41 | } 42 | 43 | func (l Locator) Inc() basestream.Locator { 44 | return l + 1 45 | } 46 | 47 | type Payload struct { 48 | Items []rlp.RawValue 49 | Keys []Locator 50 | Size uint64 51 | } 52 | 53 | func (p *Payload) AddFullBlockRecords(id Locator, brsB rlp.RawValue) { 54 | p.Items = append(p.Items, brsB) 55 | p.Keys = append(p.Keys, id) 56 | p.Size += uint64(len(brsB)) 57 | } 58 | 59 | func (p Payload) Len() int { 60 | return len(p.Keys) 61 | } 62 | 63 | func (p Payload) TotalSize() uint64 { 64 | return p.Size 65 | } 66 | 67 | func (p Payload) TotalMemSize() int { 68 | return int(p.Size) + len(p.Keys)*32 69 | } 70 | 71 | type Metric struct { 72 | Num idx.Block 73 | Size uint64 74 | } 75 | 76 | func (m Metric) String() string { 77 | return fmt.Sprintf("{Num=%d,Size=%d}", m.Num, m.Size) 78 | } 79 | -------------------------------------------------------------------------------- /gossip/protocols/blockvotes/bvprocessor/config.go: -------------------------------------------------------------------------------- 1 | package bvprocessor 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/syndtr/goleveldb/leveldb/opt" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 9 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 10 | ) 11 | 12 | type Config struct { 13 | BufferLimit dag.Metric 14 | 15 | SemaphoreTimeout time.Duration 16 | 17 | MaxTasks int 18 | } 19 | 20 | func DefaultConfig(scale cachescale.Func) Config { 21 | return Config{ 22 | BufferLimit: dag.Metric{ 23 | Num: 3000, 24 | Size: scale.U64(15 * opt.MiB), 25 | }, 26 | SemaphoreTimeout: 10 * time.Second, 27 | MaxTasks: 512, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gossip/protocols/blockvotes/bvstream/bvstreamleecher/config.go: -------------------------------------------------------------------------------- 1 | package bvstreamleecher 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamleecher/basepeerleecher" 7 | ) 8 | 9 | type Config struct { 10 | Session basepeerleecher.EpochDownloaderConfig 11 | RecheckInterval time.Duration 12 | BaseProgressWatchdog time.Duration 13 | BaseSessionWatchdog time.Duration 14 | MinSessionRestart time.Duration 15 | MaxSessionRestart time.Duration 16 | } 17 | 18 | // DefaultConfig returns default leecher config 19 | func DefaultConfig() Config { 20 | return Config{ 21 | Session: basepeerleecher.EpochDownloaderConfig{ 22 | DefaultChunkItemsNum: 500, 23 | DefaultChunkItemsSize: 512 * 1024, 24 | ParallelChunksDownload: 6, 25 | RecheckInterval: 10 * time.Millisecond, 26 | }, 27 | RecheckInterval: time.Second, 28 | BaseProgressWatchdog: time.Second * 5, 29 | BaseSessionWatchdog: time.Second * 30 * 5, 30 | MinSessionRestart: time.Second * 5, 31 | MaxSessionRestart: time.Minute * 5, 32 | } 33 | } 34 | 35 | // LiteConfig returns default leecher config for tests 36 | func LiteConfig() Config { 37 | cfg := DefaultConfig() 38 | cfg.Session.DefaultChunkItemsSize /= 10 39 | cfg.Session.DefaultChunkItemsNum /= 10 40 | cfg.Session.ParallelChunksDownload = cfg.Session.ParallelChunksDownload/2 + 1 41 | return cfg 42 | } 43 | -------------------------------------------------------------------------------- /gossip/protocols/blockvotes/bvstream/bvstreamseeder/config.go: -------------------------------------------------------------------------------- 1 | package bvstreamseeder 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamseeder" 5 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 6 | ) 7 | 8 | type Config basestreamseeder.Config 9 | 10 | func DefaultConfig(scale cachescale.Func) Config { 11 | return Config{ 12 | SenderThreads: 2, 13 | MaxSenderTasks: 64, 14 | MaxPendingResponsesSize: scale.I64(32 * 1024 * 1024), 15 | MaxResponsePayloadNum: 4096, 16 | MaxResponsePayloadSize: 8 * 1024 * 1024, 17 | MaxResponseChunks: 12, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gossip/protocols/blockvotes/bvstream/types.go: -------------------------------------------------------------------------------- 1 | package bvstream 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/rlp" 11 | 12 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream" 13 | ) 14 | 15 | type Request struct { 16 | Session Session 17 | Limit Metric 18 | Type basestream.RequestType 19 | MaxChunks uint32 20 | } 21 | 22 | type Response struct { 23 | SessionID uint32 24 | Done bool 25 | Payload []rlp.RawValue 26 | } 27 | 28 | type Session struct { 29 | ID uint32 30 | Start Locator 31 | Stop Locator 32 | } 33 | 34 | type Locator []byte 35 | 36 | func (l Locator) Compare(b basestream.Locator) int { 37 | return bytes.Compare(l, b.(Locator)) 38 | } 39 | 40 | func (l Locator) Inc() basestream.Locator { 41 | nextBn := new(big.Int).SetBytes(l) 42 | nextBn.Add(nextBn, common.Big1) 43 | return Locator(common.LeftPadBytes(nextBn.Bytes(), len(l))) 44 | } 45 | 46 | type Payload struct { 47 | Items []rlp.RawValue 48 | Keys []Locator 49 | Size uint64 50 | } 51 | 52 | func (p *Payload) AddSignedBlockVotes(id Locator, bvsB rlp.RawValue) { 53 | p.Items = append(p.Items, bvsB) 54 | p.Keys = append(p.Keys, id) 55 | p.Size += uint64(len(bvsB)) 56 | } 57 | 58 | func (p Payload) Len() int { 59 | return len(p.Keys) 60 | } 61 | 62 | func (p Payload) TotalSize() uint64 { 63 | return p.Size 64 | } 65 | 66 | func (p Payload) TotalMemSize() int { 67 | return int(p.Size) + len(p.Keys)*128 68 | } 69 | 70 | type Metric struct { 71 | Num idx.Block 72 | Size uint64 73 | } 74 | 75 | func (m Metric) String() string { 76 | return fmt.Sprintf("{Num=%d,Size=%d}", m.Num, m.Size) 77 | } 78 | -------------------------------------------------------------------------------- /gossip/protocols/dag/dagstream/dagstreamleecher/config.go: -------------------------------------------------------------------------------- 1 | package dagstreamleecher 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamleecher/basepeerleecher" 7 | ) 8 | 9 | type Config struct { 10 | Session basepeerleecher.EpochDownloaderConfig 11 | RecheckInterval time.Duration 12 | BaseProgressWatchdog time.Duration 13 | BaseSessionWatchdog time.Duration 14 | MinSessionRestart time.Duration 15 | MaxSessionRestart time.Duration 16 | } 17 | 18 | // DefaultConfig returns default leecher config 19 | func DefaultConfig() Config { 20 | return Config{ 21 | Session: basepeerleecher.EpochDownloaderConfig{ 22 | DefaultChunkItemsNum: 500, 23 | DefaultChunkItemsSize: 512 * 1024, 24 | ParallelChunksDownload: 6, 25 | RecheckInterval: 10 * time.Millisecond, 26 | }, 27 | RecheckInterval: time.Second, 28 | BaseProgressWatchdog: time.Second * 5, 29 | BaseSessionWatchdog: time.Second * 30 * 5, 30 | MinSessionRestart: time.Second * 5, 31 | MaxSessionRestart: time.Minute * 5, 32 | } 33 | } 34 | 35 | // LiteConfig returns default leecher config for tests 36 | func LiteConfig() Config { 37 | cfg := DefaultConfig() 38 | cfg.Session.DefaultChunkItemsSize /= 10 39 | cfg.Session.DefaultChunkItemsNum /= 10 40 | cfg.Session.ParallelChunksDownload = cfg.Session.ParallelChunksDownload/2 + 1 41 | return cfg 42 | } 43 | -------------------------------------------------------------------------------- /gossip/protocols/dag/dagstream/dagstreamseeder/config.go: -------------------------------------------------------------------------------- 1 | package dagstreamseeder 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamseeder" 5 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 6 | ) 7 | 8 | type Config basestreamseeder.Config 9 | 10 | func DefaultConfig(scale cachescale.Func) Config { 11 | return Config{ 12 | SenderThreads: 8, 13 | MaxSenderTasks: 128, 14 | MaxPendingResponsesSize: scale.I64(64 * 1024 * 1024), 15 | MaxResponsePayloadNum: 16384, 16 | MaxResponsePayloadSize: 8 * 1024 * 1024, 17 | MaxResponseChunks: 12, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gossip/protocols/dag/dagstream/types.go: -------------------------------------------------------------------------------- 1 | package dagstream 2 | 3 | import ( 4 | "bytes" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/rlp" 9 | 10 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream" 11 | "github.com/Fantom-foundation/lachesis-base/hash" 12 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 13 | ) 14 | 15 | type Request struct { 16 | Session Session 17 | Limit dag.Metric 18 | Type basestream.RequestType 19 | MaxChunks uint32 20 | } 21 | 22 | type Response struct { 23 | SessionID uint32 24 | Done bool 25 | IDs hash.Events 26 | Events []rlp.RawValue 27 | } 28 | 29 | type Session struct { 30 | ID uint32 31 | Start Locator 32 | Stop Locator 33 | } 34 | 35 | type Locator []byte 36 | 37 | func (l Locator) Compare(b basestream.Locator) int { 38 | return bytes.Compare(l, b.(Locator)) 39 | } 40 | 41 | func (l Locator) Inc() basestream.Locator { 42 | nextBn := new(big.Int).SetBytes(l) 43 | nextBn.Add(nextBn, common.Big1) 44 | return Locator(common.LeftPadBytes(nextBn.Bytes(), len(l))) 45 | } 46 | 47 | type Payload struct { 48 | IDs hash.Events 49 | Events []rlp.RawValue 50 | Size uint64 51 | } 52 | 53 | func (p *Payload) AddEvent(id hash.Event, eventB rlp.RawValue) { 54 | p.IDs = append(p.IDs, id) 55 | p.Events = append(p.Events, eventB) 56 | p.Size += uint64(len(eventB)) 57 | } 58 | 59 | func (p *Payload) AddID(id hash.Event, size int) { 60 | p.IDs = append(p.IDs, id) 61 | p.Size += uint64(size) 62 | } 63 | 64 | func (p Payload) Len() int { 65 | return len(p.IDs) 66 | } 67 | 68 | func (p Payload) TotalSize() uint64 { 69 | return p.Size 70 | } 71 | 72 | func (p Payload) TotalMemSize() int { 73 | if len(p.Events) != 0 { 74 | return int(p.Size) + len(p.IDs)*128 75 | } 76 | return len(p.IDs) * 128 77 | } 78 | 79 | const ( 80 | RequestIDs basestream.RequestType = 0 81 | RequestEvents basestream.RequestType = 2 82 | ) 83 | -------------------------------------------------------------------------------- /gossip/protocols/epochpacks/epprocessor/config.go: -------------------------------------------------------------------------------- 1 | package epprocessor 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/syndtr/goleveldb/leveldb/opt" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/inter/dag" 9 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 10 | ) 11 | 12 | type Config struct { 13 | BufferLimit dag.Metric 14 | 15 | SemaphoreTimeout time.Duration 16 | 17 | MaxTasks int 18 | } 19 | 20 | func DefaultConfig(scale cachescale.Func) Config { 21 | return Config{ 22 | BufferLimit: dag.Metric{ 23 | Num: 10000, 24 | Size: scale.U64(15 * opt.MiB), 25 | }, 26 | SemaphoreTimeout: 10 * time.Second, 27 | MaxTasks: 512, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gossip/protocols/epochpacks/epstream/epstreamleecher/config.go: -------------------------------------------------------------------------------- 1 | package epstreamleecher 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamleecher/basepeerleecher" 7 | ) 8 | 9 | type Config struct { 10 | Session basepeerleecher.EpochDownloaderConfig 11 | RecheckInterval time.Duration 12 | BaseProgressWatchdog time.Duration 13 | BaseSessionWatchdog time.Duration 14 | MinSessionRestart time.Duration 15 | MaxSessionRestart time.Duration 16 | } 17 | 18 | // DefaultConfig returns default leecher config 19 | func DefaultConfig() Config { 20 | return Config{ 21 | Session: basepeerleecher.EpochDownloaderConfig{ 22 | DefaultChunkItemsNum: 500, 23 | DefaultChunkItemsSize: 512 * 1024, 24 | ParallelChunksDownload: 6, 25 | RecheckInterval: 10 * time.Millisecond, 26 | }, 27 | RecheckInterval: time.Second, 28 | BaseProgressWatchdog: time.Second * 5, 29 | BaseSessionWatchdog: time.Second * 30 * 5, 30 | MinSessionRestart: time.Second * 5, 31 | MaxSessionRestart: time.Minute * 5, 32 | } 33 | } 34 | 35 | // LiteConfig returns default leecher config for tests 36 | func LiteConfig() Config { 37 | cfg := DefaultConfig() 38 | cfg.Session.DefaultChunkItemsSize /= 10 39 | cfg.Session.DefaultChunkItemsNum /= 10 40 | cfg.Session.ParallelChunksDownload = cfg.Session.ParallelChunksDownload/2 + 1 41 | return cfg 42 | } 43 | -------------------------------------------------------------------------------- /gossip/protocols/epochpacks/epstream/epstreamseeder/config.go: -------------------------------------------------------------------------------- 1 | package epstreamseeder 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream/basestreamseeder" 5 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 6 | ) 7 | 8 | type Config basestreamseeder.Config 9 | 10 | func DefaultConfig(scale cachescale.Func) Config { 11 | return Config{ 12 | SenderThreads: 2, 13 | MaxSenderTasks: 64, 14 | MaxPendingResponsesSize: scale.I64(32 * 1024 * 1024), 15 | MaxResponsePayloadNum: 4096, 16 | MaxResponsePayloadSize: 8 * 1024 * 1024, 17 | MaxResponseChunks: 12, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gossip/protocols/epochpacks/epstream/types.go: -------------------------------------------------------------------------------- 1 | package epstream 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | "github.com/ethereum/go-ethereum/rlp" 8 | 9 | "github.com/Fantom-foundation/lachesis-base/gossip/basestream" 10 | ) 11 | 12 | type Request struct { 13 | Session Session 14 | Limit Metric 15 | Type basestream.RequestType 16 | MaxChunks uint32 17 | } 18 | 19 | type Response struct { 20 | SessionID uint32 21 | Done bool 22 | Payload []rlp.RawValue 23 | } 24 | 25 | type Session struct { 26 | ID uint32 27 | Start Locator 28 | Stop Locator 29 | } 30 | 31 | type Locator idx.Epoch 32 | 33 | func (l Locator) Compare(b basestream.Locator) int { 34 | if l == b.(Locator) { 35 | return 0 36 | } 37 | if l < b.(Locator) { 38 | return -1 39 | } 40 | return 1 41 | } 42 | 43 | func (l Locator) Inc() basestream.Locator { 44 | return l + 1 45 | } 46 | 47 | type Payload struct { 48 | Items []rlp.RawValue 49 | Keys []Locator 50 | Size uint64 51 | } 52 | 53 | func (p *Payload) AddEpochPacks(id Locator, epsB rlp.RawValue) { 54 | p.Items = append(p.Items, epsB) 55 | p.Keys = append(p.Keys, id) 56 | p.Size += uint64(len(epsB)) 57 | } 58 | 59 | func (p Payload) Len() int { 60 | return len(p.Keys) 61 | } 62 | 63 | func (p Payload) TotalSize() uint64 { 64 | return p.Size 65 | } 66 | 67 | func (p Payload) TotalMemSize() int { 68 | return int(p.Size) + len(p.Keys)*32 69 | } 70 | 71 | type Metric struct { 72 | Num idx.Epoch 73 | Size uint64 74 | } 75 | 76 | func (m Metric) String() string { 77 | return fmt.Sprintf("{Num=%d,Size=%d}", m.Num, m.Size) 78 | } 79 | -------------------------------------------------------------------------------- /gossip/protocols/snap/events.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package snap 18 | 19 | import "github.com/ethereum/go-ethereum/core/types" 20 | 21 | type DoneEvent struct { 22 | Latest *types.Header 23 | } 24 | type StartEvent struct{} 25 | type FailedEvent struct{ Err error } 26 | -------------------------------------------------------------------------------- /gossip/protocols/snap/snapstream/snapleecher/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package snapleecher 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | // peerDropFn is a callback type for dropping a peer detected as malicious. 24 | type peerDropFn func(id string) 25 | 26 | // dataPack is a data message returned by a peer for some query. 27 | type dataPack interface { 28 | PeerId() string 29 | Items() int 30 | Stats() string 31 | } 32 | 33 | // statePack is a batch of states returned by a peer. 34 | type statePack struct { 35 | peerID string 36 | states [][]byte 37 | } 38 | 39 | func (p *statePack) PeerId() string { return p.peerID } 40 | func (p *statePack) Items() int { return len(p.states) } 41 | func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) } 42 | -------------------------------------------------------------------------------- /gossip/store_activation_heights.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/opera" 5 | ) 6 | 7 | func (s *Store) AddUpgradeHeight(h opera.UpgradeHeight) { 8 | orig := s.GetUpgradeHeights() 9 | // allocate new memory to avoid race condition in cache 10 | cp := make([]opera.UpgradeHeight, 0, len(orig)+1) 11 | cp = append(append(cp, orig...), h) 12 | 13 | s.rlp.Set(s.table.UpgradeHeights, []byte{}, cp) 14 | s.cache.UpgradeHeights.Store(cp) 15 | } 16 | 17 | func (s *Store) GetUpgradeHeights() []opera.UpgradeHeight { 18 | if v := s.cache.UpgradeHeights.Load(); v != nil { 19 | return v.([]opera.UpgradeHeight) 20 | } 21 | hh, ok := s.rlp.Get(s.table.UpgradeHeights, []byte{}, &[]opera.UpgradeHeight{}).(*[]opera.UpgradeHeight) 22 | if !ok { 23 | return []opera.UpgradeHeight{} 24 | } 25 | s.cache.UpgradeHeights.Store(*hh) 26 | return *hh 27 | } 28 | -------------------------------------------------------------------------------- /gossip/store_llr_state.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 5 | "github.com/ethereum/go-ethereum/log" 6 | ) 7 | 8 | type LlrState struct { 9 | LowestEpochToDecide idx.Epoch 10 | LowestEpochToFill idx.Epoch 11 | 12 | LowestBlockToDecide idx.Block 13 | LowestBlockToFill idx.Block 14 | } 15 | 16 | func (s *Store) setLlrState(llrs LlrState) { 17 | s.cache.LlrState.Store(&llrs) 18 | } 19 | 20 | func (s *Store) ModifyLlrState(f func(*LlrState)) { 21 | s.mutex.WriteLlrState.Lock() 22 | defer s.mutex.WriteLlrState.Unlock() 23 | llrs := s.GetLlrState() 24 | f(&llrs) 25 | s.setLlrState(llrs) 26 | } 27 | 28 | func (s *Store) GetLlrState() LlrState { 29 | if v := s.cache.LlrState.Load(); v != nil { 30 | return *v.(*LlrState) 31 | } 32 | v, ok := s.rlp.Get(s.table.LlrState, []byte{}, &LlrState{}).(*LlrState) 33 | if !ok { 34 | log.Crit("LLR state reading failed: genesis not applied") 35 | } 36 | s.cache.LlrState.Store(v) 37 | return *v 38 | } 39 | 40 | // FlushLlrState stores the LLR state in DB 41 | func (s *Store) FlushLlrState() { 42 | s.rlp.Set(s.table.LlrState, []byte{}, s.GetLlrState()) 43 | } 44 | -------------------------------------------------------------------------------- /gossip/tflusher.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type PeriodicFlusherCallaback struct { 9 | busy func() bool 10 | commitNeeded func() bool 11 | commit func() 12 | } 13 | 14 | // PeriodicFlusher periodically commits the Store if isCommitNeeded returns true 15 | type PeriodicFlusher struct { 16 | period time.Duration 17 | callback PeriodicFlusherCallaback 18 | 19 | wg sync.WaitGroup 20 | quit chan struct{} 21 | } 22 | 23 | func (c *PeriodicFlusher) loop() { 24 | defer c.wg.Done() 25 | ticker := time.NewTicker(c.period) 26 | defer ticker.Stop() 27 | for { 28 | select { 29 | case <-ticker.C: 30 | if !c.callback.busy() && c.callback.commitNeeded() { 31 | c.callback.commit() 32 | } 33 | case <-c.quit: 34 | return 35 | } 36 | } 37 | } 38 | 39 | func (c *PeriodicFlusher) Start() { 40 | c.wg.Add(1) 41 | go c.loop() 42 | } 43 | 44 | func (c *PeriodicFlusher) Stop() { 45 | close(c.quit) 46 | c.wg.Wait() 47 | } 48 | -------------------------------------------------------------------------------- /integration/diskusage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | //go:build !windows && !openbsd 18 | // +build !windows,!openbsd 19 | 20 | package integration 21 | 22 | import ( 23 | "fmt" 24 | 25 | "golang.org/x/sys/unix" 26 | ) 27 | 28 | func getFreeDiskSpace(path string) (uint64, error) { 29 | var stat unix.Statfs_t 30 | if err := unix.Statfs(path, &stat); err != nil { 31 | return 0, fmt.Errorf("failed to call Statfs: %v", err) 32 | } 33 | 34 | // Available blocks * size per block = available space in bytes 35 | var bavail = stat.Bavail 36 | if stat.Bavail < 0 { 37 | // FreeBSD can have a negative number of blocks available 38 | // because of the grace limit. 39 | bavail = 0 40 | } 41 | //nolint:unconvert 42 | return uint64(bavail) * uint64(stat.Bsize), nil 43 | } 44 | -------------------------------------------------------------------------------- /integration/diskusage_openbsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | //go:build openbsd 18 | // +build openbsd 19 | 20 | package integration 21 | 22 | import ( 23 | "fmt" 24 | 25 | "golang.org/x/sys/unix" 26 | ) 27 | 28 | func getFreeDiskSpace(path string) (uint64, error) { 29 | var stat unix.Statfs_t 30 | if err := unix.Statfs(path, &stat); err != nil { 31 | return 0, fmt.Errorf("failed to call Statfs: %v", err) 32 | } 33 | 34 | // Available blocks * size per block = available space in bytes 35 | var bavail = stat.F_bavail 36 | // Not sure if the following check is necessary for OpenBSD 37 | if stat.F_bavail < 0 { 38 | // FreeBSD can have a negative number of blocks available 39 | // because of the grace limit. 40 | bavail = 0 41 | } 42 | //nolint:unconvert 43 | return uint64(bavail) * uint64(stat.F_bsize), nil 44 | } 45 | -------------------------------------------------------------------------------- /integration/diskusage_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package integration 18 | 19 | import ( 20 | "fmt" 21 | 22 | "golang.org/x/sys/windows" 23 | ) 24 | 25 | func getFreeDiskSpace(path string) (uint64, error) { 26 | 27 | cwd, err := windows.UTF16PtrFromString(path) 28 | if err != nil { 29 | return 0, fmt.Errorf("failed to call UTF16PtrFromString: %v", err) 30 | } 31 | 32 | var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64 33 | if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil { 34 | return 0, fmt.Errorf("failed to call GetDiskFreeSpaceEx: %v", err) 35 | } 36 | 37 | return freeBytesAvailableToCaller, nil 38 | } 39 | -------------------------------------------------------------------------------- /integration/metric_test.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestGenericNameOfTmpDB(t *testing.T) { 10 | require := require.New(t) 11 | 12 | for name, exp := range map[string]string{ 13 | "": "", 14 | "main": "main", 15 | "main-single": "main-single", 16 | "lachesis-0": "lachesis-tmp", 17 | "lachesis-0999": "lachesis-tmp", 18 | "gossip-50": "gossip-tmp", 19 | "epoch-1": "epoch-tmp", 20 | "xxx-1a": "xxx-1a", 21 | "123": "123", 22 | } { 23 | got := genericNameOfTmpDB(name) 24 | require.Equal(exp, got, name) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /integration/status.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "os" 5 | "path" 6 | 7 | "github.com/Fantom-foundation/go-opera/utils" 8 | ) 9 | 10 | func isInterrupted(chaindataDir string) bool { 11 | return utils.FileExists(path.Join(chaindataDir, "unfinished")) 12 | } 13 | 14 | func setGenesisProcessing(chaindataDir string) { 15 | f, _ := os.Create(path.Join(chaindataDir, "unfinished")) 16 | if f != nil { 17 | _ = f.Close() 18 | } 19 | } 20 | 21 | func setGenesisComplete(chaindataDir string) { 22 | _ = os.Remove(path.Join(chaindataDir, "unfinished")) 23 | } 24 | -------------------------------------------------------------------------------- /integration/xenblocks/reporter/stack.go: -------------------------------------------------------------------------------- 1 | package reporter 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type SingleStack struct { 8 | lock sync.Mutex 9 | job *WebSocketJob 10 | } 11 | 12 | func NewSingleStack() *SingleStack { 13 | return &SingleStack{sync.Mutex{}, nil} 14 | } 15 | 16 | func (s *SingleStack) Push(v *WebSocketJob) { 17 | s.lock.Lock() 18 | defer s.lock.Unlock() 19 | 20 | s.job = v 21 | } 22 | 23 | func (s *SingleStack) Pop() *WebSocketJob { 24 | s.lock.Lock() 25 | defer s.lock.Unlock() 26 | 27 | v := s.job 28 | s.job = nil 29 | return v 30 | } 31 | -------------------------------------------------------------------------------- /inter/block.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/core/types" 7 | ) 8 | 9 | type Block struct { 10 | Time Timestamp 11 | Atropos hash.Event 12 | Events hash.Events 13 | Txs []common.Hash // non event txs (received via genesis or LLR) 14 | InternalTxs []common.Hash // DEPRECATED in favor of using only Txs fields and method internal.IsInternal 15 | SkippedTxs []uint32 // indexes of skipped txs, starting from first tx of first event, ending with last tx of last event 16 | GasUsed uint64 17 | Root hash.Hash 18 | } 19 | 20 | func (b *Block) EstimateSize() int { 21 | return (len(b.Events)+len(b.InternalTxs)+len(b.Txs)+1+1)*32 + len(b.SkippedTxs)*4 + 8 + 8 22 | } 23 | 24 | func FilterSkippedTxs(txs types.Transactions, skippedTxs []uint32) types.Transactions { 25 | if len(skippedTxs) == 0 { 26 | // short circuit if nothing to skip 27 | return txs 28 | } 29 | skipCount := 0 30 | filteredTxs := make(types.Transactions, 0, len(txs)) 31 | for i, tx := range txs { 32 | if skipCount < len(skippedTxs) && skippedTxs[skipCount] == uint32(i) { 33 | skipCount++ 34 | } else { 35 | filteredTxs = append(filteredTxs, tx) 36 | } 37 | } 38 | 39 | return filteredTxs 40 | } 41 | -------------------------------------------------------------------------------- /inter/drivertype/driver_type.go: -------------------------------------------------------------------------------- 1 | package drivertype 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | 8 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 9 | ) 10 | 11 | var ( 12 | // DoublesignBit is set if validator has a confirmed pair of fork events 13 | DoublesignBit = uint64(1 << 7) 14 | OkStatus = uint64(0) 15 | ) 16 | 17 | // Validator is the node-side representation of Driver validator 18 | type Validator struct { 19 | Weight *big.Int 20 | PubKey validatorpk.PubKey 21 | } 22 | 23 | // ValidatorAndID is pair Validator + ValidatorID 24 | type ValidatorAndID struct { 25 | ValidatorID idx.ValidatorID 26 | Validator Validator 27 | } 28 | -------------------------------------------------------------------------------- /inter/event_test.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestCalcMisbehaviourProofsHash(t *testing.T) { 11 | v := []MisbehaviourProof{ 12 | MisbehaviourProof{ 13 | EventsDoublesign: &EventsDoublesign{ 14 | Pair: [2]SignedEventLocator{ 15 | test_signed_event_locator, 16 | test_signed_event_locator, 17 | }, 18 | }, 19 | }, 20 | MisbehaviourProof{ 21 | BlockVoteDoublesign: &BlockVoteDoublesign{ 22 | Block: test_block_votes.Start, 23 | Pair: [2]LlrSignedBlockVotes{ 24 | test_llr_signed_block_votes, 25 | test_llr_signed_block_votes, 26 | }, 27 | }, 28 | }, 29 | MisbehaviourProof{ 30 | WrongBlockVote: &WrongBlockVote{ 31 | Block: test_block_votes.Start, 32 | Pals: [2]LlrSignedBlockVotes{ 33 | test_llr_signed_block_votes, 34 | test_llr_signed_block_votes, 35 | }, 36 | WrongEpoch: true, 37 | }, 38 | }, 39 | MisbehaviourProof{ 40 | EpochVoteDoublesign: &EpochVoteDoublesign{ 41 | Pair: [2]LlrSignedEpochVote{ 42 | test_llr_signed_epoch_vote, 43 | test_llr_signed_epoch_vote, 44 | }, 45 | }, 46 | }, 47 | MisbehaviourProof{ 48 | WrongEpochVote: &WrongEpochVote{ 49 | Pals: [2]LlrSignedEpochVote{ 50 | test_llr_signed_epoch_vote, 51 | test_llr_signed_epoch_vote, 52 | }, 53 | }, 54 | }, 55 | } 56 | 57 | require.Equal(t, "85834ef7fc1d75d65832b1f9b45b43c4f5677811bb2d384208553d32ab49def1", hex.EncodeToString(CalcMisbehaviourProofsHash(v).Bytes())) 58 | } 59 | -------------------------------------------------------------------------------- /inter/gas_power_left.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import "fmt" 4 | 5 | const ( 6 | ShortTermGas = 0 7 | LongTermGas = 1 8 | GasPowerConfigs = 2 9 | ) 10 | 11 | // GasPowerLeft is long-term gas power left and short-term gas power left 12 | type GasPowerLeft struct { 13 | Gas [GasPowerConfigs]uint64 14 | } 15 | 16 | // Add add to all gas power lefts 17 | func (g GasPowerLeft) Add(diff uint64) { 18 | for i := range g.Gas { 19 | g.Gas[i] += diff 20 | } 21 | } 22 | 23 | // Min returns minimum within long-term gas power left and short-term gas power left 24 | func (g GasPowerLeft) Min() uint64 { 25 | min := g.Gas[0] 26 | for _, gas := range g.Gas { 27 | if min > gas { 28 | min = gas 29 | } 30 | } 31 | return min 32 | } 33 | 34 | // Max returns maximum within long-term gas power left and short-term gas power left 35 | func (g GasPowerLeft) Max() uint64 { 36 | max := g.Gas[0] 37 | for _, gas := range g.Gas { 38 | if max < gas { 39 | max = gas 40 | } 41 | } 42 | return max 43 | } 44 | 45 | // Sub subtracts from all gas power lefts 46 | func (g GasPowerLeft) Sub(diff uint64) GasPowerLeft { 47 | cp := g 48 | for i := range cp.Gas { 49 | cp.Gas[i] -= diff 50 | } 51 | return cp 52 | } 53 | 54 | // String returns string representation. 55 | func (g GasPowerLeft) String() string { 56 | return fmt.Sprintf("{short=%d, long=%d}", g.Gas[ShortTermGas], g.Gas[LongTermGas]) 57 | } 58 | -------------------------------------------------------------------------------- /inter/gas_power_left_test.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/rlp" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestRlp(t *testing.T) { 12 | require := require.New(t) 13 | v := GasPowerLeft{ 14 | Gas: [2]uint64{0xBAADCAFE, 0xDEADBEEF}, 15 | } 16 | b, err := rlp.EncodeToBytes(v) 17 | require.NoError(err) 18 | require.Equal("cbca84baadcafe84deadbeef", common.Bytes2Hex(b)) 19 | } 20 | -------------------------------------------------------------------------------- /inter/iblockproc/legacy.go: -------------------------------------------------------------------------------- 1 | package iblockproc 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 6 | "github.com/Fantom-foundation/lachesis-base/inter/pos" 7 | 8 | "github.com/Fantom-foundation/go-opera/inter" 9 | "github.com/Fantom-foundation/go-opera/opera" 10 | ) 11 | 12 | type ValidatorEpochStateV0 struct { 13 | GasRefund uint64 14 | PrevEpochEvent hash.Event 15 | } 16 | 17 | type EpochStateV0 struct { 18 | Epoch idx.Epoch 19 | EpochStart inter.Timestamp 20 | PrevEpochStart inter.Timestamp 21 | 22 | EpochStateRoot hash.Hash 23 | 24 | Validators *pos.Validators 25 | ValidatorStates []ValidatorEpochStateV0 26 | ValidatorProfiles ValidatorProfiles 27 | 28 | Rules opera.Rules 29 | } 30 | -------------------------------------------------------------------------------- /inter/iblockproc/profiles.go: -------------------------------------------------------------------------------- 1 | package iblockproc 2 | 3 | import ( 4 | "io" 5 | "math/big" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | "github.com/Fantom-foundation/lachesis-base/inter/pos" 9 | "github.com/ethereum/go-ethereum/rlp" 10 | 11 | "github.com/Fantom-foundation/go-opera/inter/drivertype" 12 | ) 13 | 14 | type ValidatorProfiles map[idx.ValidatorID]drivertype.Validator 15 | 16 | func (vv ValidatorProfiles) Copy() ValidatorProfiles { 17 | cp := make(ValidatorProfiles, len(vv)) 18 | for k, v := range vv { 19 | cpv := v 20 | cpv.Weight = new(big.Int).Set(cpv.Weight) 21 | cpv.PubKey = cpv.PubKey.Copy() 22 | cp[k] = cpv 23 | } 24 | return cp 25 | } 26 | 27 | func (vv ValidatorProfiles) SortedArray() []drivertype.ValidatorAndID { 28 | builder := pos.NewBigBuilder() 29 | for id, profile := range vv { 30 | builder.Set(id, profile.Weight) 31 | } 32 | validators := builder.Build() 33 | sortedIds := validators.SortedIDs() 34 | arr := make([]drivertype.ValidatorAndID, validators.Len()) 35 | for i, id := range sortedIds { 36 | arr[i] = drivertype.ValidatorAndID{ 37 | ValidatorID: id, 38 | Validator: vv[id], 39 | } 40 | } 41 | return arr 42 | } 43 | 44 | // EncodeRLP is for RLP serialization. 45 | func (vv ValidatorProfiles) EncodeRLP(w io.Writer) error { 46 | return rlp.Encode(w, vv.SortedArray()) 47 | } 48 | 49 | // DecodeRLP is for RLP deserialization. 50 | func (vv *ValidatorProfiles) DecodeRLP(s *rlp.Stream) error { 51 | var arr []drivertype.ValidatorAndID 52 | if err := s.Decode(&arr); err != nil { 53 | return err 54 | } 55 | 56 | *vv = make(ValidatorProfiles, len(arr)) 57 | 58 | for _, it := range arr { 59 | (*vv)[it.ValidatorID] = it.Validator 60 | } 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /inter/ibr/inter_block_records.go: -------------------------------------------------------------------------------- 1 | package ibr 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/common/bigendian" 5 | "github.com/Fantom-foundation/lachesis-base/hash" 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | 9 | "github.com/Fantom-foundation/go-opera/inter" 10 | ) 11 | 12 | type LlrBlockVote struct { 13 | Atropos hash.Event 14 | Root hash.Hash 15 | TxHash hash.Hash 16 | ReceiptsHash hash.Hash 17 | Time inter.Timestamp 18 | GasUsed uint64 19 | } 20 | 21 | type LlrFullBlockRecord struct { 22 | Atropos hash.Event 23 | Root hash.Hash 24 | Txs types.Transactions 25 | Receipts []*types.ReceiptForStorage 26 | Time inter.Timestamp 27 | GasUsed uint64 28 | } 29 | 30 | type LlrIdxFullBlockRecord struct { 31 | LlrFullBlockRecord 32 | Idx idx.Block 33 | } 34 | 35 | func (bv LlrBlockVote) Hash() hash.Hash { 36 | return hash.Of(bv.Atropos.Bytes(), bv.Root.Bytes(), bv.TxHash.Bytes(), bv.ReceiptsHash.Bytes(), bv.Time.Bytes(), bigendian.Uint64ToBytes(bv.GasUsed)) 37 | } 38 | 39 | func (br LlrFullBlockRecord) Hash() hash.Hash { 40 | return LlrBlockVote{ 41 | Atropos: br.Atropos, 42 | Root: br.Root, 43 | TxHash: inter.CalcTxHash(br.Txs), 44 | ReceiptsHash: inter.CalcReceiptsHash(br.Receipts), 45 | Time: br.Time, 46 | GasUsed: br.GasUsed, 47 | }.Hash() 48 | } 49 | -------------------------------------------------------------------------------- /inter/iep/inter_epoch_packs.go: -------------------------------------------------------------------------------- 1 | package iep 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/inter" 5 | "github.com/Fantom-foundation/go-opera/inter/ier" 6 | ) 7 | 8 | type LlrEpochPack struct { 9 | Votes []inter.LlrSignedEpochVote 10 | Record ier.LlrIdxFullEpochRecord 11 | } 12 | -------------------------------------------------------------------------------- /inter/ier/inter_epoch_records.go: -------------------------------------------------------------------------------- 1 | package ier 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 6 | 7 | "github.com/Fantom-foundation/go-opera/inter/iblockproc" 8 | ) 9 | 10 | type LlrFullEpochRecord struct { 11 | BlockState iblockproc.BlockState 12 | EpochState iblockproc.EpochState 13 | } 14 | 15 | type LlrIdxFullEpochRecord struct { 16 | LlrFullEpochRecord 17 | Idx idx.Epoch 18 | } 19 | 20 | func (er LlrFullEpochRecord) Hash() hash.Hash { 21 | return hash.Of(er.BlockState.Hash().Bytes(), er.EpochState.Hash().Bytes()) 22 | } 23 | -------------------------------------------------------------------------------- /inter/inter_mps.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 6 | ) 7 | 8 | const ( 9 | // MinAccomplicesForProof defines how many validators must have signed the same wrong vote. 10 | // Otherwise, wrong-signer is not liable as a protection against singular software/hardware failures 11 | MinAccomplicesForProof = 2 12 | ) 13 | 14 | type EventsDoublesign struct { 15 | Pair [2]SignedEventLocator 16 | } 17 | 18 | type BlockVoteDoublesign struct { 19 | Block idx.Block 20 | Pair [2]LlrSignedBlockVotes 21 | } 22 | 23 | func (p BlockVoteDoublesign) GetVote(i int) hash.Hash { 24 | return p.Pair[i].Val.Votes[p.Block-p.Pair[i].Val.Start] 25 | } 26 | 27 | type WrongBlockVote struct { 28 | Block idx.Block 29 | Pals [MinAccomplicesForProof]LlrSignedBlockVotes 30 | WrongEpoch bool 31 | } 32 | 33 | func (p WrongBlockVote) GetVote(i int) hash.Hash { 34 | return p.Pals[i].Val.Votes[p.Block-p.Pals[i].Val.Start] 35 | } 36 | 37 | type EpochVoteDoublesign struct { 38 | Pair [2]LlrSignedEpochVote 39 | } 40 | 41 | type WrongEpochVote struct { 42 | Pals [MinAccomplicesForProof]LlrSignedEpochVote 43 | } 44 | 45 | type MisbehaviourProof struct { 46 | EventsDoublesign *EventsDoublesign `rlp:"nil"` 47 | 48 | BlockVoteDoublesign *BlockVoteDoublesign `rlp:"nil"` 49 | 50 | WrongBlockVote *WrongBlockVote `rlp:"nil"` 51 | 52 | EpochVoteDoublesign *EpochVoteDoublesign `rlp:"nil"` 53 | 54 | WrongEpochVote *WrongEpochVote `rlp:"nil"` 55 | } 56 | -------------------------------------------------------------------------------- /inter/signature.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | const SigSize = 64 4 | 5 | // Signature is a secp256k1 signature in R|S format 6 | type Signature [SigSize]byte 7 | 8 | func (s Signature) Bytes() []byte { 9 | return s[:] 10 | } 11 | 12 | func BytesToSignature(b []byte) (sig Signature) { 13 | if len(b) != SigSize { 14 | panic("invalid signature length") 15 | } 16 | copy(sig[:], b) 17 | return sig 18 | } 19 | -------------------------------------------------------------------------------- /inter/time.go: -------------------------------------------------------------------------------- 1 | package inter 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/common/bigendian" 7 | ) 8 | 9 | type ( 10 | // Timestamp is a UNIX nanoseconds timestamp 11 | Timestamp uint64 12 | ) 13 | 14 | // Bytes gets the byte representation of the index. 15 | func (t Timestamp) Bytes() []byte { 16 | return bigendian.Uint64ToBytes(uint64(t)) 17 | } 18 | 19 | // BytesToTimestamp converts bytes to timestamp. 20 | func BytesToTimestamp(b []byte) Timestamp { 21 | return Timestamp(bigendian.BytesToUint64(b)) 22 | } 23 | 24 | func FromUnix(t int64) Timestamp { 25 | return Timestamp(int64(t) * int64(time.Second)) 26 | } 27 | 28 | // Unix returns t as a Unix time, the number of seconds elapsed 29 | // since January 1, 1970 UTC. The result does not depend on the 30 | // location associated with t. 31 | func (t Timestamp) Unix() int64 { 32 | return int64(t) / int64(time.Second) 33 | } 34 | 35 | func (t Timestamp) Time() time.Time { 36 | return time.Unix(int64(t)/int64(time.Second), int64(t)%int64(time.Second)) 37 | } 38 | 39 | // MaxTimestamp return max value. 40 | func MaxTimestamp(x, y Timestamp) Timestamp { 41 | if x > y { 42 | return x 43 | } 44 | return y 45 | } 46 | -------------------------------------------------------------------------------- /inter/validatorpk/pubkey.go: -------------------------------------------------------------------------------- 1 | package validatorpk 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | const ( 9 | FakePassword = "fakepassword" 10 | ) 11 | 12 | type PubKey struct { 13 | Type uint8 14 | Raw []byte 15 | } 16 | 17 | var Types = struct { 18 | Secp256k1 uint8 19 | }{ 20 | Secp256k1: 0xc0, 21 | } 22 | 23 | func (pk PubKey) Empty() bool { 24 | return len(pk.Raw) == 0 && pk.Type == 0 25 | } 26 | 27 | func (pk PubKey) String() string { 28 | return "0x" + common.Bytes2Hex(pk.Bytes()) 29 | } 30 | 31 | func (pk PubKey) Bytes() []byte { 32 | return append([]byte{pk.Type}, pk.Raw...) 33 | } 34 | 35 | func (pk PubKey) Copy() PubKey { 36 | return PubKey{ 37 | Type: pk.Type, 38 | Raw: common.CopyBytes(pk.Raw), 39 | } 40 | } 41 | 42 | func FromString(str string) (PubKey, error) { 43 | return FromBytes(common.FromHex(str)) 44 | } 45 | 46 | func FromBytes(b []byte) (PubKey, error) { 47 | if len(b) == 0 { 48 | return PubKey{}, errors.New("empty pubkey") 49 | } 50 | return PubKey{b[0], b[1:]}, nil 51 | } 52 | 53 | // MarshalText returns the hex representation of a. 54 | func (pk *PubKey) MarshalText() ([]byte, error) { 55 | return []byte(pk.String()), nil 56 | } 57 | 58 | // UnmarshalText parses a hash in hex syntax. 59 | func (pk *PubKey) UnmarshalText(input []byte) error { 60 | res, err := FromString(string(input)) 61 | if err != nil { 62 | return err 63 | } 64 | *pk = res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /logger/instance.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/log" 5 | ) 6 | 7 | type Instance struct { 8 | Log log.Logger 9 | } 10 | 11 | func New(name ...string) Instance { 12 | if len(name) == 0 { 13 | return Instance{ 14 | Log: log.New(), 15 | } 16 | } 17 | return Instance{ 18 | Log: log.New("module", name[0]), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/log" 5 | "github.com/evalphobia/logrus_sentry" 6 | ) 7 | 8 | // init with defaults. 9 | func init() { 10 | log.Root().SetHandler( 11 | log.CallerStackHandler("%v", log.StdoutHandler)) 12 | } 13 | 14 | // SetDSN appends sentry hook to log root handler. 15 | func SetDSN(value string) { 16 | // If DSN is empty, we don't create new hook. 17 | // Otherwise we'll the same error message for each new log. 18 | if value == "" { 19 | log.Warn("Sentry client DSN is empty") 20 | return 21 | } 22 | 23 | // TODO: find or make sentry log.Handler without logrus. 24 | sentry, err := logrus_sentry.NewSentryHook(value, nil) 25 | if err != nil { 26 | log.Warn("Probably Sentry host is not running", "err", err) 27 | return 28 | } 29 | 30 | log.Root().SetHandler( 31 | log.MultiHandler( 32 | log.Root().GetHandler(), 33 | LogrusHandler(sentry), 34 | )) 35 | } 36 | 37 | // SetLevel sets level filter on log root handler. 38 | // So it should be called last. 39 | func SetLevel(l string) { 40 | lvl, err := log.LvlFromString(l) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | log.Root().SetHandler( 46 | log.LvlFilterHandler( 47 | lvl, 48 | log.Root().GetHandler())) 49 | } 50 | -------------------------------------------------------------------------------- /logger/logrus.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/log" 5 | "github.com/sirupsen/logrus" 6 | ) 7 | 8 | // LogrusHandler converts logrus hook to log handler. 9 | func LogrusHandler(hook logrus.Hook) log.Handler { 10 | return log.FuncHandler(func(r *log.Record) error { 11 | data := &logrus.Entry{ 12 | Message: r.Msg, 13 | Data: fields(r.Ctx), 14 | } 15 | return hook.Fire(data) 16 | }) 17 | } 18 | 19 | func fields(ctx []interface{}) logrus.Fields { 20 | ff := make(logrus.Fields, len(ctx)/2) 21 | for i := 1; i < len(ctx); i += 2 { 22 | k, ok := ctx[i-1].(string) 23 | if !ok { 24 | continue 25 | } 26 | ff[k] = ctx[i] 27 | } 28 | 29 | return ff 30 | } 31 | -------------------------------------------------------------------------------- /logger/periodic_log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Periodic is the same as logger.Instance, but writes only once in a period 8 | type Periodic struct { 9 | Instance 10 | prevLogTime time.Time 11 | } 12 | 13 | // Info is timed log.Info 14 | func (l *Periodic) Info(period time.Duration, msg string, ctx ...interface{}) { 15 | if time.Since(l.prevLogTime) > period { 16 | l.Log.Info(msg, ctx...) 17 | l.prevLogTime = time.Now() 18 | } 19 | } 20 | 21 | // Warn is timed log.Warn 22 | func (l *Periodic) Warn(period time.Duration, msg string, ctx ...interface{}) { 23 | if time.Since(l.prevLogTime) > period { 24 | l.Log.Warn(msg, ctx...) 25 | l.prevLogTime = time.Now() 26 | } 27 | } 28 | 29 | // Error is timed log.Error 30 | func (l *Periodic) Error(period time.Duration, msg string, ctx ...interface{}) { 31 | if time.Since(l.prevLogTime) > period { 32 | l.Log.Error(msg, ctx...) 33 | l.prevLogTime = time.Now() 34 | } 35 | } 36 | 37 | // Debug is timed log.Debug 38 | func (l *Periodic) Debug(period time.Duration, msg string, ctx ...interface{}) { 39 | if time.Since(l.prevLogTime) > period { 40 | l.Log.Debug(msg, ctx...) 41 | l.prevLogTime = time.Now() 42 | } 43 | } 44 | 45 | // Trace is timed log.Trace 46 | func (l *Periodic) Trace(period time.Duration, msg string, ctx ...interface{}) { 47 | if time.Since(l.prevLogTime) > period { 48 | l.Log.Trace(msg, ctx...) 49 | l.prevLogTime = time.Now() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /logger/test_output.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ethereum/go-ethereum/log" 7 | ) 8 | 9 | // SetTestMode sets test mode. 10 | func SetTestMode(t testing.TB) { 11 | log.Root().SetHandler( 12 | log.CallerStackHandler("%v", TestHandler(t, log.LogfmtFormat()))) 13 | } 14 | 15 | // TestHandler writes into test log. 16 | func TestHandler(t testing.TB, fmtr log.Format) log.Handler { 17 | return log.FuncHandler(func(r *log.Record) error { 18 | t.Log(string(fmtr.Format(r))) 19 | return nil 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /opera/contracts/driver/driver_predeploy.go: -------------------------------------------------------------------------------- 1 | package driver 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/gossip/contract/driver100" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // GetContractBin is NodeDriver contract genesis implementation bin code 10 | // Has to be compiled with flag bin-runtime 11 | // Built from opera-sfc be4e79d5a5a425f08efd6d65b588a72ae90f706f, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 10000 12 | func GetContractBin() []byte { 13 | return hexutil.MustDecode(driver100.ContractBinRuntime) 14 | } 15 | 16 | // ContractAddress is the NodeDriver contract address 17 | var ContractAddress = common.HexToAddress("0xd100a01e00000000000000000000000000000000") 18 | -------------------------------------------------------------------------------- /opera/contracts/driver/driverpos/positions.go: -------------------------------------------------------------------------------- 1 | package driverpos 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/crypto" 6 | ) 7 | 8 | // Events 9 | var ( 10 | // Topics of Driver contract logs 11 | Topics = struct { 12 | UpdateValidatorWeight common.Hash 13 | UpdateValidatorPubkey common.Hash 14 | UpdateNetworkRules common.Hash 15 | UpdateNetworkVersion common.Hash 16 | AdvanceEpochs common.Hash 17 | }{ 18 | UpdateValidatorWeight: crypto.Keccak256Hash([]byte("UpdateValidatorWeight(uint256,uint256)")), 19 | UpdateValidatorPubkey: crypto.Keccak256Hash([]byte("UpdateValidatorPubkey(uint256,bytes)")), 20 | UpdateNetworkRules: crypto.Keccak256Hash([]byte("UpdateNetworkRules(bytes)")), 21 | UpdateNetworkVersion: crypto.Keccak256Hash([]byte("UpdateNetworkVersion(uint256)")), 22 | AdvanceEpochs: crypto.Keccak256Hash([]byte("AdvanceEpochs(uint256)")), 23 | } 24 | ) 25 | -------------------------------------------------------------------------------- /opera/contracts/driverauth/driverauth.go: -------------------------------------------------------------------------------- 1 | package driverauth 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/gossip/contract/driverauth100" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // GetContractBin is NodeDriverAuth contract genesis implementation bin code 10 | // Has to be compiled with flag bin-runtime 11 | // Built from opera-sfc be4e79d5a5a425f08efd6d65b588a72ae90f706f, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 10000 12 | func GetContractBin() []byte { 13 | return hexutil.MustDecode(driverauth100.ContractBinRuntime) 14 | } 15 | 16 | // ContractAddress is the NodeDriverAuth contract address 17 | var ContractAddress = common.HexToAddress("0xd100ae0000000000000000000000000000000000") 18 | -------------------------------------------------------------------------------- /opera/contracts/emitterdriver/emitterdriver.go: -------------------------------------------------------------------------------- 1 | package emitterdriver 2 | 3 | import "github.com/ethereum/go-ethereum/common" 4 | 5 | // ContractAddress is the EmitterDriver contract address 6 | var ContractAddress = common.HexToAddress("0xee00d10000000000000000000000000000000000") 7 | -------------------------------------------------------------------------------- /opera/contracts/evmwriter/evm_writer_test.go: -------------------------------------------------------------------------------- 1 | package evmwriter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestSign(t *testing.T) { 10 | require := require.New(t) 11 | 12 | require.Equal([]byte{0xe3, 0x04, 0x43, 0xbc}, setBalanceMethodID) 13 | require.Equal([]byte{0xd6, 0xa0, 0xc7, 0xaf}, copyCodeMethodID) 14 | require.Equal([]byte{0x07, 0x69, 0x0b, 0x2a}, swapCodeMethodID) 15 | require.Equal([]byte{0x39, 0xe5, 0x03, 0xab}, setStorageMethodID) 16 | require.Equal([]byte{0x79, 0xbe, 0xad, 0x38}, incNonceMethodID) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /opera/contracts/netinit/netinitcalls/network_initializer_calls.go: -------------------------------------------------------------------------------- 1 | package netinitcall 2 | 3 | import ( 4 | "math/big" 5 | "strings" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | "github.com/ethereum/go-ethereum/accounts/abi" 9 | "github.com/ethereum/go-ethereum/common" 10 | 11 | "github.com/Fantom-foundation/go-opera/utils" 12 | ) 13 | 14 | const ContractABI = "[{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sealedEpoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"addresspayable\",\"name\":\"_sfc\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_lib\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_auth\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_driver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_evmWriter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"initializeAll\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" 15 | 16 | var ( 17 | sAbi, _ = abi.JSON(strings.NewReader(ContractABI)) 18 | ) 19 | 20 | // Methods 21 | 22 | func InitializeAll(sealedEpoch idx.Epoch, totalSupply *big.Int, sfcAddr common.Address, libAddr common.Address, driverAuthAddr common.Address, driverAddr common.Address, evmWriterAddr common.Address, owner common.Address) []byte { 23 | data, _ := sAbi.Pack("initializeAll", utils.U64toBig(uint64(sealedEpoch)), totalSupply, sfcAddr, libAddr, driverAuthAddr, driverAddr, evmWriterAddr, owner) 24 | return data 25 | } 26 | -------------------------------------------------------------------------------- /opera/contracts/netinit/network_initializer.go: -------------------------------------------------------------------------------- 1 | package netinit 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/gossip/contract/netinit100" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // GetContractBin is NetworkInitializer contract genesis implementation bin code 10 | // Has to be compiled with flag bin-runtime 11 | // Built from opera-sfc be4e79d5a5a425f08efd6d65b588a72ae90f706f, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 10000 12 | func GetContractBin() []byte { 13 | return hexutil.MustDecode(netinit100.ContractBinRuntime) 14 | } 15 | 16 | // ContractAddress is the NetworkInitializer contract address 17 | var ContractAddress = common.HexToAddress("0xd1005eed00000000000000000000000000000000") 18 | -------------------------------------------------------------------------------- /opera/contracts/sfc/sfc_predeploy.go: -------------------------------------------------------------------------------- 1 | package sfc 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/gossip/contract/sfc100" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // GetContractBin is SFC contract genesis implementation bin code 10 | // Has to be compiled with flag bin-runtime 11 | // Built from opera-sfc be4e79d5a5a425f08efd6d65b588a72ae90f706f, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 200 12 | func GetContractBin() []byte { 13 | return hexutil.MustDecode(sfc100.ContractBinRuntime) 14 | } 15 | 16 | // ContractAddress is the SFC contract address 17 | var ContractAddress = common.HexToAddress("0xfc00face00000000000000000000000000000000") 18 | -------------------------------------------------------------------------------- /opera/contracts/sfclib/sfclib_predeploy.go: -------------------------------------------------------------------------------- 1 | package sfclib 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/gossip/contract/sfclib100" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // GetContractBin is SFCLib contract genesis implementation bin code 10 | // Has to be compiled with flag bin-runtime 11 | // Built from opera-sfc be4e79d5a5a425f08efd6d65b588a72ae90f706f, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 200 12 | func GetContractBin() []byte { 13 | return hexutil.MustDecode(sfclib100.ContractBinRuntime) 14 | } 15 | 16 | // ContractAddress is the SFCLib contract address 17 | var ContractAddress = common.HexToAddress("0xfc01face00000000000000000000000000000000") 18 | -------------------------------------------------------------------------------- /opera/genesis/gpos/validators.go: -------------------------------------------------------------------------------- 1 | package gpos 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/inter" 5 | "github.com/ethereum/go-ethereum/common" 6 | 7 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 8 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 9 | ) 10 | 11 | type ( 12 | // Validator is a helper structure to define genesis validators 13 | Validator struct { 14 | ID idx.ValidatorID 15 | Address common.Address 16 | PubKey validatorpk.PubKey 17 | CreationTime inter.Timestamp 18 | CreationEpoch idx.Epoch 19 | DeactivatedTime inter.Timestamp 20 | DeactivatedEpoch idx.Epoch 21 | Status uint64 22 | } 23 | 24 | Validators []Validator 25 | ) 26 | 27 | // Map converts Validators to map 28 | func (gv Validators) Map() map[idx.ValidatorID]Validator { 29 | validators := map[idx.ValidatorID]Validator{} 30 | for _, val := range gv { 31 | validators[val.ID] = val 32 | } 33 | return validators 34 | } 35 | 36 | // PubKeys returns not sorted genesis pub keys 37 | func (gv Validators) PubKeys() []validatorpk.PubKey { 38 | res := make([]validatorpk.PubKey, 0, len(gv)) 39 | for _, v := range gv { 40 | res = append(res, v.PubKey) 41 | } 42 | return res 43 | } 44 | 45 | // Addresses returns not sorted genesis addresses 46 | func (gv Validators) Addresses() []common.Address { 47 | res := make([]common.Address, 0, len(gv)) 48 | for _, v := range gv { 49 | res = append(res, v.Address) 50 | } 51 | return res 52 | } 53 | -------------------------------------------------------------------------------- /opera/genesis/types.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "strings" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/hash" 9 | 10 | "github.com/Fantom-foundation/go-opera/inter/ibr" 11 | "github.com/Fantom-foundation/go-opera/inter/ier" 12 | ) 13 | 14 | type ( 15 | Hashes map[string]hash.Hash 16 | Header struct { 17 | GenesisID hash.Hash 18 | NetworkID uint64 19 | NetworkName string 20 | } 21 | Blocks interface { 22 | ForEach(fn func(ibr.LlrIdxFullBlockRecord) bool) 23 | } 24 | Epochs interface { 25 | ForEach(fn func(ier.LlrIdxFullEpochRecord) bool) 26 | } 27 | EvmItems interface { 28 | ForEach(fn func(key, value []byte) bool) 29 | } 30 | Genesis struct { 31 | Header 32 | 33 | Blocks Blocks 34 | Epochs Epochs 35 | RawEvmItems EvmItems 36 | } 37 | ) 38 | 39 | func (hh Hashes) Includes(hh2 Hashes) bool { 40 | for n, h := range hh { 41 | if hh2[n] != h { 42 | return false 43 | } 44 | } 45 | return true 46 | } 47 | 48 | func (hh Hashes) Equal(hh2 Hashes) bool { 49 | return hh.Includes(hh2) && hh2.Includes(hh) 50 | } 51 | 52 | func (hh Hashes) String() string { 53 | bb := make([]string, 0, len(hh)) 54 | for n, h := range hh { 55 | bb = append(bb, fmt.Sprintf("%s: %s", n, h.String())) 56 | } 57 | sort.Strings(bb) 58 | return "{" + strings.Join(bb, ", ") + "}" 59 | } 60 | 61 | func (h Header) Equal(h2 Header) bool { 62 | return h == h2 63 | } 64 | 65 | func (h Header) String() string { 66 | return fmt.Sprintf("{%d, net:%s, id:%s}", h.NetworkID, h.NetworkName, h.GenesisID.String()) 67 | } 68 | -------------------------------------------------------------------------------- /opera/genesisstore/filelog/filelog.go: -------------------------------------------------------------------------------- 1 | package filelog 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | 10 | "github.com/Fantom-foundation/go-opera/utils" 11 | ) 12 | 13 | type Filelog struct { 14 | io.Reader 15 | name string 16 | size uint64 17 | period time.Duration 18 | consumed uint64 19 | prevLog time.Time 20 | start time.Time 21 | } 22 | 23 | func (f *Filelog) Read(p []byte) (n int, err error) { 24 | n, err = f.Reader.Read(p) 25 | f.consumed += uint64(n) 26 | if f.prevLog.IsZero() { 27 | log.Info(fmt.Sprintf("- Reading %s", f.name)) 28 | f.prevLog = time.Now() 29 | f.start = time.Now() 30 | } else if f.consumed > 0 && f.consumed < f.size && time.Since(f.prevLog) >= f.period { 31 | elapsed := time.Since(f.start) 32 | eta := float64(f.size-f.consumed) / float64(f.consumed) * float64(elapsed) 33 | progress := float64(f.consumed) / float64(f.size) 34 | eta *= 1.0 + (1.0-progress)/2.0 // show slightly higher ETA as performance degrades over larger volumes of data 35 | progressStr := fmt.Sprintf("%.2f%%", 100*progress) 36 | log.Info(fmt.Sprintf("- Reading %s", f.name), "progress", progressStr, "elapsed", utils.PrettyDuration(elapsed), "eta", utils.PrettyDuration(eta)) 37 | f.prevLog = time.Now() 38 | } 39 | return 40 | } 41 | 42 | func Wrap(r io.Reader, name string, size uint64, period time.Duration) *Filelog { 43 | return &Filelog{ 44 | Reader: r, 45 | name: name, 46 | size: size, 47 | period: period, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /opera/genesisstore/fileshash/reader_map.go: -------------------------------------------------------------------------------- 1 | package fileshash 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | ) 8 | 9 | type Map struct { 10 | backend func(string) (io.Reader, error) 11 | } 12 | 13 | func Wrap(backend func(string) (io.Reader, error), maxMemoryUsage uint64, roots map[string]hash.Hash) func(string) (io.Reader, error) { 14 | return func(name string) (io.Reader, error) { 15 | root, ok := roots[name] 16 | if !ok { 17 | return nil, ErrRootNotFound 18 | } 19 | f, err := backend(name) 20 | if err != nil { 21 | return nil, err 22 | } 23 | return WrapReader(f, maxMemoryUsage, root), nil 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /opera/genesisstore/readersmap/reader_map.go: -------------------------------------------------------------------------------- 1 | package readersmap 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | ) 7 | 8 | type Map map[string]io.Reader 9 | 10 | type Unit struct { 11 | Name string 12 | io.Reader 13 | } 14 | 15 | var ( 16 | ErrNotFound = errors.New("not found") 17 | ErrDupFile = errors.New("unit name is duplicated") 18 | ) 19 | 20 | func Wrap(rr []Unit) (Map, error) { 21 | units := make(Map) 22 | for _, r := range rr { 23 | if units[r.Name] != nil { 24 | return nil, ErrDupFile 25 | } 26 | units[r.Name] = r.Reader 27 | } 28 | return units, nil 29 | } 30 | 31 | func (mm Map) Open(name string) (io.Reader, error) { 32 | f := mm[name] 33 | if f == nil { 34 | return nil, ErrNotFound 35 | } 36 | return f, nil 37 | } 38 | -------------------------------------------------------------------------------- /opera/genesisstore/store.go: -------------------------------------------------------------------------------- 1 | package genesisstore 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Fantom-foundation/go-opera/logger" 7 | "github.com/Fantom-foundation/go-opera/opera/genesis" 8 | ) 9 | 10 | func BlocksSection(i int) string { 11 | return getSectionName("brs", i) 12 | } 13 | 14 | func EpochsSection(i int) string { 15 | return getSectionName("ers", i) 16 | } 17 | 18 | func EvmSection(i int) string { 19 | return getSectionName("evm", i) 20 | } 21 | 22 | type FilesMap func(string) (io.Reader, error) 23 | 24 | // Store is a node persistent storage working over a physical zip archive. 25 | type Store struct { 26 | fMap FilesMap 27 | head genesis.Header 28 | close func() error 29 | 30 | logger.Instance 31 | } 32 | 33 | // NewStore creates store over key-value db. 34 | func NewStore(fMap FilesMap, head genesis.Header, close func() error) *Store { 35 | return &Store{ 36 | fMap: fMap, 37 | head: head, 38 | close: close, 39 | Instance: logger.New("genesis-store"), 40 | } 41 | } 42 | 43 | // Close leaves underlying database. 44 | func (s *Store) Close() error { 45 | s.fMap = nil 46 | return s.close() 47 | } 48 | -------------------------------------------------------------------------------- /opera/marshal.go: -------------------------------------------------------------------------------- 1 | package opera 2 | 3 | import "encoding/json" 4 | 5 | func UpdateRules(src Rules, diff []byte) (res Rules, err error) { 6 | changed := src.Copy() 7 | err = json.Unmarshal(diff, &changed) 8 | if err != nil { 9 | return src, err 10 | } 11 | // protect readonly fields 12 | res = changed 13 | res.NetworkID = src.NetworkID 14 | res.Name = src.Name 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=Fantom-foundation_go-opera 2 | sonar.projectName=go-opera 3 | 4 | sonar.sources=. 5 | sonar.exclusions=**/*_test.go,**/vendor/** 6 | 7 | sonar.tests=. 8 | sonar.test.inclusions=**/*_test.go 9 | sonar.test.exclusions=**/vendor/** 10 | -------------------------------------------------------------------------------- /system/etc/default/x1: -------------------------------------------------------------------------------- 1 | # Add additional options to the command line 2 | X1_OPTS="" 3 | -------------------------------------------------------------------------------- /system/etc/x1/config.toml: -------------------------------------------------------------------------------- 1 | [Node] 2 | Testnet = true 3 | 4 | [Node.P2P] 5 | ListenAddr = ":5050" 6 | 7 | # Share you node info with the xenblocks.io explorer 8 | # (optional) comment out to disable 9 | [XenBlocks] 10 | Endpoint = "ws://xenblocks.io:6668" 11 | -------------------------------------------------------------------------------- /system/lib/systemd/system/x1.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=X1 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | User=x1 7 | Group=x1 8 | Type=simple 9 | WorkingDirectory=~ 10 | LimitNOFILE=infinity 11 | EnvironmentFile=-/etc/default/x1 12 | KillMode=process 13 | KillSignal=SIGINT 14 | TimeoutStopSec=600 15 | Restart=on-failure 16 | RestartSec=10s 17 | SyslogIdentifier=x1 18 | ExecStart=/usr/local/bin/x1 --config /etc/x1/config.toml $X1_OPTS 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /system/usr/share/x1/configs/testnet/api-node.toml: -------------------------------------------------------------------------------- 1 | [Node] 2 | Testnet = true 3 | 4 | # RPC server 5 | HTTPHost = "0.0.0.0" 6 | HTTPVirtualHosts = ["*"] 7 | HTTPCors = ["*"] 8 | HTTPPort = 8545 9 | HTTPModules = [ 10 | "eth", 11 | "ftm", 12 | "dag", 13 | "abft", 14 | "web3", 15 | "net", 16 | "txpool" 17 | ] 18 | 19 | # Websocket server 20 | WSHost = "0.0.0.0" 21 | WSPort = 8546 22 | WSOrigins = ["*"] 23 | WSModules = [ 24 | "eth", 25 | "ftm", 26 | "dag", 27 | "abft", 28 | "web3", 29 | "net", 30 | "txpool", 31 | ] 32 | 33 | [Node.P2P] 34 | ListenAddr = ":5050" 35 | 36 | # Share you node info with the xenblocks.io explorer 37 | # (optional) comment out to disable 38 | [XenBlocks] 39 | Endpoint = "ws://xenblocks.io:6668" 40 | -------------------------------------------------------------------------------- /system/usr/share/x1/configs/testnet/archive-node.toml: -------------------------------------------------------------------------------- 1 | [Node] 2 | Testnet = true 3 | 4 | [Node.P2P] 5 | ListenAddr = ":5050" 6 | 7 | [OperaStore.EVM.Cache] 8 | TrieDirtyDisabled = true 9 | GreedyGC = false 10 | 11 | # Share you node info with the xenblocks.io explorer 12 | # (optional) comment out to disable 13 | [XenBlocks] 14 | Endpoint = "ws://xenblocks.io:6668" 15 | -------------------------------------------------------------------------------- /system/usr/share/x1/configs/testnet/full-node.toml: -------------------------------------------------------------------------------- 1 | [Node] 2 | Testnet = true 3 | 4 | [Node.P2P] 5 | ListenAddr = ":5050" 6 | 7 | # Share you node info with the xenblocks.io explorer 8 | # (optional) comment out to disable 9 | [XenBlocks] 10 | Endpoint = "ws://xenblocks.io:6668" 11 | -------------------------------------------------------------------------------- /system/usr/share/x1/configs/testnet/validator-node.toml: -------------------------------------------------------------------------------- 1 | [Node] 2 | Testnet = true 3 | 4 | [Node.P2P] 5 | ListenAddr = ":5050" 6 | 7 | [Emitter.Validator] 8 | 9 | # update with your validator ID 10 | ID = 0 11 | 12 | # update with your validator public key 13 | PubKeyString = "" 14 | 15 | # update with your validator password file path 16 | PasswordFilePath = "" 17 | 18 | # Share you node info with the xenblocks.io explorer 19 | # (optional) comment out to disable 20 | [XenBlocks] 21 | Endpoint = "ws://xenblocks.io:6668" 22 | -------------------------------------------------------------------------------- /system/x1-post-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # create an empty config if it doesn't exist 5 | if [ -d /etc ]; then 6 | if [ ! -d /etc/x1 ]; then 7 | mkdir -p /etc/x1 2>/dev/null || true 8 | fi 9 | 10 | if [ ! -f /etc/x1/config.toml ]; then 11 | cp system/etc/x1/config.toml /etc/x1/config.toml 12 | fi 13 | 14 | if [ -d /etc/default ] && [ ! -f /etc/default/x1 ]; then 15 | cp system/etc/default/x1 /etc/default/x1 16 | fi 17 | fi 18 | 19 | if [ -d /run/systemd/system ] && [ -d /etc/systemd/system/ ]; then 20 | systemctl daemon-reload 21 | systemctl try-restart x1 22 | fi 23 | -------------------------------------------------------------------------------- /system/x1-pre-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | PKG="x1" 5 | 6 | if [ -x "$(command -v useradd)" ]; then 7 | if ! getent passwd $PKG >/dev/null ; then 8 | useradd --system --user-group --create-home --home-dir /var/lib/$PKG $PKG 9 | echo "Created system user $PKG" 10 | fi 11 | fi 12 | -------------------------------------------------------------------------------- /topicsdb/key_test.go: -------------------------------------------------------------------------------- 1 | package topicsdb 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestPosToBytes(t *testing.T) { 10 | require := require.New(t) 11 | 12 | for i := 0xff / 0x0f; i >= 0; i-- { 13 | expect := uint8(0x0f * i) 14 | bb := posToBytes(expect) 15 | got := bytesToPos(bb) 16 | 17 | require.Equal(expect, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /topicsdb/record.go: -------------------------------------------------------------------------------- 1 | package topicsdb 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/core/types" 7 | ) 8 | 9 | type ( 10 | logrec struct { 11 | ID ID 12 | topicsCount uint8 13 | result *types.Log 14 | err error 15 | 16 | matched int 17 | } 18 | ) 19 | 20 | func newLogrec(rec ID, topicCount uint8) *logrec { 21 | return &logrec{ 22 | ID: rec, 23 | topicsCount: topicCount, 24 | } 25 | } 26 | 27 | // fetch record's data. 28 | func (rec *logrec) fetch( 29 | logrecTable kvdb.Reader, 30 | ) { 31 | r := &types.Log{ 32 | BlockNumber: rec.ID.BlockNumber(), 33 | TxHash: rec.ID.TxHash(), 34 | Index: rec.ID.Index(), 35 | Topics: make([]common.Hash, rec.topicsCount), 36 | } 37 | 38 | var ( 39 | buf []byte 40 | offset int 41 | ) 42 | buf, rec.err = logrecTable.Get(rec.ID.Bytes()) 43 | if rec.err != nil { 44 | return 45 | } 46 | 47 | // topics 48 | for i := 0; i < len(r.Topics); i++ { 49 | r.Topics[i] = common.BytesToHash(buf[offset : offset+common.HashLength]) 50 | offset += common.HashLength 51 | } 52 | 53 | // fields 54 | r.BlockHash = common.BytesToHash(buf[offset : offset+common.HashLength]) 55 | offset += common.HashLength 56 | r.Address = common.BytesToAddress(buf[offset : offset+common.AddressLength]) 57 | offset += common.AddressLength 58 | r.Data = buf[offset:] 59 | 60 | rec.result = r 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /topicsdb/search_test.go: -------------------------------------------------------------------------------- 1 | package topicsdb 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/core/types" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func BenchmarkSearch(b *testing.B) { 14 | topics, recs, topics4rec := genTestData(1000) 15 | 16 | index := newTestIndex() 17 | 18 | for _, rec := range recs { 19 | err := index.Push(rec) 20 | require.NoError(b, err) 21 | } 22 | 23 | var query [][][]common.Hash 24 | for i := 0; i < len(topics); i++ { 25 | from, to := topics4rec(i) 26 | tt := topics[from : to-1] 27 | 28 | qq := make([][]common.Hash, len(tt)) 29 | for pos, t := range tt { 30 | qq[pos] = []common.Hash{t} 31 | } 32 | 33 | query = append(query, qq) 34 | } 35 | 36 | pooled := withThreadPool{index} 37 | 38 | for dsc, method := range map[string]func(context.Context, idx.Block, idx.Block, [][]common.Hash) ([]*types.Log, error){ 39 | "index": index.FindInBlocks, 40 | "pooled": pooled.FindInBlocks, 41 | } { 42 | b.Run(dsc, func(b *testing.B) { 43 | b.ResetTimer() 44 | 45 | for i := 0; i < b.N; i++ { 46 | qq := query[i%len(query)] 47 | _, err := method(nil, 0, 0xffffffff, qq) 48 | require.NoError(b, err) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tracing/tx-tracing.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/opentracing/opentracing-go" 8 | ) 9 | 10 | var ( 11 | enabled bool 12 | txSpans = make(map[common.Hash]opentracing.Span) 13 | txSpansMu sync.RWMutex 14 | 15 | noopSpan = opentracing.NoopTracer{}.StartSpan("") 16 | ) 17 | 18 | func SetEnabled(val bool) { 19 | enabled = val 20 | } 21 | 22 | func Enabled() bool { 23 | return enabled 24 | } 25 | 26 | func StartTx(tx common.Hash, operation string) { 27 | if !enabled { 28 | return 29 | } 30 | 31 | txSpansMu.Lock() 32 | defer txSpansMu.Unlock() 33 | 34 | if _, ok := txSpans[tx]; ok { 35 | return 36 | } 37 | 38 | span := opentracing.StartSpan("lifecycle") 39 | span.SetTag("txhash", tx.String()) 40 | span.SetTag("enter", operation) 41 | txSpans[tx] = span 42 | } 43 | 44 | func FinishTx(tx common.Hash, operation string) { 45 | if !enabled { 46 | return 47 | } 48 | 49 | txSpansMu.Lock() 50 | defer txSpansMu.Unlock() 51 | 52 | span, ok := txSpans[tx] 53 | if !ok { 54 | return 55 | } 56 | 57 | span.SetTag("exit", operation) 58 | span.Finish() 59 | delete(txSpans, tx) 60 | } 61 | 62 | func CheckTx(tx common.Hash, operation string) opentracing.Span { 63 | if !enabled { 64 | return noopSpan 65 | } 66 | 67 | txSpansMu.RLock() 68 | defer txSpansMu.RUnlock() 69 | 70 | span, ok := txSpans[tx] 71 | 72 | if !ok { 73 | return noopSpan 74 | } 75 | 76 | return opentracing.GlobalTracer().StartSpan( 77 | operation, 78 | opentracing.ChildOf(span.Context()), 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /utils/adapters/ethdb2kvdb/adapter.go: -------------------------------------------------------------------------------- 1 | package ethdb2kvdb 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/ethereum/go-ethereum/ethdb" 6 | ) 7 | 8 | type Adapter struct { 9 | ethdb.KeyValueStore 10 | } 11 | 12 | var _ kvdb.Store = (*Adapter)(nil) 13 | 14 | func Wrap(v ethdb.KeyValueStore) *Adapter { 15 | return &Adapter{v} 16 | } 17 | 18 | func (db *Adapter) Drop() { 19 | panic("called Drop on ethdb") 20 | } 21 | 22 | // batch is a write-only memory batch that commits changes to its host 23 | // database when Write is called. A batch cannot be used concurrently. 24 | type batch struct { 25 | ethdb.Batch 26 | } 27 | 28 | // Replay replays the batch contents. 29 | func (b *batch) Replay(w kvdb.Writer) error { 30 | return b.Batch.Replay(w) 31 | } 32 | 33 | // NewBatch creates a write-only key-value store that buffers changes to its host 34 | // database until a final write is called. 35 | func (db *Adapter) NewBatch() kvdb.Batch { 36 | return &batch{db.KeyValueStore.NewBatch()} 37 | } 38 | 39 | func (db *Adapter) GetSnapshot() (kvdb.Snapshot, error) { 40 | panic("called GetSnapshot on ethdb") 41 | return nil, nil 42 | } 43 | 44 | // NewIterator creates a binary-alphabetical iterator over a subset 45 | // of database content with a particular key prefix, starting at a particular 46 | // initial key (or after, if it does not exist). 47 | func (db *Adapter) NewIterator(prefix []byte, start []byte) kvdb.Iterator { 48 | return db.KeyValueStore.NewIterator(prefix, start) 49 | } 50 | -------------------------------------------------------------------------------- /utils/adapters/kvdb2ethdb/adapter.go: -------------------------------------------------------------------------------- 1 | package kvdb2ethdb 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/ethereum/go-ethereum/ethdb" 6 | ) 7 | 8 | type Adapter struct { 9 | kvdb.Store 10 | } 11 | 12 | var _ ethdb.KeyValueStore = (*Adapter)(nil) 13 | 14 | func Wrap(v kvdb.Store) *Adapter { 15 | return &Adapter{v} 16 | } 17 | 18 | // batch is a write-only memory batch that commits changes to its host 19 | // database when Write is called. A batch cannot be used concurrently. 20 | type batch struct { 21 | kvdb.Batch 22 | } 23 | 24 | // Replay replays the batch contents. 25 | func (b *batch) Replay(w ethdb.KeyValueWriter) error { 26 | return b.Batch.Replay(w) 27 | } 28 | 29 | // NewBatch creates a write-only key-value store that buffers changes to its host 30 | // database until a final write is called. 31 | func (db *Adapter) NewBatch() ethdb.Batch { 32 | return &batch{db.Store.NewBatch()} 33 | } 34 | 35 | // NewIterator creates a binary-alphabetical iterator over a subset 36 | // of database content with a particular key prefix, starting at a particular 37 | // initial key (or after, if it does not exist). 38 | func (db *Adapter) NewIterator(prefix []byte, start []byte) ethdb.Iterator { 39 | return db.Store.NewIterator(prefix, start) 40 | } 41 | -------------------------------------------------------------------------------- /utils/adapters/snap2kvdb/adapter.go: -------------------------------------------------------------------------------- 1 | package snap2kvdb 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/Fantom-foundation/lachesis-base/kvdb/devnulldb" 6 | "github.com/ethereum/go-ethereum/log" 7 | ) 8 | 9 | type Adapter struct { 10 | kvdb.Snapshot 11 | } 12 | 13 | var _ kvdb.Store = (*Adapter)(nil) 14 | 15 | func Wrap(v kvdb.Snapshot) *Adapter { 16 | return &Adapter{v} 17 | } 18 | 19 | func (db *Adapter) Put(key []byte, value []byte) error { 20 | log.Warn("called Put on snapshot") 21 | return nil 22 | } 23 | 24 | func (db *Adapter) Delete(key []byte) error { 25 | log.Warn("called Delete on snapshot") 26 | return nil 27 | } 28 | 29 | func (db *Adapter) GetSnapshot() (kvdb.Snapshot, error) { 30 | return db.Snapshot, nil 31 | } 32 | 33 | func (db *Adapter) NewBatch() kvdb.Batch { 34 | log.Warn("called NewBatch on snapshot") 35 | return devnulldb.New().NewBatch() 36 | } 37 | 38 | func (db *Adapter) Compact(start []byte, limit []byte) error { 39 | return nil 40 | } 41 | 42 | func (db *Adapter) Close() error { 43 | return nil 44 | } 45 | 46 | func (db *Adapter) Drop() {} 47 | 48 | func (db *Adapter) Stat(property string) (string, error) { 49 | return "", nil 50 | } 51 | -------------------------------------------------------------------------------- /utils/adapters/vecmt2dagidx/vecmt2lachesis.go: -------------------------------------------------------------------------------- 1 | package vecmt2dagidx 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/abft" 5 | "github.com/Fantom-foundation/lachesis-base/abft/dagidx" 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | "github.com/Fantom-foundation/lachesis-base/vecfc" 9 | 10 | "github.com/Fantom-foundation/go-opera/vecmt" 11 | ) 12 | 13 | type Adapter struct { 14 | *vecmt.Index 15 | } 16 | 17 | var _ abft.DagIndex = (*Adapter)(nil) 18 | 19 | type AdapterSeq struct { 20 | *vecmt.HighestBefore 21 | } 22 | 23 | type BranchSeq struct { 24 | vecfc.BranchSeq 25 | } 26 | 27 | // Seq is a maximum observed e.Seq in the branch 28 | func (b *BranchSeq) Seq() idx.Event { 29 | return b.BranchSeq.Seq 30 | } 31 | 32 | // MinSeq is a minimum observed e.Seq in the branch 33 | func (b *BranchSeq) MinSeq() idx.Event { 34 | return b.BranchSeq.MinSeq 35 | } 36 | 37 | // Size of the vector clock 38 | func (b AdapterSeq) Size() int { 39 | return b.VSeq.Size() 40 | } 41 | 42 | // Get i's position in the byte-encoded vector clock 43 | func (b AdapterSeq) Get(i idx.Validator) dagidx.Seq { 44 | seq := b.HighestBefore.VSeq.Get(i) 45 | return &BranchSeq{seq} 46 | } 47 | 48 | func (v *Adapter) GetMergedHighestBefore(id hash.Event) dagidx.HighestBeforeSeq { 49 | return AdapterSeq{v.Index.GetMergedHighestBefore(id)} 50 | } 51 | 52 | func Wrap(v *vecmt.Index) *Adapter { 53 | return &Adapter{v} 54 | } 55 | -------------------------------------------------------------------------------- /utils/bitmap/bitset.go: -------------------------------------------------------------------------------- 1 | package bitmap 2 | 3 | import "github.com/ethereum/go-ethereum/log" 4 | 5 | type Set []byte 6 | 7 | func New(max int) Set { 8 | l := max / 8 9 | if max%8 != 0 { 10 | l++ 11 | } 12 | return make(Set, l) 13 | } 14 | 15 | func (s Set) Put(i int) { 16 | yi := i / 8 17 | bi := i % 8 18 | 19 | if yi >= len(s) { 20 | log.Debug("Index out of range", "index", i, "max", len(s)*8) 21 | return 22 | } 23 | 24 | s[yi] |= 1 << bi 25 | } 26 | 27 | func (s Set) Del(i int) { 28 | yi := i / 8 29 | bi := i % 8 30 | 31 | if yi >= len(s) { 32 | log.Debug("Index out of range", "index", i, "max", len(s)*8) 33 | return 34 | } 35 | 36 | s[yi] &= ^(1 << bi) 37 | } 38 | 39 | func (s Set) Has(i int) bool { 40 | yi := i / 8 41 | bi := i % 8 42 | 43 | if yi >= len(s) { 44 | log.Debug("Index out of range", "index", i, "max", len(s)*8) 45 | return false 46 | } 47 | 48 | return (s[yi] & (1 << bi)) != 0 49 | } 50 | -------------------------------------------------------------------------------- /utils/concurrent/ValidatorBlocksSet.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | ) 8 | 9 | type ValidatorBlocksSet struct { 10 | sync.RWMutex 11 | Val map[idx.ValidatorID]idx.Block 12 | } 13 | 14 | func WrapValidatorBlocksSet(v map[idx.ValidatorID]idx.Block) *ValidatorBlocksSet { 15 | return &ValidatorBlocksSet{ 16 | RWMutex: sync.RWMutex{}, 17 | Val: v, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /utils/concurrent/ValidatorEpochsSet.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | ) 8 | 9 | type ValidatorEpochsSet struct { 10 | sync.RWMutex 11 | Val map[idx.ValidatorID]idx.Epoch 12 | } 13 | 14 | func WrapValidatorEpochsSet(v map[idx.ValidatorID]idx.Epoch) *ValidatorEpochsSet { 15 | return &ValidatorEpochsSet{ 16 | RWMutex: sync.RWMutex{}, 17 | Val: v, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /utils/concurrent/ValidatorEventsSet.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | ) 9 | 10 | type ValidatorEventsSet struct { 11 | sync.RWMutex 12 | Val map[idx.ValidatorID]hash.Event 13 | } 14 | 15 | func WrapValidatorEventsSet(v map[idx.ValidatorID]hash.Event) *ValidatorEventsSet { 16 | return &ValidatorEventsSet{ 17 | RWMutex: sync.RWMutex{}, 18 | Val: v, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /utils/concurrent/events.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | ) 8 | 9 | type EventsSet struct { 10 | sync.RWMutex 11 | Val hash.EventsSet 12 | } 13 | 14 | func WrapEventsSet(v hash.EventsSet) *EventsSet { 15 | return &EventsSet{ 16 | RWMutex: sync.RWMutex{}, 17 | Val: v, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /utils/dbutil/asyncflushproducer/producer.go: -------------------------------------------------------------------------------- 1 | package asyncflushproducer 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/kvdb" 8 | "github.com/ethereum/go-ethereum/metrics" 9 | ) 10 | 11 | type Producer struct { 12 | kvdb.FullDBProducer 13 | mu sync.Mutex 14 | dbs map[string]*store 15 | stats metrics.Meter 16 | 17 | threshold uint64 18 | } 19 | 20 | func Wrap(backend kvdb.FullDBProducer, threshold uint64) *Producer { 21 | return &Producer{ 22 | stats: metrics.NewMeterForced(), 23 | FullDBProducer: backend, 24 | dbs: make(map[string]*store), 25 | threshold: threshold, 26 | } 27 | } 28 | 29 | func (f *Producer) OpenDB(name string) (kvdb.Store, error) { 30 | f.mu.Lock() 31 | defer f.mu.Unlock() 32 | // open existing DB 33 | openedDB := f.dbs[name] 34 | if openedDB != nil { 35 | return openedDB, nil 36 | } 37 | // create new DB 38 | db, err := f.FullDBProducer.OpenDB(name) 39 | if err != nil { 40 | return nil, err 41 | } 42 | if f.dbs[name] != nil { 43 | return nil, errors.New("already opened") 44 | } 45 | wrapped := &store{ 46 | Store: db, 47 | CloseFn: func() error { 48 | f.mu.Lock() 49 | delete(f.dbs, name) 50 | f.mu.Unlock() 51 | return db.Close() 52 | }, 53 | } 54 | f.dbs[name] = wrapped 55 | return wrapped, nil 56 | } 57 | 58 | func (f *Producer) Flush(id []byte) error { 59 | f.stats.Mark(int64(f.FullDBProducer.NotFlushedSizeEst())) 60 | 61 | err := f.FullDBProducer.Flush(id) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | // trigger flushing data to disk if throughput is below a threshold 67 | if uint64(f.stats.Rate1()) <= f.threshold { 68 | go func() { 69 | f.mu.Lock() 70 | defer f.mu.Unlock() 71 | for _, db := range f.dbs { 72 | _, _ = db.Stat("async_flush") 73 | } 74 | }() 75 | } 76 | 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /utils/dbutil/asyncflushproducer/store.go: -------------------------------------------------------------------------------- 1 | package asyncflushproducer 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | ) 6 | 7 | type store struct { 8 | kvdb.Store 9 | CloseFn func() error 10 | } 11 | 12 | func (s *store) Close() error { 13 | return s.CloseFn() 14 | } 15 | -------------------------------------------------------------------------------- /utils/dbutil/compactdb/log.go: -------------------------------------------------------------------------------- 1 | package compactdb 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | 8 | "github.com/Fantom-foundation/lachesis-base/kvdb" 9 | "github.com/ethereum/go-ethereum/log" 10 | "github.com/status-im/keycard-go/hexutils" 11 | ) 12 | 13 | type loggedCompacter struct { 14 | kvdb.Store 15 | name string 16 | 17 | currentOp atomic.Value 18 | 19 | wg sync.WaitGroup 20 | quit chan struct{} 21 | } 22 | 23 | type pair struct { 24 | p, l []byte 25 | } 26 | 27 | func (s *loggedCompacter) Compact(prev []byte, limit []byte) error { 28 | s.currentOp.Store(pair{prev, limit}) 29 | if err := s.Store.Compact(prev, limit); err != nil { 30 | log.Error("Compaction error", "name", s.name, "err", err) 31 | return err 32 | } 33 | return nil 34 | } 35 | 36 | func (s *loggedCompacter) StartLogging() { 37 | s.wg.Add(1) 38 | go func() { 39 | defer s.wg.Done() 40 | ticker := time.NewTicker(time.Minute) 41 | for { 42 | select { 43 | case <-ticker.C: 44 | opI := s.currentOp.Load() 45 | if opI != nil { 46 | op := opI.(pair) 47 | // trim keys for nicer human-readable logging 48 | op.p, op.l = trimAfterDiff(op.p, op.l, 2) 49 | untilStr := hexutils.BytesToHex(op.l) 50 | if len(op.l) == 0 { 51 | untilStr = "end" 52 | } 53 | log.Info("Compacting DB", "name", s.name, "until", untilStr) 54 | } 55 | case <-s.quit: 56 | return 57 | } 58 | } 59 | }() 60 | } 61 | 62 | func (s *loggedCompacter) StopLogging() { 63 | close(s.quit) 64 | s.wg.Wait() 65 | } 66 | -------------------------------------------------------------------------------- /utils/dbutil/itergc/iterator_gc.go: -------------------------------------------------------------------------------- 1 | package itergc 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/kvdb" 7 | ) 8 | 9 | type Snapshot struct { 10 | kvdb.Snapshot 11 | nextID uint64 12 | iters map[uint64]kvdb.Iterator 13 | mu sync.Locker 14 | } 15 | 16 | type Iterator struct { 17 | kvdb.Iterator 18 | mu sync.Locker 19 | id uint64 20 | iters map[uint64]kvdb.Iterator 21 | } 22 | 23 | // Wrap snapshot to automatically close all pending iterators upon snapshot release 24 | func Wrap(snapshot kvdb.Snapshot, mu sync.Locker) *Snapshot { 25 | return &Snapshot{ 26 | Snapshot: snapshot, 27 | iters: make(map[uint64]kvdb.Iterator), 28 | mu: mu, 29 | } 30 | } 31 | 32 | func (s *Snapshot) NewIterator(prefix []byte, start []byte) kvdb.Iterator { 33 | s.mu.Lock() 34 | defer s.mu.Unlock() 35 | it := s.Snapshot.NewIterator(prefix, start) 36 | id := s.nextID 37 | s.iters[id] = it 38 | s.nextID++ 39 | 40 | return &Iterator{ 41 | Iterator: it, 42 | mu: s.mu, 43 | id: id, 44 | iters: s.iters, 45 | } 46 | } 47 | 48 | func (s *Iterator) Release() { 49 | s.mu.Lock() 50 | defer s.mu.Unlock() 51 | s.Iterator.Release() 52 | delete(s.iters, s.id) 53 | } 54 | 55 | func (s *Snapshot) Release() { 56 | s.mu.Lock() 57 | defer s.mu.Unlock() 58 | // release all pending iterators 59 | for _, it := range s.iters { 60 | it.Release() 61 | } 62 | s.iters = nil 63 | s.Snapshot.Release() 64 | } 65 | -------------------------------------------------------------------------------- /utils/dbutil/threads/counted.go: -------------------------------------------------------------------------------- 1 | package threads 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | 6 | "github.com/Fantom-foundation/go-opera/logger" 7 | ) 8 | 9 | type ( 10 | countedDbProducer struct { 11 | kvdb.DBProducer 12 | } 13 | 14 | countedFullDbProducer struct { 15 | kvdb.FullDBProducer 16 | } 17 | 18 | countedStore struct { 19 | kvdb.Store 20 | } 21 | 22 | countedIterator struct { 23 | kvdb.Iterator 24 | release func(count int) 25 | } 26 | ) 27 | 28 | func CountedDBProducer(dbs kvdb.DBProducer) kvdb.DBProducer { 29 | return &countedDbProducer{dbs} 30 | } 31 | 32 | func CountedFullDBProducer(dbs kvdb.FullDBProducer) kvdb.FullDBProducer { 33 | return &countedFullDbProducer{dbs} 34 | } 35 | 36 | func (p *countedDbProducer) OpenDB(name string) (kvdb.Store, error) { 37 | s, err := p.DBProducer.OpenDB(name) 38 | return &countedStore{s}, err 39 | } 40 | 41 | func (p *countedFullDbProducer) OpenDB(name string) (kvdb.Store, error) { 42 | s, err := p.FullDBProducer.OpenDB(name) 43 | return &countedStore{s}, err 44 | } 45 | 46 | var notifier = logger.New("threads-pool") 47 | 48 | func (s *countedStore) NewIterator(prefix []byte, start []byte) kvdb.Iterator { 49 | got, release := GlobalPool.Lock(1) 50 | if got < 1 { 51 | notifier.Log.Warn("Too many DB iterators") 52 | } 53 | 54 | return &countedIterator{ 55 | Iterator: s.Store.NewIterator(prefix, start), 56 | release: release, 57 | } 58 | } 59 | 60 | func (it *countedIterator) Release() { 61 | it.Iterator.Release() 62 | it.release(1) 63 | } 64 | -------------------------------------------------------------------------------- /utils/dbutil/threads/pool.go: -------------------------------------------------------------------------------- 1 | package threads 2 | 3 | import ( 4 | "runtime/debug" 5 | "sync" 6 | ) 7 | 8 | const GoroutinesPerThread = 0.8 9 | 10 | // threadPool counts threads in use 11 | type ThreadPool struct { 12 | mu sync.Mutex 13 | cap int 14 | left int 15 | } 16 | 17 | var GlobalPool ThreadPool 18 | 19 | // init ThreadPool only on demand to give time to other packages 20 | // call debug.SetMaxThreads() if they need 21 | func (p *ThreadPool) init() { 22 | if p.cap == 0 { 23 | p.cap = int(getMaxThreads() * GoroutinesPerThread) 24 | p.left = p.cap 25 | } 26 | } 27 | 28 | // Capacity of pool 29 | func (p *ThreadPool) Cap() int { 30 | if p.cap == 0 { 31 | p.mu.Lock() 32 | defer p.mu.Unlock() 33 | p.init() 34 | } 35 | return p.cap 36 | } 37 | 38 | func (p *ThreadPool) Lock(want int) (got int, release func(count int)) { 39 | p.mu.Lock() 40 | defer p.mu.Unlock() 41 | 42 | p.init() 43 | 44 | if want < 1 { 45 | want = 0 46 | } 47 | 48 | got = min(p.left, want) 49 | p.left -= got 50 | 51 | release = func(count int) { 52 | p.mu.Lock() 53 | defer p.mu.Unlock() 54 | 55 | if 0 > count || count > got { 56 | count = got 57 | } 58 | 59 | got -= count 60 | p.left += count 61 | } 62 | 63 | return 64 | } 65 | 66 | func getMaxThreads() float64 { 67 | was := debug.SetMaxThreads(10000) 68 | debug.SetMaxThreads(was) 69 | return float64(was) 70 | } 71 | 72 | func min(a, b int) int { 73 | if a < b { 74 | return a 75 | } 76 | return b 77 | } 78 | -------------------------------------------------------------------------------- /utils/dbutil/threads/pool_test.go: -------------------------------------------------------------------------------- 1 | package threads 2 | 3 | import ( 4 | "os" 5 | "runtime/debug" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | debug.SetMaxThreads(10) 13 | 14 | os.Exit(m.Run()) 15 | } 16 | 17 | func TestThreadPool(t *testing.T) { 18 | 19 | for name, pool := range map[string]ThreadPool{ 20 | "global": GlobalPool, 21 | "local": ThreadPool{}, 22 | } { 23 | t.Run(name, func(t *testing.T) { 24 | require := require.New(t) 25 | 26 | require.Equal(8, pool.Cap()) 27 | 28 | got, release := pool.Lock(0) 29 | require.Equal(0, got) 30 | release(1) 31 | 32 | gotA, releaseA := pool.Lock(10) 33 | require.Equal(8, gotA) 34 | releaseA(1) 35 | 36 | gotB, releaseB := pool.Lock(10) 37 | require.Equal(1, gotB) 38 | releaseB(gotB) 39 | 40 | releaseA(gotA) 41 | gotB, releaseB = pool.Lock(10) 42 | require.Equal(8, gotB) 43 | 44 | // don't releaseB(gotB) to check pools isolation 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /utils/devnullfile/devnull.go: -------------------------------------------------------------------------------- 1 | package devnullfile 2 | 3 | type DevNull struct{} 4 | 5 | func (d DevNull) Read(pp []byte) (n int, err error) { 6 | for i := range pp { 7 | pp[i] = 0 8 | } 9 | return len(pp), nil 10 | } 11 | 12 | func (d DevNull) Write(pp []byte) (n int, err error) { 13 | return len(pp), nil 14 | } 15 | 16 | func (d DevNull) Close() error { 17 | return nil 18 | } 19 | 20 | func (d DevNull) Seek(offset int64, whence int) (int64, error) { 21 | return 0, nil 22 | } 23 | 24 | func (d DevNull) Drop() error { 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /utils/eventid/eventid.go: -------------------------------------------------------------------------------- 1 | package eventid 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | ) 9 | 10 | type Cache struct { 11 | ids map[hash.Event]bool 12 | mu sync.RWMutex 13 | maxSize int 14 | epoch idx.Epoch 15 | } 16 | 17 | func NewCache(maxSize int) *Cache { 18 | return &Cache{ 19 | maxSize: maxSize, 20 | } 21 | } 22 | 23 | func (c *Cache) Reset(epoch idx.Epoch) { 24 | c.mu.Lock() 25 | defer c.mu.Unlock() 26 | c.ids = make(map[hash.Event]bool) 27 | c.epoch = epoch 28 | } 29 | 30 | func (c *Cache) Has(id hash.Event) (has bool, ok bool) { 31 | c.mu.RLock() 32 | defer c.mu.RUnlock() 33 | 34 | if c.ids == nil { 35 | return false, false 36 | } 37 | if c.epoch != id.Epoch() { 38 | return false, false 39 | } 40 | return c.ids[id], true 41 | } 42 | 43 | func (c *Cache) Add(id hash.Event) bool { 44 | c.mu.Lock() 45 | defer c.mu.Unlock() 46 | 47 | if c.ids == nil { 48 | return false 49 | } 50 | if c.epoch != id.Epoch() { 51 | return false 52 | } 53 | if len(c.ids) >= c.maxSize { 54 | c.ids = nil 55 | return false 56 | } 57 | c.ids[id] = true 58 | return true 59 | } 60 | 61 | func (c *Cache) Remove(id hash.Event) { 62 | c.mu.Lock() 63 | defer c.mu.Unlock() 64 | 65 | if c.ids == nil { 66 | return 67 | } 68 | delete(c.ids, id) 69 | } 70 | -------------------------------------------------------------------------------- /utils/fast/buffer.go: -------------------------------------------------------------------------------- 1 | package fast 2 | 3 | type Reader struct { 4 | buf []byte 5 | offset int 6 | } 7 | 8 | type Writer struct { 9 | buf []byte 10 | } 11 | 12 | // NewReader wraps bytes with reading buffer. 13 | func NewReader(bb []byte) *Reader { 14 | return &Reader{ 15 | buf: bb, 16 | offset: 0, 17 | } 18 | } 19 | 20 | // NewWriter wraps bytes with writing buffer. 21 | func NewWriter(bb []byte) *Writer { 22 | return &Writer{ 23 | buf: bb, 24 | } 25 | } 26 | 27 | // WriteByte to the buffer. 28 | func (b *Writer) WriteByte(v byte) { 29 | b.buf = append(b.buf, v) 30 | } 31 | 32 | // Write the byte to the buffer. 33 | func (b *Writer) Write(v []byte) { 34 | b.buf = append(b.buf, v...) 35 | } 36 | 37 | // Read n bytes. 38 | func (b *Reader) Read(n int) []byte { 39 | var res []byte 40 | res = b.buf[b.offset : b.offset+n] 41 | b.offset += n 42 | 43 | return res 44 | } 45 | 46 | // ReadByte reads 1 byte. 47 | func (b *Reader) ReadByte() byte { 48 | var res byte 49 | res = b.buf[b.offset] 50 | b.offset++ 51 | 52 | return res 53 | } 54 | 55 | // Position of internal cursor. 56 | func (b *Reader) Position() int { 57 | return b.offset 58 | } 59 | 60 | // Bytes of internal buffer 61 | func (b *Reader) Bytes() []byte { 62 | return b.buf 63 | } 64 | 65 | // Bytes of internal buffer 66 | func (b *Writer) Bytes() []byte { 67 | return b.buf 68 | } 69 | 70 | // Empty returns true if the whole buffer is consumed 71 | func (b *Reader) Empty() bool { 72 | return len(b.buf) == b.offset 73 | } 74 | -------------------------------------------------------------------------------- /utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | ) 10 | 11 | func OpenFile(path string, isSyncMode bool) *os.File { 12 | const dirPerm = 0700 13 | if err := os.MkdirAll(filepath.Dir(path), dirPerm); err != nil { 14 | log.Crit("Failed to create file dir", "file", path, "err", err) 15 | } 16 | sync := 0 17 | if isSyncMode { 18 | sync = os.O_SYNC 19 | } 20 | fh, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|sync, 0666) 21 | if err != nil { 22 | log.Crit("Failed to open file", "file", path, "err", err) 23 | } 24 | return fh 25 | } 26 | 27 | func FileExists(path string) bool { 28 | _, err := os.Stat(path) 29 | return err == nil 30 | } 31 | 32 | func FilePut(path string, content []byte, isSyncMode bool) { 33 | fh := OpenFile(path, isSyncMode) 34 | defer fh.Close() 35 | if err := fh.Truncate(0); err != nil { 36 | log.Crit("Failed to truncate file", "file", path, "err", err) 37 | } 38 | if _, err := fh.Write(content); err != nil { 39 | log.Crit("Failed to write to file", "file", path, "err", err) 40 | } 41 | } 42 | 43 | func FileGet(path string) []byte { 44 | if !FileExists(path) { 45 | return nil 46 | } 47 | fh, err := os.Open(path) 48 | if err != nil { 49 | log.Crit("Failed to open file", "file", path, "err", err) 50 | } 51 | defer fh.Close() 52 | res, _ := ioutil.ReadAll(fh) 53 | return res 54 | } 55 | -------------------------------------------------------------------------------- /utils/ioread/ioread.go: -------------------------------------------------------------------------------- 1 | package ioread 2 | 3 | import "io" 4 | 5 | func ReadAll(reader io.Reader, buf []byte) error { 6 | consumed := 0 7 | for { 8 | n, err := reader.Read(buf[consumed:]) 9 | consumed += n 10 | if consumed == len(buf) { 11 | return nil 12 | } 13 | if err != nil { 14 | return err 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /utils/memory/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Jeremy Jay 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /utils/memory/doc.go: -------------------------------------------------------------------------------- 1 | // Package memory provides a single method reporting total system memory 2 | // accessible to the kernel. 3 | package memory 4 | 5 | // TotalMemory returns the total accessible system memory in bytes. 6 | // 7 | // The total accessible memory is installed physical memory size minus reserved 8 | // areas for the kernel and hardware, if such reservations are reported by 9 | // the operating system. 10 | // 11 | // If accessible memory size could not be determined, then 0 is returned. 12 | func TotalMemory() uint64 { 13 | return sysTotalMemory() 14 | } 15 | 16 | // FreeMemory returns the total free system memory in bytes. 17 | // 18 | // The total free memory is installed physical memory size minus reserved 19 | // areas for other applications running on the same system. 20 | // 21 | // If free memory size could not be determined, then 0 is returned. 22 | func FreeMemory() uint64 { 23 | return sysFreeMemory() 24 | } 25 | -------------------------------------------------------------------------------- /utils/memory/memory_bsd.go: -------------------------------------------------------------------------------- 1 | //go:build freebsd || openbsd || dragonfly || netbsd 2 | // +build freebsd openbsd dragonfly netbsd 3 | 4 | package memory 5 | 6 | func sysTotalMemory() uint64 { 7 | s, err := sysctlUint64("hw.physmem") 8 | if err != nil { 9 | return 0 10 | } 11 | return s 12 | } 13 | 14 | func sysFreeMemory() uint64 { 15 | s, err := sysctlUint64("hw.usermem") 16 | if err != nil { 17 | return 0 18 | } 19 | return s 20 | } 21 | -------------------------------------------------------------------------------- /utils/memory/memory_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | // +build darwin 3 | 4 | package memory 5 | 6 | import ( 7 | "os/exec" 8 | "regexp" 9 | "strconv" 10 | ) 11 | 12 | func sysTotalMemory() uint64 { 13 | s, err := sysctlUint64("hw.memsize") 14 | if err != nil { 15 | return 0 16 | } 17 | return s 18 | } 19 | 20 | func sysFreeMemory() uint64 { 21 | cmd := exec.Command("vm_stat") 22 | outBytes, err := cmd.Output() 23 | if err != nil { 24 | return 0 25 | } 26 | 27 | rePageSize := regexp.MustCompile("page size of ([0-9]*) bytes") 28 | reFreePages := regexp.MustCompile("Pages free: *([0-9]*)\\.") 29 | 30 | // default: page size of 4096 bytes 31 | matches := rePageSize.FindSubmatchIndex(outBytes) 32 | pageSize := uint64(4096) 33 | if len(matches) == 4 { 34 | pageSize, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) 35 | if err != nil { 36 | return 0 37 | } 38 | } 39 | 40 | // ex: Pages free: 1126961. 41 | matches = reFreePages.FindSubmatchIndex(outBytes) 42 | freePages := uint64(0) 43 | if len(matches) == 4 { 44 | freePages, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) 45 | if err != nil { 46 | return 0 47 | } 48 | } 49 | return freePages * pageSize 50 | } 51 | -------------------------------------------------------------------------------- /utils/memory/memory_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package memory 5 | 6 | import "syscall" 7 | 8 | func sysTotalMemory() uint64 { 9 | in := &syscall.Sysinfo_t{} 10 | err := syscall.Sysinfo(in) 11 | if err != nil { 12 | return 0 13 | } 14 | // If this is a 32-bit system, then these fields are 15 | // uint32 instead of uint64. 16 | // So we always convert to uint64 to match signature. 17 | return uint64(in.Totalram) * uint64(in.Unit) 18 | } 19 | 20 | func sysFreeMemory() uint64 { 21 | in := &syscall.Sysinfo_t{} 22 | err := syscall.Sysinfo(in) 23 | if err != nil { 24 | return 0 25 | } 26 | // If this is a 32-bit system, then these fields are 27 | // uint32 instead of uint64. 28 | // So we always convert to uint64 to match signature. 29 | return uint64(in.Freeram) * uint64(in.Unit) 30 | } 31 | -------------------------------------------------------------------------------- /utils/memory/memory_test.go: -------------------------------------------------------------------------------- 1 | package memory 2 | 3 | import "testing" 4 | 5 | func TestNonZero(t *testing.T) { 6 | if TotalMemory() == 0 { 7 | t.Fatal("TotalMemory returned 0") 8 | } 9 | if FreeMemory() == 0 { 10 | t.Fatal("FreeMemory returned 0") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /utils/memory/memory_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package memory 5 | 6 | import ( 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | // omitting a few fields for brevity... 12 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx 13 | type memStatusEx struct { 14 | dwLength uint32 15 | dwMemoryLoad uint32 16 | ullTotalPhys uint64 17 | ullAvailPhys uint64 18 | unused [5]uint64 19 | } 20 | 21 | func sysTotalMemory() uint64 { 22 | kernel32, err := syscall.LoadDLL("kernel32.dll") 23 | if err != nil { 24 | return 0 25 | } 26 | // GetPhysicallyInstalledSystemMemory is simpler, but broken on 27 | // older versions of windows (and uses this under the hood anyway). 28 | globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") 29 | if err != nil { 30 | return 0 31 | } 32 | msx := &memStatusEx{ 33 | dwLength: 64, 34 | } 35 | r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) 36 | if r == 0 { 37 | return 0 38 | } 39 | return msx.ullTotalPhys 40 | } 41 | 42 | func sysFreeMemory() uint64 { 43 | kernel32, err := syscall.LoadDLL("kernel32.dll") 44 | if err != nil { 45 | return 0 46 | } 47 | // GetPhysicallyInstalledSystemMemory is simpler, but broken on 48 | // older versions of windows (and uses this under the hood anyway). 49 | globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") 50 | if err != nil { 51 | return 0 52 | } 53 | msx := &memStatusEx{ 54 | dwLength: 64, 55 | } 56 | r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) 57 | if r == 0 { 58 | return 0 59 | } 60 | return msx.ullAvailPhys 61 | } 62 | -------------------------------------------------------------------------------- /utils/memory/memsysctl.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || freebsd || openbsd || dragonfly || netbsd 2 | // +build darwin freebsd openbsd dragonfly netbsd 3 | 4 | package memory 5 | 6 | import ( 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | func sysctlUint64(name string) (uint64, error) { 12 | s, err := syscall.Sysctl(name) 13 | if err != nil { 14 | return 0, err 15 | } 16 | // hack because the string conversion above drops a \0 17 | b := []byte(s) 18 | if len(b) < 8 { 19 | b = append(b, 0) 20 | } 21 | return *(*uint64)(unsafe.Pointer(&b[0])), nil 22 | } 23 | -------------------------------------------------------------------------------- /utils/memory/stub.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin && !windows && !freebsd && !dragonfly && !netbsd && !openbsd 2 | // +build !linux,!darwin,!windows,!freebsd,!dragonfly,!netbsd,!openbsd 3 | 4 | package memory 5 | 6 | func sysTotalMemory() uint64 { 7 | return 0 8 | } 9 | func sysFreeMemory() uint64 { 10 | return 0 11 | } 12 | -------------------------------------------------------------------------------- /utils/migration/id_store.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | // IDStore interface for stores id in migrations 4 | type IDStore interface { 5 | GetID() string 6 | SetID(string) 7 | } 8 | 9 | type inmemIDStore struct { 10 | lastID string 11 | } 12 | 13 | func (p *inmemIDStore) GetID() string { 14 | return string(p.lastID) 15 | } 16 | 17 | func (p *inmemIDStore) SetID(id string) { 18 | p.lastID = id 19 | } 20 | -------------------------------------------------------------------------------- /utils/migration/kvdb_id_store.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/ethereum/go-ethereum/log" 6 | ) 7 | 8 | // KvdbIDStore stores id 9 | type KvdbIDStore struct { 10 | table kvdb.Store 11 | key []byte 12 | } 13 | 14 | // NewKvdbIDStore constructor 15 | func NewKvdbIDStore(table kvdb.Store) *KvdbIDStore { 16 | return &KvdbIDStore{ 17 | table: table, 18 | key: []byte("id"), 19 | } 20 | } 21 | 22 | // GetID is a getter 23 | func (p *KvdbIDStore) GetID() string { 24 | id, err := p.table.Get(p.key) 25 | if err != nil { 26 | log.Crit("Failed to get key-value", "err", err) 27 | } 28 | 29 | if id == nil { 30 | return "" 31 | } 32 | return string(id) 33 | } 34 | 35 | // SetID is a setter 36 | func (p *KvdbIDStore) SetID(id string) { 37 | err := p.table.Put(p.key, []byte(id)) 38 | if err != nil { 39 | log.Crit("Failed to put key-value", "err", err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /utils/nameof.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | ) 9 | 10 | // NameOf returns human readable string representation. 11 | func NameOf(p idx.ValidatorID) string { 12 | if name := hash.GetNodeName(p); len(name) > 0 { 13 | return name 14 | } 15 | 16 | return fmt.Sprintf("%d", p) 17 | } 18 | -------------------------------------------------------------------------------- /utils/num_queue.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type NumQueue struct { 8 | mu sync.Mutex 9 | lastDone uint64 10 | waiters []chan struct{} 11 | } 12 | 13 | func NewNumQueue(init uint64) *NumQueue { 14 | return &NumQueue{ 15 | lastDone: init, 16 | } 17 | } 18 | 19 | func (q *NumQueue) Done(n uint64) { 20 | q.mu.Lock() 21 | defer q.mu.Unlock() 22 | 23 | if n <= q.lastDone { 24 | panic("Already done!") 25 | } 26 | 27 | pos := int(n - q.lastDone - 1) 28 | for i := 0; i < len(q.waiters) && i <= pos; i++ { 29 | close(q.waiters[i]) 30 | } 31 | if pos < len(q.waiters) { 32 | q.waiters = q.waiters[pos+1:] 33 | } else { 34 | q.waiters = make([]chan struct{}, 0, 1000) 35 | } 36 | 37 | q.lastDone = n 38 | } 39 | 40 | func (q *NumQueue) WaitFor(n uint64) { 41 | q.mu.Lock() 42 | 43 | if n <= q.lastDone { 44 | q.mu.Unlock() 45 | return 46 | } 47 | 48 | count := int(n - q.lastDone) 49 | for i := len(q.waiters); i < count; i++ { 50 | q.waiters = append(q.waiters, make(chan struct{})) 51 | } 52 | ch := q.waiters[count-1] 53 | q.mu.Unlock() 54 | <-ch 55 | } 56 | -------------------------------------------------------------------------------- /utils/num_queue_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestNumQueue(t *testing.T) { 12 | 13 | t.Run("Simple", func(t *testing.T) { 14 | N := uint64(100) 15 | q := NewNumQueue(0) 16 | for i := uint64(1); i <= N; i++ { 17 | var iter sync.WaitGroup 18 | iter.Add(1) 19 | go func(i uint64) { 20 | defer iter.Done() 21 | q.WaitFor(i) 22 | }(i) 23 | 24 | q.Done(i) 25 | iter.Wait() 26 | } 27 | }) 28 | 29 | t.Run("Random", func(t *testing.T) { 30 | require := require.New(t) 31 | N := 100 32 | 33 | q := NewNumQueue(0) 34 | output := make(chan uint64, 10) 35 | nums := rand.Perm(N) 36 | 37 | for _, n := range nums { 38 | go func(n uint64) { 39 | q.WaitFor(n - 1) 40 | output <- n 41 | if n == uint64(N) { 42 | close(output) 43 | } 44 | q.Done(n) 45 | 46 | }(uint64(n + 1)) 47 | } 48 | 49 | var prev uint64 50 | for got := range output { 51 | require.Less(prev, got) 52 | prev = got 53 | } 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /utils/pretty_duration.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | ) 9 | 10 | // PrettyDuration is a combination of common.PrettyDuration and common.PrettyAge 11 | // It is a pretty printed version of a time.Duration value that rounds 12 | // the values up to a single most significant unit, 13 | // while showing the least significant part if duration isn't too large. 14 | type PrettyDuration time.Duration 15 | 16 | // ageUnits is a list of units the age pretty printing uses. 17 | var ageUnits = []struct { 18 | Size time.Duration 19 | Symbol string 20 | }{ 21 | {12 * 30 * 24 * time.Hour, "y"}, 22 | {30 * 24 * time.Hour, "mo"}, 23 | {24 * time.Hour, "d"}, 24 | {time.Hour, "h"}, 25 | {time.Minute, "m"}, 26 | } 27 | 28 | // String implements the Stringer interface, allowing pretty printing of duration 29 | // values rounded to the most significant time unit. 30 | func (t PrettyDuration) String() string { 31 | // Calculate the time difference and handle the 0 cornercase 32 | diff := time.Duration(t) 33 | // Accumulate a precision of 3 components before returning 34 | result, prec := "", 0 35 | if diff < 0 { 36 | diff = -diff 37 | result = "-" 38 | } 39 | 40 | for _, unit := range ageUnits { 41 | if diff > unit.Size { 42 | result = fmt.Sprintf("%s%d%s", result, diff/unit.Size, unit.Symbol) 43 | diff %= unit.Size 44 | 45 | if prec += 1; prec >= 3 { 46 | break 47 | } 48 | } 49 | } 50 | if prec < 3 { 51 | return fmt.Sprintf("%s%s", result, common.PrettyDuration(diff).String()) 52 | } 53 | return result 54 | } 55 | -------------------------------------------------------------------------------- /utils/pretty_duration_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestPrettyDuration_String(t *testing.T) { 11 | for _, testcase := range []struct { 12 | str string 13 | val time.Duration 14 | }{ 15 | {"0s", 0}, 16 | {"1ns", time.Nanosecond}, 17 | {"1µs", time.Microsecond}, 18 | {"1ms", time.Millisecond}, 19 | {"1s", time.Second}, 20 | {"1.000s", time.Second + time.Microsecond + time.Nanosecond}, 21 | {"1.001s", time.Second + time.Millisecond + time.Microsecond + time.Nanosecond}, 22 | {"1m1.001s", time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond}, 23 | {"1h1m1.001s", time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond}, 24 | {"1d1h1m", 24*time.Hour + time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond}, 25 | {"1mo1d1h", 30*24*time.Hour + 24*time.Hour + time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond}, 26 | {"26y4mo23d", time.Duration(9503.123456789 * 24 * float64(time.Hour))}, 27 | } { 28 | require.Equal(t, testcase.str, PrettyDuration(testcase.val).String()) 29 | if testcase.val > 0 { 30 | require.Equal(t, "-"+testcase.str, PrettyDuration(-testcase.val).String()) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils/randat/rand_at.go: -------------------------------------------------------------------------------- 1 | package randat 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | type cached struct { 8 | seed uint64 9 | r uint64 10 | } 11 | 12 | var ( 13 | gSeed = rand.Int63() 14 | cache = cached{} 15 | ) 16 | 17 | // RandAt returns random number with seed 18 | // Not safe for concurrent use 19 | func RandAt(seed uint64) uint64 { 20 | if seed != 0 && cache.seed == seed { 21 | return cache.r 22 | } 23 | cache.seed = seed 24 | cache.r = rand.New(rand.NewSource(gSeed ^ int64(seed))).Uint64() 25 | return cache.r 26 | } 27 | -------------------------------------------------------------------------------- /utils/rate/gauge_test.go: -------------------------------------------------------------------------------- 1 | package rate 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | func TestGauge_Concurrency(t *testing.T) { 9 | for try := 0; try < 100; try++ { 10 | testGaugeConcurrency(t) 11 | } 12 | } 13 | 14 | func testGaugeConcurrency(t *testing.T) { 15 | g := NewGauge() 16 | barrier := make(chan struct{}) 17 | wg := sync.WaitGroup{} 18 | end := int64(32) 19 | for i := int64(0); i <= end; i++ { 20 | wg.Add(1) 21 | turn := i 22 | go func() { 23 | defer wg.Done() 24 | select { 25 | case <-barrier: 26 | g.Mark(turn) 27 | if v := int64(g.rateToGauge(float64(2*end), 1)); v < turn { 28 | t.Errorf("%d >= %d", v, end) 29 | } 30 | return 31 | } 32 | }() 33 | } 34 | close(barrier) 35 | wg.Wait() 36 | if v := int64(g.rateToGauge(float64(2*end), 1)); v != end { 37 | t.Errorf("%d != %d", v, end) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /utils/rlpstore/rlpstore.go: -------------------------------------------------------------------------------- 1 | package rlpstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/logger" 5 | "github.com/Fantom-foundation/lachesis-base/kvdb" 6 | "github.com/ethereum/go-ethereum/rlp" 7 | ) 8 | 9 | type Helper struct { 10 | logger.Instance 11 | } 12 | 13 | // Set RLP value 14 | func (s *Helper) Set(table kvdb.Store, key []byte, val interface{}) { 15 | buf, err := rlp.EncodeToBytes(val) 16 | if err != nil { 17 | s.Log.Crit("Failed to encode rlp", "err", err) 18 | } 19 | 20 | if err := table.Put(key, buf); err != nil { 21 | s.Log.Crit("Failed to put key-value", "err", err) 22 | } 23 | } 24 | 25 | // Get RLP value 26 | func (s *Helper) Get(table kvdb.Store, key []byte, to interface{}) interface{} { 27 | buf, err := table.Get(key) 28 | if err != nil { 29 | s.Log.Crit("Failed to get key-value", "err", err) 30 | } 31 | if buf == nil { 32 | return nil 33 | } 34 | 35 | err = rlp.DecodeBytes(buf, to) 36 | if err != nil { 37 | s.Log.Crit("Failed to decode rlp", "err", err, "size", len(buf)) 38 | } 39 | return to 40 | } 41 | -------------------------------------------------------------------------------- /utils/self-table.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/kvdb" 5 | "github.com/Fantom-foundation/lachesis-base/kvdb/table" 6 | ) 7 | 8 | func NewTableOrSelf(db kvdb.Store, prefix []byte) kvdb.Store { 9 | if len(prefix) == 0 { 10 | return db 11 | } 12 | return table.New(db, prefix) 13 | } 14 | -------------------------------------------------------------------------------- /utils/signers/gsignercache/global_cache.go: -------------------------------------------------------------------------------- 1 | package gsignercache 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/core/types" 6 | lru "github.com/hashicorp/golang-lru" 7 | ) 8 | 9 | var ( 10 | globalCache, _ = lru.New(40000) 11 | ) 12 | 13 | type WlruCache struct { 14 | Cache *lru.Cache 15 | } 16 | 17 | func (w *WlruCache) Add(txid common.Hash, c types.CachedSender) { 18 | w.Cache.Add(txid, c) 19 | } 20 | 21 | func (w *WlruCache) Get(txid common.Hash) *types.CachedSender { 22 | ic, ok := w.Cache.Get(txid) 23 | if !ok { 24 | return nil 25 | } 26 | c := ic.(types.CachedSender) 27 | return &c 28 | } 29 | 30 | func Wrap(signer types.Signer) types.Signer { 31 | return types.WrapWithCachedSigner(signer, &WlruCache{globalCache}) 32 | } 33 | -------------------------------------------------------------------------------- /utils/signers/internaltx/internaltx.go: -------------------------------------------------------------------------------- 1 | package internaltx 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/core/types" 6 | ) 7 | 8 | func IsInternal(tx *types.Transaction) bool { 9 | v, r, _ := tx.RawSignatureValues() 10 | return v.Sign() == 0 && r.Sign() == 0 11 | } 12 | 13 | func InternalSender(tx *types.Transaction) common.Address { 14 | _, _, s := tx.RawSignatureValues() 15 | return common.BytesToAddress(s.Bytes()) 16 | } 17 | 18 | func Sender(signer types.Signer, tx *types.Transaction) (common.Address, error) { 19 | if !IsInternal(tx) { 20 | return types.Sender(signer, tx) 21 | } 22 | return InternalSender(tx), nil 23 | } 24 | -------------------------------------------------------------------------------- /utils/spin_lock.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "runtime" 5 | "sync/atomic" 6 | ) 7 | 8 | // SpinLock implements a simple atomic spin lock, the zero value for a SpinLock is an unlocked spinlock. 9 | type SpinLock struct { 10 | f uint32 11 | } 12 | 13 | // Lock locks sl. If the lock is already in use, the caller blocks until Unlock is called 14 | func (sl *SpinLock) Lock() { 15 | for !sl.TryLock() { 16 | runtime.Gosched() // allow other goroutines to do work. 17 | } 18 | } 19 | 20 | // Unlock unlocks sl, unlike [Mutex.Unlock](http://golang.org/pkg/sync/#Mutex.Unlock), 21 | // there's no harm calling it on an unlocked SpinLock 22 | func (sl *SpinLock) Unlock() { 23 | atomic.StoreUint32(&sl.f, 0) 24 | } 25 | 26 | // TryLock will try to lock sl and return whether it succeed or not without blocking. 27 | func (sl *SpinLock) TryLock() bool { 28 | return atomic.CompareAndSwapUint32(&sl.f, 0, 1) 29 | } 30 | 31 | // String is human readable lock state 32 | func (sl *SpinLock) String() string { 33 | if atomic.LoadUint32(&sl.f) == 1 { 34 | return "Locked" 35 | } 36 | return "Unlocked" 37 | } 38 | -------------------------------------------------------------------------------- /utils/to_ftm.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "math/big" 4 | 5 | // ToFtm number of FTM to Wei 6 | func ToFtm(ftm uint64) *big.Int { 7 | return new(big.Int).Mul(new(big.Int).SetUint64(ftm), big.NewInt(1e18)) 8 | } 9 | -------------------------------------------------------------------------------- /utils/txtime/txtime.go: -------------------------------------------------------------------------------- 1 | package txtime 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/utils/wlru" 7 | "github.com/ethereum/go-ethereum/common" 8 | ) 9 | 10 | var ( 11 | globalFinalized, _ = wlru.New(30000, 30000) 12 | globalNonFinalized, _ = wlru.New(5000, 5000) 13 | Enabled = false 14 | ) 15 | 16 | func Saw(txid common.Hash, t time.Time) { 17 | if !Enabled { 18 | return 19 | } 20 | globalNonFinalized.ContainsOrAdd(txid, t, 1) 21 | } 22 | 23 | func Validated(txid common.Hash, t time.Time) { 24 | if !Enabled { 25 | return 26 | } 27 | v, has := globalNonFinalized.Peek(txid) 28 | if has { 29 | t = v.(time.Time) 30 | } 31 | globalFinalized.ContainsOrAdd(txid, t, 1) 32 | } 33 | 34 | func Of(txid common.Hash) time.Time { 35 | if !Enabled { 36 | return time.Time{} 37 | } 38 | v, has := globalFinalized.Get(txid) 39 | if has { 40 | return v.(time.Time) 41 | } 42 | v, has = globalNonFinalized.Get(txid) 43 | if has { 44 | return v.(time.Time) 45 | } 46 | now := time.Now() 47 | Saw(txid, now) 48 | return now 49 | } 50 | -------------------------------------------------------------------------------- /utils/uint2hash.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | // BigTo256 converts big number to 32 bytes array 10 | func BigTo256(b *big.Int) common.Hash { 11 | return common.BytesToHash(b.Bytes()) 12 | } 13 | 14 | // U64to256 converts uint64 to 32 bytes array 15 | func U64to256(u64 uint64) common.Hash { 16 | return BigTo256(new(big.Int).SetUint64(u64)) 17 | } 18 | 19 | // U64toBig converts uint64 to big number 20 | func U64toBig(u64 uint64) *big.Int { 21 | return new(big.Int).SetUint64(u64) 22 | } 23 | 24 | // I64to256 converts int64 to 32 bytes array 25 | func I64to256(i64 int64) common.Hash { 26 | return BigTo256(new(big.Int).SetInt64(i64)) 27 | } 28 | -------------------------------------------------------------------------------- /utils/wgmutex/wg_mutex.go: -------------------------------------------------------------------------------- 1 | package wgmutex 2 | 3 | import "sync" 4 | 5 | type WgMutex struct { 6 | *sync.RWMutex 7 | wg *sync.WaitGroup 8 | } 9 | 10 | func New(m *sync.RWMutex, wg *sync.WaitGroup) *WgMutex { 11 | return &WgMutex{ 12 | RWMutex: m, 13 | wg: wg, 14 | } 15 | } 16 | 17 | func (m *WgMutex) Lock() { 18 | m.RWMutex.Lock() 19 | m.wg.Wait() 20 | } 21 | 22 | func (m *WgMutex) RLock() { 23 | m.RWMutex.RLock() 24 | m.wg.Wait() 25 | } 26 | -------------------------------------------------------------------------------- /valkeystore/cache.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 7 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 8 | ) 9 | 10 | var ( 11 | ErrAlreadyUnlocked = errors.New("already unlocked") 12 | ErrLocked = errors.New("key is locked") 13 | ) 14 | 15 | type CachedKeystore struct { 16 | backend RawKeystoreI 17 | cache map[string]*encryption.PrivateKey 18 | } 19 | 20 | func NewCachedKeystore(backend RawKeystoreI) *CachedKeystore { 21 | return &CachedKeystore{ 22 | backend: backend, 23 | cache: make(map[string]*encryption.PrivateKey), 24 | } 25 | } 26 | 27 | func (c *CachedKeystore) Unlocked(pubkey validatorpk.PubKey) bool { 28 | _, ok := c.cache[c.idxOf(pubkey)] 29 | return ok 30 | } 31 | 32 | func (c *CachedKeystore) Has(pubkey validatorpk.PubKey) bool { 33 | if c.Unlocked(pubkey) { 34 | return true 35 | } 36 | return c.backend.Has(pubkey) 37 | } 38 | 39 | func (c *CachedKeystore) Unlock(pubkey validatorpk.PubKey, auth string) error { 40 | if c.Unlocked(pubkey) { 41 | return ErrAlreadyUnlocked 42 | } 43 | key, err := c.backend.Get(pubkey, auth) 44 | if err != nil { 45 | return err 46 | } 47 | c.cache[c.idxOf(pubkey)] = key 48 | return nil 49 | } 50 | 51 | func (c *CachedKeystore) GetUnlocked(pubkey validatorpk.PubKey) (*encryption.PrivateKey, error) { 52 | if !c.Unlocked(pubkey) { 53 | return nil, ErrLocked 54 | } 55 | return c.cache[c.idxOf(pubkey)], nil 56 | } 57 | 58 | func (c *CachedKeystore) idxOf(pubkey validatorpk.PubKey) string { 59 | return string(pubkey.Bytes()) 60 | } 61 | 62 | func (c *CachedKeystore) Add(pubkey validatorpk.PubKey, key []byte, auth string) error { 63 | return c.backend.Add(pubkey, key, auth) 64 | } 65 | 66 | func (c *CachedKeystore) Get(pubkey validatorpk.PubKey, auth string) (*encryption.PrivateKey, error) { 67 | return c.backend.Get(pubkey, auth) 68 | } 69 | -------------------------------------------------------------------------------- /valkeystore/default.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts/keystore" 5 | 6 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 7 | ) 8 | 9 | func NewDefaultFileRawKeystore(dir string) *FileKeystore { 10 | enc := encryption.New(keystore.StandardScryptN, keystore.StandardScryptP) 11 | return NewFileKeystore(dir, enc) 12 | } 13 | 14 | func NewDefaultMemKeystore() *SyncedKeystore { 15 | return NewSyncedKeystore(NewCachedKeystore(NewMemKeystore())) 16 | } 17 | 18 | func NewDefaultFileKeystore(dir string) *SyncedKeystore { 19 | return NewSyncedKeystore(NewCachedKeystore(NewDefaultFileRawKeystore(dir))) 20 | } 21 | -------------------------------------------------------------------------------- /valkeystore/encryption/io.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func writeTemporaryKeyFile(file string, content []byte) (string, error) { 10 | // Create the keystore directory with appropriate permissions 11 | // in case it is not present yet. 12 | const dirPerm = 0700 13 | if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { 14 | return "", err 15 | } 16 | // Atomic write: create a temporary hidden file first 17 | // then move it into place. TempFile assigns mode 0600. 18 | f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file[0:16])+".tmp") 19 | if err != nil { 20 | return "", err 21 | } 22 | if _, err := f.Write(content); err != nil { 23 | f.Close() 24 | os.Remove(f.Name()) 25 | return "", err 26 | } 27 | f.Close() 28 | return f.Name(), nil 29 | } 30 | -------------------------------------------------------------------------------- /valkeystore/encryption/migration.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/accounts/keystore" 9 | "github.com/ethereum/go-ethereum/common" 10 | 11 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 12 | ) 13 | 14 | type encryptedAccountKeyJSONV3 struct { 15 | Address string `json:"address"` 16 | Crypto keystore.CryptoJSON `json:"crypto"` 17 | Id string `json:"id"` 18 | Version int `json:"version"` 19 | } 20 | 21 | func MigrateAccountToValidatorKey(acckeypath string, valkeypath string, pubkey validatorpk.PubKey) error { 22 | acckeyjson, err := ioutil.ReadFile(acckeypath) 23 | if err != nil { 24 | return err 25 | } 26 | acck := new(encryptedAccountKeyJSONV3) 27 | if err := json.Unmarshal(acckeyjson, acck); err != nil { 28 | return err 29 | } 30 | 31 | valk := EncryptedKeyJSON{ 32 | Type: validatorpk.Types.Secp256k1, 33 | PublicKey: common.Bytes2Hex(pubkey.Raw), 34 | Crypto: acck.Crypto, 35 | } 36 | valkeyjson, err := json.Marshal(valk) 37 | if err != nil { 38 | return err 39 | } 40 | tmpName, err := writeTemporaryKeyFile(valkeypath, valkeyjson) 41 | if err != nil { 42 | return err 43 | } 44 | return os.Rename(tmpName, valkeypath) 45 | } 46 | -------------------------------------------------------------------------------- /valkeystore/files.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | 10 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 11 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 12 | ) 13 | 14 | var ( 15 | ErrNotFound = errors.New("key is not found") 16 | ErrAlreadyExists = errors.New("key already exists") 17 | ) 18 | 19 | type FileKeystore struct { 20 | enc *encryption.Keystore 21 | dir string 22 | } 23 | 24 | func NewFileKeystore(dir string, enc *encryption.Keystore) *FileKeystore { 25 | return &FileKeystore{ 26 | enc: enc, 27 | dir: dir, 28 | } 29 | } 30 | 31 | func (f *FileKeystore) Has(pubkey validatorpk.PubKey) bool { 32 | return fileExists(f.PathOf(pubkey)) 33 | } 34 | 35 | func (f *FileKeystore) Add(pubkey validatorpk.PubKey, key []byte, auth string) error { 36 | if f.Has(pubkey) { 37 | return ErrAlreadyExists 38 | } 39 | return f.enc.StoreKey(f.PathOf(pubkey), pubkey, key, auth) 40 | } 41 | 42 | func (f *FileKeystore) Get(pubkey validatorpk.PubKey, auth string) (*encryption.PrivateKey, error) { 43 | if !f.Has(pubkey) { 44 | return nil, ErrNotFound 45 | } 46 | return f.enc.ReadKey(pubkey, f.PathOf(pubkey), auth) 47 | } 48 | 49 | func (f *FileKeystore) PathOf(pubkey validatorpk.PubKey) string { 50 | return path.Join(f.dir, common.Bytes2Hex(pubkey.Bytes())) 51 | } 52 | 53 | func fileExists(filename string) bool { 54 | info, err := os.Stat(filename) 55 | if err != nil { 56 | return false 57 | } 58 | return !info.IsDir() 59 | } 60 | -------------------------------------------------------------------------------- /valkeystore/keystore.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 5 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 6 | ) 7 | 8 | type RawKeystoreI interface { 9 | Has(pubkey validatorpk.PubKey) bool 10 | Add(pubkey validatorpk.PubKey, key []byte, auth string) error 11 | Get(pubkey validatorpk.PubKey, auth string) (*encryption.PrivateKey, error) 12 | } 13 | 14 | type KeystoreI interface { 15 | RawKeystoreI 16 | Unlock(pubkey validatorpk.PubKey, auth string) error 17 | Unlocked(pubkey validatorpk.PubKey) bool 18 | GetUnlocked(pubkey validatorpk.PubKey) (*encryption.PrivateKey, error) 19 | } 20 | -------------------------------------------------------------------------------- /valkeystore/mem.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | 8 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 9 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 10 | ) 11 | 12 | type MemKeystore struct { 13 | mem map[string]*encryption.PrivateKey 14 | auth map[string]string 15 | } 16 | 17 | func NewMemKeystore() *MemKeystore { 18 | return &MemKeystore{ 19 | mem: make(map[string]*encryption.PrivateKey), 20 | auth: make(map[string]string), 21 | } 22 | } 23 | 24 | func (m *MemKeystore) Has(pubkey validatorpk.PubKey) bool { 25 | _, ok := m.mem[m.idxOf(pubkey)] 26 | return ok 27 | } 28 | 29 | func (m *MemKeystore) Add(pubkey validatorpk.PubKey, key []byte, auth string) error { 30 | if m.Has(pubkey) { 31 | return ErrAlreadyExists 32 | } 33 | if pubkey.Type != validatorpk.Types.Secp256k1 { 34 | return encryption.ErrNotSupportedType 35 | } 36 | decoded, err := crypto.ToECDSA(key) 37 | if err != nil { 38 | return err 39 | } 40 | m.mem[m.idxOf(pubkey)] = &encryption.PrivateKey{ 41 | Type: pubkey.Type, 42 | Bytes: key, 43 | Decoded: decoded, 44 | } 45 | m.auth[m.idxOf(pubkey)] = auth 46 | return nil 47 | } 48 | 49 | func (m *MemKeystore) Get(pubkey validatorpk.PubKey, auth string) (*encryption.PrivateKey, error) { 50 | if !m.Has(pubkey) { 51 | return nil, ErrNotFound 52 | } 53 | if m.auth[m.idxOf(pubkey)] != auth { 54 | return nil, errors.New("could not decrypt key with given password") 55 | } 56 | return m.mem[m.idxOf(pubkey)], nil 57 | } 58 | 59 | func (m *MemKeystore) idxOf(pubkey validatorpk.PubKey) string { 60 | return string(pubkey.Bytes()) 61 | } 62 | -------------------------------------------------------------------------------- /valkeystore/mem_test.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestMemKeystoreAdd(t *testing.T) { 10 | require := require.New(t) 11 | keystore := NewMemKeystore() 12 | 13 | key, err := keystore.Get(pubkey1, "auth1") 14 | require.EqualError(err, ErrNotFound.Error()) 15 | require.Nil(key) 16 | 17 | err = keystore.Add(pubkey1, key1, "auth1") 18 | require.NoError(err) 19 | 20 | testGet(t, keystore, pubkey1, key1, "auth1") 21 | 22 | err = keystore.Add(pubkey2, key2, "auth2") 23 | require.NoError(err) 24 | 25 | testGet(t, keystore, pubkey1, key1, "auth1") 26 | testGet(t, keystore, pubkey2, key2, "auth2") 27 | 28 | err = keystore.Add(pubkey2, key2, "auth1") 29 | require.Error(err, ErrAlreadyExists.Error()) 30 | 31 | testGet(t, keystore, pubkey2, key2, "auth2") 32 | } 33 | -------------------------------------------------------------------------------- /valkeystore/signer.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | 8 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 9 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 10 | ) 11 | 12 | type SignerI interface { 13 | Sign(pubkey validatorpk.PubKey, digest []byte) ([]byte, error) 14 | } 15 | 16 | type Signer struct { 17 | backend KeystoreI 18 | } 19 | 20 | func NewSigner(backend KeystoreI) *Signer { 21 | return &Signer{ 22 | backend: backend, 23 | } 24 | } 25 | 26 | func (s *Signer) Sign(pubkey validatorpk.PubKey, digest []byte) ([]byte, error) { 27 | if pubkey.Type != validatorpk.Types.Secp256k1 { 28 | return nil, encryption.ErrNotSupportedType 29 | } 30 | key, err := s.backend.GetUnlocked(pubkey) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | secp256k1Key := key.Decoded.(*ecdsa.PrivateKey) 36 | 37 | sigRSV, err := crypto.Sign(digest, secp256k1Key) 38 | if err != nil { 39 | return nil, err 40 | } 41 | sigRS := sigRSV[:64] 42 | return sigRS, err 43 | } 44 | -------------------------------------------------------------------------------- /valkeystore/sync.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 7 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 8 | ) 9 | 10 | type SyncedKeystore struct { 11 | backend KeystoreI 12 | mu sync.Mutex 13 | } 14 | 15 | func NewSyncedKeystore(backend KeystoreI) *SyncedKeystore { 16 | return &SyncedKeystore{ 17 | backend: backend, 18 | } 19 | } 20 | 21 | func (s *SyncedKeystore) Unlocked(pubkey validatorpk.PubKey) bool { 22 | s.mu.Lock() 23 | defer s.mu.Unlock() 24 | return s.backend.Unlocked(pubkey) 25 | } 26 | 27 | func (s *SyncedKeystore) Has(pubkey validatorpk.PubKey) bool { 28 | s.mu.Lock() 29 | defer s.mu.Unlock() 30 | return s.backend.Has(pubkey) 31 | } 32 | 33 | func (s *SyncedKeystore) Unlock(pubkey validatorpk.PubKey, auth string) error { 34 | s.mu.Lock() 35 | defer s.mu.Unlock() 36 | return s.backend.Unlock(pubkey, auth) 37 | } 38 | 39 | func (s *SyncedKeystore) GetUnlocked(pubkey validatorpk.PubKey) (*encryption.PrivateKey, error) { 40 | s.mu.Lock() 41 | defer s.mu.Unlock() 42 | return s.backend.GetUnlocked(pubkey) 43 | } 44 | 45 | func (s *SyncedKeystore) Add(pubkey validatorpk.PubKey, key []byte, auth string) error { 46 | s.mu.Lock() 47 | defer s.mu.Unlock() 48 | return s.backend.Add(pubkey, key, auth) 49 | } 50 | 51 | func (s *SyncedKeystore) Get(pubkey validatorpk.PubKey, auth string) (*encryption.PrivateKey, error) { 52 | s.mu.Lock() 53 | defer s.mu.Unlock() 54 | return s.backend.Get(pubkey, auth) 55 | } 56 | -------------------------------------------------------------------------------- /vecmt/no_cheaters.go: -------------------------------------------------------------------------------- 1 | package vecmt 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/hash" 7 | ) 8 | 9 | // NoCheaters excludes events which are observed by selfParents as cheaters. 10 | // Called by emitter to exclude cheater's events from potential parents list. 11 | func (vi *Index) NoCheaters(selfParent *hash.Event, options hash.Events) hash.Events { 12 | if selfParent == nil { 13 | return options 14 | } 15 | vi.InitBranchesInfo() 16 | 17 | if !vi.Engine.AtLeastOneFork() { 18 | return options 19 | } 20 | 21 | // no need to merge, because every branch is marked by IsForkDetected if fork is observed 22 | highest := vi.Base.GetHighestBefore(*selfParent) 23 | filtered := make(hash.Events, 0, len(options)) 24 | for _, id := range options { 25 | e := vi.getEvent(id) 26 | if e == nil { 27 | vi.crit(errors.New("event not found")) 28 | } 29 | if !highest.Get(vi.validatorIdxs[e.Creator()]).IsForkDetected() { 30 | filtered.Add(id) 31 | } 32 | } 33 | return filtered 34 | } 35 | -------------------------------------------------------------------------------- /vecmt/store_vectors.go: -------------------------------------------------------------------------------- 1 | package vecmt 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | "github.com/Fantom-foundation/lachesis-base/kvdb" 6 | ) 7 | 8 | func (vi *Index) getBytes(table kvdb.Store, id hash.Event) []byte { 9 | key := id.Bytes() 10 | b, err := table.Get(key) 11 | if err != nil { 12 | vi.crit(err) 13 | } 14 | return b 15 | } 16 | 17 | func (vi *Index) setBytes(table kvdb.Store, id hash.Event, b []byte) { 18 | key := id.Bytes() 19 | err := table.Put(key, b) 20 | if err != nil { 21 | vi.crit(err) 22 | } 23 | } 24 | 25 | // GetHighestBeforeTime reads the vector from DB 26 | func (vi *Index) GetHighestBeforeTime(id hash.Event) *HighestBeforeTime { 27 | if bVal, okGet := vi.cache.HighestBeforeTime.Get(id); okGet { 28 | return bVal.(*HighestBeforeTime) 29 | } 30 | 31 | b := HighestBeforeTime(vi.getBytes(vi.table.HighestBeforeTime, id)) 32 | if b == nil { 33 | return nil 34 | } 35 | vi.cache.HighestBeforeTime.Add(id, &b, uint(len(b))) 36 | return &b 37 | } 38 | 39 | // GetHighestBefore reads the vector from DB 40 | func (vi *Index) GetHighestBefore(id hash.Event) *HighestBefore { 41 | return &HighestBefore{ 42 | VSeq: vi.Base.GetHighestBefore(id), 43 | VTime: vi.GetHighestBeforeTime(id), 44 | } 45 | } 46 | 47 | // SetHighestBeforeTime stores the vector into DB 48 | func (vi *Index) SetHighestBeforeTime(id hash.Event, vec *HighestBeforeTime) { 49 | vi.setBytes(vi.table.HighestBeforeTime, id, *vec) 50 | 51 | vi.cache.HighestBeforeTime.Add(id, vec, uint(len(*vec))) 52 | } 53 | 54 | // SetHighestBefore stores the vectors into DB 55 | func (vi *Index) SetHighestBefore(id hash.Event, vec *HighestBefore) { 56 | vi.Base.SetHighestBefore(id, vec.VSeq) 57 | vi.SetHighestBeforeTime(id, vec.VTime) 58 | } 59 | -------------------------------------------------------------------------------- /vecmt/vector.go: -------------------------------------------------------------------------------- 1 | package vecmt 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | "github.com/Fantom-foundation/lachesis-base/vecfc" 8 | 9 | "github.com/Fantom-foundation/go-opera/inter" 10 | ) 11 | 12 | /* 13 | * Use binary form for optimization, to avoid serialization. As a result, DB cache works as elements cache. 14 | */ 15 | 16 | type ( 17 | // HighestBeforeTime is a vector of highest events (their CreationTime) which are observed by source event 18 | HighestBeforeTime []byte 19 | 20 | HighestBefore struct { 21 | VSeq *vecfc.HighestBeforeSeq 22 | VTime *HighestBeforeTime 23 | } 24 | ) 25 | 26 | // NewHighestBefore creates new HighestBefore vector. 27 | func NewHighestBefore(size idx.Validator) *HighestBefore { 28 | return &HighestBefore{ 29 | VSeq: vecfc.NewHighestBeforeSeq(size), 30 | VTime: NewHighestBeforeTime(size), 31 | } 32 | } 33 | 34 | // NewHighestBeforeTime creates new HighestBeforeTime vector. 35 | func NewHighestBeforeTime(size idx.Validator) *HighestBeforeTime { 36 | b := make(HighestBeforeTime, size*8) 37 | return &b 38 | } 39 | 40 | // Get i's position in the byte-encoded vector clock 41 | func (b HighestBeforeTime) Get(i idx.Validator) inter.Timestamp { 42 | for i >= b.Size() { 43 | return 0 44 | } 45 | return inter.Timestamp(binary.LittleEndian.Uint64(b[i*8 : (i+1)*8])) 46 | } 47 | 48 | // Set i's position in the byte-encoded vector clock 49 | func (b *HighestBeforeTime) Set(i idx.Validator, time inter.Timestamp) { 50 | for i >= b.Size() { 51 | // append zeros if exceeds size 52 | *b = append(*b, []byte{0, 0, 0, 0, 0, 0, 0, 0}...) 53 | } 54 | binary.LittleEndian.PutUint64((*b)[i*8:(i+1)*8], uint64(time)) 55 | } 56 | 57 | // Size of the vector clock 58 | func (b HighestBeforeTime) Size() idx.Validator { 59 | return idx.Validator(len(b) / 8) 60 | } 61 | --------------------------------------------------------------------------------