├── .codacy.yml ├── .dockerignore ├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── release_build.yml │ └── test_build.yml ├── .gitignore ├── .goreleaser.yml ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYING.LESSER ├── FUZZING.md ├── Makefile ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── 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 │ ├── tracing │ │ └── tracing.go │ ├── usage.go │ ├── validator.go │ ├── validatorcmd.go │ └── valkeystore.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.opera ├── 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 │ └── 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 ├── metric.go ├── presets.go ├── routing.go └── status.go ├── inter ├── block.go ├── drivertype │ └── driver_type.go ├── event.go ├── event_serializer.go ├── event_serializer_test.go ├── events.go ├── gas_power_left.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_mps.go ├── signature.go ├── time.go ├── transaction_serializer.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 │ ├── evmwriter │ │ ├── evm_writer.go │ │ └── evm_writer_test.go │ ├── netinit │ │ ├── netinitcalls │ │ │ └── network_initializer_calls.go │ │ └── network_initializer.go │ └── sfc │ │ └── sfc_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 ├── topicsdb ├── key.go ├── key_test.go ├── record.go ├── search_parallel.go ├── search_test.go ├── sync.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 ├── compactdb │ └── compactdb.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 ├── devnullfile │ └── devnull.go ├── errlock │ └── errlock.go ├── eventid │ └── eventid.go ├── fast │ ├── buffer.go │ └── buffer_test.go ├── iodb │ └── io_db.go ├── ioread │ └── ioread.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 ├── signers │ ├── gsignercache │ │ └── global_cache.go │ └── internaltx │ │ ├── internaltx.go │ │ └── internaltx_test.go ├── spin_lock.go ├── switchable │ ├── snapshot.go │ └── snapshot_test.go ├── to_ftm.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 @integraloleg @devintegral @devintegral2 @SamuelMarks @Maxime2 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/release_build.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | 15 | - name: Golang dependency 16 | uses: actions/setup-go@v3 17 | with: 18 | go-version: '^1.18' 19 | 20 | - name: Build 21 | run: make opera 22 | 23 | - name: Release 24 | uses: ncipollo/release-action@v1 25 | with: 26 | artifacts: "./build/opera" 27 | token: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/test_build.yml: -------------------------------------------------------------------------------- 1 | name: Check build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | check-build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v3 11 | 12 | - name: Golang dependency 13 | uses: actions/setup-go@v3 14 | with: 15 | go-version: '^1.18' 16 | 17 | - name: Build 18 | run: make opera 19 | 20 | - name: Publish 21 | uses: actions/upload-artifact@v2 22 | with: 23 | name: opera 24 | path: ./build/opera 25 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - make clean proto vendor 4 | builds: 5 | - main: ./cmd/opera/main.go 6 | binary: go-opera 7 | ldflags: 8 | - -linkmode external -extldflags -static -s -w 9 | - -X main.gitCommit={{ .ShortCommit }} 10 | env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - linux 14 | goarch: 15 | - amd64 16 | archive: 17 | replacements: 18 | darwin: Darwin 19 | linux: Linux 20 | windows: Windows 21 | 386: i386 22 | amd64: x86_64 23 | checksum: 24 | name_template: 'checksums.txt' 25 | snapshot: 26 | name_template: "{{ .Tag }}-next" 27 | changelog: 28 | sort: asc 29 | filters: 30 | exclude: 31 | - '^docs:' 32 | - '^test:' 33 | nfpm: 34 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" 35 | replacements: 36 | amd64: 64-bit 37 | 386: 32-bit 38 | darwin: macOS 39 | linux: Tux 40 | 41 | vendor: Fanton Foundation 42 | homepage: https://fantom.foundation 43 | maintainer: Samuel Marks 44 | description: BFT Consensus platform for distributed applications. 45 | license: MIT 46 | 47 | formats: 48 | - deb 49 | - rpm 50 | 51 | empty_folders: 52 | - /var/log/go-opera 53 | 54 | files: 55 | "scripts/daemon/go-opera.service": "/lib/systemd/system/go-opera.service" 56 | 57 | # scripts: 58 | # preinstall: "scripts/preinstall.bash" 59 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## UNRELEASED 2 | 3 | SECURITY: 4 | 5 | FEATURES: 6 | 7 | IMPROVEMENTS: 8 | 9 | BUG FIXES: 10 | 11 | ## v0.4.0 (October 14, 2018) 12 | 13 | SECURITY: 14 | 15 | * keygen: write keys to files instead of tty. 16 | 17 | FEATURES: 18 | 19 | * proxy: Introduced in-memory proxy. 20 | * cmd: Enable reading config from file (lachesis.toml, .json, or .yaml) 21 | 22 | IMPROVEMENTS: 23 | 24 | * node: major refactoring of configuration and initialization of Lachesis node. 25 | * node: Node ID is calculated from public key rather than from sorting the 26 | peers.json file. 27 | 28 | ## v0.3.0 (September 4, 2018) 29 | 30 | FEATURES: 31 | 32 | * poset: Replaced Leemon Baird's original "Fair" ordering method with 33 | Lamport timestamps. 34 | * poset: Introduced the concept of Frames and Roots to enable initializing a 35 | poset from a "non-zero" state. 36 | * node: Added FastSync protocol to enable nodes to catch up with other nodes 37 | without downloading the entire poset. 38 | * proxy: Introduce Snapshot/Restore functionality. 39 | 40 | IMPROVEMENTS: 41 | 42 | * poset: Refactored the consensus methods around the concept of Frames. 43 | * poset: Removed special case for "initial" Events, and make use of Roots 44 | instead. 45 | * docs: Added sections on Lachesis and FastSync. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: opera 3 | 4 | GOPROXY ?= "https://proxy.golang.org,direct" 5 | .PHONY: opera 6 | opera: 7 | GIT_COMMIT=`git rev-list -1 HEAD 2>/dev/null || echo ""` && \ 8 | GIT_DATE=`git log -1 --date=short --pretty=format:%ct 2>/dev/null || echo ""` && \ 9 | GOPROXY=$(GOPROXY) \ 10 | go build \ 11 | -ldflags "-s -w -X github.com/Fantom-foundation/go-opera/cmd/opera/launcher.gitCommit=$${GIT_COMMIT} -X github.com/Fantom-foundation/go-opera/cmd/opera/launcher.gitDate=$${GIT_DATE}" \ 12 | -o build/opera \ 13 | ./cmd/opera 14 | 15 | 16 | TAG ?= "latest" 17 | .PHONY: opera-image 18 | opera-image: 19 | docker build \ 20 | --network=host \ 21 | -f ./docker/Dockerfile.opera -t "opera:$(TAG)" . 22 | 23 | .PHONY: test 24 | test: 25 | go test ./... 26 | 27 | .PHONY: coverage 28 | coverage: 29 | go test -coverprofile=cover.prof $$(go list ./... | grep -v '/gossip/contract/' | grep -v '/gossip/emitter/mock' | xargs) 30 | go tool cover -func cover.prof | grep -e "^total:" 31 | 32 | .PHONY: fuzz 33 | fuzz: 34 | CGO_ENABLED=1 \ 35 | mkdir -p ./fuzzing && \ 36 | go run github.com/dvyukov/go-fuzz/go-fuzz-build -o=./fuzzing/gossip-fuzz.zip ./gossip && \ 37 | go run github.com/dvyukov/go-fuzz/go-fuzz -workdir=./fuzzing -bin=./fuzzing/gossip-fuzz.zip 38 | 39 | 40 | .PHONY: clean 41 | clean: 42 | rm -fr ./build/* 43 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/check.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 7 | "github.com/ethereum/go-ethereum/cmd/utils" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/log" 10 | "gopkg.in/urfave/cli.v1" 11 | 12 | "github.com/Fantom-foundation/go-opera/inter" 13 | ) 14 | 15 | func checkEvm(ctx *cli.Context) error { 16 | if len(ctx.Args()) != 0 { 17 | utils.Fatalf("This command doesn't require an argument.") 18 | } 19 | 20 | cfg := makeAllConfigs(ctx) 21 | 22 | rawDbs := makeDirectDBsProducer(cfg) 23 | gdb := makeGossipStore(rawDbs, cfg) 24 | defer gdb.Close() 25 | evms := gdb.EvmStore() 26 | 27 | start, reported := time.Now(), time.Now() 28 | 29 | var prevPoint idx.Block 30 | var prevIndex idx.Block 31 | checkBlocks := func(stateOK func(root common.Hash) (bool, error)) { 32 | var ( 33 | lastIdx = gdb.GetLatestBlockIndex() 34 | prevPointRootExist bool 35 | ) 36 | gdb.ForEachBlock(func(index idx.Block, block *inter.Block) { 37 | prevIndex = index 38 | found, err := stateOK(common.Hash(block.Root)) 39 | if found != prevPointRootExist { 40 | if index > 0 && found { 41 | log.Warn("EVM history is pruned", "fromBlock", prevPoint, "toBlock", index-1) 42 | } 43 | prevPointRootExist = found 44 | prevPoint = index 45 | } 46 | if index == lastIdx && !found { 47 | log.Crit("State trie for the latest block is not found", "block", index) 48 | } 49 | if !found { 50 | return 51 | } 52 | if err != nil { 53 | log.Crit("State trie error", "err", err, "block", index) 54 | } 55 | if time.Since(reported) >= statsReportLimit { 56 | log.Info("Checking presence of every node", "last", index, "pruned", !prevPointRootExist, "elapsed", common.PrettyDuration(time.Since(start))) 57 | reported = time.Now() 58 | } 59 | }) 60 | } 61 | 62 | if err := evms.CheckEvm(checkBlocks, true); err != nil { 63 | return err 64 | } 65 | log.Info("EVM storage is verified", "last", prevIndex, "elapsed", common.PrettyDuration(time.Since(start))) 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /cmd/opera/launcher/config_custom.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/p2p/enode" 7 | "github.com/naoina/toml" 8 | "github.com/naoina/toml/ast" 9 | ) 10 | 11 | // asDefault is slice with one empty element 12 | // which indicates that network default bootnodes should be substituted 13 | var asDefault = []*enode.Node{{}} 14 | 15 | func needDefaultBootnodes(nn []*enode.Node) bool { 16 | return len(nn) == len(asDefault) && nn[0] == asDefault[0] 17 | } 18 | 19 | func isBootstrapNodesDefault(root *ast.Table) ( 20 | bootstrapNodes bool, 21 | bootstrapNodesV5 bool, 22 | ) { 23 | table := root 24 | for _, path := range []string{"Node", "P2P"} { 25 | val, ok := table.Fields[path] 26 | if !ok { 27 | return 28 | } 29 | table = val.(*ast.Table) 30 | } 31 | 32 | emptyNode := fmt.Sprintf("\"%s\"", asDefault[0]) 33 | 34 | var res = map[string]bool{ 35 | "BootstrapNodes": false, 36 | "BootstrapNodesV5": false, 37 | } 38 | for name := range res { 39 | if val, ok := table.Fields[name]; ok { 40 | kv := val.(*ast.KeyValue) 41 | arr := kv.Value.(*ast.Array) 42 | if len(arr.Value) == len(asDefault) && arr.Value[0].Source() == emptyNode { 43 | res[name] = true 44 | delete(table.Fields, name) 45 | } 46 | } 47 | } 48 | bootstrapNodes = res["BootstrapNodes"] 49 | bootstrapNodesV5 = res["BootstrapNodesV5"] 50 | 51 | return 52 | } 53 | 54 | // UnmarshalTOML implements toml.Unmarshaler. 55 | func (c *config) UnmarshalTOML(input []byte) error { 56 | ast, err := toml.Parse(input) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | defaultBootstrapNodes, defaultBootstrapNodesV5 := isBootstrapNodesDefault(ast) 62 | 63 | type rawCfg config 64 | var raw = rawCfg(*c) 65 | err = toml.UnmarshalTable(ast, &raw) 66 | if err != nil { 67 | return err 68 | } 69 | *c = config(raw) 70 | 71 | if defaultBootstrapNodes { 72 | c.Node.P2P.BootstrapNodes = asDefault 73 | } 74 | if defaultBootstrapNodesV5 { 75 | c.Node.P2P.BootstrapNodesV5 = asDefault 76 | } 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /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/atomic" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | "github.com/ethereum/go-ethereum/metrics" 10 | ) 11 | 12 | var ( 13 | // TODO: refactor it 14 | dbDir atomic.Value 15 | dbSizeMetric = metrics.NewRegisteredFunctionalGauge("db_size", nil, measureDbDir) 16 | ) 17 | 18 | func SetDataDir(datadir string) { 19 | dbDir.Store(datadir) 20 | } 21 | 22 | func measureDbDir() (size int64) { 23 | datadir, ok := dbDir.Load().(string) 24 | if !ok || datadir == "" || datadir == "inmemory" { 25 | return 26 | } 27 | return sizeOfDir(datadir) 28 | } 29 | 30 | func sizeOfDir(dir string) (size int64) { 31 | err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 32 | if err != nil { 33 | log.Debug("datadir walk", "path", path, "err", err) 34 | return filepath.SkipDir 35 | } 36 | 37 | if info.IsDir() { 38 | return nil 39 | } 40 | 41 | dst, err := filepath.EvalSymlinks(path) 42 | if err == nil && dst != path { 43 | size += sizeOfDir(dst) 44 | } else { 45 | size += info.Size() 46 | } 47 | 48 | return nil 49 | }) 50 | 51 | if err != nil { 52 | log.Debug("datadir walk", "path", dir, "err", err) 53 | } 54 | 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /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/Fantom-foundation/go-opera/e529a4e7317e2f02e284c194677b301bb640cd73/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/Fantom-foundation/go-opera/e529a4e7317e2f02e284c194677b301bb640cd73/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/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/validator.go: -------------------------------------------------------------------------------- 1 | package launcher 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | cli "gopkg.in/urfave/cli.v1" 6 | 7 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 8 | 9 | "github.com/Fantom-foundation/go-opera/gossip/emitter" 10 | "github.com/Fantom-foundation/go-opera/integration/makefakegenesis" 11 | "github.com/Fantom-foundation/go-opera/inter/validatorpk" 12 | ) 13 | 14 | var validatorIDFlag = cli.UintFlag{ 15 | Name: "validator.id", 16 | Usage: "ID of a validator to create events from", 17 | Value: 0, 18 | } 19 | 20 | var validatorPubkeyFlag = cli.StringFlag{ 21 | Name: "validator.pubkey", 22 | Usage: "Public key of a validator to create events from", 23 | Value: "", 24 | } 25 | 26 | var validatorPasswordFlag = cli.StringFlag{ 27 | Name: "validator.password", 28 | Usage: "Password to unlock validator private key", 29 | Value: "", 30 | } 31 | 32 | // setValidatorID retrieves the validator ID either from the directly specified 33 | // command line flags or from the keystore if CLI indexed. 34 | func setValidator(ctx *cli.Context, cfg *emitter.Config) error { 35 | // Extract the current validator address, new flag overriding legacy one 36 | if ctx.GlobalIsSet(FakeNetFlag.Name) { 37 | id, num, err := parseFakeGen(ctx.GlobalString(FakeNetFlag.Name)) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | if ctx.GlobalIsSet(validatorIDFlag.Name) && id != 0 { 43 | return errors.New("specified validator ID with both --fakenet and --validator.id") 44 | } 45 | 46 | cfg.Validator.ID = id 47 | validators := makefakegenesis.GetFakeValidators(num) 48 | cfg.Validator.PubKey = validators.Map()[cfg.Validator.ID].PubKey 49 | } 50 | 51 | if ctx.GlobalIsSet(validatorIDFlag.Name) { 52 | cfg.Validator.ID = idx.ValidatorID(ctx.GlobalInt(validatorIDFlag.Name)) 53 | } 54 | 55 | if ctx.GlobalIsSet(validatorPubkeyFlag.Name) { 56 | pk, err := validatorpk.FromString(ctx.GlobalString(validatorPubkeyFlag.Name)) 57 | if err != nil { 58 | return err 59 | } 60 | cfg.Validator.PubKey = pk 61 | } 62 | 63 | if cfg.Validator.ID != 0 && cfg.Validator.PubKey.Empty() { 64 | return errors.New("validator public key is not set") 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /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 | // +build go1.6 18 | 19 | package debug 20 | 21 | import "runtime/debug" 22 | 23 | // LoudPanic panics in a way that gets all goroutine stacks printed on stderr. 24 | func LoudPanic(x interface{}) { 25 | debug.SetTraceback("all") 26 | panic(x) 27 | } 28 | -------------------------------------------------------------------------------- /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 | // +build !go1.6 18 | 19 | package debug 20 | 21 | // LoudPanic panics in a way that gets all goroutine stacks printed on stderr. 22 | func LoudPanic(x interface{}) { 23 | panic(x) 24 | } 25 | -------------------------------------------------------------------------------- /debug/trace.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 | //+build go1.5 18 | 19 | package debug 20 | 21 | import ( 22 | "errors" 23 | "os" 24 | "runtime/trace" 25 | 26 | "github.com/ethereum/go-ethereum/log" 27 | ) 28 | 29 | // StartGoTrace turns on tracing, writing to the given file. 30 | func (h *HandlerT) StartGoTrace(file string) error { 31 | h.mu.Lock() 32 | defer h.mu.Unlock() 33 | if h.traceW != nil { 34 | return errors.New("trace already in progress") 35 | } 36 | f, err := os.Create(expandHome(file)) 37 | if err != nil { 38 | return err 39 | } 40 | if err := trace.Start(f); err != nil { 41 | f.Close() 42 | return err 43 | } 44 | h.traceW = f 45 | h.traceFile = file 46 | log.Info("Go tracing started", "dump", h.traceFile) 47 | return nil 48 | } 49 | 50 | // StopTrace stops an ongoing trace. 51 | func (h *HandlerT) StopGoTrace() error { 52 | h.mu.Lock() 53 | defer h.mu.Unlock() 54 | trace.Stop() 55 | if h.traceW == nil { 56 | return errors.New("trace not in progress") 57 | } 58 | log.Info("Done writing Go trace", "dump", h.traceFile) 59 | h.traceW.Close() 60 | h.traceW = nil 61 | h.traceFile = "" 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /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 | //+build !go1.5 18 | 19 | // no-op implementation of tracing methods for Go < 1.5. 20 | 21 | package debug 22 | 23 | import "errors" 24 | 25 | func (*HandlerT) StartGoTrace(string) error { 26 | return errors.New("tracing is not supported on Go < 1.5") 27 | } 28 | 29 | func (*HandlerT) StopGoTrace() error { 30 | return errors.New("tracing is not supported on Go < 1.5") 31 | } 32 | -------------------------------------------------------------------------------- /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 | --verbosity=3 --tracing >> opera$i.log 2>&1)& 29 | 30 | echo -e "\tnode$i ok" 31 | done 32 | 33 | echo -e "\nConnect nodes to ring:\n" 34 | for ((i=0;i<$N;i+=1)) 35 | do 36 | for ((n=0;n<$M;n+=1)) 37 | do 38 | j=$(((i+n+1) % N)) 39 | 40 | enode=$(attach_and_exec $j 'admin.nodeInfo.enode') 41 | echo " p2p address = ${enode}" 42 | 43 | echo " connecting node-$i to node-$j:" 44 | res=$(attach_and_exec $i "admin.addPeer(${enode})") 45 | echo " result = ${res}" 46 | done 47 | done 48 | -------------------------------------------------------------------------------- /demo/stop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | killall demo_opera 4 | -------------------------------------------------------------------------------- /docker/Dockerfile.opera: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-alpine as builder 2 | 3 | RUN apk add --no-cache make gcc musl-dev linux-headers git 4 | 5 | WORKDIR /go/go-opera 6 | COPY . . 7 | 8 | ARG GOPROXY 9 | RUN go mod download 10 | RUN make opera 11 | 12 | 13 | 14 | FROM alpine:latest 15 | 16 | RUN apk add --no-cache ca-certificates 17 | 18 | COPY --from=builder /go/go-opera/build/opera / 19 | 20 | EXPOSE 5050 18545 18546 18547 19090 21 | 22 | ENTRYPOINT ["/opera"] 23 | -------------------------------------------------------------------------------- /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/interface.go: -------------------------------------------------------------------------------- 1 | package blockproc 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 5 | "github.com/ethereum/go-ethereum/core/state" 6 | "github.com/ethereum/go-ethereum/core/types" 7 | "github.com/ethereum/go-ethereum/params" 8 | 9 | "github.com/Fantom-foundation/go-opera/evmcore" 10 | "github.com/Fantom-foundation/go-opera/inter" 11 | "github.com/Fantom-foundation/go-opera/inter/iblockproc" 12 | "github.com/Fantom-foundation/go-opera/opera" 13 | ) 14 | 15 | type TxListener interface { 16 | OnNewLog(*types.Log) 17 | OnNewReceipt(tx *types.Transaction, r *types.Receipt, originator idx.ValidatorID) 18 | Finalize() iblockproc.BlockState 19 | Update(bs iblockproc.BlockState, es iblockproc.EpochState) 20 | } 21 | 22 | type TxListenerModule interface { 23 | Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, statedb *state.StateDB) TxListener 24 | } 25 | 26 | type TxTransactor interface { 27 | PopInternalTxs(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb *state.StateDB) types.Transactions 28 | } 29 | 30 | type SealerProcessor interface { 31 | EpochSealing() bool 32 | SealEpoch() (iblockproc.BlockState, iblockproc.EpochState) 33 | Update(bs iblockproc.BlockState, es iblockproc.EpochState) 34 | } 35 | 36 | type SealerModule interface { 37 | Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState) SealerProcessor 38 | } 39 | 40 | type ConfirmedEventsProcessor interface { 41 | ProcessConfirmedEvent(inter.EventI) 42 | Finalize(block iblockproc.BlockCtx, blockSkipped bool) iblockproc.BlockState 43 | } 44 | 45 | type ConfirmedEventsModule interface { 46 | Start(bs iblockproc.BlockState, es iblockproc.EpochState) ConfirmedEventsProcessor 47 | } 48 | 49 | type EVMProcessor interface { 50 | Execute(txs types.Transactions) types.Receipts 51 | Finalize() (evmBlock *evmcore.EvmBlock, skippedTxs []uint32, receipts types.Receipts) 52 | } 53 | 54 | type EVM interface { 55 | Start(block iblockproc.BlockCtx, statedb *state.StateDB, reader evmcore.DummyChain, onNewLog func(*types.Log), net opera.Rules, evmCfg *params.ChainConfig) EVMProcessor 56 | } 57 | -------------------------------------------------------------------------------- /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/emitter/parents.go: -------------------------------------------------------------------------------- 1 | package emitter 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/emitter/ancestor" 7 | "github.com/Fantom-foundation/lachesis-base/hash" 8 | "github.com/Fantom-foundation/lachesis-base/inter/idx" 9 | ) 10 | 11 | // buildSearchStrategies returns a strategy for each parent search 12 | func (em *Emitter) buildSearchStrategies(maxParents idx.Event) []ancestor.SearchStrategy { 13 | strategies := make([]ancestor.SearchStrategy, 0, maxParents) 14 | if maxParents == 0 { 15 | return strategies 16 | } 17 | payloadStrategy := em.payloadIndexer.SearchStrategy() 18 | for idx.Event(len(strategies)) < 1 { 19 | strategies = append(strategies, payloadStrategy) 20 | } 21 | randStrategy := ancestor.NewRandomStrategy(nil) 22 | for idx.Event(len(strategies)) < maxParents/2 { 23 | strategies = append(strategies, randStrategy) 24 | } 25 | quorumStrategy := em.quorumIndexer.SearchStrategy() 26 | for idx.Event(len(strategies)) < maxParents { 27 | strategies = append(strategies, quorumStrategy) 28 | } 29 | return strategies 30 | } 31 | 32 | // chooseParents selects an "optimal" parents set for the validator 33 | func (em *Emitter) chooseParents(epoch idx.Epoch, myValidatorID idx.ValidatorID) (*hash.Event, hash.Events, bool) { 34 | selfParent := em.world.GetLastEvent(epoch, myValidatorID) 35 | heads := em.world.GetHeads(epoch) // events with no descendants 36 | 37 | if selfParent != nil && len(em.world.DagIndex().NoCheaters(selfParent, hash.Events{*selfParent})) == 0 { 38 | em.Periodic.Error(time.Second, "Events emitting isn't allowed due to the doublesign", "validator", myValidatorID) 39 | return nil, nil, false 40 | } 41 | 42 | var parents hash.Events 43 | if selfParent != nil { 44 | parents = hash.Events{*selfParent} 45 | } 46 | parents = ancestor.ChooseParents(parents, heads, em.buildSearchStrategies(em.maxParents-idx.Event(len(parents)))) 47 | return selfParent, parents, true 48 | } 49 | -------------------------------------------------------------------------------- /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" 5 | "github.com/Fantom-foundation/lachesis-base/kvdb/batched" 6 | 7 | "github.com/Fantom-foundation/go-opera/opera/genesis" 8 | ) 9 | 10 | // ApplyGenesis writes initial state. 11 | func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) { 12 | batch := s.EvmDb.NewBatch() 13 | defer batch.Reset() 14 | g.RawEvmItems.ForEach(func(key, value []byte) bool { 15 | if err != nil { 16 | return false 17 | } 18 | err = batch.Put(key, value) 19 | if err != nil { 20 | return false 21 | } 22 | if batch.ValueSize() > kvdb.IdealBatchSize { 23 | err = batch.Write() 24 | if err != nil { 25 | return false 26 | } 27 | batch.Reset() 28 | } 29 | return true 30 | }) 31 | if err != nil { 32 | return err 33 | } 34 | return batch.Write() 35 | } 36 | 37 | func (s *Store) WrapTablesAsBatched() (unwrap func()) { 38 | origTables := s.table 39 | 40 | batchedTxs := batched.Wrap(s.table.Txs) 41 | s.table.Txs = batchedTxs 42 | 43 | batchedTxPositions := batched.Wrap(s.table.TxPositions) 44 | s.table.TxPositions = batchedTxPositions 45 | 46 | unwrapLogs := s.EvmLogs.WrapTablesAsBatched() 47 | 48 | batchedReceipts := batched.Wrap(s.table.Receipts) 49 | s.table.Receipts = batchedReceipts 50 | return func() { 51 | _ = batchedTxs.Flush() 52 | _ = batchedTxPositions.Flush() 53 | _ = batchedReceipts.Flush() 54 | unwrapLogs() 55 | s.table = origTables 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gossip/evmstore/config.go: -------------------------------------------------------------------------------- 1 | package evmstore 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/utils/cachescale" 5 | "github.com/syndtr/goleveldb/leveldb/opt" 6 | ) 7 | 8 | type ( 9 | // StoreCacheConfig is a config for the db. 10 | StoreCacheConfig struct { 11 | // Cache size for Receipts (size in bytes). 12 | ReceiptsSize uint 13 | // Cache size for Receipts (number of blocks). 14 | ReceiptsBlocks int 15 | // Cache size for TxPositions. 16 | TxPositions int 17 | // Cache size for EVM database. 18 | EvmDatabase int 19 | // Cache size for EVM snapshot. 20 | EvmSnap int 21 | // Cache size for EvmBlock (number of blocks). 22 | EvmBlocksNum int 23 | // Cache size for EvmBlock (size in bytes). 24 | EvmBlocksSize uint 25 | // Disk journal for saving clean cache entries. 26 | TrieCleanJournal string 27 | // Whether to disable trie write caching and GC altogether (archive node) 28 | TrieDirtyDisabled bool 29 | // Memory limit (MB) at which to start flushing dirty trie nodes to disk 30 | TrieDirtyLimit uint 31 | // Whether to enable greedy gc mode 32 | GreedyGC bool 33 | } 34 | // StoreConfig is a config for store db. 35 | StoreConfig struct { 36 | Cache StoreCacheConfig 37 | // Enables tracking of SHA3 preimages in the VM 38 | EnablePreimageRecording bool 39 | } 40 | ) 41 | 42 | // DefaultStoreConfig for product. 43 | func DefaultStoreConfig(scale cachescale.Func) StoreConfig { 44 | return StoreConfig{ 45 | Cache: StoreCacheConfig{ 46 | ReceiptsSize: scale.U(4 * opt.MiB), 47 | ReceiptsBlocks: scale.I(4000), 48 | TxPositions: scale.I(20000), 49 | EvmDatabase: scale.I(32 * opt.MiB), 50 | EvmSnap: scale.I(32 * opt.MiB), 51 | EvmBlocksNum: scale.I(5000), 52 | EvmBlocksSize: scale.U(6 * opt.MiB), 53 | TrieDirtyDisabled: true, 54 | GreedyGC: false, 55 | TrieDirtyLimit: scale.U(256 * opt.MiB), 56 | }, 57 | EnablePreimageRecording: true, 58 | } 59 | } 60 | 61 | // LiteStoreConfig is for tests or inmemory. 62 | func LiteStoreConfig() StoreConfig { 63 | return DefaultStoreConfig(cachescale.Ratio{Base: 10, Target: 1}) 64 | } 65 | -------------------------------------------------------------------------------- /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 | // +build !windows,!openbsd 18 | 19 | package integration 20 | 21 | import ( 22 | "fmt" 23 | 24 | "golang.org/x/sys/unix" 25 | ) 26 | 27 | func getFreeDiskSpace(path string) (uint64, error) { 28 | var stat unix.Statfs_t 29 | if err := unix.Statfs(path, &stat); err != nil { 30 | return 0, fmt.Errorf("failed to call Statfs: %v", err) 31 | } 32 | 33 | // Available blocks * size per block = available space in bytes 34 | var bavail = stat.Bavail 35 | if stat.Bavail < 0 { 36 | // FreeBSD can have a negative number of blocks available 37 | // because of the grace limit. 38 | bavail = 0 39 | } 40 | //nolint:unconvert 41 | return uint64(bavail) * uint64(stat.Bsize), nil 42 | } 43 | -------------------------------------------------------------------------------- /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 | // +build openbsd 18 | 19 | package integration 20 | 21 | import ( 22 | "fmt" 23 | 24 | "golang.org/x/sys/unix" 25 | ) 26 | 27 | func getFreeDiskSpace(path string) (uint64, error) { 28 | var stat unix.Statfs_t 29 | if err := unix.Statfs(path, &stat); err != nil { 30 | return 0, fmt.Errorf("failed to call Statfs: %v", err) 31 | } 32 | 33 | // Available blocks * size per block = available space in bytes 34 | var bavail = stat.F_bavail 35 | // Not sure if the following check is necessary for OpenBSD 36 | if stat.F_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.F_bsize), nil 43 | } 44 | -------------------------------------------------------------------------------- /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/routing.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/kvdb" 7 | "github.com/Fantom-foundation/lachesis-base/kvdb/cachedproducer" 8 | "github.com/Fantom-foundation/lachesis-base/kvdb/multidb" 9 | "github.com/Fantom-foundation/lachesis-base/kvdb/skipkeys" 10 | ) 11 | 12 | type RoutingConfig struct { 13 | Table map[string]multidb.Route 14 | } 15 | 16 | func MakeMultiProducer(rawProducers map[multidb.TypeName]kvdb.IterableDBProducer, scopedProducers map[multidb.TypeName]kvdb.FullDBProducer, cfg RoutingConfig) (kvdb.FullDBProducer, error) { 17 | cachedProducers := make(map[multidb.TypeName]kvdb.FullDBProducer) 18 | var flushID []byte 19 | var err error 20 | for typ, producer := range scopedProducers { 21 | flushID, err = producer.Initialize(rawProducers[typ].Names(), flushID) 22 | if err != nil { 23 | return nil, fmt.Errorf("failed to open existing databases: %v. Try to use 'db heal' to recover", err) 24 | } 25 | cachedProducers[typ] = cachedproducer.WrapAll(producer) 26 | } 27 | 28 | p, err := makeMultiProducer(cachedProducers, cfg) 29 | return p, err 30 | } 31 | 32 | func MakeDirectMultiProducer(rawProducers map[multidb.TypeName]kvdb.IterableDBProducer, cfg RoutingConfig) (kvdb.FullDBProducer, error) { 33 | dproducers := map[multidb.TypeName]kvdb.FullDBProducer{} 34 | for typ, producer := range rawProducers { 35 | dproducers[typ] = &DummyScopedProducer{producer} 36 | } 37 | return MakeMultiProducer(rawProducers, dproducers, cfg) 38 | } 39 | 40 | func makeMultiProducer(scopedProducers map[multidb.TypeName]kvdb.FullDBProducer, cfg RoutingConfig) (kvdb.FullDBProducer, error) { 41 | multi, err := multidb.NewProducer(scopedProducers, cfg.Table, TablesKey) 42 | if err != nil { 43 | return nil, fmt.Errorf("failed to construct multidb: %v", err) 44 | } 45 | 46 | err = multi.Verify() 47 | if err != nil { 48 | return nil, fmt.Errorf("incompatible chainstore DB layout: %v. Try to use 'db transform' to recover", err) 49 | } 50 | return skipkeys.WrapAllProducer(multi, MetadataPrefix), nil 51 | } 52 | -------------------------------------------------------------------------------- /integration/status.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "os" 5 | "path" 6 | ) 7 | 8 | func isInterrupted(chaindataDir string) bool { 9 | _, err := os.Stat(path.Join(chaindataDir, "unfinished")) 10 | return err == nil 11 | } 12 | 13 | func setGenesisProcessing(chaindataDir string) { 14 | f, _ := os.Create(path.Join(chaindataDir, "unfinished")) 15 | if f != nil { 16 | _ = f.Close() 17 | } 18 | } 19 | 20 | func setGenesisComplete(chaindataDir string) { 21 | _ = os.Remove(path.Join(chaindataDir, "unfinished")) 22 | } 23 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /inter/validatorpk/pubkey_test.go: -------------------------------------------------------------------------------- 1 | package validatorpk 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestFromString(t *testing.T) { 11 | require := require.New(t) 12 | exp := PubKey{ 13 | Type: Types.Secp256k1, 14 | Raw: common.FromHex("45b86101f804f3f4f2012ef31fff807e87de579a3faa7947d1b487a810e35dc2c3b6071ac465046634b5f4a8e09bf8e1f2e7eccb699356b9e6fd496ca4b1677d1"), 15 | } 16 | { 17 | got, err := FromString("c0045b86101f804f3f4f2012ef31fff807e87de579a3faa7947d1b487a810e35dc2c3b6071ac465046634b5f4a8e09bf8e1f2e7eccb699356b9e6fd496ca4b1677d1") 18 | require.NoError(err) 19 | require.Equal(exp, got) 20 | } 21 | { 22 | got, err := FromString("0xc0045b86101f804f3f4f2012ef31fff807e87de579a3faa7947d1b487a810e35dc2c3b6071ac465046634b5f4a8e09bf8e1f2e7eccb699356b9e6fd496ca4b1677d1") 23 | require.NoError(err) 24 | require.Equal(exp, got) 25 | } 26 | { 27 | _, err := FromString("") 28 | require.Error(err) 29 | } 30 | { 31 | _, err := FromString("0x") 32 | require.Error(err) 33 | } 34 | { 35 | _, err := FromString("-") 36 | require.Error(err) 37 | } 38 | } 39 | 40 | func TestString(t *testing.T) { 41 | require := require.New(t) 42 | pk := PubKey{ 43 | Type: Types.Secp256k1, 44 | Raw: common.FromHex("45b86101f804f3f4f2012ef31fff807e87de579a3faa7947d1b487a810e35dc2c3b6071ac465046634b5f4a8e09bf8e1f2e7eccb699356b9e6fd496ca4b1677d1"), 45 | } 46 | require.Equal("0xc0045b86101f804f3f4f2012ef31fff807e87de579a3faa7947d1b487a810e35dc2c3b6071ac465046634b5f4a8e09bf8e1f2e7eccb699356b9e6fd496ca4b1677d1", pk.String()) 47 | } 48 | -------------------------------------------------------------------------------- /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/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/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\":\"address\",\"name\":\"_sfc\",\"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, driverAuthAddr common.Address, driverAddr common.Address, evmWriterAddr common.Address, owner common.Address) []byte { 23 | data, _ := sAbi.Pack("initializeAll", utils.U64toBig(uint64(sealedEpoch)), totalSupply, sfcAddr, driverAuthAddr, driverAddr, evmWriterAddr, owner) 24 | return data 25 | } 26 | -------------------------------------------------------------------------------- /opera/contracts/netinit/network_initializer.go: -------------------------------------------------------------------------------- 1 | package netinit 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | ) 7 | 8 | // GetContractBin is NetworkInitializer contract genesis implementation bin code 9 | // Has to be compiled with flag bin-runtime 10 | // Built from opera-sfc c1d33c81f74abf82c0e22807f16e609578e10ad8, solc 0.5.17+commit.d19bba13.Emscripten.clang, optimize-runs 10000 11 | func GetContractBin() []byte { 12 | return hexutil.MustDecode("0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c80e151314610030575b600080fd5b610091600480360360e081101561004657600080fd5b5080359060208101359073ffffffffffffffffffffffffffffffffffffffff60408201358116916060810135821691608082013581169160a081013582169160c09091013516610093565b005b604080517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152848116602483015291519185169163485cc9559160448082019260009290919082900301818387803b15801561010c57600080fd5b505af1158015610120573d6000803e3d6000fd5b5050604080517fc0c53b8b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152878116602483015285811660448301529151918816935063c0c53b8b925060648082019260009290919082900301818387803b1580156101a557600080fd5b505af11580156101b9573d6000803e3d6000fd5b5050604080517f019e2729000000000000000000000000000000000000000000000000000000008152600481018b9052602481018a905273ffffffffffffffffffffffffffffffffffffffff888116604483015285811660648301529151918916935063019e2729925060848082019260009290919082900301818387803b15801561024457600080fd5b505af1158015610258573d6000803e3d6000fd5b50600092505050fffea265627a7a72315820335fde84a547de34d8d678665aeca278de801945c81fdc1ce389633b38fa0cef64736f6c63430005110032") 13 | } 14 | 15 | // ContractAddress is the NetworkInitializer contract address 16 | var ContractAddress = common.HexToAddress("0xd1005eed00000000000000000000000000000000") 17 | -------------------------------------------------------------------------------- /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 | "github.com/Fantom-foundation/lachesis-base/hash" 5 | 6 | "github.com/Fantom-foundation/go-opera/inter/ibr" 7 | "github.com/Fantom-foundation/go-opera/inter/ier" 8 | ) 9 | 10 | type ( 11 | Hashes map[string]hash.Hash 12 | Header struct { 13 | GenesisID hash.Hash 14 | NetworkID uint64 15 | NetworkName string 16 | } 17 | Blocks interface { 18 | ForEach(fn func(ibr.LlrIdxFullBlockRecord) bool) 19 | } 20 | Epochs interface { 21 | ForEach(fn func(ier.LlrIdxFullEpochRecord) bool) 22 | } 23 | EvmItems interface { 24 | ForEach(fn func(key, value []byte) bool) 25 | } 26 | Genesis struct { 27 | Header 28 | 29 | Blocks Blocks 30 | Epochs Epochs 31 | RawEvmItems EvmItems 32 | } 33 | ) 34 | 35 | func (hh Hashes) Includes(hh2 Hashes) bool { 36 | for n, h := range hh { 37 | if hh2[n] != h { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | 44 | func (hh Hashes) Equal(hh2 Hashes) bool { 45 | return hh.Includes(hh2) && hh2.Includes(hh) 46 | } 47 | 48 | func (h Header) Equal(h2 Header) bool { 49 | return h == h2 50 | } 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /topicsdb/key.go: -------------------------------------------------------------------------------- 1 | package topicsdb 2 | 3 | import ( 4 | "github.com/Fantom-foundation/lachesis-base/common/bigendian" 5 | "github.com/ethereum/go-ethereum/common" 6 | ) 7 | 8 | const ( 9 | uint8Size = 1 10 | uint64Size = 8 11 | hashSize = common.HashLength 12 | 13 | logrecKeySize = uint64Size + hashSize + uint64Size 14 | topicKeySize = hashSize + uint8Size + logrecKeySize 15 | otherKeySize = logrecKeySize + uint8Size 16 | ) 17 | 18 | type ( 19 | // ID of log record 20 | ID [logrecKeySize]byte 21 | ) 22 | 23 | func NewID(block uint64, tx common.Hash, logIndex uint) (id ID) { 24 | copy(id[:], uintToBytes(block)) 25 | copy(id[uint64Size:], tx.Bytes()) 26 | copy(id[uint64Size+hashSize:], uintToBytes(uint64(logIndex))) 27 | return 28 | } 29 | 30 | func (id *ID) Bytes() []byte { 31 | return (*id)[:] 32 | } 33 | 34 | func (id *ID) BlockNumber() uint64 { 35 | return bytesToUint((*id)[:uint64Size]) 36 | } 37 | 38 | func (id *ID) TxHash() (tx common.Hash) { 39 | copy(tx[:], (*id)[uint64Size:uint64Size+hashSize]) 40 | return 41 | } 42 | 43 | func (id *ID) Index() uint { 44 | return uint(bytesToUint( 45 | (*id)[uint64Size+hashSize : uint64Size+hashSize+uint64Size])) 46 | } 47 | 48 | func topicKey(topic common.Hash, pos uint8, logrec ID) []byte { 49 | key := make([]byte, 0, topicKeySize) 50 | 51 | key = append(key, topic.Bytes()...) 52 | key = append(key, posToBytes(pos)...) 53 | key = append(key, logrec.Bytes()...) 54 | 55 | return key 56 | } 57 | 58 | func posToBytes(pos uint8) []byte { 59 | return []byte{pos} 60 | } 61 | 62 | func bytesToPos(b []byte) uint8 { 63 | return uint8(b[0]) 64 | } 65 | 66 | func uintToBytes(n uint64) []byte { 67 | return bigendian.Uint64ToBytes(n) 68 | } 69 | 70 | func bytesToUint(b []byte) uint64 { 71 | return bigendian.BytesToUint64(b) 72 | } 73 | 74 | func extractLogrecID(key []byte) (id ID) { 75 | switch len(key) { 76 | case topicKeySize: 77 | copy(id[:], key[hashSize+uint8Size:]) 78 | return 79 | default: 80 | panic("wrong key type") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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/Fantom-foundation/lachesis-base/kvdb/memorydb" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/core/types" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func BenchmarkSearch(b *testing.B) { 15 | topics, recs, topics4rec := genTestData(1000) 16 | 17 | mem := memorydb.NewProducer("") 18 | index := New(mem) 19 | 20 | for _, rec := range recs { 21 | err := index.Push(rec) 22 | require.NoError(b, err) 23 | } 24 | 25 | var query [][][]common.Hash 26 | for i := 0; i < len(topics); i++ { 27 | from, to := topics4rec(i) 28 | tt := topics[from : to-1] 29 | 30 | qq := make([][]common.Hash, len(tt)) 31 | for pos, t := range tt { 32 | qq[pos] = []common.Hash{t} 33 | } 34 | 35 | query = append(query, qq) 36 | } 37 | 38 | for dsc, method := range map[string]func(context.Context, idx.Block, idx.Block, [][]common.Hash) ([]*types.Log, error){ 39 | "sync": index.FindInBlocks, 40 | "async": index.FindInBlocksAsync, 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 | type Set []byte 4 | 5 | func New(max int) Set { 6 | l := max / 8 7 | if max%8 != 0 { 8 | l++ 9 | } 10 | return make(Set, l) 11 | } 12 | 13 | func (s Set) Put(i int) { 14 | yi := i / 8 15 | bi := i % 8 16 | s[yi] |= 1 << bi 17 | } 18 | 19 | func (s Set) Del(i int) { 20 | yi := i / 8 21 | bi := i % 8 22 | s[yi] &= ^(1 << bi) 23 | } 24 | 25 | func (s Set) Has(i int) bool { 26 | yi := i / 8 27 | bi := i % 8 28 | return (s[yi] & (1 << bi)) != 0 29 | } 30 | -------------------------------------------------------------------------------- /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/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/errlock/errlock.go: -------------------------------------------------------------------------------- 1 | package errlock 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | 9 | "github.com/ethereum/go-ethereum/cmd/utils" 10 | ) 11 | 12 | // Check if errlock is written 13 | func Check() { 14 | locked, reason, eLockPath, _ := read(datadir) 15 | if locked { 16 | utils.Fatalf("Node isn't allowed to start due to a previous error. Please fix the issue and then delete file \"%s\". Error message:\n%s", eLockPath, reason) 17 | } 18 | } 19 | 20 | var ( 21 | datadir string 22 | ) 23 | 24 | // SetDefaultDatadir for errlock files 25 | func SetDefaultDatadir(dir string) { 26 | datadir = dir 27 | } 28 | 29 | // Permanent error 30 | func Permanent(err error) { 31 | eLockPath, _ := write(datadir, err.Error()) 32 | utils.Fatalf("Node is permanently stopping due to an issue. Please fix the issue and then delete file \"%s\". Error message:\n%s", eLockPath, err.Error()) 33 | } 34 | 35 | func readAll(reader io.Reader, max int) ([]byte, error) { 36 | buf := make([]byte, max) 37 | consumed := 0 38 | for { 39 | n, err := reader.Read(buf[consumed:]) 40 | consumed += n 41 | if consumed == len(buf) || err == io.EOF { 42 | return buf[:consumed], nil 43 | } 44 | if err != nil { 45 | return nil, err 46 | } 47 | } 48 | } 49 | 50 | // read errlock file 51 | func read(dir string) (bool, string, string, error) { 52 | eLockPath := path.Join(dir, "errlock") 53 | 54 | data, err := os.Open(eLockPath) 55 | if err != nil { 56 | return false, "", eLockPath, err 57 | } 58 | defer data.Close() 59 | 60 | // read no more than N bytes 61 | maxFileLen := 5000 62 | eLockBytes, err := readAll(data, maxFileLen) 63 | if err != nil { 64 | return true, "", eLockPath, err 65 | } 66 | return true, string(eLockBytes), eLockPath, nil 67 | } 68 | 69 | // write errlock file 70 | func write(dir string, eLockStr string) (string, error) { 71 | eLockPath := path.Join(dir, "errlock") 72 | 73 | return eLockPath, ioutil.WriteFile(eLockPath, []byte(eLockStr), 0666) // assume no custom encoding needed 74 | } 75 | -------------------------------------------------------------------------------- /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/fast/buffer_test.go: -------------------------------------------------------------------------------- 1 | package fast 2 | 3 | import ( 4 | "bytes" 5 | "math/rand" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestBuffer(t *testing.T) { 12 | const N = 100 13 | 14 | var ( 15 | w *Writer 16 | r *Reader 17 | bb = []byte{0, 0, 0xFF, 9, 0} 18 | ) 19 | 20 | t.Run("Writer", func(t *testing.T) { 21 | require := require.New(t) 22 | 23 | w = NewWriter(make([]byte, 0, N/2)) 24 | for i := byte(0); i < N; i++ { 25 | w.WriteByte(i) 26 | } 27 | require.Equal(N, len(w.Bytes())) 28 | w.Write(bb) 29 | require.Equal(N+len(bb), len(w.Bytes())) 30 | }) 31 | 32 | t.Run("Reader", func(t *testing.T) { 33 | require := require.New(t) 34 | 35 | r = NewReader(w.Bytes()) 36 | require.Equal(N+len(bb), len(r.Bytes())) 37 | require.False(r.Empty()) 38 | for exp := byte(0); exp < N; exp++ { 39 | got := r.ReadByte() 40 | require.Equal(exp, got) 41 | } 42 | require.Equal(N, r.Position()) 43 | got := r.Read(len(bb)) 44 | require.Equal(bb, got) 45 | require.True(r.Empty()) 46 | }) 47 | } 48 | 49 | func Benchmark(b *testing.B) { 50 | b.Run("Write", func(b *testing.B) { 51 | b.Run("Std", func(b *testing.B) { 52 | w := bytes.NewBuffer(make([]byte, 0, b.N)) 53 | for i := 0; i < b.N; i++ { 54 | w.WriteByte(byte(i)) 55 | } 56 | require.Equal(b, b.N, len(w.Bytes())) 57 | }) 58 | b.Run("Fast", func(b *testing.B) { 59 | w := NewWriter(make([]byte, 0, b.N)) 60 | for i := 0; i < b.N; i++ { 61 | w.WriteByte(byte(i)) 62 | } 63 | require.Equal(b, b.N, len(w.Bytes())) 64 | }) 65 | }) 66 | 67 | b.Run("Read", func(b *testing.B) { 68 | src := make([]byte, 1000) 69 | rand.Read(src) 70 | 71 | b.Run("Std", func(b *testing.B) { 72 | for i := 0; i < b.N; i++ { 73 | r := bytes.NewReader(src) 74 | for j := 0; j < len(src); j++ { 75 | _, _ = r.ReadByte() 76 | } 77 | } 78 | }) 79 | b.Run("Fast", func(b *testing.B) { 80 | for i := 0; i < b.N; i++ { 81 | r := NewReader(src) 82 | for j := 0; j < len(src); j++ { 83 | _ = r.ReadByte() 84 | } 85 | } 86 | }) 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /utils/iodb/io_db.go: -------------------------------------------------------------------------------- 1 | package iodb 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Fantom-foundation/lachesis-base/common/bigendian" 7 | "github.com/Fantom-foundation/lachesis-base/kvdb" 8 | 9 | "github.com/Fantom-foundation/go-opera/utils/ioread" 10 | ) 11 | 12 | func Write(writer io.Writer, it kvdb.Iterator) error { 13 | for it.Next() { 14 | _, err := writer.Write(bigendian.Uint32ToBytes(uint32(len(it.Key())))) 15 | if err != nil { 16 | return err 17 | } 18 | _, err = writer.Write(it.Key()) 19 | if err != nil { 20 | return err 21 | } 22 | _, err = writer.Write(bigendian.Uint32ToBytes(uint32(len(it.Value())))) 23 | if err != nil { 24 | return err 25 | } 26 | _, err = writer.Write(it.Value()) 27 | if err != nil { 28 | return err 29 | } 30 | } 31 | return nil 32 | } 33 | 34 | func NewIterator(reader io.Reader) kvdb.Iterator { 35 | return &Iterator{ 36 | reader: reader, 37 | } 38 | } 39 | 40 | type Iterator struct { 41 | reader io.Reader 42 | key, value []byte 43 | err error 44 | } 45 | 46 | func (it *Iterator) Next() bool { 47 | if it.err != nil { 48 | return false 49 | } 50 | var lenB [4]byte 51 | it.err = ioread.ReadAll(it.reader, lenB[:]) 52 | if it.err == io.EOF { 53 | it.err = nil 54 | return false 55 | } 56 | if it.err != nil { 57 | return false 58 | } 59 | 60 | lenKey := bigendian.BytesToUint32(lenB[:]) 61 | key := make([]byte, lenKey) 62 | it.err = ioread.ReadAll(it.reader, key) 63 | if it.err != nil { 64 | return false 65 | } 66 | 67 | it.err = ioread.ReadAll(it.reader, lenB[:]) 68 | if it.err != nil { 69 | return false 70 | } 71 | 72 | lenValue := bigendian.BytesToUint32(lenB[:]) 73 | value := make([]byte, lenValue) 74 | it.err = ioread.ReadAll(it.reader, value) 75 | if it.err != nil { 76 | return false 77 | } 78 | 79 | it.key = key 80 | it.value = value 81 | return true 82 | } 83 | 84 | func (it *Iterator) Error() error { 85 | return it.err 86 | } 87 | 88 | func (it *Iterator) Key() []byte { 89 | return it.key 90 | } 91 | 92 | func (it *Iterator) Value() []byte { 93 | return it.value 94 | } 95 | 96 | func (it *Iterator) Release() { 97 | it.reader = nil 98 | } 99 | -------------------------------------------------------------------------------- /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/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/migration/migration.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "crypto/sha256" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | ) 10 | 11 | // Migration is a migration step. 12 | type Migration struct { 13 | name string 14 | exec func() error 15 | prev *Migration 16 | } 17 | 18 | // Begin with empty unique migration step. 19 | func Begin(appName string) *Migration { 20 | return &Migration{ 21 | name: appName, 22 | } 23 | } 24 | 25 | // Next creates next migration. 26 | func (m *Migration) Next(name string, exec func() error) *Migration { 27 | if name == "" { 28 | panic("empty name") 29 | } 30 | 31 | if exec == nil { 32 | panic("empty exec") 33 | } 34 | 35 | return &Migration{ 36 | name: name, 37 | exec: exec, 38 | prev: m, 39 | } 40 | } 41 | 42 | func idOf(name string) string { 43 | digest := sha256.New() 44 | digest.Write([]byte(name)) 45 | 46 | bytes := digest.Sum(nil) 47 | return fmt.Sprintf("%x", bytes) 48 | } 49 | 50 | // ID is an uniq migration's id. 51 | func (m *Migration) ID() string { 52 | return idOf(m.name) 53 | } 54 | 55 | // Exec method run migrations chain in order 56 | func (m *Migration) Exec(curr IDStore, flush func() error) error { 57 | currID := curr.GetID() 58 | myID := m.ID() 59 | 60 | if m.veryFirst() { 61 | if currID != myID { 62 | return errors.New("unknown version: " + currID) 63 | } 64 | return nil 65 | } 66 | 67 | if currID == myID { 68 | return nil 69 | } 70 | 71 | err := m.prev.Exec(curr, flush) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | log.Warn("Applying migration", "name", m.name) 77 | err = m.exec() 78 | if err != nil { 79 | log.Error("'"+m.name+"' migration failed", "err", err) 80 | return err 81 | } 82 | 83 | curr.SetID(myID) 84 | 85 | return flush() 86 | } 87 | 88 | func (m *Migration) veryFirst() bool { 89 | return m.exec == nil 90 | } 91 | 92 | // IDs return list of migrations ids in chain 93 | func (m *Migration) IDs() []string { 94 | if m.prev == nil { 95 | return []string{m.ID()} 96 | } 97 | 98 | return append(m.prev.IDs(), m.ID()) 99 | } 100 | -------------------------------------------------------------------------------- /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/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/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)+".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/files_test.go: -------------------------------------------------------------------------------- 1 | package valkeystore 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path" 7 | "testing" 8 | 9 | "github.com/ethereum/go-ethereum/accounts/keystore" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/Fantom-foundation/go-opera/valkeystore/encryption" 13 | ) 14 | 15 | func TestFileKeystoreAdd(t *testing.T) { 16 | dir, err := ioutil.TempDir("", "valkeystore_test") 17 | if err != nil { 18 | return 19 | } 20 | defer os.RemoveAll(dir) 21 | 22 | require := require.New(t) 23 | keystore := NewFileKeystore(dir, encryption.New(keystore.LightScryptN, keystore.LightScryptP)) 24 | 25 | key, err := keystore.Get(pubkey1, "auth1") 26 | require.EqualError(err, ErrNotFound.Error()) 27 | require.Nil(key) 28 | 29 | err = keystore.Add(pubkey1, key1, "auth1") 30 | require.NoError(err) 31 | _, err = os.Stat(path.Join(dir, name1)) 32 | require.NoError(err) 33 | 34 | testGet(t, keystore, pubkey1, key1, "auth1") 35 | 36 | err = keystore.Add(pubkey2, key2, "auth2") 37 | require.NoError(err) 38 | _, err = os.Stat(path.Join(dir, name2)) 39 | require.NoError(err) 40 | 41 | testGet(t, keystore, pubkey1, key1, "auth1") 42 | testGet(t, keystore, pubkey2, key2, "auth2") 43 | 44 | err = keystore.Add(pubkey2, key2, "auth1") 45 | require.Error(err, ErrAlreadyExists.Error()) 46 | 47 | testGet(t, keystore, pubkey2, key2, "auth2") 48 | } 49 | 50 | func TestFileKeystoreRead(t *testing.T) { 51 | dir, err := ioutil.TempDir("", "valkeystore_test") 52 | if err != nil { 53 | return 54 | } 55 | defer os.RemoveAll(dir) 56 | 57 | require := require.New(t) 58 | keystore := NewFileKeystore(dir, encryption.New(keystore.LightScryptN, keystore.LightScryptP)) 59 | 60 | fd, err := os.Create(path.Join(dir, name1)) 61 | require.NoError(err) 62 | _, err = fd.Write(file1) 63 | require.NoError(err) 64 | 65 | testGet(t, keystore, pubkey1, key1, "auth1") 66 | 67 | fd, err = os.Create(path.Join(dir, name2)) 68 | require.NoError(err) 69 | _, err = fd.Write(file2) 70 | require.NoError(err) 71 | 72 | testGet(t, keystore, pubkey1, key1, "auth1") 73 | testGet(t, keystore, pubkey2, key2, "auth2") 74 | } 75 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/params" 8 | ) 9 | 10 | func init() { 11 | params.VersionMajor = 1 // Major version component of the current release 12 | params.VersionMinor = 1 // Minor version component of the current release 13 | params.VersionPatch = 2 // Patch version component of the current release 14 | params.VersionMeta = "rc.3" // Version metadata to append to the version string 15 | } 16 | 17 | func BigToString(b *big.Int) string { 18 | if len(b.Bytes()) > 8 { 19 | return "_malformed_version_" 20 | } 21 | return U64ToString(b.Uint64()) 22 | } 23 | 24 | func AsString() string { 25 | return ToString(uint16(params.VersionMajor), uint16(params.VersionMinor), uint16(params.VersionPatch)) 26 | } 27 | 28 | func AsU64() uint64 { 29 | return ToU64(uint16(params.VersionMajor), uint16(params.VersionMinor), uint16(params.VersionPatch)) 30 | } 31 | 32 | func AsBigInt() *big.Int { 33 | return new(big.Int).SetUint64(AsU64()) 34 | } 35 | 36 | func ToU64(vMajor, vMinor, vPatch uint16) uint64 { 37 | return uint64(vMajor)*1e12 + uint64(vMinor)*1e6 + uint64(vPatch) 38 | } 39 | 40 | func ToString(major, minor, patch uint16) string { 41 | return fmt.Sprintf("%d.%d.%d", major, minor, patch) 42 | } 43 | 44 | func U64ToString(v uint64) string { 45 | return ToString(uint16((v/1e12)%1e6), uint16((v/1e6)%1e6), uint16(v%1e6)) 46 | } 47 | -------------------------------------------------------------------------------- /version/version_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | type testcase struct { 11 | vMajor, vMinor, vPatch uint16 12 | result uint64 13 | str string 14 | } 15 | 16 | func TestAsBigInt(t *testing.T) { 17 | require := require.New(t) 18 | 19 | prev := testcase{0, 0, 0, 0, "0.0.0"} 20 | for _, next := range []testcase{ 21 | {0, 0, 1, 1, "0.0.1"}, 22 | {0, 0, 2, 2, "0.0.2"}, 23 | {0, 1, 0, 1000000, "0.1.0"}, 24 | {0, 1, math.MaxUint16, 1065535, "0.1.65535"}, 25 | {1, 0, 0, 1000000000000, "1.0.0"}, 26 | {1, 0, math.MaxUint16, 1000000065535, "1.0.65535"}, 27 | {1, 1, 0, 1000001000000, "1.1.0"}, 28 | {2, 9, 9, 2000009000009, "2.9.9"}, 29 | {3, 1, 0, 3000001000000, "3.1.0"}, 30 | {math.MaxUint16, math.MaxUint16, math.MaxUint16, 65535065535065535, "65535.65535.65535"}, 31 | } { 32 | a := ToU64(prev.vMajor, prev.vMinor, prev.vPatch) 33 | b := ToU64(next.vMajor, next.vMinor, next.vPatch) 34 | require.Equal(a, prev.result) 35 | require.Equal(b, next.result) 36 | require.Equal(U64ToString(a), prev.str) 37 | require.Equal(U64ToString(b), next.str) 38 | require.Greater(b, a) 39 | prev = next 40 | } 41 | } 42 | --------------------------------------------------------------------------------