Merge pull request #11569 from filecoin-project/release/v1.25.2

chore: merge `release/v1.25.2` into `releases`
This commit is contained in:
Aayush Rajasekaran 2024-01-11 10:33:31 -05:00 committed by GitHub
commit f51f83bfec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
260 changed files with 13506 additions and 1443 deletions

View File

@ -1,7 +1,7 @@
version: 2.1 version: 2.1
orbs: orbs:
aws-cli: circleci/aws-cli@1.3.2 aws-cli: circleci/aws-cli@4.1.1
docker: circleci/docker@2.1.4 docker: circleci/docker@2.3.0
executors: executors:
golang: golang:
@ -70,8 +70,6 @@ commands:
name: Restore parameters cache name: Restore parameters cache
keys: keys:
- 'v26-2k-lotus-params' - 'v26-2k-lotus-params'
paths:
- /var/tmp/filecoin-proof-parameters/
- run: ./lotus fetch-params 2048 - run: ./lotus fetch-params 2048
- save_cache: - save_cache:
name: Save parameters cache name: Save parameters cache
@ -96,6 +94,7 @@ commands:
git fetch --all git fetch --all
install-ubuntu-deps: install-ubuntu-deps:
steps: steps:
- run: sudo apt install curl ca-certificates gnupg
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt-get install ocl-icd-opencl-dev libhwloc-dev - run: sudo apt-get install ocl-icd-opencl-dev libhwloc-dev
check-go-version: check-go-version:
@ -143,9 +142,9 @@ jobs:
Run tests with gotestsum. Run tests with gotestsum.
working_directory: ~/lotus working_directory: ~/lotus
parameters: &test-params parameters: &test-params
executor: resource_class:
type: executor type: string
default: golang default: medium+
go-test-flags: go-test-flags:
type: string type: string
default: "-timeout 20m" default: "-timeout 20m"
@ -164,7 +163,14 @@ jobs:
type: string type: string
default: unit default: unit
description: Test suite name to report to CircleCI. description: Test suite name to report to CircleCI.
executor: << parameters.executor >> docker:
- image: cimg/go:1.20
environment:
LOTUS_HARMONYDB_HOSTS: yugabyte
- image: yugabytedb/yugabyte:2.18.0.0-b65
command: bin/yugabyted start --daemon=false
name: yugabyte
resource_class: << parameters.resource_class >>
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -182,6 +188,8 @@ jobs:
command: | command: |
mkdir -p /tmp/test-reports/<< parameters.suite >> mkdir -p /tmp/test-reports/<< parameters.suite >>
mkdir -p /tmp/test-artifacts mkdir -p /tmp/test-artifacts
dockerize -wait tcp://yugabyte:5433 -timeout 3m
env
gotestsum \ gotestsum \
--format standard-verbose \ --format standard-verbose \
--junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \ --junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \
@ -209,7 +217,9 @@ jobs:
Branch on github.com/filecoin-project/test-vectors to checkout and Branch on github.com/filecoin-project/test-vectors to checkout and
test with. If empty (the default) the commit defined by the git test with. If empty (the default) the commit defined by the git
submodule is used. submodule is used.
executor: << parameters.executor >> docker:
- image: cimg/go:1.20
resource_class: << parameters.resource_class >>
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -396,15 +406,14 @@ jobs:
Run golangci-lint. Run golangci-lint.
working_directory: ~/lotus working_directory: ~/lotus
parameters: parameters:
executor:
type: executor
default: golang
args: args:
type: string type: string
default: '' default: ''
description: | description: |
Arguments to pass to golangci-lint Arguments to pass to golangci-lint
executor: << parameters.executor >> docker:
- image: cimg/go:1.20
resource_class: medium+
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -575,7 +584,7 @@ workflows:
- build - build
suite: itest-deals_concurrent suite: itest-deals_concurrent
target: "./itests/deals_concurrent_test.go" target: "./itests/deals_concurrent_test.go"
executor: golang-2xl resource_class: 2xlarge
- test: - test:
name: test-itest-deals_invalid_utf8_label name: test-itest-deals_invalid_utf8_label
requires: requires:
@ -768,6 +777,18 @@ workflows:
- build - build
suite: itest-get_messages_in_ts suite: itest-get_messages_in_ts
target: "./itests/get_messages_in_ts_test.go" target: "./itests/get_messages_in_ts_test.go"
- test:
name: test-itest-harmonydb
requires:
- build
suite: itest-harmonydb
target: "./itests/harmonydb_test.go"
- test:
name: test-itest-harmonytask
requires:
- build
suite: itest-harmonytask
target: "./itests/harmonytask_test.go"
- test: - test:
name: test-itest-lite_migration name: test-itest-lite_migration
requires: requires:
@ -976,14 +997,14 @@ workflows:
- build - build
suite: itest-wdpost_worker_config suite: itest-wdpost_worker_config
target: "./itests/wdpost_worker_config_test.go" target: "./itests/wdpost_worker_config_test.go"
executor: golang-2xl resource_class: 2xlarge
- test: - test:
name: test-itest-worker name: test-itest-worker
requires: requires:
- build - build
suite: itest-worker suite: itest-worker
target: "./itests/worker_test.go" target: "./itests/worker_test.go"
executor: golang-2xl resource_class: 2xlarge
- test: - test:
name: test-itest-worker_upgrade name: test-itest-worker_upgrade
requires: requires:
@ -996,32 +1017,28 @@ workflows:
- build - build
suite: utest-unit-cli suite: utest-unit-cli
target: "./cli/... ./cmd/... ./api/..." target: "./cli/... ./cmd/... ./api/..."
resource_class: 2xlarge
get-params: true get-params: true
executor: golang-2xl
- test: - test:
name: test-unit-node name: test-unit-node
requires: requires:
- build - build
suite: utest-unit-node suite: utest-unit-node
target: "./node/..." target: "./node/..."
- test: - test:
name: test-unit-rest name: test-unit-rest
requires: requires:
- build - build
suite: utest-unit-rest suite: utest-unit-rest
target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./tools/..." target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./tools/..."
resource_class: 2xlarge
executor: golang-2xl
- test: - test:
name: test-unit-storage name: test-unit-storage
requires: requires:
- build - build
suite: utest-unit-storage suite: utest-unit-storage
target: "./storage/... ./extern/..." target: "./storage/... ./extern/..."
get-params: true
- test: - test:
go-test-flags: "-run=TestMulticoreSDR" go-test-flags: "-run=TestMulticoreSDR"
requires: requires:

View File

@ -10,11 +10,25 @@ import (
"text/template" "text/template"
) )
var GoVersion = "" // from init below. Ex: 1.19.7
//go:generate go run ./gen.go .. //go:generate go run ./gen.go ..
//go:embed template.yml //go:embed template.yml
var templateFile embed.FS var templateFile embed.FS
func init() {
b, err := os.ReadFile("../go.mod")
if err != nil {
panic("cannot find go.mod in parent folder")
}
for _, line := range strings.Split(string(b), "\n") {
if strings.HasPrefix(line, "go ") {
GoVersion = line[3:]
}
}
}
type ( type (
dirs = []string dirs = []string
suite = string suite = string
@ -111,6 +125,7 @@ func main() {
Networks []string Networks []string
ItestFiles []string ItestFiles []string
UnitSuites map[string]string UnitSuites map[string]string
GoVersion string
} }
in := data{ in := data{
Networks: []string{"mainnet", "butterflynet", "calibnet", "debug"}, Networks: []string{"mainnet", "butterflynet", "calibnet", "debug"},
@ -125,6 +140,7 @@ func main() {
} }
return ret return ret
}(), }(),
GoVersion: GoVersion,
} }
out, err := os.Create("./config.yml") out, err := os.Create("./config.yml")

View File

@ -1,7 +1,7 @@
version: 2.1 version: 2.1
orbs: orbs:
aws-cli: circleci/aws-cli@1.3.2 aws-cli: circleci/aws-cli@4.1.1
docker: circleci/docker@2.1.4 docker: circleci/docker@2.3.0
executors: executors:
golang: golang:
@ -70,8 +70,6 @@ commands:
name: Restore parameters cache name: Restore parameters cache
keys: keys:
- 'v26-2k-lotus-params' - 'v26-2k-lotus-params'
paths:
- /var/tmp/filecoin-proof-parameters/
- run: ./lotus fetch-params 2048 - run: ./lotus fetch-params 2048
- save_cache: - save_cache:
name: Save parameters cache name: Save parameters cache
@ -96,6 +94,7 @@ commands:
git fetch --all git fetch --all
install-ubuntu-deps: install-ubuntu-deps:
steps: steps:
- run: sudo apt install curl ca-certificates gnupg
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt-get install ocl-icd-opencl-dev libhwloc-dev - run: sudo apt-get install ocl-icd-opencl-dev libhwloc-dev
check-go-version: check-go-version:
@ -143,9 +142,9 @@ jobs:
Run tests with gotestsum. Run tests with gotestsum.
working_directory: ~/lotus working_directory: ~/lotus
parameters: &test-params parameters: &test-params
executor: resource_class:
type: executor type: string
default: golang default: medium+
go-test-flags: go-test-flags:
type: string type: string
default: "-timeout 20m" default: "-timeout 20m"
@ -164,7 +163,14 @@ jobs:
type: string type: string
default: unit default: unit
description: Test suite name to report to CircleCI. description: Test suite name to report to CircleCI.
executor: << parameters.executor >> docker:
- image: cimg/go:[[ .GoVersion]]
environment:
LOTUS_HARMONYDB_HOSTS: yugabyte
- image: yugabytedb/yugabyte:2.18.0.0-b65
command: bin/yugabyted start --daemon=false
name: yugabyte
resource_class: << parameters.resource_class >>
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -182,6 +188,8 @@ jobs:
command: | command: |
mkdir -p /tmp/test-reports/<< parameters.suite >> mkdir -p /tmp/test-reports/<< parameters.suite >>
mkdir -p /tmp/test-artifacts mkdir -p /tmp/test-artifacts
dockerize -wait tcp://yugabyte:5433 -timeout 3m
env
gotestsum \ gotestsum \
--format standard-verbose \ --format standard-verbose \
--junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \ --junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \
@ -209,7 +217,9 @@ jobs:
Branch on github.com/filecoin-project/test-vectors to checkout and Branch on github.com/filecoin-project/test-vectors to checkout and
test with. If empty (the default) the commit defined by the git test with. If empty (the default) the commit defined by the git
submodule is used. submodule is used.
executor: << parameters.executor >> docker:
- image: cimg/go:[[ .GoVersion]]
resource_class: << parameters.resource_class >>
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -396,15 +406,14 @@ jobs:
Run golangci-lint. Run golangci-lint.
working_directory: ~/lotus working_directory: ~/lotus
parameters: parameters:
executor:
type: executor
default: golang
args: args:
type: string type: string
default: '' default: ''
description: | description: |
Arguments to pass to golangci-lint Arguments to pass to golangci-lint
executor: << parameters.executor >> docker:
- image: cimg/go:[[ .GoVersion]]
resource_class: medium+
steps: steps:
- install-ubuntu-deps - install-ubuntu-deps
- attach_workspace: - attach_workspace:
@ -543,7 +552,7 @@ workflows:
suite: itest-[[ $name ]] suite: itest-[[ $name ]]
target: "./itests/[[ $file ]]" target: "./itests/[[ $file ]]"
[[- if or (eq $name "worker") (eq $name "deals_concurrent") (eq $name "wdpost_worker_config")]] [[- if or (eq $name "worker") (eq $name "deals_concurrent") (eq $name "wdpost_worker_config")]]
executor: golang-2xl resource_class: 2xlarge
[[- end]] [[- end]]
[[- if or (eq $name "wdpost") (eq $name "sector_pledge")]] [[- if or (eq $name "wdpost") (eq $name "sector_pledge")]]
get-params: true get-params: true
@ -557,9 +566,16 @@ workflows:
- build - build
suite: utest-[[ $suite ]] suite: utest-[[ $suite ]]
target: "[[ $pkgs ]]" target: "[[ $pkgs ]]"
[[if eq $suite "unit-cli"]]get-params: true[[end]] [[- if eq $suite "unit-storage"]]
[[if eq $suite "unit-cli"]]executor: golang-2xl[[end]] get-params: true
[[- if eq $suite "unit-rest"]]executor: golang-2xl[[end]] [[- end -]]
[[- if eq $suite "unit-cli"]]
resource_class: 2xlarge
get-params: true
[[- end -]]
[[- if eq $suite "unit-rest"]]
resource_class: 2xlarge
[[- end -]]
[[- end]] [[- end]]
- test: - test:
go-test-flags: "-run=TestMulticoreSDR" go-test-flags: "-run=TestMulticoreSDR"

2
.gitignore vendored
View File

@ -6,6 +6,7 @@
/lotus-chainwatch /lotus-chainwatch
/lotus-shed /lotus-shed
/lotus-sim /lotus-sim
/lotus-provider
/lotus-townhall /lotus-townhall
/lotus-fountain /lotus-fountain
/lotus-stats /lotus-stats
@ -41,6 +42,7 @@ build/paramfetch.sh
bin/ipget bin/ipget
bin/tmp/* bin/tmp/*
.idea .idea
.vscode
scratchpad scratchpad
build/builtin-actors/v* build/builtin-actors/v*

View File

@ -14,6 +14,7 @@ linters:
- varcheck - varcheck
- deadcode - deadcode
- scopelint - scopelint
- unused
# We don't want to skip builtin/ # We don't want to skip builtin/
skip-dirs-use-default: false skip-dirs-use-default: false

View File

@ -1,5 +1,192 @@
# Lotus changelog # Lotus changelog
# UNRELEASED
## Improvements
# v1.25.2 / 2024-01-11
This is an optional but **highly recommended feature release** of Lotus, as it includes fixes for synchronizations issues that users have experienced. The feature release also introduces `Lotus-Provider` in its alpha testing phase, as well as the ability to call external PC2-binaries during the sealing process.
## ☢️ Upgrade Warnings ☢️
There are no upgrade warnings for this feature release.
## ⭐️ Highlights ⭐️
### Lotus-Provider
The feature release ships the alpha release of the new Lotus-Provider binary, together with its initial features - High Availability of WindowPoSt and WinningPoSt.
So what is so exciting about Lotus-Provider:
**High Availability**
- You can run as many `Lotus-Provider` instances as you want for both WindowPoSt and WinningPOSt.
- You can connect them to as many clustered Yugabyte instances as you want to. This allows for an NxN configuration where all instances can communicate with all others.
- You have the option to connect different instances to different chain daemons.
**Simplicity**
- Once the configuration is in the database, setting up a new machine with Lotus-Provider is straightforward. Simply start the binary with the correct flags to find YugabyteDB and specify which configuration layers it should use.
**Durability**
- `Lotus-Provider` is designed with robustness in mind. Updates to the system are handled seamlessly, ensuring that performance and stability are maintained when taking down machines in your cluster for updates.
Read more about [`Lotus-Provider` in the documentation here](https://lotus.filecoin.io/storage-providers/lotus-provider/overview/). And check out the how you can migrate from [Lotus-Miner to Lotus-Provider here](https://lotus.filecoin.io/storage-providers/lotus-provider/setup/). **(Only recommended in testnets while its in Alpha)**
### External PC2-binaries
In this feature release, storage providers can call external PC2-binaries during the sealing process. This allows storage providers to leverage the SupraSeal PC2 binary, which has been shown to improve sealing speed in the PC2-phase. For instance, our current benchmarks show that an NVIDIA RTX A5000 card was able to complete PC2 in approximately 2.5 minutes.
We have verified that SupraSeal PC2 functions properly with Committed Capacity (CC) sectors, both SyntheticPoReps and non-Synthetic PoReps. However calling SupraSeal PC2 with deal sectors is not supported in this feature release.
For more information on how to use SupraSeal PC2 with your `lotus-worker`, as well as how to use feature, please [refer to the documentation](https://lotus.filecoin.io/tutorials/lotus-miner/supra-seal-pc2/).
## New features
- feat: sturdypost work branch ([filecoin-project/lotus#11405](https://github.com/filecoin-project/lotus/pull/11405))
- Adds the `Lotus-Provider` binary, and the HarmonyDB framework.
- feat: worker: Support delegating precommit2 to external binary ([filecoin-project/lotus#11185](https://github.com/filecoin-project/lotus/pull/11185))
- Allows for delegating PreCommit2 to an exteranl binary.
- feat: build: Add SupraSeal-PC2 binary script ([filecoin-project/lotus#11430](https://github.com/filecoin-project/lotus/pull/11430))
- Adds a script for building the SupraSeal-PC2 binary easily.
- Feat: daemon: Auto remove existing chain if importing chain file or snapshot ([filecoin-project/lotus#11277](https://github.com/filecoin-project/lotus/pull/11277))
- Auto removes the existing chain when importing a snapshot.
- feat: Add ETA to lotus sync wait (#11211) ([filecoin-project/lotus#11211](https://github.com/filecoin-project/lotus/pull/11211))
- Adds a ETA indicator to `lotus sync wait`, so you can get an estimate for how long until sync is completed.
- feat: mpool/wdpost: Maximize feecap config ([filecoin-project/lotus#9746](https://github.com/filecoin-project/lotus/pull/9746))
- Adds a Maximixe FeeCap Config
- feat: Add lotus-bench cli option to stress test any binary ([filecoin-project/lotus#11270](https://github.com/filecoin-project/lotus/pull/11270))
- Enables the `Lotus-Bench` to run any binary and analyze their latency and histogram distribution, track most common errors, perform stress testing under different concurrency levels and see how it works under different QPS.
- feat: chain import: don't walk to genesis - 2-3x faster snapshot import (#11446) ([filecoin-project/lotus#11446](https://github.com/filecoin-project/lotus/pull/11446))
- Improves Snapshot import speed, by not walking back to genesis on import.
- feat: metric: export Mpool message count ([filecoin-project/lotus#11361](https://github.com/filecoin-project/lotus/pull/11361))
- Adds the mpool count as a prometheus metric.
- feat: bench: flag to output GenerateWinningPoStWithVanilla params ([filecoin-project/lotus#11460](https://github.com/filecoin-project/lotus/pull/11460))
## Improvements
- feat: bootstrap: add glif bootstrap node on calibration ([filecoin-project/lotus#11175](https://github.com/filecoin-project/lotus/pull/11175))
- fix: bench: Set ticket and seed to a non-all zero value ([filecoin-project/lotus#11429](https://github.com/filecoin-project/lotus/pull/11429))
- fix: alert: Check UDPbuffer-size ([filecoin-project/lotus#11360](https://github.com/filecoin-project/lotus/pull/11360))
- feat: cli: sort actor CIDs alphabetically before printing (#11345) ([filecoin-project/lotus#11345](https://github.com/filecoin-project/lotus/pull/11345))
- fix: worker: Connect when --listen is not set ([filecoin-project/lotus#11294](https://github.com/filecoin-project/lotus/pull/11294))
- fix: miner info: Show correct sector state counts ([filecoin-project/lotus#11456](https://github.com/filecoin-project/lotus/pull/11456))
- feat: miner: defensive check for equivocation ([filecoin-project/lotus#11321](https://github.com/filecoin-project/lotus/pull/11321))
- feat: Instructions for setting up Grafana/Prometheus for monitoring local lotus node ([filecoin-project/lotus#11276](https://github.com/filecoin-project/lotus/pull/11276))
- fix: cli: Wrap error in wallet sign ([filecoin-project/lotus#11273](https://github.com/filecoin-project/lotus/pull/11273))
- fix: Add time slicing to splitstore purging to reduce lock congestion ([filecoin-project/lotus#11269](https://github.com/filecoin-project/lotus/pull/11269))
- feat: sealing: load SectorsSummary from sealing SectorStats instead of calling API each time ([filecoin-project/lotus#11353](https://github.com/filecoin-project/lotus/pull/11353))
- fix: shed: additional metrics in `mpool miner-select-messages` ([filecoin-project/lotus#11253](https://github.com/filecoin-project/lotus/pull/11253))
- storage: Return soft err when sector alloc fails in acquire ([filecoin-project/lotus#11338](https://github.com/filecoin-project/lotus/pull/11338))
- feat: miner: log detailed timing breakdown when mining takes longer than the block's timestamp ([filecoin-project/lotus#11228](https://github.com/filecoin-project/lotus/pull/11228))
- fix: shed: make invariants checker work with splitstore ([filecoin-project/lotus#11391](https://github.com/filecoin-project/lotus/pull/11391))
- feat: eth: encode eth tx input as solidity ABI (#11402) ([filecoin-project/lotus#11402](https://github.com/filecoin-project/lotus/pull/11402))
- fix: eth: use the correct state-tree when resolving addresses (#11387) ([filecoin-project/lotus#11387](https://github.com/filecoin-project/lotus/pull/11387))
- fix: eth: remove trace sanity check (#11385) ([filecoin-project/lotus#11385](https://github.com/filecoin-project/lotus/pull/11385))
- fix: chain: make failure to load the chain state fatal (#11426) ([filecoin-project/lotus#11426](https://github.com/filecoin-project/lotus/pull/11426))
- fix: build: an epoch is near an upgrade iff the upgrade is enabled (#11401) ([filecoin-project/lotus#11401](https://github.com/filecoin-project/lotus/pull/11401))
- fix: eth: handle unresolvable addresses (#11433) ([filecoin-project/lotus#11433](https://github.com/filecoin-project/lotus/pull/11433))
- fix: eth: correctly encode and simplify native input/output encoding (#11382) ([filecoin-project/lotus#11382](https://github.com/filecoin-project/lotus/pull/11382))
- fix: worker: listen for interrupt signals in GetStorageMinerAPI loop (#11309) ([filecoin-project/lotus#11309](https://github.com/filecoin-project/lotus/pull/11309))
- fix: sync: iterate over returned messages directly (#11373) ([filecoin-project/lotus#11373](https://github.com/filecoin-project/lotus/pull/11373))
- fix: miner: correct duration logs in mineOne ([filecoin-project/lotus#11241](https://github.com/filecoin-project/lotus/pull/11241))
- fix: cli: Add print to unseal cmd ([filecoin-project/lotus#11271](https://github.com/filecoin-project/lotus/pull/11271))
- fix: networking: avoid dialing when trying to handshake peers ([filecoin-project/lotus#11262](https://github.com/filecoin-project/lotus/pull/11262))
- metric milliseconds computation with golang original method (#11403) ([filecoin-project/lotus#11403](https://github.com/filecoin-project/lotus/pull/11403))
- feat: shed: fix blockstore prune (#11197) ([filecoin-project/lotus#11197](https://github.com/filecoin-project/lotus/pull/11197))
- refactor:ffi: replace ClearLayerData with ClearCache (#11352) ([filecoin-project/lotus#11352](https://github.com/filecoin-project/lotus/pull/11352))
- fix: api: compute gasUsedRatio based on max gas in the tipset (#11354) ([filecoin-project/lotus#11354](https://github.com/filecoin-project/lotus/pull/11354))
- fix: api: compute the effective gas cost with the correct base-fee (#11357) ([filecoin-project/lotus#11357](https://github.com/filecoin-project/lotus/pull/11357))
- fix: api: return errors on failure to lookup an eth txn receipt (#11329) ([filecoin-project/lotus#11329](https://github.com/filecoin-project/lotus/pull/11329))
- fix: api: exclude reverted events in `eth_getLogs` results (#11318) ([filecoin-project/lotus#11318](https://github.com/filecoin-project/lotus/pull/11318))
- api: Add block param to eth_estimateGas ([filecoin-project/lotus#11462](https://github.com/filecoin-project/lotus/pull/11462))
- opt: fix duplicate check exitcode ([filecoin-project/lotus#11171](https://github.com/filecoin-project/lotus/pull/11171))
- fix: lotus-provider: show addresses in log ([filecoin-project/lotus#11490](https://github.com/filecoin-project/lotus/pull/11490))
- fix: lotus-provider: Wait for the correct taskID ([filecoin-project/lotus#11493](https://github.com/filecoin-project/lotus/pull/11493))
- harmony: Fix task reclaim on restart ([filecoin-project/lotus#11498](https://github.com/filecoin-project/lotus/pull/11498))
- fix: lotus-provider: Fix log output format in wdPostTaskCmd ([filecoin-project/lotus#11504](https://github.com/filecoin-project/lotus/pull/11504))
- fix: lp docsgen ([filecoin-project/lotus#11488](https://github.com/filecoin-project/lotus/pull/11488))
- fix: lotus-provider do not suggest default layer ([filecoin-project/lotus#11486](https://github.com/filecoin-project/lotus/pull/11486))
- feat: syncer: optimize syncFork for one-epoch forks ([filecoin-project/lotus#11533](https://github.com/filecoin-project/lotus/pull/11533))
- fix: sync: do not include incoming in return of syncFork ([filecoin-project/lotus#11541](https://github.com/filecoin-project/lotus/pull/11541))
- fix: wdpost: fix vanilla proof indexes ([filecoin-project/lotus#11550](https://github.com/filecoin-project/lotus/pull/11550))
- feat: exchange: change GetBlocks to always fetch the requested number of tipsets ([filecoin-project/lotus#11565](https://github.com/filecoin-project/lotus/pull/11565))
## Dependencies
- update go-libp2p to v0.31.0 ([filecoin-project/lotus#11225](https://github.com/filecoin-project/lotus/pull/11225))
- deps: gostatetype (#11437) ([filecoin-project/lotus#11437](https://github.com/filecoin-project/lotus/pull/11437))
- fix: deps: stop using go-libp2p deprecated peer.ID.Pretty ([filecoin-project/lotus#11263](https://github.com/filecoin-project/lotus/pull/11263))
- chore:libp2p:update libp2p deps in release-v1.25.2 to v0.31.1 ([filecoin-project/lotus#11524](https://github.com/filecoin-project/lotus/pull/11524))
- deps: update go-multiaddr to v0.12.0 ([filecoin-project/lotus#11524](https://github.com/filecoin-project/lotus/pull/11558))
- dep: go-multi-address to v0.12.1 ([filecoin-project/lotus#11564](https://github.com/filecoin-project/lotus/pull/11564))
## Others
- chore: update FFI (#11431) ([filecoin-project/lotus#11431](https://github.com/filecoin-project/lotus/pull/11431))
- chore: build: bump master to v1.25.1-dev ([filecoin-project/lotus#11450](https://github.com/filecoin-project/lotus/pull/11450))
- chore: releases :merge releases into master ([filecoin-project/lotus#11448](https://github.com/filecoin-project/lotus/pull/11448))
- chore: actors: update v12 to the final release ([filecoin-project/lotus#11440](https://github.com/filecoin-project/lotus/pull/11440))
- chore: Remove ipfs main bootstrap nodes (#11200) ([filecoin-project/lotus#11200](https://github.com/filecoin-project/lotus/pull/11200))
- Remove PL's european bootstrap nodes from mainnet.pi ([filecoin-project/lotus#11315](https://github.com/filecoin-project/lotus/pull/11315))
- chore: deps: update to go-state-types v0.12.7 ([filecoin-project/lotus#11428](https://github.com/filecoin-project/lotus/pull/11428))
- fix: Add .vscode to gitignore ([filecoin-project/lotus#11275](https://github.com/filecoin-project/lotus/pull/11275))
- fix: test: temporarily exempt SynthPorep constants from test ([filecoin-project/lotus#11259](https://github.com/filecoin-project/lotus/pull/11259))
- feat: skip TestSealAndVerify3 until it's fixed ([filecoin-project/lotus#11230](https://github.com/filecoin-project/lotus/pull/11230))
- Update RELEASE_ISSUE_TEMPLATE.md ([filecoin-project/lotus#11250](https://github.com/filecoin-project/lotus/pull/11250))
- fix: config: Update ColdStoreType comments ([filecoin-project/lotus#11274](https://github.com/filecoin-project/lotus/pull/11274))
- readme: bump up golang version (#11347) ([filecoin-project/lotus#11347](https://github.com/filecoin-project/lotus/pull/11347))
- chore: watermelon: upgrade epoch ([filecoin-project/lotus#11374](https://github.com/filecoin-project/lotus/pull/11374))
- add support for v12 check invariants and also a default case to reduce future confusion (#11371) ([filecoin-project/lotus#11371](https://github.com/filecoin-project/lotus/pull/11371))
- test: drand: switch tests to drand testnet (from devnet) (#11359) ([filecoin-project/lotus#11359](https://github.com/filecoin-project/lotus/pull/11359))
- feat: chain: light-weight patch to fix calibrationnet again by removing move_partitions from built-in actors (#11409) ([filecoin-project/lotus#11409](https://github.com/filecoin-project/lotus/pull/11409))
- chore: cli: Revert move-partitions cmd ([filecoin-project/lotus#11408](https://github.com/filecoin-project/lotus/pull/11408))
- chore: forward-port calibnet hotfix to master ([filecoin-project/lotus#11407](https://github.com/filecoin-project/lotus/pull/11407))
- fix: migration: set premigration to 90 minutes ([filecoin-project/lotus#11395](https://github.com/filecoin-project/lotus/pull/11395))
- feat: chain: light-weight patch to fix calibrationnet (#11363) ([filecoin-project/lotus#11363](https://github.com/filecoin-project/lotus/pull/11363))
- chore: merge feat/nv21 into master ([filecoin-project/lotus#11336](https://github.com/filecoin-project/lotus/pull/11336))
- docs: Link the release section in the release flow doc ([filecoin-project/lotus#11299](https://github.com/filecoin-project/lotus/pull/11299))
- fix: ci: fetch params for the storage unit tests ([filecoin-project/lotus#11441](https://github.com/filecoin-project/lotus/pull/11441))
- Update mainnet.pi ([filecoin-project/lotus#11288](https://github.com/filecoin-project/lotus/pull/11288))
- add go linter - "unused" (#11235) ([filecoin-project/lotus#11235](https://github.com/filecoin-project/lotus/pull/11235))
- Fix/texts (#11298) ([filecoin-project/lotus#11298](https://github.com/filecoin-project/lotus/pull/11298))
- fix typo in rate-limit flag description (#11316) ([filecoin-project/lotus#11316](https://github.com/filecoin-project/lotus/pull/11316))
- eth_filter flake debug ([filecoin-project/lotus#11261](https://github.com/filecoin-project/lotus/pull/11261))
- fix: sealing: typo in FinalizeReplicaUpdate ([filecoin-project/lotus#11255](https://github.com/filecoin-project/lotus/pull/11255))
- chore: slice loop replace (#11349) ([filecoin-project/lotus#11349](https://github.com/filecoin-project/lotus/pull/11349))
- backport: docker build fix for v1.25.2 ([filecoin-project/lotus#11560](https://github.com/filecoin-project/lotus/pull/11560))
## Contributors
| Contributor | Commits | Lines ± | Files Changed |
|-------------|---------|---------|---------------|
| Andrew Jackson (Ajax) | 161 | +24328/-12464 | 4148 |
| Łukasz Magiera | 99 | +5238/-2690 | 260 |
| Shrenuj Bansal | 27 | +3402/-1265 | 76 |
| Fridrik Asmundsson | 15 | +1148/-307 | 58 |
| Steven Allen | 15 | +674/-337 | 35 |
| Ian Norden | 1 | +625/-3 | 4 |
| Aarsh Shah | 4 | +227/-167 | 14 |
| Phi | 19 | +190/-183 | 32 |
| Aayush Rajasekaran | 3 | +291/-56 | 16 |
| Mikers | 2 | +76/-262 | 19 |
| Aayush | 14 | +111/-59 | 21 |
| Friðrik Ásmundsson | 1 | +101/-1 | 2 |
| Alejandro Criado-Pérez | 1 | +36/-36 | 27 |
| Jie Hou | 5 | +36/-10 | 5 |
| Florian RUEN | 2 | +24/-19 | 5 |
| Phi-rjan | 3 | +20/-8 | 3 |
| Icarus9913 | 1 | +11/-11 | 6 |
| Jiaying Wang | 3 | +8/-7 | 5 |
| guangwu | 1 | +3/-10 | 2 |
| Marten Seemann | 1 | +6/-6 | 2 |
| simlecode | 1 | +0/-6 | 2 |
| GlacierWalrus | 2 | +0/-5 | 2 |
| Anton Evangelatov | 1 | +2/-2 | 1 |
| Ales Dumikau | 3 | +2/-2 | 3 |
| renran | 1 | +2/-1 | 1 |
| Volker Mische | 1 | +1/-1 | 1 |
| Icarus Wu | 1 | +1/-1 | 1 |
| Hubert | 1 | +1/-1 | 1 |
| Aloxaf | 1 | +1/-1 | 1 |
| Alejandro | 1 | +1/-1 | 1 |
| lazavikmaria | 1 | +1/-0 | 1 |
# v1.25.1 / 2023-12-09 # v1.25.1 / 2023-12-09
This is a **highly recommended PATCH RELEASE.** The patch release fixes the issue were node operators trying to catch up sync were unable to sync large message blocks/epochs due to an increased number of messages on the network. This is a **highly recommended PATCH RELEASE.** The patch release fixes the issue were node operators trying to catch up sync were unable to sync large message blocks/epochs due to an increased number of messages on the network.
@ -9,6 +196,7 @@ This patch release allows for up to 10k messages per block. Additionally, it int
## Improvements ## Improvements
- fix: exchange: allow up to 10k messages per block ([filecoin-project/lotus#11506](https://github.com/filecoin-project/lotus/pull/11506)) - fix: exchange: allow up to 10k messages per block ([filecoin-project/lotus#11506](https://github.com/filecoin-project/lotus/pull/11506))
>>>>>>> releases
# v 1.25.0 / 2023-11-22 # v 1.25.0 / 2023-11-22
@ -94,6 +282,9 @@ Lotus-workers can now be built to leverage the SupraSeal C2 sealing optimization
- fix(client): single-root error message ([filecoin-project/lotus#11214](https://github.com/filecoin-project/lotus/pull/11214)) - fix(client): single-root error message ([filecoin-project/lotus#11214](https://github.com/filecoin-project/lotus/pull/11214))
- fix: worker: Convert `DC_[SectorSize]_[ResourceRestriction]` if set ([filecoin-project/lotus#11224](https://github.com/filecoin-project/lotus/pull/11224)) - fix: worker: Convert `DC_[SectorSize]_[ResourceRestriction]` if set ([filecoin-project/lotus#11224](https://github.com/filecoin-project/lotus/pull/11224))
- chore: backport #11338 onto release/v1.25.0 ([filecoin-project/lotus#11350](https://github.com/filecoin-project/lotus/pull/11350)) - chore: backport #11338 onto release/v1.25.0 ([filecoin-project/lotus#11350](https://github.com/filecoin-project/lotus/pull/11350))
- fix: lotus-provider: lotus-provider msg sending ([filecoin-project/lotus#11480](https://github.com/filecoin-project/lotus/pull/11480))
- fix: lotus-provider: Fix winning PoSt ([filecoin-project/lotus#11483](https://github.com/filecoin-project/lotus/pull/11483))
- chore: fix: sql Scan cannot write to an object ([filecoin-project/lotus#11487](https://github.com/filecoin-project/lotus/pull/11487))
## Dependencies ## Dependencies
- deps: update go-libp2p to v0.28.1 ([filecoin-project/lotus#10998](https://github.com/filecoin-project/lotus/pull/10998)) - deps: update go-libp2p to v0.28.1 ([filecoin-project/lotus#10998](https://github.com/filecoin-project/lotus/pull/10998))
@ -255,7 +446,7 @@ This feature release requires a **minimum Go version of v1.19.12 or higher to su
- feat: sealing: flag to run data_cid untied from addpiece ([filecoin-project/lotus#10797](https://github.com/filecoin-project/lotus/pull/10797)) - feat: sealing: flag to run data_cid untied from addpiece ([filecoin-project/lotus#10797](https://github.com/filecoin-project/lotus/pull/10797))
- feat: Lotus Gateway: add MpoolPending, ChainGetBlock and MinerGetBaseInfo ([filecoin-project/lotus#10929](https://github.com/filecoin-project/lotus/pull/10929)) - feat: Lotus Gateway: add MpoolPending, ChainGetBlock and MinerGetBaseInfo ([filecoin-project/lotus#10929](https://github.com/filecoin-project/lotus/pull/10929))
## Improvements && Bug Fixe ## Improvements && Bug Fixes
- chore: update ffi & fvm ([filecoin-project/lotus#11040](https://github.com/filecoin-project/lotus/pull/11040)) - chore: update ffi & fvm ([filecoin-project/lotus#11040](https://github.com/filecoin-project/lotus/pull/11040))
- feat: Make sure we don't store duplidate actor events caused to reorgs in events.db ([filecoin-project/lotus#11015](https://github.com/filecoin-project/lotus/pull/11015)) - feat: Make sure we don't store duplidate actor events caused to reorgs in events.db ([filecoin-project/lotus#11015](https://github.com/filecoin-project/lotus/pull/11015))
- sealing: Use only non-assigned deals when selecting snap sectors ([filecoin-project/lotus#11002](https://github.com/filecoin-project/lotus/pull/11002)) - sealing: Use only non-assigned deals when selecting snap sectors ([filecoin-project/lotus#11002](https://github.com/filecoin-project/lotus/pull/11002))

View File

@ -109,6 +109,7 @@ COPY --from=lotus-builder /opt/filecoin/lotus-wallet /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-gateway /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-gateway /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-miner /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-miner /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-worker /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-worker /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-provider /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-stats /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-stats /usr/local/bin/
COPY --from=lotus-builder /opt/filecoin/lotus-fountain /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-fountain /usr/local/bin/
@ -117,11 +118,13 @@ RUN mkdir /var/lib/lotus
RUN mkdir /var/lib/lotus-miner RUN mkdir /var/lib/lotus-miner
RUN mkdir /var/lib/lotus-worker RUN mkdir /var/lib/lotus-worker
RUN mkdir /var/lib/lotus-wallet RUN mkdir /var/lib/lotus-wallet
RUN mkdir /var/lib/lotus-provider
RUN chown fc: /var/tmp/filecoin-proof-parameters RUN chown fc: /var/tmp/filecoin-proof-parameters
RUN chown fc: /var/lib/lotus RUN chown fc: /var/lib/lotus
RUN chown fc: /var/lib/lotus-miner RUN chown fc: /var/lib/lotus-miner
RUN chown fc: /var/lib/lotus-worker RUN chown fc: /var/lib/lotus-worker
RUN chown fc: /var/lib/lotus-wallet RUN chown fc: /var/lib/lotus-wallet
RUN chown fc: /var/lib/lotus-provider
VOLUME /var/tmp/filecoin-proof-parameters VOLUME /var/tmp/filecoin-proof-parameters
@ -129,6 +132,7 @@ VOLUME /var/lib/lotus
VOLUME /var/lib/lotus-miner VOLUME /var/lib/lotus-miner
VOLUME /var/lib/lotus-worker VOLUME /var/lib/lotus-worker
VOLUME /var/lib/lotus-wallet VOLUME /var/lib/lotus-wallet
VOLUME /var/lib/lotus-provider
EXPOSE 1234 EXPOSE 1234
EXPOSE 2345 EXPOSE 2345

View File

@ -73,7 +73,7 @@ All releases under an odd minor version number indicate **feature releases**. Th
Feature releases include new development and bug fixes. They are not mandatory, but still highly recommended, **as they may contain critical security fixes**. Note that some of these releases may be very small patch releases that include critical hotfixes. There is no way to distinguish between a bug fix release and a feature release on the "feature" version. Both cases will use the "patch" version number. Feature releases include new development and bug fixes. They are not mandatory, but still highly recommended, **as they may contain critical security fixes**. Note that some of these releases may be very small patch releases that include critical hotfixes. There is no way to distinguish between a bug fix release and a feature release on the "feature" version. Both cases will use the "patch" version number.
We aim to ship a new feature release of the Lotus software from our development (master) branch every 3 weeks, so users can expect a regular cadence of Lotus feature releases. Note that mandatory releases for network upgrades may disrupt this schedule. For more, see the Release Cycle section (TODO: Link). We aim to ship a new feature release of the Lotus software from our development (master) branch every 3 weeks, so users can expect a regular cadence of Lotus feature releases. Note that mandatory releases for network upgrades may disrupt this schedule. For more, see the [Release Cycle section](#release-cycle).
### Examples Scenarios ### Examples Scenarios

View File

@ -66,7 +66,7 @@ CLEAN+=build/.update-modules
deps: $(BUILD_DEPS) deps: $(BUILD_DEPS)
.PHONY: deps .PHONY: deps
build-devnets: build lotus-seed lotus-shed build-devnets: build lotus-seed lotus-shed lotus-provider
.PHONY: build-devnets .PHONY: build-devnets
debug: GOFLAGS+=-tags=debug debug: GOFLAGS+=-tags=debug
@ -97,6 +97,15 @@ lotus-miner: $(BUILD_DEPS)
.PHONY: lotus-miner .PHONY: lotus-miner
BINS+=lotus-miner BINS+=lotus-miner
lotus-provider: $(BUILD_DEPS)
rm -f lotus-provider
$(GOCC) build $(GOFLAGS) -o lotus-provider ./cmd/lotus-provider
.PHONY: lotus-provider
BINS+=lotus-provider
lp2k: GOFLAGS+=-tags=2k
lp2k: lotus-provider
lotus-worker: $(BUILD_DEPS) lotus-worker: $(BUILD_DEPS)
rm -f lotus-worker rm -f lotus-worker
$(GOCC) build $(GOFLAGS) -o lotus-worker ./cmd/lotus-worker $(GOCC) build $(GOFLAGS) -o lotus-worker ./cmd/lotus-worker
@ -121,7 +130,7 @@ an existing lotus binary in your PATH. This may cause problems if you don't run
.PHONY: build .PHONY: build
install: install-daemon install-miner install-worker install: install-daemon install-miner install-worker install-provider
install-daemon: install-daemon:
install -C ./lotus /usr/local/bin/lotus install -C ./lotus /usr/local/bin/lotus
@ -129,6 +138,9 @@ install-daemon:
install-miner: install-miner:
install -C ./lotus-miner /usr/local/bin/lotus-miner install -C ./lotus-miner /usr/local/bin/lotus-miner
install-provider:
install -C ./lotus-provider /usr/local/bin/lotus-provider
install-worker: install-worker:
install -C ./lotus-worker /usr/local/bin/lotus-worker install -C ./lotus-worker /usr/local/bin/lotus-worker
@ -144,6 +156,9 @@ uninstall-daemon:
uninstall-miner: uninstall-miner:
rm -f /usr/local/bin/lotus-miner rm -f /usr/local/bin/lotus-miner
uninstall-provider:
rm -f /usr/local/bin/lotus-provider
uninstall-worker: uninstall-worker:
rm -f /usr/local/bin/lotus-worker rm -f /usr/local/bin/lotus-worker
@ -241,6 +256,14 @@ install-miner-service: install-miner install-daemon-service
@echo @echo
@echo "lotus-miner service installed. Don't forget to run 'sudo systemctl start lotus-miner' to start it and 'sudo systemctl enable lotus-miner' for it to be enabled on startup." @echo "lotus-miner service installed. Don't forget to run 'sudo systemctl start lotus-miner' to start it and 'sudo systemctl enable lotus-miner' for it to be enabled on startup."
install-provider-service: install-provider install-daemon-service
mkdir -p /etc/systemd/system
mkdir -p /var/log/lotus
install -C -m 0644 ./scripts/lotus-provider.service /etc/systemd/system/lotus-provider.service
systemctl daemon-reload
@echo
@echo "lotus-provider service installed. Don't forget to run 'sudo systemctl start lotus-provider' to start it and 'sudo systemctl enable lotus-provider' for it to be enabled on startup."
install-main-services: install-miner-service install-main-services: install-miner-service
install-all-services: install-main-services install-all-services: install-main-services
@ -259,6 +282,12 @@ clean-miner-service:
rm -f /etc/systemd/system/lotus-miner.service rm -f /etc/systemd/system/lotus-miner.service
systemctl daemon-reload systemctl daemon-reload
clean-provider-service:
-systemctl stop lotus-provider
-systemctl disable lotus-provider
rm -f /etc/systemd/system/lotus-provider.service
systemctl daemon-reload
clean-main-services: clean-daemon-service clean-main-services: clean-daemon-service
clean-all-services: clean-main-services clean-all-services: clean-main-services
@ -294,7 +323,8 @@ actors-code-gen:
$(GOCC) run ./chain/actors/agen $(GOCC) run ./chain/actors/agen
$(GOCC) fmt ./... $(GOCC) fmt ./...
actors-gen: actors-code-gen fiximports actors-gen: actors-code-gen
./scripts/fiximports
.PHONY: actors-gen .PHONY: actors-gen
bundle-gen: bundle-gen:
@ -328,7 +358,7 @@ docsgen-md-bin: api-gen actors-gen
docsgen-openrpc-bin: api-gen actors-gen docsgen-openrpc-bin: api-gen actors-gen
$(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd $(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd
docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker docsgen-md-provider
docsgen-md-full: docsgen-md-bin docsgen-md-full: docsgen-md-bin
./docgen-md "api/api_full.go" "FullNode" "api" "./api" > documentation/en/api-v1-unstable-methods.md ./docgen-md "api/api_full.go" "FullNode" "api" "./api" > documentation/en/api-v1-unstable-methods.md
@ -337,6 +367,8 @@ docsgen-md-storage: docsgen-md-bin
./docgen-md "api/api_storage.go" "StorageMiner" "api" "./api" > documentation/en/api-v0-methods-miner.md ./docgen-md "api/api_storage.go" "StorageMiner" "api" "./api" > documentation/en/api-v0-methods-miner.md
docsgen-md-worker: docsgen-md-bin docsgen-md-worker: docsgen-md-bin
./docgen-md "api/api_worker.go" "Worker" "api" "./api" > documentation/en/api-v0-methods-worker.md ./docgen-md "api/api_worker.go" "Worker" "api" "./api" > documentation/en/api-v0-methods-worker.md
docsgen-md-provider: docsgen-md-bin
./docgen-md "api/api_lp.go" "Provider" "api" "./api" > documentation/en/api-v0-methods-provider.md
docsgen-openrpc: docsgen-openrpc-full docsgen-openrpc-storage docsgen-openrpc-worker docsgen-openrpc-gateway docsgen-openrpc: docsgen-openrpc-full docsgen-openrpc-storage docsgen-openrpc-worker docsgen-openrpc-gateway
@ -354,21 +386,23 @@ docsgen-openrpc-gateway: docsgen-openrpc-bin
fiximports: fiximports:
./scripts/fiximports ./scripts/fiximports
gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci fiximports gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci
./scripts/fiximports
@echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO RUN 'make docsgen-cli'" @echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO RUN 'make docsgen-cli'"
.PHONY: gen .PHONY: gen
jen: gen jen: gen
snap: lotus lotus-miner lotus-worker snap: lotus lotus-miner lotus-worker lotus-provider
snapcraft snapcraft
# snapcraft upload ./lotus_*.snap # snapcraft upload ./lotus_*.snap
# separate from gen because it needs binaries # separate from gen because it needs binaries
docsgen-cli: lotus lotus-miner lotus-worker docsgen-cli: lotus lotus-miner lotus-worker lotus-provider
python3 ./scripts/generate-lotus-cli.py python3 ./scripts/generate-lotus-cli.py
./lotus config default > documentation/en/default-lotus-config.toml ./lotus config default > documentation/en/default-lotus-config.toml
./lotus-miner config default > documentation/en/default-lotus-miner-config.toml ./lotus-miner config default > documentation/en/default-lotus-miner-config.toml
./lotus-provider config default > documentation/en/default-lotus-provider-config.toml
.PHONY: docsgen-cli .PHONY: docsgen-cli
print-%: print-%:

View File

@ -71,10 +71,10 @@ For other distributions you can find the required dependencies [here.](https://l
#### Go #### Go
To build Lotus, you need a working installation of [Go 1.19.12 or higher](https://golang.org/dl/): To build Lotus, you need a working installation of [Go 1.20.10 or higher](https://golang.org/dl/):
```bash ```bash
wget -c https://golang.org/dl/go1.19.12.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local wget -c https://golang.org/dl/go1.20.10.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local
``` ```
**TIP:** **TIP:**
@ -133,6 +133,8 @@ Note: The default branch `master` is the dev branch where the latest new feature
6. You should now have Lotus installed. You can now [start the Lotus daemon and sync the chain](https://lotus.filecoin.io/lotus/install/linux/#start-the-lotus-daemon-and-sync-the-chain). 6. You should now have Lotus installed. You can now [start the Lotus daemon and sync the chain](https://lotus.filecoin.io/lotus/install/linux/#start-the-lotus-daemon-and-sync-the-chain).
7. (Optional) Follow the [Setting Up Prometheus and Grafana](https://github.com/filecoin-project/lotus/tree/master/metrics/README.md) guide for detailed instructions on setting up a working monitoring system running against a local running lotus node.
## License ## License
Dual-licensed under [MIT](https://github.com/filecoin-project/lotus/blob/master/LICENSE-MIT) + [Apache 2.0](https://github.com/filecoin-project/lotus/blob/master/LICENSE-APACHE) Dual-licensed under [MIT](https://github.com/filecoin-project/lotus/blob/master/LICENSE-MIT) + [Apache 2.0](https://github.com/filecoin-project/lotus/blob/master/LICENSE-APACHE)

View File

@ -824,7 +824,7 @@ type FullNode interface {
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) //perm:read
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read
EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) //perm:read EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) //perm:read

View File

@ -114,7 +114,7 @@ type Gateway interface {
EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error)
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error)
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error)
EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error)
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error)
EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error)
EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error)

10
api/api_lp.go Normal file
View File

@ -0,0 +1,10 @@
package api
import "context"
type LotusProvider interface {
Version(context.Context) (Version, error) //perm:admin
// Trigger shutdown
Shutdown(context.Context) error //perm:admin
}

View File

@ -15,6 +15,16 @@ import (
"github.com/filecoin-project/lotus/lib/rpcenc" "github.com/filecoin-project/lotus/lib/rpcenc"
) )
// NewProviderRpc creates a new http jsonrpc client.
func NewProviderRpc(ctx context.Context, addr string, requestHeader http.Header) (api.LotusProvider, jsonrpc.ClientCloser, error) {
var res v1api.LotusProviderStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))
return &res, closer, err
}
// NewCommonRPCV0 creates a new http jsonrpc client. // NewCommonRPCV0 creates a new http jsonrpc client.
func NewCommonRPCV0(ctx context.Context, addr string, requestHeader http.Header) (api.CommonNet, jsonrpc.ClientCloser, error) { func NewCommonRPCV0(ctx context.Context, addr string, requestHeader http.Header) (api.CommonNet, jsonrpc.ClientCloser, error) {
var res v0api.CommonNetStruct var res v0api.CommonNetStruct

View File

@ -432,6 +432,10 @@ func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []r
i = &api.GatewayStruct{} i = &api.GatewayStruct{}
t = reflect.TypeOf(new(struct{ api.Gateway })).Elem() t = reflect.TypeOf(new(struct{ api.Gateway })).Elem()
permStruct = append(permStruct, reflect.TypeOf(api.GatewayStruct{}.Internal)) permStruct = append(permStruct, reflect.TypeOf(api.GatewayStruct{}.Internal))
case "Provider":
i = &api.LotusProviderStruct{}
t = reflect.TypeOf(new(struct{ api.LotusProvider })).Elem()
permStruct = append(permStruct, reflect.TypeOf(api.LotusProviderStruct{}.Internal))
default: default:
panic("unknown type") panic("unknown type")
} }

View File

@ -1042,7 +1042,7 @@ func (mr *MockFullNodeMockRecorder) EthChainId(arg0 interface{}) *gomock.Call {
} }
// EthEstimateGas mocks base method. // EthEstimateGas mocks base method.
func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 ethtypes.EthCall) (ethtypes.EthUint64, error) { func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1) ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1)
ret0, _ := ret[0].(ethtypes.EthUint64) ret0, _ := ret[0].(ethtypes.EthUint64)

View File

@ -41,6 +41,12 @@ func PermissionedWorkerAPI(a Worker) Worker {
return &out return &out
} }
func PermissionedAPI[T, P any](a T) *P {
var out P
permissionedProxies(a, &out)
return &out
}
func PermissionedWalletAPI(a Wallet) Wallet { func PermissionedWalletAPI(a Wallet) Wallet {
var out WalletStruct var out WalletStruct
permissionedProxies(a, &out) permissionedProxies(a, &out)

View File

@ -255,7 +255,7 @@ type FullNodeMethods struct {
EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"`
EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `perm:"read"` EthEstimateGas func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) `perm:"read"`
EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `perm:"read"` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `perm:"read"`
@ -679,7 +679,7 @@ type GatewayMethods struct {
EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) ``
EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `` EthEstimateGas func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) ``
EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) ``
@ -831,6 +831,19 @@ type GatewayMethods struct {
type GatewayStub struct { type GatewayStub struct {
} }
type LotusProviderStruct struct {
Internal LotusProviderMethods
}
type LotusProviderMethods struct {
Shutdown func(p0 context.Context) error `perm:"admin"`
Version func(p0 context.Context) (Version, error) `perm:"admin"`
}
type LotusProviderStub struct {
}
type NetStruct struct { type NetStruct struct {
Internal NetMethods Internal NetMethods
} }
@ -2134,14 +2147,14 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {
if s.Internal.EthEstimateGas == nil { if s.Internal.EthEstimateGas == nil {
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
return s.Internal.EthEstimateGas(p0, p1) return s.Internal.EthEstimateGas(p0, p1)
} }
func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
@ -4400,14 +4413,14 @@ func (s *GatewayStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error)
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {
if s.Internal.EthEstimateGas == nil { if s.Internal.EthEstimateGas == nil {
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
return s.Internal.EthEstimateGas(p0, p1) return s.Internal.EthEstimateGas(p0, p1)
} }
func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
@ -5214,6 +5227,28 @@ func (s *GatewayStub) Web3ClientVersion(p0 context.Context) (string, error) {
return "", ErrNotSupported return "", ErrNotSupported
} }
func (s *LotusProviderStruct) Shutdown(p0 context.Context) error {
if s.Internal.Shutdown == nil {
return ErrNotSupported
}
return s.Internal.Shutdown(p0)
}
func (s *LotusProviderStub) Shutdown(p0 context.Context) error {
return ErrNotSupported
}
func (s *LotusProviderStruct) Version(p0 context.Context) (Version, error) {
if s.Internal.Version == nil {
return *new(Version), ErrNotSupported
}
return s.Internal.Version(p0)
}
func (s *LotusProviderStub) Version(p0 context.Context) (Version, error) {
return *new(Version), ErrNotSupported
}
func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) { func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) {
if s.Internal.ID == nil { if s.Internal.ID == nil {
return *new(peer.ID), ErrNotSupported return *new(peer.ID), ErrNotSupported
@ -7442,6 +7477,7 @@ var _ CommonNet = new(CommonNetStruct)
var _ EthSubscriber = new(EthSubscriberStruct) var _ EthSubscriber = new(EthSubscriberStruct)
var _ FullNode = new(FullNodeStruct) var _ FullNode = new(FullNodeStruct)
var _ Gateway = new(GatewayStruct) var _ Gateway = new(GatewayStruct)
var _ LotusProvider = new(LotusProviderStruct)
var _ Net = new(NetStruct) var _ Net = new(NetStruct)
var _ Signable = new(SignableStruct) var _ Signable = new(SignableStruct)
var _ StorageMiner = new(StorageMinerStruct) var _ StorageMiner = new(StorageMinerStruct)

View File

@ -56,9 +56,17 @@ type PubsubScore struct {
Score *pubsub.PeerScoreSnapshot Score *pubsub.PeerScoreSnapshot
} }
// MessageSendSpec contains optional fields which modify message sending behavior
type MessageSendSpec struct { type MessageSendSpec struct {
// MaxFee specifies a cap on network fees related to this message
MaxFee abi.TokenAmount MaxFee abi.TokenAmount
// MsgUuid specifies a unique message identifier which can be used on node (or node cluster)
// level to prevent double-sends of messages even when nonce generation is not handled by sender
MsgUuid uuid.UUID MsgUuid uuid.UUID
// MaximizeFeeCap makes message FeeCap be based entirely on MaxFee
MaximizeFeeCap bool
} }
type MpoolMessageWhole struct { type MpoolMessageWhole struct {

View File

@ -12,3 +12,5 @@ type RawFullNodeAPI FullNode
func PermissionedFullAPI(a FullNode) FullNode { func PermissionedFullAPI(a FullNode) FullNode {
return api.PermissionedFullAPI(a) return api.PermissionedFullAPI(a)
} }
type LotusProviderStruct = api.LotusProviderStruct

View File

@ -59,6 +59,8 @@ var (
MinerAPIVersion0 = newVer(1, 5, 0) MinerAPIVersion0 = newVer(1, 5, 0)
WorkerAPIVersion0 = newVer(1, 7, 0) WorkerAPIVersion0 = newVer(1, 7, 0)
ProviderAPIVersion0 = newVer(1, 0, 0)
) )
//nolint:varcheck,deadcode //nolint:varcheck,deadcode

View File

@ -183,3 +183,17 @@ func (b *idstore) Close() error {
func (b *idstore) Flush(ctx context.Context) error { func (b *idstore) Flush(ctx context.Context) error {
return b.bs.Flush(ctx) return b.bs.Flush(ctx)
} }
func (b *idstore) CollectGarbage(ctx context.Context, options ...BlockstoreGCOption) error {
if bs, ok := b.bs.(BlockstoreGC); ok {
return bs.CollectGarbage(ctx, options...)
}
return xerrors.Errorf("not supported")
}
func (b *idstore) GCOnce(ctx context.Context, options ...BlockstoreGCOption) error {
if bs, ok := b.bs.(BlockstoreGCOnce); ok {
return bs.GCOnce(ctx, options...)
}
return xerrors.Errorf("not supported")
}

View File

@ -182,7 +182,6 @@ type SplitStore struct {
compactionIndex int64 compactionIndex int64
pruneIndex int64 pruneIndex int64
onlineGCCnt int64
ctx context.Context ctx context.Context
cancel func() cancel func()

View File

@ -68,6 +68,7 @@ var (
const ( const (
batchSize = 16384 batchSize = 16384
cidKeySize = 128 cidKeySize = 128
purgeWorkSliceDuration = time.Second
) )
func (s *SplitStore) HeadChange(_, apply []*types.TipSet) error { func (s *SplitStore) HeadChange(_, apply []*types.TipSet) error {
@ -1372,9 +1373,21 @@ func (s *SplitStore) purge(coldr *ColdSetReader, checkpoint *Checkpoint, markSet
return err return err
} }
now := time.Now()
err := coldr.ForEach(func(c cid.Cid) error { err := coldr.ForEach(func(c cid.Cid) error {
batch = append(batch, c) batch = append(batch, c)
if len(batch) == batchSize { if len(batch) == batchSize {
// add some time slicing to the purge as this a very disk I/O heavy operation that
// requires write access to txnLk that may starve other operations that require
// access to the blockstore.
elapsed := time.Since(now)
if elapsed > purgeWorkSliceDuration {
// work 1 slice, sleep 4 slices, or 20% utilization
time.Sleep(4 * elapsed)
now = time.Now()
}
return deleteBatch() return deleteBatch()
} }

View File

@ -3,9 +3,9 @@ package splitstore
import ( import (
"context" "context"
"crypto/rand"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"

View File

@ -1,9 +1,6 @@
/dns4/bootstrap-0.mainnet.filops.net/tcp/1347/p2p/12D3KooWCVe8MmsEMes2FzgTpt9fXtmCY7wrq91GRiaC8PHSCCBj /dns4/bootstrap-0.mainnet.filops.net/tcp/1347/p2p/12D3KooWCVe8MmsEMes2FzgTpt9fXtmCY7wrq91GRiaC8PHSCCBj
/dns4/bootstrap-1.mainnet.filops.net/tcp/1347/p2p/12D3KooWCwevHg1yLCvktf2nvLu7L9894mcrJR4MsBCcm4syShVc /dns4/bootstrap-1.mainnet.filops.net/tcp/1347/p2p/12D3KooWCwevHg1yLCvktf2nvLu7L9894mcrJR4MsBCcm4syShVc
/dns4/bootstrap-2.mainnet.filops.net/tcp/1347/p2p/12D3KooWEWVwHGn2yR36gKLozmb4YjDJGerotAPGxmdWZx2nxMC4 /dns4/bootstrap-2.mainnet.filops.net/tcp/1347/p2p/12D3KooWEWVwHGn2yR36gKLozmb4YjDJGerotAPGxmdWZx2nxMC4
/dns4/bootstrap-3.mainnet.filops.net/tcp/1347/p2p/12D3KooWKhgq8c7NQ9iGjbyK7v7phXvG6492HQfiDaGHLHLQjk7R
/dns4/bootstrap-4.mainnet.filops.net/tcp/1347/p2p/12D3KooWL6PsFNPhYftrJzGgF5U18hFoaVhfGk7xwzD8yVrHJ3Uc
/dns4/bootstrap-5.mainnet.filops.net/tcp/1347/p2p/12D3KooWLFynvDQiUpXoHroV1YxKHhPJgysQGH2k3ZGwtWzR4dFH
/dns4/bootstrap-6.mainnet.filops.net/tcp/1347/p2p/12D3KooWP5MwCiqdMETF9ub1P3MbCvQCcfconnYHbWg6sUJcDRQQ /dns4/bootstrap-6.mainnet.filops.net/tcp/1347/p2p/12D3KooWP5MwCiqdMETF9ub1P3MbCvQCcfconnYHbWg6sUJcDRQQ
/dns4/bootstrap-7.mainnet.filops.net/tcp/1347/p2p/12D3KooWRs3aY1p3juFjPy8gPN95PEQChm2QKGUCAdcDCC4EBMKf /dns4/bootstrap-7.mainnet.filops.net/tcp/1347/p2p/12D3KooWRs3aY1p3juFjPy8gPN95PEQChm2QKGUCAdcDCC4EBMKf
/dns4/bootstrap-8.mainnet.filops.net/tcp/1347/p2p/12D3KooWScFR7385LTyR4zU1bYdzSiiAb5rnNABfVahPvVSzyTkR /dns4/bootstrap-8.mainnet.filops.net/tcp/1347/p2p/12D3KooWScFR7385LTyR4zU1bYdzSiiAb5rnNABfVahPvVSzyTkR
@ -11,7 +8,5 @@
/dns4/bootstrap-0.starpool.in/tcp/12757/p2p/12D3KooWGHpBMeZbestVEWkfdnC9u7p6uFHXL1n7m1ZBqsEmiUzz /dns4/bootstrap-0.starpool.in/tcp/12757/p2p/12D3KooWGHpBMeZbestVEWkfdnC9u7p6uFHXL1n7m1ZBqsEmiUzz
/dns4/bootstrap-1.starpool.in/tcp/12757/p2p/12D3KooWQZrGH1PxSNZPum99M1zNvjNFM33d1AAu5DcvdHptuU7u /dns4/bootstrap-1.starpool.in/tcp/12757/p2p/12D3KooWQZrGH1PxSNZPum99M1zNvjNFM33d1AAu5DcvdHptuU7u
/dns4/node.glif.io/tcp/1235/p2p/12D3KooWBF8cpp65hp2u9LK5mh19x67ftAam84z9LsfaquTDSBpt /dns4/node.glif.io/tcp/1235/p2p/12D3KooWBF8cpp65hp2u9LK5mh19x67ftAam84z9LsfaquTDSBpt
/dns4/bootstrap-0.ipfsmain.cn/tcp/34721/p2p/12D3KooWQnwEGNqcM2nAcPtRR9rAX8Hrg4k9kJLCHoTR5chJfz6d
/dns4/bootstrap-1.ipfsmain.cn/tcp/34723/p2p/12D3KooWMKxMkD5DMpSWsW7dBddKxKT7L2GgbNuckz9otxvkvByP
/dns4/bootstarp-0.1475.io/tcp/61256/p2p/12D3KooWRzCVDwHUkgdK7eRgnoXbjDAELhxPErjHzbRLguSV1aRt /dns4/bootstarp-0.1475.io/tcp/61256/p2p/12D3KooWRzCVDwHUkgdK7eRgnoXbjDAELhxPErjHzbRLguSV1aRt
/dns4/bootstrap-venus.mainnet.filincubator.com/tcp/8888/p2p/QmQu8C6deXwKvJP2D8B6QGyhngc3ZiDnFzEHBDx8yeBXST /dns4/bootstrap-venus.mainnet.filincubator.com/tcp/8888/p2p/QmQu8C6deXwKvJP2D8B6QGyhngc3ZiDnFzEHBDx8yeBXST

View File

@ -5,5 +5,8 @@ import (
) )
func IsNearUpgrade(epoch, upgradeEpoch abi.ChainEpoch) bool { func IsNearUpgrade(epoch, upgradeEpoch abi.ChainEpoch) bool {
if upgradeEpoch < 0 {
return false
}
return epoch > upgradeEpoch-Finality && epoch < upgradeEpoch+Finality return epoch > upgradeEpoch-Finality && epoch < upgradeEpoch+Finality
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -37,7 +37,7 @@ func BuildTypeString() string {
} }
// BuildVersion is the local build version // BuildVersion is the local build version
const BuildVersion = "1.25.1" const BuildVersion = "1.25.2"
func UserVersion() string { func UserVersion() string {
if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" {

View File

@ -72,7 +72,7 @@ func (s *state{{.v}}) AvailableBalance(bal abi.TokenAmount) (available abi.Token
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available{{if (ge .v 2)}}, err{{end}} = s.GetAvailableBalance(bal) available{{if (ge .v 2)}}, err{{end}} = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state0) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available = s.GetAvailableBalance(bal) available = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state10) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmou
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state11) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmou
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state12) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmou
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -61,7 +61,7 @@ func (s *state2) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state3) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state4) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state5) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state6) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state7) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state8) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -62,7 +62,7 @@ func (s *state9) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmoun
available = abi.NewTokenAmount(0) available = abi.NewTokenAmount(0)
} }
}() }()
// this panics if the miner doesnt have enough funds to cover their locked pledge // this panics if the miner doesn't have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal) available, err = s.GetAvailableBalance(bal)
return available, err return available, err
} }

View File

@ -29,7 +29,7 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
// Load returns an abstract copy of payment channel state, irregardless of actor version // Load returns an abstract copy of payment channel state, regardless of actor version
func Load(store adt.Store, act *types.Actor) (State, error) { func Load(store adt.Store, act *types.Actor) (State, error) {
if name, av, ok := actors.GetActorMetaByCode(act.Code); ok { if name, av, ok := actors.GetActorMetaByCode(act.Code); ok {
if name != manifest.PaychKey { if name != manifest.PaychKey {

View File

@ -29,7 +29,7 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
// Load returns an abstract copy of payment channel state, irregardless of actor version // Load returns an abstract copy of payment channel state, regardless of actor version
func Load(store adt.Store, act *types.Actor) (State, error) { func Load(store adt.Store, act *types.Actor) (State, error) {
if name, av, ok := actors.GetActorMetaByCode(act.Code); ok { if name, av, ok := actors.GetActorMetaByCode(act.Code); ok {
if name != manifest.PaychKey { if name != manifest.PaychKey {

View File

@ -13,7 +13,7 @@ import (
func SerializeParams(i cbg.CBORMarshaler) ([]byte, aerrors.ActorError) { func SerializeParams(i cbg.CBORMarshaler) ([]byte, aerrors.ActorError) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := i.MarshalCBOR(buf); err != nil { if err := i.MarshalCBOR(buf); err != nil {
// TODO: shouldnt this be a fatal error? // TODO: shouldn't this be a fatal error?
return nil, aerrors.Absorb(err, exitcode.ErrSerialization, "failed to encode parameter") return nil, aerrors.Absorb(err, exitcode.ErrSerialization, "failed to encode parameter")
} }
return buf.Bytes(), nil return buf.Bytes(), nil

View File

@ -867,6 +867,24 @@ func AggregatePreCommitNetworkFee(nwVer network.Version, aggregateSize int, base
} }
} }
var PoStToSealMap map[abi.RegisteredPoStProof]abi.RegisteredSealProof
func init() {
PoStToSealMap = make(map[abi.RegisteredPoStProof]abi.RegisteredSealProof)
for sealProof, info := range abi.SealProofInfos {
PoStToSealMap[info.WinningPoStProof] = sealProof
PoStToSealMap[info.WindowPoStProof] = sealProof
}
}
func GetSealProofFromPoStProof(postProof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
sealProof, exists := PoStToSealMap[postProof]
if !exists {
return 0, xerrors.New("no corresponding RegisteredSealProof for the given RegisteredPoStProof")
}
return sealProof, nil
}
func min(a, b int) int { func min(a, b int) int {
if a < b { if a < b {
return a return a

View File

@ -343,6 +343,23 @@ func AggregatePreCommitNetworkFee(nwVer network.Version, aggregateSize int, base
} }
} }
var PoStToSealMap map[abi.RegisteredPoStProof]abi.RegisteredSealProof
func init() {
PoStToSealMap = make(map[abi.RegisteredPoStProof]abi.RegisteredSealProof)
for sealProof, info := range abi.SealProofInfos {
PoStToSealMap[info.WinningPoStProof] = sealProof
PoStToSealMap[info.WindowPoStProof] = sealProof
}
}
func GetSealProofFromPoStProof(postProof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
sealProof, exists := PoStToSealMap[postProof]
if !exists {
return 0, xerrors.New("no corresponding RegisteredSealProof for the given RegisteredPoStProof")
}
return sealProof, nil
}
func min(a, b int) int { func min(a, b int) int {
if a < b { if a < b {
return a return a

View File

@ -29,19 +29,6 @@ import (
var log = logging.Logger("drand") var log = logging.Logger("drand")
type drandPeer struct {
addr string
tls bool
}
func (dp *drandPeer) Address() string {
return dp.addr
}
func (dp *drandPeer) IsTLS() bool {
return dp.tls
}
// DrandBeacon connects Lotus with a drand network in order to provide // DrandBeacon connects Lotus with a drand network in order to provide
// randomness to the system in a way that's aligned with Filecoin rounds/epochs. // randomness to the system in a way that's aligned with Filecoin rounds/epochs.
// //

View File

@ -17,7 +17,7 @@ import (
) )
func TestPrintGroupInfo(t *testing.T) { func TestPrintGroupInfo(t *testing.T) {
server := build.DrandConfigs[build.DrandDevnet].Servers[0] server := build.DrandConfigs[build.DrandTestnet].Servers[0]
c, err := hclient.New(server, nil, nil) c, err := hclient.New(server, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
cg := c.(interface { cg := c.(interface {
@ -31,7 +31,7 @@ func TestPrintGroupInfo(t *testing.T) {
func TestMaxBeaconRoundForEpoch(t *testing.T) { func TestMaxBeaconRoundForEpoch(t *testing.T) {
todayTs := uint64(1652222222) todayTs := uint64(1652222222)
db, err := NewDrandBeacon(todayTs, build.BlockDelaySecs, nil, build.DrandConfigs[build.DrandDevnet]) db, err := NewDrandBeacon(todayTs, build.BlockDelaySecs, nil, build.DrandConfigs[build.DrandTestnet])
assert.NoError(t, err) assert.NoError(t, err)
mbr15 := db.MaxBeaconRoundForEpoch(network.Version15, 100) mbr15 := db.MaxBeaconRoundForEpoch(network.Version15, 100)
mbr16 := db.MaxBeaconRoundForEpoch(network.Version16, 100) mbr16 := db.MaxBeaconRoundForEpoch(network.Version16, 100)

View File

@ -362,7 +362,8 @@ func CreateBlockHeader(ctx context.Context, sm *stmgr.StateManager, pts *types.T
var blsMsgCids, secpkMsgCids []cid.Cid var blsMsgCids, secpkMsgCids []cid.Cid
var blsSigs []crypto.Signature var blsSigs []crypto.Signature
nv := sm.GetNetworkVersion(ctx, bt.Epoch) nv := sm.GetNetworkVersion(ctx, bt.Epoch)
for _, msg := range bt.Messages { for _, msgTmp := range bt.Messages {
msg := msgTmp
if msg.Signature.Type == crypto.SigTypeBLS { if msg.Signature.Type == crypto.SigTypeBLS {
blsSigs = append(blsSigs, msg.Signature) blsSigs = append(blsSigs, msg.Signature)
blsMessages = append(blsMessages, &msg.Message) blsMessages = append(blsMessages, &msg.Message)

View File

@ -147,9 +147,6 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
return xerrors.Errorf("callback failed on cron message: %w", err) return xerrors.Errorf("callback failed on cron message: %w", err)
} }
} }
if ret.ExitCode != 0 {
return xerrors.Errorf("cron exit was non-zero: %d", ret.ExitCode)
}
return nil return nil
} }

View File

@ -91,9 +91,6 @@ var RewardFunc = func(ctx context.Context, vmi vm.Interface, em stmgr.ExecMonito
} }
} }
if ret.ExitCode != 0 {
return xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr)
}
return nil return nil
} }

View File

@ -180,7 +180,7 @@ func (e *heightEventsObserver) Revert(ctx context.Context, from, to *types.TipSe
// Update the head first so we don't accidental skip reverting a concurrent call to ChainAt. // Update the head first so we don't accidental skip reverting a concurrent call to ChainAt.
e.updateHead(to) e.updateHead(to)
// Call revert on all hights between the two tipsets, handling empty tipsets. // Call revert on all heights between the two tipsets, handling empty tipsets.
for h := from.Height(); h > to.Height(); h-- { for h := from.Height(); h > to.Height(); h-- {
e.lk.Lock() e.lk.Lock()
triggers := e.tsHeights[h] triggers := e.tsHeights[h]

View File

@ -673,7 +673,7 @@ func TestCalled(t *testing.T) {
}, 3, 20, matchAddrMethod(t0123, 5)) }, 3, 20, matchAddrMethod(t0123, 5))
require.NoError(t, err) require.NoError(t, err)
// create few blocks to make sure nothing get's randomly called // create few blocks to make sure nothing gets randomly called
fcs.advance(0, 4, 0, nil) // H=5 fcs.advance(0, 4, 0, nil) // H=5
require.Equal(t, false, applied) require.Equal(t, false, applied)
@ -991,7 +991,7 @@ func TestCalledNull(t *testing.T) {
}, 3, 20, matchAddrMethod(t0123, 5)) }, 3, 20, matchAddrMethod(t0123, 5))
require.NoError(t, err) require.NoError(t, err)
// create few blocks to make sure nothing get's randomly called // create few blocks to make sure nothing gets randomly called
fcs.advance(0, 4, 0, nil) // H=5 fcs.advance(0, 4, 0, nil) // H=5
require.Equal(t, false, applied) require.Equal(t, false, applied)
@ -1050,7 +1050,7 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
}, 3, 20, matchAddrMethod(t0123, 5)) }, 3, 20, matchAddrMethod(t0123, 5))
require.NoError(t, err) require.NoError(t, err)
// create few blocks to make sure nothing get's randomly called // create few blocks to make sure nothing gets randomly called
fcs.advance(0, 4, 0, nil) // H=5 fcs.advance(0, 4, 0, nil) // H=5
require.Equal(t, false, applied) require.Equal(t, false, applied)
@ -1155,7 +1155,7 @@ func TestStateChanged(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
// create few blocks to make sure nothing get's randomly called // create few blocks to make sure nothing gets randomly called
fcs.advance(0, 4, 0, nil) // H=5 fcs.advance(0, 4, 0, nil) // H=5
require.Equal(t, false, applied) require.Equal(t, false, applied)

View File

@ -388,7 +388,7 @@ func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight a
if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight { if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight {
// Filter needs historic events // Filter needs historic events
if err := m.EventIndex.PrefillFilter(ctx, f); err != nil { if err := m.EventIndex.PrefillFilter(ctx, f, true); err != nil {
return nil, err return nil, err
} }
} }

View File

@ -481,7 +481,7 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever
} }
// PrefillFilter fills a filter's collection of events from the historic index // PrefillFilter fills a filter's collection of events from the historic index
func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, excludeReverted bool) error {
clauses := []string{} clauses := []string{}
values := []any{} values := []any{}
joins := []string{} joins := []string{}
@ -500,6 +500,11 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error {
} }
} }
if excludeReverted {
clauses = append(clauses, "event.reverted=?")
values = append(values, false)
}
if len(f.addresses) > 0 { if len(f.addresses) > 0 {
subclauses := []string{} subclauses := []string{}
for _, addr := range f.addresses { for _, addr := range f.addresses {

View File

@ -272,7 +272,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
tc := tc // appease lint tc := tc // appease lint
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
if err := ei.PrefillFilter(context.Background(), tc.filter); err != nil { if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil {
require.NoError(t, err, "prefill filter events") require.NoError(t, err, "prefill filter events")
} }
@ -281,3 +281,619 @@ func TestEventIndexPrefillFilter(t *testing.T) {
}) })
} }
} }
func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
rng := pseudo.New(pseudo.NewSource(299792458))
a1 := randomF4Addr(t, rng)
a2 := randomF4Addr(t, rng)
a3 := randomF4Addr(t, rng)
a1ID := abi.ActorID(1)
a2ID := abi.ActorID(2)
addrMap := addressMap{}
addrMap.add(a1ID, a1)
addrMap.add(a2ID, a2)
ev1 := fakeEvent(
a1ID,
[]kv{
{k: "type", v: []byte("approval")},
{k: "signer", v: []byte("addr1")},
},
[]kv{
{k: "amount", v: []byte("2988181")},
},
)
ev2 := fakeEvent(
a2ID,
[]kv{
{k: "type", v: []byte("approval")},
{k: "signer", v: []byte("addr2")},
},
[]kv{
{k: "amount", v: []byte("2988182")},
},
)
st := newStore()
events := []*types.Event{ev1}
revertedEvents := []*types.Event{ev2}
em := executedMessage{
msg: fakeMessage(randomF4Addr(t, rng), randomF4Addr(t, rng)),
rct: fakeReceipt(t, rng, st, events),
evs: events,
}
revertedEm := executedMessage{
msg: fakeMessage(randomF4Addr(t, rng), randomF4Addr(t, rng)),
rct: fakeReceipt(t, rng, st, revertedEvents),
evs: revertedEvents,
}
events14000 := buildTipSetEvents(t, rng, 14000, em)
revertedEvents14000 := buildTipSetEvents(t, rng, 14000, revertedEm)
cid14000, err := events14000.msgTs.Key().Cid()
require.NoError(t, err, "tipset cid")
reveredCID14000, err := revertedEvents14000.msgTs.Key().Cid()
require.NoError(t, err, "tipset cid")
noCollectedEvents := []*CollectedEvent{}
oneCollectedEvent := []*CollectedEvent{
{
Entries: ev1.Entries,
EmitterAddr: a1,
EventIdx: 0,
Reverted: false,
Height: 14000,
TipSetKey: events14000.msgTs.Key(),
MsgIdx: 0,
MsgCid: em.msg.Cid(),
},
}
twoCollectedEvent := []*CollectedEvent{
{
Entries: ev1.Entries,
EmitterAddr: a1,
EventIdx: 0,
Reverted: false,
Height: 14000,
TipSetKey: events14000.msgTs.Key(),
MsgIdx: 0,
MsgCid: em.msg.Cid(),
},
{
Entries: ev2.Entries,
EmitterAddr: a2,
EventIdx: 0,
Reverted: true,
Height: 14000,
TipSetKey: revertedEvents14000.msgTs.Key(),
MsgIdx: 0,
MsgCid: revertedEm.msg.Cid(),
},
}
oneCollectedRevertedEvent := []*CollectedEvent{
{
Entries: ev2.Entries,
EmitterAddr: a2,
EventIdx: 0,
Reverted: true,
Height: 14000,
TipSetKey: revertedEvents14000.msgTs.Key(),
MsgIdx: 0,
MsgCid: revertedEm.msg.Cid(),
},
}
workDir, err := os.MkdirTemp("", "lotusevents")
require.NoError(t, err, "create temporary work directory")
defer func() {
_ = os.RemoveAll(workDir)
}()
t.Logf("using work dir %q", workDir)
dbPath := filepath.Join(workDir, "actorevents.db")
ei, err := NewEventIndex(context.Background(), dbPath, nil)
require.NoError(t, err, "create event index")
if err := ei.CollectEvents(context.Background(), revertedEvents14000, false, addrMap.ResolveAddress); err != nil {
require.NoError(t, err, "collect reverted events")
}
if err := ei.CollectEvents(context.Background(), revertedEvents14000, true, addrMap.ResolveAddress); err != nil {
require.NoError(t, err, "revert reverted events")
}
if err := ei.CollectEvents(context.Background(), events14000, false, addrMap.ResolveAddress); err != nil {
require.NoError(t, err, "collect events")
}
inclusiveTestCases := []struct {
name string
filter *EventFilter
te *TipSetEvents
want []*CollectedEvent
}{
{
name: "nomatch tipset min height",
filter: &EventFilter{
minHeight: 14001,
maxHeight: -1,
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch tipset max height",
filter: &EventFilter{
minHeight: -1,
maxHeight: 13999,
},
te: events14000,
want: noCollectedEvents,
},
{
name: "match tipset min height",
filter: &EventFilter{
minHeight: 14000,
maxHeight: -1,
},
te: events14000,
want: twoCollectedEvent,
},
{
name: "match tipset cid",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
tipsetCid: cid14000,
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match tipset cid",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
tipsetCid: reveredCID14000,
},
te: revertedEvents14000,
want: oneCollectedRevertedEvent,
},
{
name: "nomatch address",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a3},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "match address 2",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a2},
},
te: revertedEvents14000,
want: oneCollectedRevertedEvent,
},
{
name: "match address 1",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a1},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match one entry",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
},
},
te: events14000,
want: twoCollectedEvent,
},
{
name: "match one entry with alternate values",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("cancel"),
[]byte("propose"),
[]byte("approval"),
},
},
},
te: events14000,
want: twoCollectedEvent,
},
{
name: "nomatch one entry by missing value",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("cancel"),
[]byte("propose"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry by missing key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"method": {
[]byte("approval"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "match one entry with multiple keys",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr1"),
},
},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match one entry with multiple keys",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr2"),
},
},
},
te: revertedEvents14000,
want: oneCollectedRevertedEvent,
},
{
name: "nomatch one entry with one mismatching key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"approver": {
[]byte("addr1"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with one mismatching value",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr3"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with one unindexed key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"amount": {
[]byte("2988181"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with one unindexed key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"amount": {
[]byte("2988182"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
}
exclusiveTestCases := []struct {
name string
filter *EventFilter
te *TipSetEvents
want []*CollectedEvent
}{
{
name: "nomatch tipset min height",
filter: &EventFilter{
minHeight: 14001,
maxHeight: -1,
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch tipset max height",
filter: &EventFilter{
minHeight: -1,
maxHeight: 13999,
},
te: events14000,
want: noCollectedEvents,
},
{
name: "match tipset min height",
filter: &EventFilter{
minHeight: 14000,
maxHeight: -1,
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match tipset cid",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
tipsetCid: cid14000,
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match tipset cid but reverted",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
tipsetCid: reveredCID14000,
},
te: revertedEvents14000,
want: noCollectedEvents,
},
{
name: "nomatch address",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a3},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch address 2 but reverted",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a2},
},
te: revertedEvents14000,
want: noCollectedEvents,
},
{
name: "match address",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
addresses: []address.Address{a1},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match one entry",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "match one entry with alternate values",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("cancel"),
[]byte("propose"),
[]byte("approval"),
},
},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "nomatch one entry by missing value",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("cancel"),
[]byte("propose"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry by missing key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"method": {
[]byte("approval"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "match one entry with multiple keys",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr1"),
},
},
},
te: events14000,
want: oneCollectedEvent,
},
{
name: "nomatch one entry with one mismatching key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"approver": {
[]byte("addr1"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with matching reverted value",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr2"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with one mismatching value",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"type": {
[]byte("approval"),
},
"signer": {
[]byte("addr3"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
{
name: "nomatch one entry with one unindexed key",
filter: &EventFilter{
minHeight: -1,
maxHeight: -1,
keys: map[string][][]byte{
"amount": {
[]byte("2988181"),
},
},
},
te: events14000,
want: noCollectedEvents,
},
}
for _, tc := range inclusiveTestCases {
tc := tc // appease lint
t.Run(tc.name, func(t *testing.T) {
if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil {
require.NoError(t, err, "prefill filter events")
}
coll := tc.filter.TakeCollectedEvents(context.Background())
require.ElementsMatch(t, coll, tc.want, tc.name)
})
}
for _, tc := range exclusiveTestCases {
tc := tc // appease lint
t.Run(tc.name, func(t *testing.T) {
if err := ei.PrefillFilter(context.Background(), tc.filter, true); err != nil {
require.NoError(t, err, "prefill filter events")
}
coll := tc.filter.TakeCollectedEvents(context.Background())
require.ElementsMatch(t, coll, tc.want, tc.name)
})
}
}

View File

@ -1,25 +0,0 @@
package state
import (
"context"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
)
type contextStore struct {
ctx context.Context
cst *cbor.BasicIpldStore
}
func (cs *contextStore) Context() context.Context {
return cs.ctx
}
func (cs *contextStore) Get(ctx context.Context, c cid.Cid, out interface{}) error {
return cs.cst.Get(ctx, c, out)
}
func (cs *contextStore) Put(ctx context.Context, v interface{}) (cid.Cid, error) {
return cs.cst.Put(ctx, v)
}

View File

@ -247,7 +247,7 @@ func (c *client) processResponse(req *Request, res *Response, tipsets []*types.T
// If we didn't request the headers they should have been provided // If we didn't request the headers they should have been provided
// by the caller. // by the caller.
if len(tipsets) < len(res.Chain) { if len(tipsets) < len(res.Chain) {
return nil, xerrors.Errorf("not enought tipsets provided for message response validation, needed %d, have %d", len(res.Chain), len(tipsets)) return nil, xerrors.Errorf("not enough tipsets provided for message response validation, needed %d, have %d", len(res.Chain), len(tipsets))
} }
chain := make([]*BSTipSet, 0, resLength) chain := make([]*BSTipSet, 0, resLength)
for i, resChain := range res.Chain { for i, resChain := range res.Chain {
@ -284,16 +284,18 @@ func (c *client) validateCompressedIndices(chain []*BSTipSet) error {
len(msgs.SecpkIncludes), blocksNum) len(msgs.SecpkIncludes), blocksNum)
} }
blsLen := uint64(len(msgs.Bls))
secpLen := uint64(len(msgs.Secpk))
for blockIdx := 0; blockIdx < blocksNum; blockIdx++ { for blockIdx := 0; blockIdx < blocksNum; blockIdx++ {
for _, mi := range msgs.BlsIncludes[blockIdx] { for _, mi := range msgs.BlsIncludes[blockIdx] {
if int(mi) >= len(msgs.Bls) { if mi >= blsLen {
return xerrors.Errorf("index in BlsIncludes (%d) exceeds number of messages (%d)", return xerrors.Errorf("index in BlsIncludes (%d) exceeds number of messages (%d)",
mi, len(msgs.Bls)) mi, len(msgs.Bls))
} }
} }
for _, mi := range msgs.SecpkIncludes[blockIdx] { for _, mi := range msgs.SecpkIncludes[blockIdx] {
if int(mi) >= len(msgs.Secpk) { if mi >= secpLen {
return xerrors.Errorf("index in SecpkIncludes (%d) exceeds number of messages (%d)", return xerrors.Errorf("index in SecpkIncludes (%d) exceeds number of messages (%d)",
mi, len(msgs.Secpk)) mi, len(msgs.Secpk))
} }
@ -315,18 +317,36 @@ func (c *client) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int)
) )
} }
var ret []*types.TipSet
start := tsk.Cids()
for len(ret) < count {
req := &Request{ req := &Request{
Head: tsk.Cids(), Head: start,
Length: uint64(count), Length: uint64(count - len(ret)),
Options: Headers, Options: Headers,
} }
validRes, err := c.doRequest(ctx, req, nil, nil) validRes, err := c.doRequest(ctx, req, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to doRequest: %w", err)
} }
return validRes.tipsets, nil if len(validRes.tipsets) == 0 {
return nil, xerrors.Errorf("doRequest fetched zero tipsets: %w", err)
}
ret = append(ret, validRes.tipsets...)
last := validRes.tipsets[len(validRes.tipsets)-1]
if last.Height() <= 1 {
// we've walked all the way up to genesis, return
break
}
start = last.Parents().Cids()
}
return ret, nil
} }
// GetFullTipSet implements Client.GetFullTipSet(). Refer to the godocs there. // GetFullTipSet implements Client.GetFullTipSet(). Refer to the godocs there.
@ -341,12 +361,16 @@ func (c *client) GetFullTipSet(ctx context.Context, peer peer.ID, tsk types.TipS
validRes, err := c.doRequest(ctx, req, &peer, nil) validRes, err := c.doRequest(ctx, req, &peer, nil)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to doRequest: %w", err)
} }
return validRes.toFullTipSets()[0], nil fullTipsets := validRes.toFullTipSets()
// If `doRequest` didn't fail we are guaranteed to have at least
// *one* tipset here, so it's safe to index directly. if len(fullTipsets) == 0 {
return nil, xerrors.New("unexpectedly got no tipsets in exchange")
}
return fullTipsets[0], nil
} }
// GetChainMessages implements Client.GetChainMessages(). Refer to the godocs there. // GetChainMessages implements Client.GetChainMessages(). Refer to the godocs there.
@ -386,7 +410,7 @@ func (c *client) sendRequestToPeer(ctx context.Context, peer peer.ID, req *Reque
defer span.End() defer span.End()
if span.IsRecordingEvents() { if span.IsRecordingEvents() {
span.AddAttributes( span.AddAttributes(
trace.StringAttribute("peer", peer.Pretty()), trace.StringAttribute("peer", peer.String()),
) )
} }
defer func() { defer func() {

View File

@ -28,8 +28,8 @@ type Server interface {
// used by the Syncer. // used by the Syncer.
type Client interface { type Client interface {
// GetBlocks fetches block headers from the network, from the provided // GetBlocks fetches block headers from the network, from the provided
// tipset *backwards*, returning as many tipsets as the count parameter, // tipset *backwards*, returning as many tipsets as the count parameter.
// or less. // The ONLY case in which we return fewer than `count` tipsets is if we hit genesis.
GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ([]*types.TipSet, error) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ([]*types.TipSet, error)
// GetChainMessages fetches messages from the network, starting from the first provided tipset // GetChainMessages fetches messages from the network, starting from the first provided tipset

View File

@ -251,7 +251,8 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal
} }
params := &markettypes.PublishStorageDealsParams{} params := &markettypes.PublishStorageDealsParams{}
for _, preseal := range m.Sectors { for _, presealTmp := range m.Sectors {
preseal := presealTmp
preseal.Deal.VerifiedDeal = true preseal.Deal.VerifiedDeal = true
preseal.Deal.EndEpoch = minerInfos[i].presealExp preseal.Deal.EndEpoch = minerInfos[i].presealExp
p := markettypes.ClientDealProposal{ p := markettypes.ClientDealProposal{

View File

@ -131,7 +131,7 @@ func NewMsgIndex(lctx context.Context, basePath string, cs ChainStore) (MsgIndex
db, err := sql.Open("sqlite3", dbPath) db, err := sql.Open("sqlite3", dbPath)
if err != nil { if err != nil {
// TODO [nice to have]: automaticaly delete corrupt databases // TODO [nice to have]: automatically delete corrupt databases
// but for now we can just error and let the operator delete. // but for now we can just error and let the operator delete.
return nil, xerrors.Errorf("error opening msgindex database: %w", err) return nil, xerrors.Errorf("error opening msgindex database: %w", err)
} }

View File

@ -39,23 +39,6 @@ func (ps *Store) save(ctx context.Context, state *FundedAddressState) error {
return ps.ds.Put(ctx, k, b) return ps.ds.Put(ctx, k, b)
} }
// get the state for the given address
func (ps *Store) get(ctx context.Context, addr address.Address) (*FundedAddressState, error) {
k := dskeyForAddr(addr)
data, err := ps.ds.Get(ctx, k)
if err != nil {
return nil, err
}
var state FundedAddressState
err = cborrpc.ReadCborRPC(bytes.NewReader(data), &state)
if err != nil {
return nil, err
}
return &state, nil
}
// forEach calls iter with each address in the datastore // forEach calls iter with each address in the datastore
func (ps *Store) forEach(ctx context.Context, iter func(*FundedAddressState)) error { func (ps *Store) forEach(ctx context.Context, iter func(*FundedAddressState)) error {
res, err := ps.ds.Query(ctx, dsq.Query{Prefix: dsKeyAddr}) res, err := ps.ds.Query(ctx, dsq.Query{Prefix: dsKeyAddr})

View File

@ -5,7 +5,6 @@ import (
"math" "math"
"math/rand" "math/rand"
"testing" "testing"
"time"
) )
func TestBlockProbability(t *testing.T) { func TestBlockProbability(t *testing.T) {
@ -23,7 +22,6 @@ func TestBlockProbability(t *testing.T) {
func TestWinnerProba(t *testing.T) { func TestWinnerProba(t *testing.T) {
//stm: @OTHER_IMPLEMENTATION_BLOCK_PROB_002 //stm: @OTHER_IMPLEMENTATION_BLOCK_PROB_002
rand.Seed(time.Now().UnixNano())
const N = 1000000 const N = 1000000
winnerProba := noWinnersProb() winnerProba := noWinnersProb()
sum := 0 sum := 0

View File

@ -21,6 +21,7 @@ import (
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/minio/blake2b-simd" "github.com/minio/blake2b-simd"
"github.com/raulk/clock" "github.com/raulk/clock"
"go.opencensus.io/stats"
"golang.org/x/xerrors" "golang.org/x/xerrors"
ffi "github.com/filecoin-project/filecoin-ffi" ffi "github.com/filecoin-project/filecoin-ffi"
@ -210,8 +211,10 @@ func ComputeRBF(curPrem abi.TokenAmount, replaceByFeeRatio types.Percent) abi.To
func CapGasFee(mff dtypes.DefaultMaxFeeFunc, msg *types.Message, sendSpec *api.MessageSendSpec) { func CapGasFee(mff dtypes.DefaultMaxFeeFunc, msg *types.Message, sendSpec *api.MessageSendSpec) {
var maxFee abi.TokenAmount var maxFee abi.TokenAmount
var maximizeFeeCap bool
if sendSpec != nil { if sendSpec != nil {
maxFee = sendSpec.MaxFee maxFee = sendSpec.MaxFee
maximizeFeeCap = sendSpec.MaximizeFeeCap
} }
if maxFee.Int == nil || maxFee.Equals(big.Zero()) { if maxFee.Int == nil || maxFee.Equals(big.Zero()) {
mf, err := mff() mf, err := mff()
@ -222,15 +225,12 @@ func CapGasFee(mff dtypes.DefaultMaxFeeFunc, msg *types.Message, sendSpec *api.M
maxFee = mf maxFee = mf
} }
gl := types.NewInt(uint64(msg.GasLimit)) gaslimit := types.NewInt(uint64(msg.GasLimit))
totalFee := types.BigMul(msg.GasFeeCap, gl) totalFee := types.BigMul(msg.GasFeeCap, gaslimit)
if maximizeFeeCap || totalFee.GreaterThan(maxFee) {
if totalFee.LessThanEqual(maxFee) { msg.GasFeeCap = big.Div(maxFee, gaslimit)
msg.GasPremium = big.Min(msg.GasFeeCap, msg.GasPremium) // cap premium at FeeCap
return
} }
msg.GasFeeCap = big.Div(maxFee, gl)
msg.GasPremium = big.Min(msg.GasFeeCap, msg.GasPremium) // cap premium at FeeCap msg.GasPremium = big.Min(msg.GasFeeCap, msg.GasPremium) // cap premium at FeeCap
} }
@ -1022,6 +1022,9 @@ func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, st
} }
}) })
// Record the current size of the Mpool
stats.Record(ctx, metrics.MpoolMessageCount.M(int64(mp.currentSize)))
return nil return nil
} }
@ -1214,6 +1217,9 @@ func (mp *MessagePool) remove(ctx context.Context, from address.Address, nonce u
return return
} }
} }
// Record the current size of the Mpool
stats.Record(ctx, metrics.MpoolMessageCount.M(int64(mp.currentSize)))
} }
func (mp *MessagePool) Pending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) { func (mp *MessagePool) Pending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) {

View File

@ -438,7 +438,8 @@ func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
return cid.Undef, xerrors.Errorf("tried to flush state tree with snapshots on the stack") return cid.Undef, xerrors.Errorf("tried to flush state tree with snapshots on the stack")
} }
for addr, sto := range st.snaps.layers[0].actors { for addr, stoTmp := range st.snaps.layers[0].actors {
sto := stoTmp
if sto.Delete { if sto.Delete {
if err := st.root.Delete(abi.AddrKey(addr)); err != nil { if err := st.root.Delete(abi.AddrKey(addr)); err != nil {
return cid.Undef, err return cid.Undef, err
@ -570,7 +571,7 @@ func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error
} }
// no need to record anything here, there are no duplicates in the actors HAMT // no need to record anything here, there are no duplicates in the actors HAMT
// iself. // itself.
if _, ok := seen[addr]; ok { if _, ok := seen[addr]; ok {
return nil return nil
} }
@ -588,7 +589,7 @@ func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error
} }
// no need to record anything here, there are no duplicates in the actors HAMT // no need to record anything here, there are no duplicates in the actors HAMT
// iself. // itself.
if _, ok := seen[addr]; ok { if _, ok := seen[addr]; ok {
return nil return nil
} }

View File

@ -235,11 +235,6 @@ func (sm *StateManager) hasExpensiveForkBetween(parent, height abi.ChainEpoch) b
return false return false
} }
func (sm *StateManager) hasExpensiveFork(height abi.ChainEpoch) bool {
_, ok := sm.expensiveUpgrades[height]
return ok
}
func runPreMigration(ctx context.Context, sm *StateManager, fn PreMigrationFunc, cache *nv16.MemMigrationCache, ts *types.TipSet) { func runPreMigration(ctx context.Context, sm *StateManager, fn PreMigrationFunc, cache *nv16.MemMigrationCache, ts *types.TipSet) {
height := ts.Height() height := ts.Height()
parent := ts.ParentState() parent := ts.ParentState()

View File

@ -42,7 +42,7 @@ func TestIndexSeeks(t *testing.T) {
cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), filcns.Weight, nil) cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), filcns.Weight, nil)
defer cs.Close() //nolint:errcheck defer cs.Close() //nolint:errcheck
_, err = cs.Import(ctx, bytes.NewReader(gencar)) _, _, err = cs.Import(ctx, bytes.NewReader(gencar))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -212,13 +212,8 @@ func (cs *ChainStore) MessagesForTipset(ctx context.Context, ts *types.TipSet) (
var out []types.ChainMsg var out []types.ChainMsg
for _, bm := range bmsgs { for _, bm := range bmsgs {
for _, blsm := range bm.BlsMessages { out = append(out, bm.BlsMessages...)
out = append(out, blsm) out = append(out, bm.SecpkMessages...)
}
for _, secm := range bm.SecpkMessages {
out = append(out, secm)
}
} }
return out, nil return out, nil

View File

@ -60,7 +60,7 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo
}) })
} }
func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, error) { func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (head *types.TipSet, genesis *types.BlockHeader, err error) {
// TODO: writing only to the state blockstore is incorrect. // TODO: writing only to the state blockstore is incorrect.
// At this time, both the state and chain blockstores are backed by the // At this time, both the state and chain blockstores are backed by the
// universal store. When we physically segregate the stores, we will need // universal store. When we physically segregate the stores, we will need
@ -69,7 +69,7 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e
br, err := carv2.NewBlockReader(r) br, err := carv2.NewBlockReader(r)
if err != nil { if err != nil {
return nil, xerrors.Errorf("loadcar failed: %w", err) return nil, nil, xerrors.Errorf("loadcar failed: %w", err)
} }
s := cs.StateBlockstore() s := cs.StateBlockstore()
@ -80,27 +80,51 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e
putThrottle <- nil putThrottle <- nil
} }
if len(br.Roots) == 0 {
return nil, nil, xerrors.Errorf("no roots in snapshot car file")
}
nextTailCid := br.Roots[0]
var tailBlock types.BlockHeader
tailBlock.Height = abi.ChainEpoch(-1)
var buf []blocks.Block var buf []blocks.Block
for { for {
blk, err := br.Next() blk, err := br.Next()
if err != nil { if err != nil {
// we're at the end
if err == io.EOF { if err == io.EOF {
if len(buf) > 0 { if len(buf) > 0 {
if err := s.PutMany(ctx, buf); err != nil { if err := s.PutMany(ctx, buf); err != nil {
return nil, err return nil, nil, err
} }
} }
break break
} }
return nil, err return nil, nil, err
} }
// check for header block, looking for genesis
if blk.Cid() == nextTailCid && tailBlock.Height != 0 {
if err := tailBlock.UnmarshalCBOR(bytes.NewReader(blk.RawData())); err != nil {
return nil, nil, xerrors.Errorf("failed to unmarshal genesis block: %w", err)
}
if len(tailBlock.Parents) > 0 {
nextTailCid = tailBlock.Parents[0]
} else {
// note: even the 0th block has a parent linking to the cbor genesis block
return nil, nil, xerrors.Errorf("current block (epoch %d cid %s) has no parents", tailBlock.Height, tailBlock.Cid())
}
}
// append to batch
buf = append(buf, blk) buf = append(buf, blk)
if len(buf) > 1000 { if len(buf) > 1000 {
if lastErr := <-putThrottle; lastErr != nil { // consume one error to have the right to add one if lastErr := <-putThrottle; lastErr != nil { // consume one error to have the right to add one
return nil, lastErr return nil, nil, lastErr
} }
go func(buf []blocks.Block) { go func(buf []blocks.Block) {
@ -113,13 +137,17 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e
// check errors // check errors
for i := 0; i < parallelPuts; i++ { for i := 0; i < parallelPuts; i++ {
if lastErr := <-putThrottle; lastErr != nil { if lastErr := <-putThrottle; lastErr != nil {
return nil, lastErr return nil, nil, lastErr
} }
} }
if tailBlock.Height != 0 {
return nil, nil, xerrors.Errorf("expected genesis block to have height 0 (genesis), got %d: %s", tailBlock.Height, tailBlock.Cid())
}
root, err := cs.LoadTipSet(ctx, types.NewTipSetKey(br.Roots...)) root, err := cs.LoadTipSet(ctx, types.NewTipSetKey(br.Roots...))
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err) return nil, nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err)
} }
ts := root ts := root
@ -135,10 +163,10 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e
} }
if err := cs.PersistTipsets(ctx, tssToPersist); err != nil { if err := cs.PersistTipsets(ctx, tssToPersist); err != nil {
return nil, xerrors.Errorf("failed to persist tipsets: %w", err) return nil, nil, xerrors.Errorf("failed to persist tipsets: %w", err)
} }
return root, nil return root, &tailBlock, nil
} }
type walkSchedTaskType int type walkSchedTaskType int
@ -167,7 +195,7 @@ func (t walkSchedTaskType) String() string {
case dagTask: case dagTask:
return "dag" return "dag"
} }
panic(fmt.Sprintf("unknow task %d", t)) panic(fmt.Sprintf("unknown task %d", t))
} }
type walkTask struct { type walkTask struct {
@ -656,9 +684,7 @@ func (cs *ChainStore) WalkSnapshot(ctx context.Context, ts *types.TipSet, inclRe
} }
if b.Height > 0 { if b.Height > 0 {
for _, p := range b.Parents { blocksToWalk = append(blocksToWalk, b.Parents...)
blocksToWalk = append(blocksToWalk, p)
}
} else { } else {
// include the genesis block // include the genesis block
cids = append(cids, b.Parents...) cids = append(cids, b.Parents...)

View File

@ -118,7 +118,7 @@ func TestChainExportImport(t *testing.T) {
cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil) cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil)
defer cs.Close() //nolint:errcheck defer cs.Close() //nolint:errcheck
root, err := cs.Import(context.TODO(), buf) root, _, err := cs.Import(context.TODO(), buf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -153,7 +153,7 @@ func TestChainImportTipsetKeyCid(t *testing.T) {
cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil) cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil)
defer cs.Close() //nolint:errcheck defer cs.Close() //nolint:errcheck
root, err := cs.Import(ctx, buf) root, _, err := cs.Import(ctx, buf)
require.NoError(t, err) require.NoError(t, err)
require.Truef(t, root.Equals(last), "imported chain differed from exported chain") require.Truef(t, root.Equals(last), "imported chain differed from exported chain")
@ -202,7 +202,7 @@ func TestChainExportImportFull(t *testing.T) {
cs := store.NewChainStore(nbs, nbs, ds, filcns.Weight, nil) cs := store.NewChainStore(nbs, nbs, ds, filcns.Weight, nil)
defer cs.Close() //nolint:errcheck defer cs.Close() //nolint:errcheck
root, err := cs.Import(context.TODO(), buf) root, _, err := cs.Import(context.TODO(), buf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -516,7 +516,7 @@ func (v *IndexerMessageValidator) Validate(ctx context.Context, pid peer.ID, msg
return pubsub.ValidationReject return pubsub.ValidationReject
} }
if len(idxrMsg.ExtraData) == 0 { if len(idxrMsg.ExtraData) == 0 {
log.Debugw("ignoring messsage missing miner id", "peer", originPeer) log.Debugw("ignoring message missing miner id", "peer", originPeer)
return pubsub.ValidationIgnore return pubsub.ValidationIgnore
} }
@ -552,7 +552,7 @@ func (v *IndexerMessageValidator) Validate(ctx context.Context, pid peer.ID, msg
// Check that the miner ID maps to the peer that sent the message. // Check that the miner ID maps to the peer that sent the message.
err = v.authenticateMessage(ctx, minerAddr, originPeer) err = v.authenticateMessage(ctx, minerAddr, originPeer)
if err != nil { if err != nil {
log.Warnw("cannot authenticate messsage", "err", err, "peer", originPeer, "minerID", minerAddr) log.Warnw("cannot authenticate message", "err", err, "peer", originPeer, "minerID", minerAddr)
stats.Record(ctx, metrics.IndexerMessageValidationFailure.M(1)) stats.Record(ctx, metrics.IndexerMessageValidationFailure.M(1))
return pubsub.ValidationReject return pubsub.ValidationReject
} }

View File

@ -0,0 +1,61 @@
package ratelimit
import (
"testing"
)
func TestQueue(t *testing.T) {
const size = 3
q := &queue{buf: make([]int64, size)}
if q.len() != 0 {
t.Fatalf("q.len() = %d, expect 0", q.len())
}
if q.cap() != size {
t.Fatalf("q.cap() = %d, expect %d", q.cap(), size)
}
for i := int64(0); i < int64(size); i++ {
err := q.push(i)
if err != nil {
t.Fatalf("cannot push element %d", i)
}
}
if q.len() != size {
t.Fatalf("q.len() = %d, expect %d", q.len(), size)
}
err := q.push(int64(size))
if err != ErrRateLimitExceeded {
t.Fatalf("pushing element beyond capacity should have failed with err: %s, got %s", ErrRateLimitExceeded, err)
}
if q.front() != 0 {
t.Fatalf("q.front() = %d, expect 0", q.front())
}
if q.back() != int64(size-1) {
t.Fatalf("q.back() = %d, expect %d", q.back(), size-1)
}
popVal := q.pop()
if popVal != 0 {
t.Fatalf("q.pop() = %d, expect 0", popVal)
}
if q.len() != size-1 {
t.Fatalf("q.len() = %d, expect %d", q.len(), size-1)
}
// Testing truncation.
threshold := int64(1)
q.truncate(threshold)
if q.len() != 1 {
t.Fatalf("q.len() after truncate = %d, expect 1", q.len())
}
if q.front() != 2 {
t.Fatalf("q.front() after truncate = %d, expect 2", q.front())
}
}

View File

@ -844,7 +844,7 @@ loop:
return nil, xerrors.Errorf("failed to load next local tipset: %w", err) return nil, xerrors.Errorf("failed to load next local tipset: %w", err)
} }
if base.IsChildOf(knownParent) { if base.IsChildOf(knownParent) {
// common case: receiving a block thats potentially part of the same tipset as our best block // common case: receiving a block that's potentially part of the same tipset as our best block
return blockSet, nil return blockSet, nil
} }
@ -886,6 +886,35 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know
} }
} }
incomingParentsTsk := incoming.Parents()
commonParent := false
for _, incomingParent := range incomingParentsTsk.Cids() {
if known.Contains(incomingParent) {
commonParent = true
}
}
if commonParent {
// known contains at least one of incoming's Parents => the common ancestor is known's Parents (incoming's Grandparents)
// in this case, we need to return {incoming.Parents()}
incomingParents, err := syncer.store.LoadTipSet(ctx, incomingParentsTsk)
if err != nil {
// fallback onto the network
tips, err := syncer.Exchange.GetBlocks(ctx, incoming.Parents(), 1)
if err != nil {
return nil, xerrors.Errorf("failed to fetch incomingParents from the network: %w", err)
}
if len(tips) == 0 {
return nil, xerrors.Errorf("network didn't return any tipsets")
}
incomingParents = tips[0]
}
return []*types.TipSet{incomingParents}, nil
}
// TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? Yes. // TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? Yes.
// Would it not be better to ask in smaller chunks, given that an ~ForkLengthThreshold is very rare? // Would it not be better to ask in smaller chunks, given that an ~ForkLengthThreshold is very rare?
tips, err := syncer.Exchange.GetBlocks(ctx, incoming.Parents(), int(build.ForkLengthThreshold)) tips, err := syncer.Exchange.GetBlocks(ctx, incoming.Parents(), int(build.ForkLengthThreshold))

View File

@ -11,7 +11,6 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore" ds "github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p/core/peer"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -344,13 +343,6 @@ func (tu *syncTestUtil) addClientNode() int {
return len(tu.nds) - 1 return len(tu.nds) - 1
} }
func (tu *syncTestUtil) pid(n int) peer.ID {
nal, err := tu.nds[n].NetAddrsListen(tu.ctx)
require.NoError(tu.t, err)
return nal.ID
}
func (tu *syncTestUtil) connect(from, to int) { func (tu *syncTestUtil) connect(from, to int) {
toPI, err := tu.nds[to].NetAddrsListen(tu.ctx) toPI, err := tu.nds[to].NetAddrsListen(tu.ctx)
require.NoError(tu.t, err) require.NoError(tu.t, err)

View File

@ -7,9 +7,6 @@ import (
) )
func TestDecodeBlockMsg(t *testing.T) { func TestDecodeBlockMsg(t *testing.T) {
type args struct {
b []byte
}
tests := []struct { tests := []struct {
name string name string
data []byte data []byte

View File

@ -62,9 +62,14 @@ type EthTxArgs struct {
// - BlockHash // - BlockHash
// - BlockNumber // - BlockNumber
// - TransactionIndex // - TransactionIndex
// - From
// - Hash // - Hash
func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
// The from address is always an f410f address, never an ID or other address.
if !IsEthAddress(smsg.Message.From) {
return EthTx{}, xerrors.Errorf("sender must be an eth account, was %s", smsg.Message.From)
}
// Probably redundant, but we might as well check.
if smsg.Signature.Type != typescrypto.SigTypeDelegated { if smsg.Signature.Type != typescrypto.SigTypeDelegated {
return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type) return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type)
} }
@ -79,10 +84,18 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err) return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err)
} }
from, err := EthAddressFromFilecoinAddress(smsg.Message.From)
if err != nil {
// This should be impossible as we've already asserted that we have an EthAddress
// sender...
return EthTx{}, xerrors.Errorf("sender was not an eth account")
}
return EthTx{ return EthTx{
Nonce: EthUint64(txArgs.Nonce), Nonce: EthUint64(txArgs.Nonce),
ChainID: EthUint64(txArgs.ChainID), ChainID: EthUint64(txArgs.ChainID),
To: txArgs.To, To: txArgs.To,
From: from,
Value: EthBigInt(txArgs.Value), Value: EthBigInt(txArgs.Value),
Type: Eip1559TxType, Type: Eip1559TxType,
Gas: EthUint64(txArgs.GasLimit), Gas: EthUint64(txArgs.GasLimit),

View File

@ -799,6 +799,45 @@ func GetContractEthAddressFromCode(sender EthAddress, salt [32]byte, initcode []
return ethAddr, nil return ethAddr, nil
} }
// EthEstimateGasParams handles raw jsonrpc params for eth_estimateGas
type EthEstimateGasParams struct {
Tx EthCall
BlkParam *EthBlockNumberOrHash
}
func (e *EthEstimateGasParams) UnmarshalJSON(b []byte) error {
var params []json.RawMessage
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch len(params) {
case 2:
err = json.Unmarshal(params[1], &e.BlkParam)
if err != nil {
return err
}
fallthrough
case 1:
err = json.Unmarshal(params[0], &e.Tx)
if err != nil {
return err
}
default:
return xerrors.Errorf("expected 1 or 2 params, got %d", len(params))
}
return nil
}
func (e EthEstimateGasParams) MarshalJSON() ([]byte, error) {
if e.BlkParam != nil {
return json.Marshal([]interface{}{e.Tx, e.BlkParam})
}
return json.Marshal([]interface{}{e.Tx})
}
// EthFeeHistoryParams handles raw jsonrpc params for eth_feeHistory // EthFeeHistoryParams handles raw jsonrpc params for eth_feeHistory
type EthFeeHistoryParams struct { type EthFeeHistoryParams struct {
BlkCount EthUint64 BlkCount EthUint64

View File

@ -12,6 +12,9 @@ import (
type FIL BigInt type FIL BigInt
func (f FIL) String() string { func (f FIL) String() string {
if f.Int == nil {
return "0 FIL"
}
return f.Unitless() + " FIL" return f.Unitless() + " FIL"
} }

View File

@ -140,7 +140,7 @@ func (t *messageReceiptV0) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt: case cbg.MajNegativeInt:
extraI = int64(extra) extraI = int64(extra)
if extraI < 0 { if extraI < 0 {
return fmt.Errorf("int64 negative oveflow") return fmt.Errorf("int64 negative overflow")
} }
extraI = -1 - extraI extraI = -1 - extraI
default: default:
@ -186,7 +186,7 @@ func (t *messageReceiptV0) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt: case cbg.MajNegativeInt:
extraI = int64(extra) extraI = int64(extra)
if extraI < 0 { if extraI < 0 {
return fmt.Errorf("int64 negative oveflow") return fmt.Errorf("int64 negative overflow")
} }
extraI = -1 - extraI extraI = -1 - extraI
default: default:
@ -278,7 +278,7 @@ func (t *messageReceiptV1) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt: case cbg.MajNegativeInt:
extraI = int64(extra) extraI = int64(extra)
if extraI < 0 { if extraI < 0 {
return fmt.Errorf("int64 negative oveflow") return fmt.Errorf("int64 negative overflow")
} }
extraI = -1 - extraI extraI = -1 - extraI
default: default:
@ -324,7 +324,7 @@ func (t *messageReceiptV1) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt: case cbg.MajNegativeInt:
extraI = int64(extra) extraI = int64(extra)
if extraI < 0 { if extraI < 0 {
return fmt.Errorf("int64 negative oveflow") return fmt.Errorf("int64 negative overflow")
} }
extraI = -1 - extraI extraI = -1 - extraI
default: default:

View File

@ -27,24 +27,3 @@ type StateTree interface {
Version() StateTreeVersion Version() StateTreeVersion
} }
type storageWrapper struct {
s Storage
}
func (sw *storageWrapper) Put(i cbg.CBORMarshaler) (cid.Cid, error) {
c, err := sw.s.Put(i)
if err != nil {
return cid.Undef, err
}
return c, nil
}
func (sw *storageWrapper) Get(c cid.Cid, out cbg.CBORUnmarshaler) error {
if err := sw.s.Get(c, out); err != nil {
return err
}
return nil
}

View File

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
crand "crypto/rand"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
@ -145,7 +146,10 @@ func MakeUnsignedMessageVectors() []vectors.UnsignedMessageVector {
} }
params := make([]byte, 32) params := make([]byte, 32)
rand.Read(params) _, err = crand.Read(params)
if err != nil {
panic(err)
}
msg := &types.Message{ msg := &types.Message{
To: to, To: to,

View File

@ -1,6 +1,7 @@
package cli package cli
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -8,7 +9,6 @@ import (
"syscall" "syscall"
ufcli "github.com/urfave/cli/v2" ufcli "github.com/urfave/cli/v2"
"golang.org/x/xerrors"
) )
type PrintHelpErr struct { type PrintHelpErr struct {
@ -52,7 +52,7 @@ func RunApp(app *ufcli.App) {
fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) // nolint:errcheck fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) // nolint:errcheck
} }
var phe *PrintHelpErr var phe *PrintHelpErr
if xerrors.As(err, &phe) { if errors.As(err, &phe) {
_ = ufcli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name) _ = ufcli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name)
} }
os.Exit(1) os.Exit(1)

View File

@ -282,7 +282,7 @@ var NetDisconnect = &cli.Command{
fmt.Println("failure") fmt.Println("failure")
return err return err
} }
fmt.Printf("disconnect %s: ", pid.Pretty()) fmt.Printf("disconnect %s: ", pid)
err = api.NetDisconnect(ctx, pid) err = api.NetDisconnect(ctx, pid)
if err != nil { if err != nil {
fmt.Println("failure") fmt.Println("failure")
@ -312,7 +312,7 @@ var NetConnect = &cli.Command{
} }
for _, pi := range pis { for _, pi := range pis {
fmt.Printf("connect %s: ", pi.ID.Pretty()) fmt.Printf("connect %s: ", pi.ID)
err := api.NetConnect(ctx, pi) err := api.NetConnect(ctx, pi)
if err != nil { if err != nil {
fmt.Println("failure") fmt.Println("failure")
@ -847,7 +847,8 @@ var NetStatCmd = &cli.Command{
}) })
for _, stat := range stats { for _, stat := range stats {
printScope(&stat.stat, name+stat.name) tmp := stat.stat
printScope(&tmp, name+stat.name)
} }
} }

View File

@ -1920,8 +1920,29 @@ var StateSysActorCIDsCmd = &cli.Command{
if err != nil { if err != nil {
return err return err
} }
for name, cid := range actorsCids {
_, _ = fmt.Fprintf(tw, "%v\t%v\n", name, cid) var actorsCidTuples []struct {
actorName string
actorCid cid.Cid
}
for name, actorCid := range actorsCids {
keyVal := struct {
actorName string
actorCid cid.Cid
}{
actorName: name,
actorCid: actorCid,
}
actorsCidTuples = append(actorsCidTuples, keyVal)
}
sort.Slice(actorsCidTuples, func(i, j int) bool {
return actorsCidTuples[i].actorName < actorsCidTuples[j].actorName
})
for _, keyVal := range actorsCidTuples {
_, _ = fmt.Fprintf(tw, "%v\t%v\n", keyVal.actorName, keyVal.actorCid)
} }
return tw.Flush() return tw.Flush()
}, },

View File

@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/api/v0api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
cliutil "github.com/filecoin-project/lotus/cli/util"
) )
var SyncCmd = &cli.Command{ var SyncCmd = &cli.Command{
@ -262,6 +263,9 @@ func SyncWait(ctx context.Context, napi v0api.FullNode, watch bool) error {
} }
firstApp = state.VMApplied firstApp = state.VMApplied
// eta computes the ETA for the sync to complete (with a lookback of 10 processed items)
eta := cliutil.NewETA(10)
for { for {
state, err := napi.SyncState(ctx) state, err := napi.SyncState(ctx)
if err != nil { if err != nil {
@ -312,8 +316,10 @@ func SyncWait(ctx context.Context, napi v0api.FullNode, watch bool) error {
fmt.Print("\r\x1b[2K\x1b[A") fmt.Print("\r\x1b[2K\x1b[A")
} }
todo := theight - ss.Height
fmt.Printf("Worker: %d; Base: %d; Target: %d (diff: %d)\n", workerID, baseHeight, theight, heightDiff) fmt.Printf("Worker: %d; Base: %d; Target: %d (diff: %d)\n", workerID, baseHeight, theight, heightDiff)
fmt.Printf("State: %s; Current Epoch: %d; Todo: %d\n", ss.Stage, ss.Height, theight-ss.Height) fmt.Printf("State: %s; Current Epoch: %d; Todo: %d, ETA: %s\n", ss.Stage, ss.Height, todo, eta.Update(int64(todo)))
lastLines = 2 lastLines = 2
if i%samples == 0 { if i%samples == 0 {

View File

@ -119,7 +119,7 @@ func GetAPIInfoMulti(ctx *cli.Context, t repo.RepoType) ([]APIInfo, error) {
} }
} }
return []APIInfo{}, fmt.Errorf("could not determine API endpoint for node type: %v", t.Type()) return []APIInfo{}, fmt.Errorf("could not determine API endpoint for node type: %v. Try setting environment variable: %s", t.Type(), primaryEnv)
} }
func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) { func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
@ -164,6 +164,28 @@ func GetRawAPIMulti(ctx *cli.Context, t repo.RepoType, version string) ([]HttpHe
return httpHeads, nil return httpHeads, nil
} }
func GetRawAPIMultiV2(ctx *cli.Context, ainfoCfg []string, version string) ([]HttpHead, error) {
var httpHeads []HttpHead
if len(ainfoCfg) == 0 {
return httpHeads, xerrors.Errorf("could not get API info: none configured. \nConsider getting base.toml with './lotus-provider config get base >/tmp/base.toml' \nthen adding \n[APIs] \n ChainApiInfo = [\" result_from lotus auth api-info --perm=admin \"]\n and updating it with './lotus-provider config set /tmp/base.toml'")
}
for _, i := range ainfoCfg {
ainfo := ParseApiInfo(i)
addr, err := ainfo.DialArgs(version)
if err != nil {
return httpHeads, xerrors.Errorf("could not get DialArgs: %w", err)
}
httpHeads = append(httpHeads, HttpHead{addr: addr, header: ainfo.AuthHeader()})
}
if IsVeryVerbose {
_, _ = fmt.Fprintf(ctx.App.Writer, "using raw API %s endpoint: %s\n", version, httpHeads[0].addr)
}
return httpHeads, nil
}
func GetRawAPI(ctx *cli.Context, t repo.RepoType, version string) (string, http.Header, error) { func GetRawAPI(ctx *cli.Context, t repo.RepoType, version string) (string, http.Header, error) {
heads, err := GetRawAPIMulti(ctx, t, version) heads, err := GetRawAPIMulti(ctx, t, version)
if err != nil { if err != nil {
@ -393,6 +415,68 @@ func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNo
return &v1API, finalCloser, nil return &v1API, finalCloser, nil
} }
func GetFullNodeAPIV1LotusProvider(ctx *cli.Context, ainfoCfg []string, opts ...GetFullNodeOption) (v1api.FullNode, jsonrpc.ClientCloser, error) {
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
return tn.(v1api.FullNode), func() {}, nil
}
var options GetFullNodeOptions
for _, opt := range opts {
opt(&options)
}
var rpcOpts []jsonrpc.Option
if options.ethSubHandler != nil {
rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.ethSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription"))
}
heads, err := GetRawAPIMultiV2(ctx, ainfoCfg, "v1")
if err != nil {
return nil, nil, err
}
if IsVeryVerbose {
_, _ = fmt.Fprintln(ctx.App.Writer, "using full node API v1 endpoint:", heads[0].addr)
}
var fullNodes []api.FullNode
var closers []jsonrpc.ClientCloser
for _, head := range heads {
v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...)
if err != nil {
log.Warnf("Not able to establish connection to node with addr: %s", head.addr)
continue
}
fullNodes = append(fullNodes, v1api)
closers = append(closers, closer)
}
// When running in cluster mode and trying to establish connections to multiple nodes, fail
// if less than 2 lotus nodes are actually running
if len(heads) > 1 && len(fullNodes) < 2 {
return nil, nil, xerrors.Errorf("Not able to establish connection to more than a single node")
}
finalCloser := func() {
for _, c := range closers {
c()
}
}
var v1API api.FullNodeStruct
FullNodeProxy(fullNodes, &v1API)
v, err := v1API.Version(ctx.Context)
if err != nil {
return nil, nil, err
}
if !v.APIVersion.EqMajorMinor(api.FullAPIVersion1) {
return nil, nil, xerrors.Errorf("Remote API version didn't match (expected %s, remote %s)", api.FullAPIVersion1, v.APIVersion)
}
return &v1API, finalCloser, nil
}
type GetStorageMinerOptions struct { type GetStorageMinerOptions struct {
PreferHttp bool PreferHttp bool
} }

94
cli/util/eta.go Normal file
View File

@ -0,0 +1,94 @@
package cliutil
import (
"fmt"
"math"
"time"
)
// ETA implements a very simple eta calculator based on the number of remaining items. It does not
// require knowing the work size in advance and is therefore suitable for streaming workloads and
// also does not require that consecutive updates have a monotonically decreasing remaining value.
type ETA struct {
// max number of items to keep in memory
maxItems int
// a queue of most recently updated items
items []item
// we store the last calculated ETA which we reuse if there was not change in remaining items
lastETA string
}
type item struct {
timestamp time.Time
remaining int64
}
// NewETA creates a new ETA calculator of the given size
func NewETA(maxItems int) *ETA {
return &ETA{
maxItems: maxItems,
items: make([]item, 0),
}
}
// Update updates the ETA calculator with the remaining number of items and returns the ETA
func (e *ETA) Update(remaining int64) string {
item := item{
timestamp: time.Now(),
remaining: remaining,
}
if len(e.items) == 0 {
e.items = append(e.items, item)
return ""
}
if e.items[len(e.items)-1].remaining == remaining {
// we ignore updates with the same remaining value and just return the previous ETA
return e.lastETA
} else if e.items[len(e.items)-1].remaining < remaining {
// remaining went up from previous update, lets estimate how many items were processed using the
// average number processed items in the queue.
var avgProcessedPerItem int64 = 1
if len(e.items) > 1 {
diffRemaining := e.items[0].remaining - e.items[len(e.items)-1].remaining
avgProcessedPerItem = int64(math.Round(float64(diffRemaining) / float64(len(e.items))))
}
// diff is the difference in increase in remaining since last update plus the average number of processed
// items we estimate that were processed this round
diff := remaining - e.items[len(e.items)-1].remaining + avgProcessedPerItem
// we update all items in the queue by shifting their remaining value accordingly. This means that we
// always have strictly decreasing remaining values in the queue
for i := range e.items {
e.items[i].remaining += diff
}
}
// append the item to the queue and remove the oldest item if needed
if len(e.items) >= e.maxItems {
e.items = e.items[1:]
}
e.items = append(e.items, item)
// calculate the average processing time per item in the queue
diffMs := e.items[len(e.items)-1].timestamp.Sub(e.items[0].timestamp).Milliseconds()
nrItemsProcessed := e.items[0].remaining - e.items[len(e.items)-1].remaining
avg := diffMs / nrItemsProcessed
// use that average processing time to estimate how long the remaining items will take
// and cache that ETA so we don't have to recalculate it on every update unless the
// remaining value changes
e.lastETA = msToETA(avg * remaining)
return e.lastETA
}
func msToETA(ms int64) string {
seconds := ms / 1000
minutes := seconds / 60
hours := minutes / 60
return fmt.Sprintf("%02dh:%02dm:%02ds", hours, minutes%60, seconds%60)
}

View File

@ -22,6 +22,7 @@ import (
"github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/tablewriter" "github.com/filecoin-project/lotus/lib/tablewriter"
) )
@ -459,7 +460,12 @@ var walletSign = &cli.Command{
sig, err := api.WalletSign(ctx, addr, msg) sig, err := api.WalletSign(ctx, addr, msg)
if err != nil { if err != nil {
return err // Check if the address is a multisig address
act, actErr := api.StateGetActor(ctx, addr, types.EmptyTSK)
if actErr == nil && builtin.IsMultisigActor(act.Code) {
return xerrors.Errorf("specified signer address is a multisig actor, it doesnt have keys to sign transactions. To send a message with a multisig, signers of the multisig need to propose and approve transactions.")
}
return xerrors.Errorf("failed to sign message: %w", err)
} }
sigBytes := append([]byte{byte(sig.Type)}, sig.Data...) sigBytes := append([]byte{byte(sig.Type)}, sig.Data...)

312
cmd/lotus-bench/cli.go Normal file
View File

@ -0,0 +1,312 @@
package main
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"strconv"
"strings"
"sync"
"time"
"github.com/urfave/cli/v2"
)
var cliCmd = &cli.Command{
Name: "cli",
Usage: "Runs a concurrent stress test on one or more binaries commands and prints the performance metrics including latency distribution and histogram",
Description: `This benchmark has the following features:
* Can query each command both sequentially and concurrently
* Supports rate limiting
* Can query multiple different commands at once (supporting different concurrency level and rate limiting for each command)
* Gives a nice reporting summary of the stress testing of each command (including latency distribution, histogram and more)
* Easy to use
To use this benchmark you must specify the commands you want to test using the --cmd options, the format of it is:
--cmd=CMD[:CONCURRENCY][:QPS] where only NAME is required.
Here are some real examples:
lotus-bench cli --cmd='lotus-shed mpool miner-select-messages' // runs the command with default concurrency and qps
lotus-bench cli --cmd='lotus-shed mpool miner-select-messages:3' // override concurrency to 3
lotus-bench cli --cmd='lotus-shed mpool miner-select-messages::100' // override to 100 qps while using default concurrency
lotus-bench cli --cmd='lotus-shed mpool miner-select-messages:3:100' // run using 3 workers but limit to 100 qps
lotus-bench cli --cmd='lotus-shed mpool miner-select-messages' --cmd='lotus sync wait' // run two commands at once
`,
Flags: []cli.Flag{
&cli.DurationFlag{
Name: "duration",
Value: 60 * time.Second,
Usage: "Duration of benchmark in seconds",
},
&cli.IntFlag{
Name: "concurrency",
Value: 10,
Usage: "How many workers should be used per command (can be overridden per command)",
},
&cli.IntFlag{
Name: "qps",
Value: 0,
Usage: "How many requests per second should be sent per command (can be overridden per command), a value of 0 means no limit",
},
&cli.StringSliceFlag{
Name: "cmd",
Usage: `Command to benchmark, you can specify multiple commands by repeating this flag. You can also specify command specific options to set the concurrency and qps for each command (see usage).`,
},
&cli.DurationFlag{
Name: "watch",
Value: 0 * time.Second,
Usage: "If >0 then generates reports every N seconds (only supports linux/unix)",
},
&cli.BoolFlag{
Name: "print-response",
Value: false,
Usage: "print the response of each request",
},
},
Action: func(cctx *cli.Context) error {
if len(cctx.StringSlice("cmd")) == 0 {
return errors.New("you must specify and least one cmd to benchmark")
}
var cmds []*CMD
for _, str := range cctx.StringSlice("cmd") {
entries := strings.SplitN(str, ":", 3)
if len(entries) == 0 {
return errors.New("invalid cmd format")
}
// check if concurrency was specified
concurrency := cctx.Int("concurrency")
if len(entries) > 1 {
if len(entries[1]) > 0 {
var err error
concurrency, err = strconv.Atoi(entries[1])
if err != nil {
return fmt.Errorf("could not parse concurrency value from command %s: %v", entries[0], err)
}
}
}
// check if qps was specified
qps := cctx.Int("qps")
if len(entries) > 2 {
if len(entries[2]) > 0 {
var err error
qps, err = strconv.Atoi(entries[2])
if err != nil {
return fmt.Errorf("could not parse qps value from command %s: %v", entries[0], err)
}
}
}
cmds = append(cmds, &CMD{
w: os.Stdout,
cmd: entries[0],
concurrency: concurrency,
qps: qps,
printResp: cctx.Bool("print-response"),
})
}
// terminate early on ctrl+c
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
fmt.Println("Received interrupt, stopping...")
for _, cmd := range cmds {
cmd.Stop()
}
}()
// stop all threads after duration
go func() {
time.Sleep(cctx.Duration("duration"))
for _, cmd := range cmds {
cmd.Stop()
}
}()
// start all threads
var wg sync.WaitGroup
wg.Add(len(cmds))
for _, cmd := range cmds {
go func(cmd *CMD) {
defer wg.Done()
err := cmd.Run()
if err != nil {
fmt.Printf("error running cmd: %v\n", err)
}
}(cmd)
}
// if watch is set then print a report every N seconds
var progressCh chan struct{}
if cctx.Duration("watch") > 0 {
progressCh = make(chan struct{}, 1)
go func(progressCh chan struct{}) {
ticker := time.NewTicker(cctx.Duration("watch"))
for {
clearAndPrintReport := func() {
// clear the screen move the cursor to the top left
fmt.Print("\033[2J")
fmt.Printf("\033[%d;%dH", 1, 1)
for i, cmd := range cmds {
cmd.Report()
if i < len(cmds)-1 {
fmt.Println()
}
}
}
select {
case <-ticker.C:
clearAndPrintReport()
case <-progressCh:
clearAndPrintReport()
return
}
}
}(progressCh)
}
wg.Wait()
if progressCh != nil {
// wait for the watch go routine to return
progressCh <- struct{}{}
// no need to print the report again
return nil
}
// print the report for each command
for i, cmd := range cmds {
cmd.Report()
if i < len(cmds)-1 {
fmt.Println()
}
}
return nil
},
}
// CMD handles the benchmarking of a single command.
type CMD struct {
w io.Writer
// the cmd we want to benchmark
cmd string
// the number of concurrent requests to make to this command
concurrency int
// if >0 then limit to qps is the max number of requests per second to make to this command (0 = no limit)
qps int
// whether or not to print the response of each request (useful for debugging)
printResp bool
// instruct the worker go routines to stop
stopCh chan struct{}
// when the command bencharking started
start time.Time
// results channel is used by the workers to send results to the reporter
results chan *result
// reporter handles reading the results from workers and printing the report statistics
reporter *Reporter
}
func (c *CMD) Run() error {
var wg sync.WaitGroup
wg.Add(c.concurrency)
c.results = make(chan *result, c.concurrency*1_000)
c.stopCh = make(chan struct{}, c.concurrency)
go func() {
c.reporter = NewReporter(c.results, c.w)
c.reporter.Run()
}()
c.start = time.Now()
// throttle the number of requests per second
var qpsTicker *time.Ticker
if c.qps > 0 {
qpsTicker = time.NewTicker(time.Second / time.Duration(c.qps))
}
for i := 0; i < c.concurrency; i++ {
go func() {
c.startWorker(qpsTicker)
wg.Done()
}()
}
wg.Wait()
// close the results channel so reporter will stop
close(c.results)
// wait until the reporter is done
<-c.reporter.doneCh
return nil
}
func (c *CMD) startWorker(qpsTicker *time.Ticker) {
for {
// check if we should stop
select {
case <-c.stopCh:
return
default:
}
// wait for the next tick if we are rate limiting this command
if qpsTicker != nil {
<-qpsTicker.C
}
start := time.Now()
var statusCode int = 0
arr := strings.Fields(c.cmd)
data, err := exec.Command(arr[0], arr[1:]...).Output()
if err != nil {
fmt.Println("1")
if exitError, ok := err.(*exec.ExitError); ok {
statusCode = exitError.ExitCode()
} else {
statusCode = 1
}
} else {
if c.printResp {
fmt.Printf("[%s] %s", c.cmd, string(data))
}
}
c.results <- &result{
statusCode: &statusCode,
err: err,
duration: time.Since(start),
}
}
}
func (c *CMD) Stop() {
for i := 0; i < c.concurrency; i++ {
c.stopCh <- struct{}{}
}
}
func (c *CMD) Report() {
total := time.Since(c.start)
fmt.Fprintf(c.w, "[%s]:\n", c.cmd)
fmt.Fprintf(c.w, "- Options:\n")
fmt.Fprintf(c.w, " - concurrency: %d\n", c.concurrency)
fmt.Fprintf(c.w, " - qps: %d\n", c.qps)
c.reporter.Print(total, c.w)
}

View File

@ -304,7 +304,7 @@ var importBenchCmd = &cli.Command{
return fmt.Errorf("no CAR file provided for import") return fmt.Errorf("no CAR file provided for import")
} }
head, err = cs.Import(cctx.Context, carFile) head, _, err = cs.Import(cctx.Context, carFile)
if err != nil { if err != nil {
return err return err
} }
@ -497,21 +497,6 @@ type Invocation struct {
const GasPerNs = 10 const GasPerNs = 10
func countGasCosts(et *types.ExecutionTrace) int64 {
var cgas int64
for _, gc := range et.GasCharges {
cgas += gc.ComputeGas
}
for _, sub := range et.Subcalls {
c := countGasCosts(&sub) //nolint
cgas += c
}
return cgas
}
type stats struct { type stats struct {
timeTaken meanVar timeTaken meanVar
gasRatio meanVar gasRatio meanVar

View File

@ -3,10 +3,10 @@ package main
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/rand"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "math/big"
"math/rand"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -120,6 +120,7 @@ func main() {
sealBenchCmd, sealBenchCmd,
simpleCmd, simpleCmd,
importBenchCmd, importBenchCmd,
cliCmd,
rpcCmd, rpcCmd,
}, },
} }
@ -546,7 +547,10 @@ var sealBenchCmd = &cli.Command{
} }
var challenge [32]byte var challenge [32]byte
rand.Read(challenge[:]) _, err = rand.Read(challenge[:])
if err != nil {
return err
}
beforePost := time.Now() beforePost := time.Now()
@ -776,9 +780,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
start := time.Now() start := time.Now()
log.Infof("[%d] Writing piece into sector...", i) log.Infof("[%d] Writing piece into sector...", i)
r := rand.New(rand.NewSource(100 + int64(i))) pi, err := sb.AddPiece(context.TODO(), sid, nil, abi.PaddedPieceSize(sectorSize).Unpadded(), rand.Reader)
pi, err := sb.AddPiece(context.TODO(), sid, nil, abi.PaddedPieceSize(sectorSize).Unpadded(), r)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

181
cmd/lotus-bench/reporter.go Normal file
View File

@ -0,0 +1,181 @@
package main
import (
"fmt"
"io"
"sort"
"strings"
"sync"
"text/tabwriter"
"time"
)
// result is the result of a single rpc method request.
type result struct {
err error
statusCode *int
duration time.Duration
}
// Reporter reads the results from the workers through the results channel and aggregates the results.
type Reporter struct {
// write the report to this writer
w io.Writer
// the reporter read the results from this channel
results chan *result
// doneCh is used to signal that the reporter has finished reading the results (channel has closed)
doneCh chan bool
// lock protect the following fields during critical sections (if --watch was specified)
lock sync.Mutex
// the latencies of all requests
latencies []int64
// the number of requests that returned each status code
statusCodes map[int]int
// the number of errors that occurred
errors map[string]int
}
func NewReporter(results chan *result, w io.Writer) *Reporter {
return &Reporter{
w: w,
results: results,
doneCh: make(chan bool, 1),
statusCodes: make(map[int]int),
errors: make(map[string]int),
}
}
func (r *Reporter) Run() {
for res := range r.results {
r.lock.Lock()
r.latencies = append(r.latencies, res.duration.Milliseconds())
if res.statusCode != nil {
r.statusCodes[*res.statusCode]++
}
if res.err != nil {
if len(r.errors) < 1_000_000 {
r.errors[res.err.Error()]++
} else {
// we don't want to store too many errors in memory
r.errors["hidden"]++
}
} else {
r.errors["nil"]++
}
r.lock.Unlock()
}
r.doneCh <- true
}
func (r *Reporter) Print(elapsed time.Duration, w io.Writer) {
r.lock.Lock()
defer r.lock.Unlock()
nrReq := int64(len(r.latencies))
if nrReq == 0 {
fmt.Println("No requests were made")
return
}
// we need to sort the latencies slice to calculate the percentiles
sort.Slice(r.latencies, func(i, j int) bool {
return r.latencies[i] < r.latencies[j]
})
var totalLatency int64 = 0
for _, latency := range r.latencies {
totalLatency += latency
}
fmt.Fprintf(w, "- Total Requests: %d\n", nrReq)
fmt.Fprintf(w, "- Total Duration: %dms\n", elapsed.Milliseconds())
fmt.Fprintf(w, "- Requests/sec: %f\n", float64(nrReq)/elapsed.Seconds())
fmt.Fprintf(w, "- Avg latency: %dms\n", totalLatency/nrReq)
fmt.Fprintf(w, "- Median latency: %dms\n", r.latencies[nrReq/2])
fmt.Fprintf(w, "- Latency distribution:\n")
percentiles := []float64{0.1, 0.5, 0.9, 0.95, 0.99, 0.999}
for _, p := range percentiles {
idx := int64(p * float64(nrReq))
fmt.Fprintf(w, " %s%% in %dms\n", fmt.Sprintf("%.2f", p*100.0), r.latencies[idx])
}
// create a simple histogram with 10 buckets spanning the range of latency
// into equal ranges
//
nrBucket := 10
buckets := make([]Bucket, nrBucket)
latencyRange := r.latencies[len(r.latencies)-1]
bucketRange := latencyRange / int64(nrBucket)
// mark the end of each bucket
for i := 0; i < nrBucket; i++ {
buckets[i].start = int64(i) * bucketRange
buckets[i].end = buckets[i].start + bucketRange
// extend the last bucked by any remaning range caused by the integer division
if i == nrBucket-1 {
buckets[i].end = latencyRange
}
}
// count the number of requests in each bucket
currBucket := 0
for i := 0; i < len(r.latencies); {
if r.latencies[i] <= buckets[currBucket].end {
buckets[currBucket].cnt++
i++
} else {
currBucket++
}
}
// print the histogram using a tabwriter which will align the columns nicely
fmt.Fprintf(w, "- Histogram:\n")
const padding = 2
tabWriter := tabwriter.NewWriter(w, 0, 0, padding, ' ', tabwriter.AlignRight|tabwriter.Debug)
for i := 0; i < nrBucket; i++ {
ratio := float64(buckets[i].cnt) / float64(nrReq)
bars := strings.Repeat("#", int(ratio*100))
fmt.Fprintf(tabWriter, " %d-%dms\t%d\t%s (%s%%)\n", buckets[i].start, buckets[i].end, buckets[i].cnt, bars, fmt.Sprintf("%.2f", ratio*100))
}
tabWriter.Flush() //nolint:errcheck
fmt.Fprintf(w, "- Status codes:\n")
for code, cnt := range r.statusCodes {
fmt.Fprintf(w, " [%d]: %d\n", code, cnt)
}
// print the 10 most occurring errors (in case error values are not unique)
//
type kv struct {
err string
cnt int
}
var sortedErrors []kv
for err, cnt := range r.errors {
sortedErrors = append(sortedErrors, kv{err, cnt})
}
sort.Slice(sortedErrors, func(i, j int) bool {
return sortedErrors[i].cnt > sortedErrors[j].cnt
})
fmt.Fprintf(w, "- Errors (top 10):\n")
for i, se := range sortedErrors {
if i > 10 {
break
}
fmt.Fprintf(w, " [%s]: %d\n", se.err, se.cnt)
}
}
type Bucket struct {
start int64
// the end value of the bucket
end int64
// how many entries are in the bucket
cnt int
}

View File

@ -9,11 +9,9 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"text/tabwriter"
"time" "time"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -243,13 +241,6 @@ type RPCMethod struct {
reporter *Reporter reporter *Reporter
} }
// result is the result of a single rpc method request.
type result struct {
err error
statusCode *int
duration time.Duration
}
func (rpc *RPCMethod) Run() error { func (rpc *RPCMethod) Run() error {
client := &http.Client{ client := &http.Client{
Timeout: 0, Timeout: 0,
@ -411,166 +402,3 @@ func (rpc *RPCMethod) Report() {
fmt.Fprintf(rpc.w, " - qps: %d\n", rpc.qps) fmt.Fprintf(rpc.w, " - qps: %d\n", rpc.qps)
rpc.reporter.Print(total, rpc.w) rpc.reporter.Print(total, rpc.w)
} }
// Reporter reads the results from the workers through the results channel and aggregates the results.
type Reporter struct {
// write the report to this writer
w io.Writer
// the reporter read the results from this channel
results chan *result
// doneCh is used to signal that the reporter has finished reading the results (channel has closed)
doneCh chan bool
// lock protect the following fields during critical sections (if --watch was specified)
lock sync.Mutex
// the latencies of all requests
latencies []int64
// the number of requests that returned each status code
statusCodes map[int]int
// the number of errors that occurred
errors map[string]int
}
func NewReporter(results chan *result, w io.Writer) *Reporter {
return &Reporter{
w: w,
results: results,
doneCh: make(chan bool, 1),
statusCodes: make(map[int]int),
errors: make(map[string]int),
}
}
func (r *Reporter) Run() {
for res := range r.results {
r.lock.Lock()
r.latencies = append(r.latencies, res.duration.Milliseconds())
if res.statusCode != nil {
r.statusCodes[*res.statusCode]++
}
if res.err != nil {
if len(r.errors) < 1_000_000 {
r.errors[res.err.Error()]++
} else {
// we don't want to store too many errors in memory
r.errors["hidden"]++
}
} else {
r.errors["nil"]++
}
r.lock.Unlock()
}
r.doneCh <- true
}
func (r *Reporter) Print(elapsed time.Duration, w io.Writer) {
r.lock.Lock()
defer r.lock.Unlock()
nrReq := int64(len(r.latencies))
if nrReq == 0 {
fmt.Println("No requests were made")
return
}
// we need to sort the latencies slice to calculate the percentiles
sort.Slice(r.latencies, func(i, j int) bool {
return r.latencies[i] < r.latencies[j]
})
var totalLatency int64 = 0
for _, latency := range r.latencies {
totalLatency += latency
}
fmt.Fprintf(w, "- Total Requests: %d\n", nrReq)
fmt.Fprintf(w, "- Total Duration: %dms\n", elapsed.Milliseconds())
fmt.Fprintf(w, "- Requests/sec: %f\n", float64(nrReq)/elapsed.Seconds())
fmt.Fprintf(w, "- Avg latency: %dms\n", totalLatency/nrReq)
fmt.Fprintf(w, "- Median latency: %dms\n", r.latencies[nrReq/2])
fmt.Fprintf(w, "- Latency distribution:\n")
percentiles := []float64{0.1, 0.5, 0.9, 0.95, 0.99, 0.999}
for _, p := range percentiles {
idx := int64(p * float64(nrReq))
fmt.Fprintf(w, " %s%% in %dms\n", fmt.Sprintf("%.2f", p*100.0), r.latencies[idx])
}
// create a simple histogram with 10 buckets spanning the range of latency
// into equal ranges
//
nrBucket := 10
buckets := make([]Bucket, nrBucket)
latencyRange := r.latencies[len(r.latencies)-1]
bucketRange := latencyRange / int64(nrBucket)
// mark the end of each bucket
for i := 0; i < nrBucket; i++ {
buckets[i].start = int64(i) * bucketRange
buckets[i].end = buckets[i].start + bucketRange
// extend the last bucked by any remaning range caused by the integer division
if i == nrBucket-1 {
buckets[i].end = latencyRange
}
}
// count the number of requests in each bucket
currBucket := 0
for i := 0; i < len(r.latencies); {
if r.latencies[i] <= buckets[currBucket].end {
buckets[currBucket].cnt++
i++
} else {
currBucket++
}
}
// print the histogram using a tabwriter which will align the columns nicely
fmt.Fprintf(w, "- Histogram:\n")
const padding = 2
tabWriter := tabwriter.NewWriter(w, 0, 0, padding, ' ', tabwriter.AlignRight|tabwriter.Debug)
for i := 0; i < nrBucket; i++ {
ratio := float64(buckets[i].cnt) / float64(nrReq)
bars := strings.Repeat("#", int(ratio*100))
fmt.Fprintf(tabWriter, " %d-%dms\t%d\t%s (%s%%)\n", buckets[i].start, buckets[i].end, buckets[i].cnt, bars, fmt.Sprintf("%.2f", ratio*100))
}
tabWriter.Flush() //nolint:errcheck
fmt.Fprintf(w, "- Status codes:\n")
for code, cnt := range r.statusCodes {
fmt.Fprintf(w, " [%d]: %d\n", code, cnt)
}
// print the 10 most occurring errors (in case error values are not unique)
//
type kv struct {
err string
cnt int
}
var sortedErrors []kv
for err, cnt := range r.errors {
sortedErrors = append(sortedErrors, kv{err, cnt})
}
sort.Slice(sortedErrors, func(i, j int) bool {
return sortedErrors[i].cnt > sortedErrors[j].cnt
})
fmt.Fprintf(w, "- Errors (top 10):\n")
for i, se := range sortedErrors {
if i > 10 {
break
}
fmt.Fprintf(w, " [%s]: %d\n", se.err, se.cnt)
}
}
type Bucket struct {
start int64
// the end value of the bucket
end int64
// how many entries are in the bucket
cnt int
}

View File

@ -266,7 +266,10 @@ var simplePreCommit1 = &cli.Command{
ProofType: spt(sectorSize, cctx.Bool("synthetic")), ProofType: spt(sectorSize, cctx.Bool("synthetic")),
} }
var ticket [32]byte // all zero ticket := [32]byte{}
for i := range ticket {
ticket[i] = 1
}
pieces, err := ParsePieceInfos(cctx, 3) pieces, err := ParsePieceInfos(cctx, 3)
if err != nil { if err != nil {
@ -305,7 +308,36 @@ var simplePreCommit2 = &cli.Command{
Name: "synthetic", Name: "synthetic",
Usage: "generate synthetic PoRep proofs", Usage: "generate synthetic PoRep proofs",
}, },
&cli.StringFlag{
Name: "external-pc2",
Usage: "command for computing PC2 externally",
}, },
},
Description: `Compute PreCommit2 inputs and seal a sector.
--external-pc2 can be used to compute the PreCommit2 inputs externally.
The flag behaves similarly to the related lotus-worker flag, using it in
lotus-bench may be useful for testing if the external PreCommit2 command is
invoked correctly.
The command will be called with a number of environment variables set:
* EXTSEAL_PC2_SECTOR_NUM: the sector number
* EXTSEAL_PC2_SECTOR_MINER: the miner id
* EXTSEAL_PC2_PROOF_TYPE: the proof type
* EXTSEAL_PC2_SECTOR_SIZE: the sector size in bytes
* EXTSEAL_PC2_CACHE: the path to the cache directory
* EXTSEAL_PC2_SEALED: the path to the sealed sector file (initialized with unsealed data by the caller)
* EXTSEAL_PC2_PC1OUT: output from rust-fil-proofs precommit1 phase (base64 encoded json)
The command is expected to:
* Create cache sc-02-data-tree-r* files
* Create cache sc-02-data-tree-c* files
* Create cache p_aux / t_aux files
* Transform the sealed file in place
Example invocation of lotus-bench as external executor:
'./lotus-bench simple precommit2 --sector-size $EXTSEAL_PC2_SECTOR_SIZE $EXTSEAL_PC2_SEALED $EXTSEAL_PC2_CACHE $EXTSEAL_PC2_PC1OUT'
`,
ArgsUsage: "[sealed] [cache] [pc1 out]", ArgsUsage: "[sealed] [cache] [pc1 out]",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
ctx := cctx.Context ctx := cctx.Context
@ -330,7 +362,18 @@ var simplePreCommit2 = &cli.Command{
storiface.FTSealed: cctx.Args().Get(0), storiface.FTSealed: cctx.Args().Get(0),
storiface.FTCache: cctx.Args().Get(1), storiface.FTCache: cctx.Args().Get(1),
} }
sealer, err := ffiwrapper.New(pp)
var opts []ffiwrapper.FFIWrapperOpt
if cctx.IsSet("external-pc2") {
extSeal := ffiwrapper.ExternalSealer{
PreCommit2: ffiwrapper.MakeExternPrecommit2(cctx.String("external-pc2")),
}
opts = append(opts, ffiwrapper.WithExternalSealCalls(extSeal))
}
sealer, err := ffiwrapper.New(pp, opts...)
if err != nil { if err != nil {
return err return err
} }
@ -420,7 +463,12 @@ var simpleCommit1 = &cli.Command{
start := time.Now() start := time.Now()
var ticket, seed [32]byte // all zero ticket := [32]byte{}
seed := [32]byte{}
for i := range ticket {
ticket[i] = 1
seed[i] = 1
}
commd, err := cid.Parse(cctx.Args().Get(2)) commd, err := cid.Parse(cctx.Args().Get(2))
if err != nil { if err != nil {
@ -650,6 +698,10 @@ var simpleWinningPost = &cli.Command{
Usage: "pass miner address (only necessary if using existing sectorbuilder)", Usage: "pass miner address (only necessary if using existing sectorbuilder)",
Value: "t01000", Value: "t01000",
}, },
&cli.BoolFlag{
Name: "show-inputs",
Usage: "output inputs for winning post generation",
},
}, },
ArgsUsage: "[sealed] [cache] [comm R] [sector num]", ArgsUsage: "[sealed] [cache] [comm R] [sector num]",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
@ -720,6 +772,17 @@ var simpleWinningPost = &cli.Command{
fmt.Printf("Vanilla %s (%s)\n", challenge.Sub(start), bps(sectorSize, 1, challenge.Sub(start))) fmt.Printf("Vanilla %s (%s)\n", challenge.Sub(start), bps(sectorSize, 1, challenge.Sub(start)))
fmt.Printf("Proof %s (%s)\n", end.Sub(challenge), bps(sectorSize, 1, end.Sub(challenge))) fmt.Printf("Proof %s (%s)\n", end.Sub(challenge), bps(sectorSize, 1, end.Sub(challenge)))
fmt.Println(base64.StdEncoding.EncodeToString(proof[0].ProofBytes)) fmt.Println(base64.StdEncoding.EncodeToString(proof[0].ProofBytes))
if cctx.Bool("show-inputs") {
fmt.Println("GenerateWinningPoStWithVanilla info:")
fmt.Printf(" wpt: %d\n", wpt)
fmt.Printf(" mid: %d\n", mid)
fmt.Printf(" rand: %x\n", rand)
fmt.Printf(" vp: %x\n", vp)
fmt.Printf(" proof: %x\n", proof)
}
return nil return nil
}, },
} }

View File

@ -143,7 +143,7 @@ var runCmd = &cli.Command{
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: "rate-limit-timeout", Name: "rate-limit-timeout",
Usage: "the maximum time to wait for the rate limter before returning an error to clients", Usage: "the maximum time to wait for the rate limiter before returning an error to clients",
Value: gateway.DefaultRateLimitTimeout, Value: gateway.DefaultRateLimitTimeout,
}, },
&cli.Int64Flag{ &cli.Int64Flag{

View File

@ -463,7 +463,7 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode
wsts := statestore.New(namespace.Wrap(mds, modules.WorkerCallsPrefix)) wsts := statestore.New(namespace.Wrap(mds, modules.WorkerCallsPrefix))
smsts := statestore.New(namespace.Wrap(mds, modules.ManagerWorkPrefix)) smsts := statestore.New(namespace.Wrap(mds, modules.ManagerWorkPrefix))
si := paths.NewIndex(nil) si := paths.NewMemIndex(nil)
lstor, err := paths.NewLocal(ctx, lr, si, nil) lstor, err := paths.NewLocal(ctx, lr, si, nil)
if err != nil { if err != nil {

Some files were not shown because too many files have changed in this diff Show More