resolved conflicts
This commit is contained in:
commit
4f0a96c9c7
@ -112,7 +112,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
command: make debug
|
command: make debug
|
||||||
|
|
||||||
test: &test
|
test:
|
||||||
description: |
|
description: |
|
||||||
Run tests with gotestsum.
|
Run tests with gotestsum.
|
||||||
parameters: &test-params
|
parameters: &test-params
|
||||||
@ -123,26 +123,20 @@ jobs:
|
|||||||
type: string
|
type: string
|
||||||
default: "-timeout 30m"
|
default: "-timeout 30m"
|
||||||
description: Flags passed to go test.
|
description: Flags passed to go test.
|
||||||
packages:
|
target:
|
||||||
type: string
|
type: string
|
||||||
default: "./..."
|
default: "./..."
|
||||||
description: Import paths of packages to be tested.
|
description: Import paths of packages to be tested.
|
||||||
winpost-test:
|
|
||||||
type: string
|
|
||||||
default: "0"
|
|
||||||
deadline-test:
|
|
||||||
type: string
|
|
||||||
default: "0"
|
|
||||||
proofs-log-test:
|
proofs-log-test:
|
||||||
type: string
|
type: string
|
||||||
default: "0"
|
default: "0"
|
||||||
test-suite-name:
|
suite:
|
||||||
type: string
|
type: string
|
||||||
default: unit
|
default: unit
|
||||||
description: Test suite name to report to CircleCI.
|
description: Test suite name to report to CircleCI.
|
||||||
gotestsum-format:
|
gotestsum-format:
|
||||||
type: string
|
type: string
|
||||||
default: pkgname-and-test-fails
|
default: standard-verbose
|
||||||
description: gotestsum format. https://github.com/gotestyourself/gotestsum#format
|
description: gotestsum format. https://github.com/gotestyourself/gotestsum#format
|
||||||
coverage:
|
coverage:
|
||||||
type: string
|
type: string
|
||||||
@ -150,7 +144,7 @@ jobs:
|
|||||||
description: Coverage flag. Set to the empty string to disable.
|
description: Coverage flag. Set to the empty string to disable.
|
||||||
codecov-upload:
|
codecov-upload:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: true
|
||||||
description: |
|
description: |
|
||||||
Upload coverage report to https://codecov.io/. Requires the codecov API token to be
|
Upload coverage report to https://codecov.io/. Requires the codecov API token to be
|
||||||
set as an environment variable for private projects.
|
set as an environment variable for private projects.
|
||||||
@ -168,26 +162,24 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: go test
|
name: go test
|
||||||
environment:
|
environment:
|
||||||
LOTUS_TEST_WINDOW_POST: << parameters.winpost-test >>
|
|
||||||
LOTUS_TEST_DEADLINE_TOGGLING: << parameters.deadline-test >>
|
|
||||||
TEST_RUSTPROOFS_LOGS: << parameters.proofs-log-test >>
|
TEST_RUSTPROOFS_LOGS: << parameters.proofs-log-test >>
|
||||||
SKIP_CONFORMANCE: "1"
|
SKIP_CONFORMANCE: "1"
|
||||||
command: |
|
command: |
|
||||||
mkdir -p /tmp/test-reports/<< parameters.test-suite-name >>
|
mkdir -p /tmp/test-reports/<< parameters.suite >>
|
||||||
mkdir -p /tmp/test-artifacts
|
mkdir -p /tmp/test-artifacts
|
||||||
gotestsum \
|
gotestsum \
|
||||||
--format << parameters.gotestsum-format >> \
|
--format << parameters.gotestsum-format >> \
|
||||||
--junitfile /tmp/test-reports/<< parameters.test-suite-name >>/junit.xml \
|
--junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \
|
||||||
--jsonfile /tmp/test-artifacts/<< parameters.test-suite-name >>.json \
|
--jsonfile /tmp/test-artifacts/<< parameters.suite >>.json \
|
||||||
-- \
|
-- \
|
||||||
<< parameters.coverage >> \
|
<< parameters.coverage >> \
|
||||||
<< parameters.go-test-flags >> \
|
<< parameters.go-test-flags >> \
|
||||||
<< parameters.packages >>
|
<< parameters.target >>
|
||||||
no_output_timeout: 30m
|
no_output_timeout: 30m
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: /tmp/test-reports
|
path: /tmp/test-reports
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: /tmp/test-artifacts/<< parameters.test-suite-name >>.json
|
path: /tmp/test-artifacts/<< parameters.suite >>.json
|
||||||
- when:
|
- when:
|
||||||
condition: << parameters.codecov-upload >>
|
condition: << parameters.codecov-upload >>
|
||||||
steps:
|
steps:
|
||||||
@ -198,26 +190,6 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
bash <(curl -s https://codecov.io/bash)
|
bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
test-chain:
|
|
||||||
<<: *test
|
|
||||||
test-node:
|
|
||||||
<<: *test
|
|
||||||
test-storage:
|
|
||||||
<<: *test
|
|
||||||
test-cli:
|
|
||||||
<<: *test
|
|
||||||
test-short:
|
|
||||||
<<: *test
|
|
||||||
test-window-post:
|
|
||||||
<<: *test
|
|
||||||
test-window-post-dispute:
|
|
||||||
<<: *test
|
|
||||||
test-deadline-toggling:
|
|
||||||
<<: *test
|
|
||||||
test-terminate:
|
|
||||||
<<: *test
|
|
||||||
check-proofs-multicore-sdr:
|
|
||||||
<<: *test
|
|
||||||
test-conformance:
|
test-conformance:
|
||||||
description: |
|
description: |
|
||||||
Run tests using a corpus of interoperable test vectors for Filecoin
|
Run tests using a corpus of interoperable test vectors for Filecoin
|
||||||
@ -462,7 +434,7 @@ jobs:
|
|||||||
name: prepare workspace
|
name: prepare workspace
|
||||||
command: |
|
command: |
|
||||||
mkdir appimage
|
mkdir appimage
|
||||||
mv Lotus-latest-x86_64.AppImage appimage
|
mv Lotus-*.AppImage appimage
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: "."
|
root: "."
|
||||||
paths:
|
paths:
|
||||||
@ -598,7 +570,6 @@ jobs:
|
|||||||
name: publish snap
|
name: publish snap
|
||||||
command: snapcraft push *.snap --release << parameters.channel >>
|
command: snapcraft push *.snap --release << parameters.channel >>
|
||||||
|
|
||||||
|
|
||||||
build-and-push-image:
|
build-and-push-image:
|
||||||
description: build and push docker images to public AWS ECR registry
|
description: build and push docker images to public AWS ECR registry
|
||||||
executor: aws-cli/default
|
executor: aws-cli/default
|
||||||
@ -776,64 +747,145 @@ workflows:
|
|||||||
- gen-check
|
- gen-check
|
||||||
- docs-check
|
- docs-check
|
||||||
- test:
|
- test:
|
||||||
codecov-upload: true
|
name: test-itest-api
|
||||||
test-suite-name: full
|
suite: itest-api
|
||||||
- test-chain:
|
target: "./itests/api_test.go"
|
||||||
codecov-upload: true
|
|
||||||
test-suite-name: chain
|
- test:
|
||||||
packages: "./chain/..."
|
name: test-itest-batch_deal
|
||||||
- test-node:
|
suite: itest-batch_deal
|
||||||
codecov-upload: true
|
target: "./itests/batch_deal_test.go"
|
||||||
test-suite-name: node
|
|
||||||
packages: "./node/..."
|
- test:
|
||||||
- test-storage:
|
name: test-itest-ccupgrade
|
||||||
codecov-upload: true
|
suite: itest-ccupgrade
|
||||||
test-suite-name: storage
|
target: "./itests/ccupgrade_test.go"
|
||||||
packages: "./storage/... ./extern/..."
|
|
||||||
- test-cli:
|
- test:
|
||||||
codecov-upload: true
|
name: test-itest-cli
|
||||||
test-suite-name: cli
|
suite: itest-cli
|
||||||
packages: "./cli/... ./cmd/... ./api/..."
|
target: "./itests/cli_test.go"
|
||||||
- test-window-post:
|
|
||||||
codecov-upload: true
|
- test:
|
||||||
go-test-flags: "-run=TestWindowedPost"
|
name: test-itest-deadlines
|
||||||
winpost-test: "1"
|
suite: itest-deadlines
|
||||||
test-suite-name: window-post
|
target: "./itests/deadlines_test.go"
|
||||||
- test-window-post-dispute:
|
|
||||||
codecov-upload: true
|
- test:
|
||||||
go-test-flags: "-run=TestWindowPostDispute"
|
name: test-itest-deals_concurrent
|
||||||
winpost-test: "1"
|
suite: itest-deals_concurrent
|
||||||
test-suite-name: window-post-dispute
|
target: "./itests/deals_concurrent_test.go"
|
||||||
- test-terminate:
|
|
||||||
codecov-upload: true
|
- test:
|
||||||
go-test-flags: "-run=TestTerminate"
|
name: test-itest-deals_offline
|
||||||
winpost-test: "1"
|
suite: itest-deals_offline
|
||||||
test-suite-name: terminate
|
target: "./itests/deals_offline_test.go"
|
||||||
- test-deadline-toggling:
|
|
||||||
codecov-upload: true
|
- test:
|
||||||
go-test-flags: "-run=TestDeadlineToggling"
|
name: test-itest-deals_power
|
||||||
deadline-test: "1"
|
suite: itest-deals_power
|
||||||
test-suite-name: deadline-toggling
|
target: "./itests/deals_power_test.go"
|
||||||
- test-short:
|
|
||||||
go-test-flags: "--timeout 10m --short"
|
- test:
|
||||||
test-suite-name: short
|
name: test-itest-deals_pricing
|
||||||
filters:
|
suite: itest-deals_pricing
|
||||||
tags:
|
target: "./itests/deals_pricing_test.go"
|
||||||
only:
|
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- test:
|
||||||
- check-proofs-multicore-sdr:
|
name: test-itest-deals_publish
|
||||||
codecov-upload: true
|
suite: itest-deals_publish
|
||||||
|
target: "./itests/deals_publish_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-deals
|
||||||
|
suite: itest-deals
|
||||||
|
target: "./itests/deals_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-gateway
|
||||||
|
suite: itest-gateway
|
||||||
|
target: "./itests/gateway_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-multisig
|
||||||
|
suite: itest-multisig
|
||||||
|
target: "./itests/multisig_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-paych_api
|
||||||
|
suite: itest-paych_api
|
||||||
|
target: "./itests/paych_api_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-paych_cli
|
||||||
|
suite: itest-paych_cli
|
||||||
|
target: "./itests/paych_cli_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-sdr_upgrade
|
||||||
|
suite: itest-sdr_upgrade
|
||||||
|
target: "./itests/sdr_upgrade_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-sector_pledge
|
||||||
|
suite: itest-sector_pledge
|
||||||
|
target: "./itests/sector_pledge_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-sector_terminate
|
||||||
|
suite: itest-sector_terminate
|
||||||
|
target: "./itests/sector_terminate_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-tape
|
||||||
|
suite: itest-tape
|
||||||
|
target: "./itests/tape_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-verifreg
|
||||||
|
suite: itest-verifreg
|
||||||
|
target: "./itests/verifreg_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-wdpost_dispute
|
||||||
|
suite: itest-wdpost_dispute
|
||||||
|
target: "./itests/wdpost_dispute_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-itest-wdpost
|
||||||
|
suite: itest-wdpost
|
||||||
|
target: "./itests/wdpost_test.go"
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: test-unit-cli
|
||||||
|
suite: utest-unit-cli
|
||||||
|
target: "./cli/... ./cmd/... ./api/..."
|
||||||
|
- test:
|
||||||
|
name: test-unit-node
|
||||||
|
suite: utest-unit-node
|
||||||
|
target: "./node/..."
|
||||||
|
- test:
|
||||||
|
name: test-unit-rest
|
||||||
|
suite: utest-unit-rest
|
||||||
|
target: "./api/... ./blockstore/... ./build/... ./chain/... ./cli/... ./cmd/... ./conformance/... ./extern/... ./gateway/... ./journal/... ./lib/... ./markets/... ./node/... ./paychmgr/... ./storage/... ./tools/..."
|
||||||
|
- test:
|
||||||
|
name: test-unit-storage
|
||||||
|
suite: utest-unit-storage
|
||||||
|
target: "./storage/... ./extern/..."
|
||||||
|
- test:
|
||||||
go-test-flags: "-run=TestMulticoreSDR"
|
go-test-flags: "-run=TestMulticoreSDR"
|
||||||
test-suite-name: multicore-sdr-check
|
suite: multicore-sdr-check
|
||||||
packages: "./extern/sector-storage/ffiwrapper"
|
target: "./extern/sector-storage/ffiwrapper"
|
||||||
proofs-log-test: "1"
|
proofs-log-test: "1"
|
||||||
- test-conformance:
|
- test-conformance:
|
||||||
test-suite-name: conformance
|
suite: conformance
|
||||||
packages: "./conformance"
|
codecov-upload: false
|
||||||
|
target: "./conformance"
|
||||||
- test-conformance:
|
- test-conformance:
|
||||||
name: test-conformance-bleeding-edge
|
name: test-conformance-bleeding-edge
|
||||||
test-suite-name: conformance-bleeding-edge
|
codecov-upload: false
|
||||||
packages: "./conformance"
|
suite: conformance-bleeding-edge
|
||||||
|
target: "./conformance"
|
||||||
vectors-branch: master
|
vectors-branch: master
|
||||||
- trigger-testplans:
|
- trigger-testplans:
|
||||||
filters:
|
filters:
|
||||||
@ -842,37 +894,27 @@ workflows:
|
|||||||
- master
|
- master
|
||||||
- build-debug
|
- build-debug
|
||||||
- build-all:
|
- build-all:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only:
|
only:
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
- build-ntwk-calibration:
|
- build-ntwk-calibration:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only:
|
only:
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
- build-ntwk-butterfly:
|
- build-ntwk-butterfly:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only:
|
only:
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
- build-ntwk-nerpa:
|
- build-ntwk-nerpa:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only:
|
only:
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
- build-lotus-soup
|
- build-lotus-soup
|
||||||
- build-macos:
|
- build-macos:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
@ -881,8 +923,6 @@ workflows:
|
|||||||
only:
|
only:
|
||||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
- build-appimage:
|
- build-appimage:
|
||||||
requires:
|
|
||||||
- test-short
|
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
|
136
.circleci/gen.go
Normal file
136
.circleci/gen.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run ./gen.go ..
|
||||||
|
|
||||||
|
//go:embed template.yml
|
||||||
|
var templateFile embed.FS
|
||||||
|
|
||||||
|
type (
|
||||||
|
dirs = []string
|
||||||
|
suite = string
|
||||||
|
)
|
||||||
|
|
||||||
|
// groupedUnitTests maps suite names to top-level directories that should be
|
||||||
|
// included in that suite. The program adds an implicit group "rest" that
|
||||||
|
// includes all other top-level directories.
|
||||||
|
var groupedUnitTests = map[suite]dirs{
|
||||||
|
"unit-node": {"node"},
|
||||||
|
"unit-storage": {"storage", "extern"},
|
||||||
|
"unit-cli": {"cli", "cmd", "api"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
panic("expected path to repo as argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := os.Args[1]
|
||||||
|
|
||||||
|
tmpl := template.New("template.yml")
|
||||||
|
tmpl.Delims("[[", "]]")
|
||||||
|
tmpl.Funcs(template.FuncMap{
|
||||||
|
"stripSuffix": func(in string) string {
|
||||||
|
return strings.TrimSuffix(in, "_test.go")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tmpl = template.Must(tmpl.ParseFS(templateFile, "*"))
|
||||||
|
|
||||||
|
// list all itests.
|
||||||
|
itests, err := filepath.Glob(filepath.Join(repo, "./itests/*_test.go"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip the dir from all entries.
|
||||||
|
for i, f := range itests {
|
||||||
|
itests[i] = filepath.Base(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the exclusion set of unit test directories to exclude because
|
||||||
|
// they are already included in a grouped suite.
|
||||||
|
var excluded = map[string]struct{}{}
|
||||||
|
for _, ss := range groupedUnitTests {
|
||||||
|
for _, s := range ss {
|
||||||
|
e, err := filepath.Abs(filepath.Join(repo, s))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
excluded[e] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all unit tests top-level dirs that are not itests, nor included in other suites.
|
||||||
|
var rest = map[string]struct{}{}
|
||||||
|
err = filepath.Walk(repo, func(path string, f os.FileInfo, err error) error {
|
||||||
|
// include all tests that aren't in the itests directory.
|
||||||
|
if strings.Contains(path, "itests") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
// exclude all tests included in other suites
|
||||||
|
if f.IsDir() {
|
||||||
|
if _, ok := excluded[path]; ok {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, "_test.go") {
|
||||||
|
rel, err := filepath.Rel(repo, path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// take the first directory
|
||||||
|
rest[strings.Split(rel, string(os.PathSeparator))[0]] = struct{}{}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add other directories to a 'rest' suite.
|
||||||
|
for k := range rest {
|
||||||
|
groupedUnitTests["unit-rest"] = append(groupedUnitTests["unit-rest"], k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// map iteration guarantees no order, so sort the array in-place.
|
||||||
|
sort.Strings(groupedUnitTests["unit-rest"])
|
||||||
|
|
||||||
|
// form the input data.
|
||||||
|
type data struct {
|
||||||
|
ItestFiles []string
|
||||||
|
UnitSuites map[string]string
|
||||||
|
}
|
||||||
|
in := data{
|
||||||
|
ItestFiles: itests,
|
||||||
|
UnitSuites: func() map[string]string {
|
||||||
|
ret := make(map[string]string)
|
||||||
|
for name, dirs := range groupedUnitTests {
|
||||||
|
for i, d := range dirs {
|
||||||
|
dirs[i] = fmt.Sprintf("./%s/...", d) // turn into package
|
||||||
|
}
|
||||||
|
ret[name] = strings.Join(dirs, " ")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create("./config.yml")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
// execute the template.
|
||||||
|
if err := tmpl.Execute(out, in); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
902
.circleci/template.yml
Normal file
902
.circleci/template.yml
Normal file
@ -0,0 +1,902 @@
|
|||||||
|
version: 2.1
|
||||||
|
orbs:
|
||||||
|
go: gotest/tools@0.0.13
|
||||||
|
aws-cli: circleci/aws-cli@1.3.2
|
||||||
|
packer: salaxander/packer@0.0.3
|
||||||
|
|
||||||
|
executors:
|
||||||
|
golang:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.16.4
|
||||||
|
resource_class: 2xlarge
|
||||||
|
ubuntu:
|
||||||
|
docker:
|
||||||
|
- image: ubuntu:20.04
|
||||||
|
|
||||||
|
commands:
|
||||||
|
install-deps:
|
||||||
|
steps:
|
||||||
|
- go/install-ssh
|
||||||
|
- go/install: {package: git}
|
||||||
|
prepare:
|
||||||
|
parameters:
|
||||||
|
linux:
|
||||||
|
default: true
|
||||||
|
description: is a linux build environment?
|
||||||
|
type: boolean
|
||||||
|
darwin:
|
||||||
|
default: false
|
||||||
|
description: is a darwin build environment?
|
||||||
|
type: boolean
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- git_fetch_all_tags
|
||||||
|
- checkout
|
||||||
|
- when:
|
||||||
|
condition: << parameters.linux >>
|
||||||
|
steps:
|
||||||
|
- run: sudo apt-get update
|
||||||
|
- run: sudo apt-get install ocl-icd-opencl-dev libhwloc-dev
|
||||||
|
- run: git submodule sync
|
||||||
|
- run: git submodule update --init
|
||||||
|
download-params:
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
name: Restore parameters cache
|
||||||
|
keys:
|
||||||
|
- 'v25-2k-lotus-params'
|
||||||
|
paths:
|
||||||
|
- /var/tmp/filecoin-proof-parameters/
|
||||||
|
- run: ./lotus fetch-params 2048
|
||||||
|
- save_cache:
|
||||||
|
name: Save parameters cache
|
||||||
|
key: 'v25-2k-lotus-params'
|
||||||
|
paths:
|
||||||
|
- /var/tmp/filecoin-proof-parameters/
|
||||||
|
install_ipfs:
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
apt update
|
||||||
|
apt install -y wget
|
||||||
|
wget https://github.com/ipfs/go-ipfs/releases/download/v0.4.22/go-ipfs_v0.4.22_linux-amd64.tar.gz
|
||||||
|
wget https://github.com/ipfs/go-ipfs/releases/download/v0.4.22/go-ipfs_v0.4.22_linux-amd64.tar.gz.sha512
|
||||||
|
if [ "$(sha512sum go-ipfs_v0.4.22_linux-amd64.tar.gz)" != "$(cat go-ipfs_v0.4.22_linux-amd64.tar.gz.sha512)" ]
|
||||||
|
then
|
||||||
|
echo "ipfs failed checksum check"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
tar -xf go-ipfs_v0.4.22_linux-amd64.tar.gz
|
||||||
|
mv go-ipfs/ipfs /usr/local/bin/ipfs
|
||||||
|
chmod +x /usr/local/bin/ipfs
|
||||||
|
git_fetch_all_tags:
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: fetch all tags
|
||||||
|
command: |
|
||||||
|
git fetch --all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mod-tidy-check:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- go/mod-tidy-check
|
||||||
|
|
||||||
|
build-all:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: sudo apt-get update
|
||||||
|
- run: sudo apt-get install npm
|
||||||
|
- run:
|
||||||
|
command: make buildall
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus-miner
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus-worker
|
||||||
|
- run: mkdir linux && mv lotus lotus-miner lotus-worker linux/
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- linux
|
||||||
|
|
||||||
|
build-debug:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
command: make debug
|
||||||
|
|
||||||
|
test:
|
||||||
|
description: |
|
||||||
|
Run tests with gotestsum.
|
||||||
|
parameters: &test-params
|
||||||
|
executor:
|
||||||
|
type: executor
|
||||||
|
default: golang
|
||||||
|
go-test-flags:
|
||||||
|
type: string
|
||||||
|
default: "-timeout 30m"
|
||||||
|
description: Flags passed to go test.
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
default: "./..."
|
||||||
|
description: Import paths of packages to be tested.
|
||||||
|
proofs-log-test:
|
||||||
|
type: string
|
||||||
|
default: "0"
|
||||||
|
suite:
|
||||||
|
type: string
|
||||||
|
default: unit
|
||||||
|
description: Test suite name to report to CircleCI.
|
||||||
|
gotestsum-format:
|
||||||
|
type: string
|
||||||
|
default: standard-verbose
|
||||||
|
description: gotestsum format. https://github.com/gotestyourself/gotestsum#format
|
||||||
|
coverage:
|
||||||
|
type: string
|
||||||
|
default: -coverprofile=coverage.txt -coverpkg=github.com/filecoin-project/lotus/...
|
||||||
|
description: Coverage flag. Set to the empty string to disable.
|
||||||
|
codecov-upload:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
description: |
|
||||||
|
Upload coverage report to https://codecov.io/. Requires the codecov API token to be
|
||||||
|
set as an environment variable for private projects.
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
command: make deps lotus
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- download-params
|
||||||
|
- go/install-gotestsum:
|
||||||
|
gobin: $HOME/.local/bin
|
||||||
|
version: 0.5.2
|
||||||
|
- run:
|
||||||
|
name: go test
|
||||||
|
environment:
|
||||||
|
TEST_RUSTPROOFS_LOGS: << parameters.proofs-log-test >>
|
||||||
|
SKIP_CONFORMANCE: "1"
|
||||||
|
command: |
|
||||||
|
mkdir -p /tmp/test-reports/<< parameters.suite >>
|
||||||
|
mkdir -p /tmp/test-artifacts
|
||||||
|
gotestsum \
|
||||||
|
--format << parameters.gotestsum-format >> \
|
||||||
|
--junitfile /tmp/test-reports/<< parameters.suite >>/junit.xml \
|
||||||
|
--jsonfile /tmp/test-artifacts/<< parameters.suite >>.json \
|
||||||
|
-- \
|
||||||
|
<< parameters.coverage >> \
|
||||||
|
<< parameters.go-test-flags >> \
|
||||||
|
<< parameters.target >>
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- store_test_results:
|
||||||
|
path: /tmp/test-reports
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/test-artifacts/<< parameters.suite >>.json
|
||||||
|
- when:
|
||||||
|
condition: << parameters.codecov-upload >>
|
||||||
|
steps:
|
||||||
|
- go/install: {package: bash}
|
||||||
|
- go/install: {package: curl}
|
||||||
|
- run:
|
||||||
|
shell: /bin/bash -eo pipefail
|
||||||
|
command: |
|
||||||
|
bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
|
test-conformance:
|
||||||
|
description: |
|
||||||
|
Run tests using a corpus of interoperable test vectors for Filecoin
|
||||||
|
implementations to test their correctness and compliance with the Filecoin
|
||||||
|
specifications.
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
vectors-branch:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: |
|
||||||
|
Branch on github.com/filecoin-project/test-vectors to checkout and
|
||||||
|
test with. If empty (the default) the commit defined by the git
|
||||||
|
submodule is used.
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
command: make deps lotus
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- download-params
|
||||||
|
- when:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
equal: [ "", << parameters.vectors-branch >> ]
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: checkout vectors branch
|
||||||
|
command: |
|
||||||
|
cd extern/test-vectors
|
||||||
|
git fetch
|
||||||
|
git checkout origin/<< parameters.vectors-branch >>
|
||||||
|
- go/install-gotestsum:
|
||||||
|
gobin: $HOME/.local/bin
|
||||||
|
version: 0.5.2
|
||||||
|
- run:
|
||||||
|
name: install statediff globally
|
||||||
|
command: |
|
||||||
|
## statediff is optional; we succeed even if compilation fails.
|
||||||
|
mkdir -p /tmp/statediff
|
||||||
|
git clone https://github.com/filecoin-project/statediff.git /tmp/statediff
|
||||||
|
cd /tmp/statediff
|
||||||
|
go install ./cmd/statediff || exit 0
|
||||||
|
- run:
|
||||||
|
name: go test
|
||||||
|
environment:
|
||||||
|
SKIP_CONFORMANCE: "0"
|
||||||
|
command: |
|
||||||
|
mkdir -p /tmp/test-reports
|
||||||
|
mkdir -p /tmp/test-artifacts
|
||||||
|
gotestsum \
|
||||||
|
--format pkgname-and-test-fails \
|
||||||
|
--junitfile /tmp/test-reports/junit.xml \
|
||||||
|
-- \
|
||||||
|
-v -coverpkg ./chain/vm/,github.com/filecoin-project/specs-actors/... -coverprofile=/tmp/conformance.out ./conformance/
|
||||||
|
go tool cover -html=/tmp/conformance.out -o /tmp/test-artifacts/conformance-coverage.html
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- store_test_results:
|
||||||
|
path: /tmp/test-reports
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/test-artifacts/conformance-coverage.html
|
||||||
|
build-ntwk-calibration:
|
||||||
|
description: |
|
||||||
|
Compile lotus binaries for the calibration network
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: make calibnet
|
||||||
|
- run: mkdir linux-calibrationnet && mv lotus lotus-miner lotus-worker linux-calibrationnet
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- linux-calibrationnet
|
||||||
|
build-ntwk-butterfly:
|
||||||
|
description: |
|
||||||
|
Compile lotus binaries for the butterfly network
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: make butterflynet
|
||||||
|
- run: mkdir linux-butterflynet && mv lotus lotus-miner lotus-worker linux-butterflynet
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- linux-butterflynet
|
||||||
|
build-ntwk-nerpa:
|
||||||
|
description: |
|
||||||
|
Compile lotus binaries for the nerpa network
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: make nerpanet
|
||||||
|
- run: mkdir linux-nerpanet && mv lotus lotus-miner lotus-worker linux-nerpanet
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- linux-nerpanet
|
||||||
|
build-lotus-soup:
|
||||||
|
description: |
|
||||||
|
Compile `lotus-soup` Testground test plan
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: cd extern/filecoin-ffi && make
|
||||||
|
- run:
|
||||||
|
name: "go get lotus@master"
|
||||||
|
command: cd testplans/lotus-soup && go mod edit -replace=github.com/filecoin-project/lotus=../.. && go mod tidy
|
||||||
|
- run:
|
||||||
|
name: "build lotus-soup testplan"
|
||||||
|
command: pushd testplans/lotus-soup && go build -tags=testground .
|
||||||
|
trigger-testplans:
|
||||||
|
description: |
|
||||||
|
Trigger `lotus-soup` test cases on TaaS
|
||||||
|
parameters:
|
||||||
|
<<: *test-params
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
name: "download testground"
|
||||||
|
command: wget https://gist.github.com/nonsense/5fbf3167cac79945f658771aed32fc44/raw/2e17eb0debf7ec6bdf027c1bdafc2c92dd97273b/testground-d3e9603 -O ~/testground-cli && chmod +x ~/testground-cli
|
||||||
|
- run:
|
||||||
|
name: "prepare .env.toml"
|
||||||
|
command: pushd testplans/lotus-soup && mkdir -p $HOME/testground && cp env-ci.toml $HOME/testground/.env.toml && echo 'endpoint="https://ci.testground.ipfs.team"' >> $HOME/testground/.env.toml && echo 'user="circleci"' >> $HOME/testground/.env.toml
|
||||||
|
- run:
|
||||||
|
name: "prepare testground home dir and link test plans"
|
||||||
|
command: mkdir -p $HOME/testground/plans && ln -s $(pwd)/testplans/lotus-soup $HOME/testground/plans/lotus-soup && ln -s $(pwd)/testplans/graphsync $HOME/testground/plans/graphsync
|
||||||
|
- run:
|
||||||
|
name: "go get lotus@master"
|
||||||
|
command: cd testplans/lotus-soup && go get github.com/filecoin-project/lotus@master
|
||||||
|
- run:
|
||||||
|
name: "trigger deals baseline testplan on taas"
|
||||||
|
command: ~/testground-cli run composition -f $HOME/testground/plans/lotus-soup/_compositions/baseline-k8s-3-1.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
|
||||||
|
- run:
|
||||||
|
name: "trigger payment channel stress testplan on taas"
|
||||||
|
command: ~/testground-cli run composition -f $HOME/testground/plans/lotus-soup/_compositions/paych-stress-k8s.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
|
||||||
|
- run:
|
||||||
|
name: "trigger graphsync testplan on taas"
|
||||||
|
command: ~/testground-cli run composition -f $HOME/testground/plans/graphsync/_compositions/stress-k8s.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
|
||||||
|
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
description: build darwin lotus binary
|
||||||
|
macos:
|
||||||
|
xcode: "10.0.0"
|
||||||
|
working_directory: ~/go/src/github.com/filecoin-project/lotus
|
||||||
|
steps:
|
||||||
|
- prepare:
|
||||||
|
linux: false
|
||||||
|
darwin: true
|
||||||
|
- run:
|
||||||
|
name: Install go
|
||||||
|
command: |
|
||||||
|
curl -O https://dl.google.com/go/go1.16.4.darwin-amd64.pkg && \
|
||||||
|
sudo installer -pkg go1.16.4.darwin-amd64.pkg -target /
|
||||||
|
- run:
|
||||||
|
name: Install pkg-config
|
||||||
|
command: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config
|
||||||
|
- run: go version
|
||||||
|
- run:
|
||||||
|
name: Install Rust
|
||||||
|
command: |
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
- run:
|
||||||
|
name: Install jq
|
||||||
|
command: |
|
||||||
|
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output /usr/local/bin/jq
|
||||||
|
chmod +x /usr/local/bin/jq
|
||||||
|
- run:
|
||||||
|
name: Install hwloc
|
||||||
|
command: |
|
||||||
|
mkdir ~/hwloc
|
||||||
|
curl --location https://download.open-mpi.org/release/hwloc/v2.4/hwloc-2.4.1.tar.gz --output ~/hwloc/hwloc-2.4.1.tar.gz
|
||||||
|
cd ~/hwloc
|
||||||
|
tar -xvzpf hwloc-2.4.1.tar.gz
|
||||||
|
cd hwloc-2.4.1
|
||||||
|
./configure && make && sudo make install
|
||||||
|
- restore_cache:
|
||||||
|
name: restore cargo cache
|
||||||
|
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
|
||||||
|
- install-deps
|
||||||
|
- run:
|
||||||
|
command: make build
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus-miner
|
||||||
|
- store_artifacts:
|
||||||
|
path: lotus-worker
|
||||||
|
- run: mkdir darwin && mv lotus lotus-miner lotus-worker darwin/
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- darwin
|
||||||
|
- save_cache:
|
||||||
|
name: save cargo cache
|
||||||
|
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
|
||||||
|
paths:
|
||||||
|
- "~/.rustup"
|
||||||
|
- "~/.cargo"
|
||||||
|
|
||||||
|
build-appimage:
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202104-01
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- run:
|
||||||
|
name: install appimage-builder
|
||||||
|
command: |
|
||||||
|
# docs: https://appimage-builder.readthedocs.io/en/latest/intro/install.html
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace
|
||||||
|
sudo curl -Lo /usr/local/bin/appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
|
sudo chmod +x /usr/local/bin/appimagetool
|
||||||
|
sudo pip3 install appimage-builder
|
||||||
|
- run:
|
||||||
|
name: install lotus dependencies
|
||||||
|
command: sudo apt install ocl-icd-opencl-dev libhwloc-dev
|
||||||
|
- run:
|
||||||
|
name: build appimage
|
||||||
|
command: |
|
||||||
|
sed -i "s/version: latest/version: ${CIRCLE_TAG:-latest}/" AppImageBuilder.yml
|
||||||
|
make appimage
|
||||||
|
- run:
|
||||||
|
name: prepare workspace
|
||||||
|
command: |
|
||||||
|
mkdir appimage
|
||||||
|
mv Lotus-*.AppImage appimage
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: "."
|
||||||
|
paths:
|
||||||
|
- appimage
|
||||||
|
|
||||||
|
|
||||||
|
gofmt:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
command: "! go fmt ./... 2>&1 | read"
|
||||||
|
|
||||||
|
gen-check:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: make deps
|
||||||
|
- run: go install golang.org/x/tools/cmd/goimports
|
||||||
|
- run: go install github.com/hannahhoward/cbor-gen-for
|
||||||
|
- run: make gen
|
||||||
|
- run: git --no-pager diff
|
||||||
|
- run: git --no-pager diff --quiet
|
||||||
|
- run: make docsgen-cli
|
||||||
|
- run: git --no-pager diff
|
||||||
|
- run: git --no-pager diff --quiet
|
||||||
|
|
||||||
|
docs-check:
|
||||||
|
executor: golang
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run: go install golang.org/x/tools/cmd/goimports
|
||||||
|
- run: zcat build/openrpc/full.json.gz | jq > ../pre-openrpc-full
|
||||||
|
- run: zcat build/openrpc/miner.json.gz | jq > ../pre-openrpc-miner
|
||||||
|
- run: zcat build/openrpc/worker.json.gz | jq > ../pre-openrpc-worker
|
||||||
|
- run: make deps
|
||||||
|
- run: make docsgen
|
||||||
|
- run: zcat build/openrpc/full.json.gz | jq > ../post-openrpc-full
|
||||||
|
- run: zcat build/openrpc/miner.json.gz | jq > ../post-openrpc-miner
|
||||||
|
- run: zcat build/openrpc/worker.json.gz | jq > ../post-openrpc-worker
|
||||||
|
- run: git --no-pager diff
|
||||||
|
- run: diff ../pre-openrpc-full ../post-openrpc-full
|
||||||
|
- run: diff ../pre-openrpc-miner ../post-openrpc-miner
|
||||||
|
- run: diff ../pre-openrpc-worker ../post-openrpc-worker
|
||||||
|
- run: git --no-pager diff --quiet
|
||||||
|
|
||||||
|
lint: &lint
|
||||||
|
description: |
|
||||||
|
Run golangci-lint.
|
||||||
|
parameters:
|
||||||
|
executor:
|
||||||
|
type: executor
|
||||||
|
default: golang
|
||||||
|
golangci-lint-version:
|
||||||
|
type: string
|
||||||
|
default: 1.27.0
|
||||||
|
concurrency:
|
||||||
|
type: string
|
||||||
|
default: '2'
|
||||||
|
description: |
|
||||||
|
Concurrency used to run linters. Defaults to 2 because NumCPU is not
|
||||||
|
aware of container CPU limits.
|
||||||
|
args:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: |
|
||||||
|
Arguments to pass to golangci-lint
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- install-deps
|
||||||
|
- prepare
|
||||||
|
- run:
|
||||||
|
command: make deps
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- go/install-golangci-lint:
|
||||||
|
gobin: $HOME/.local/bin
|
||||||
|
version: << parameters.golangci-lint-version >>
|
||||||
|
- run:
|
||||||
|
name: Lint
|
||||||
|
command: |
|
||||||
|
$HOME/.local/bin/golangci-lint run -v --timeout 2m \
|
||||||
|
--concurrency << parameters.concurrency >> << parameters.args >>
|
||||||
|
lint-all:
|
||||||
|
<<: *lint
|
||||||
|
|
||||||
|
publish:
|
||||||
|
description: publish binary artifacts
|
||||||
|
executor: ubuntu
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Install git jq curl
|
||||||
|
command: apt update && apt install -y git jq curl
|
||||||
|
- checkout
|
||||||
|
- git_fetch_all_tags
|
||||||
|
- checkout
|
||||||
|
- install_ipfs
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- run:
|
||||||
|
name: Create bundles
|
||||||
|
command: ./scripts/build-bundle.sh
|
||||||
|
- run:
|
||||||
|
name: Publish release
|
||||||
|
command: ./scripts/publish-release.sh
|
||||||
|
|
||||||
|
publish-snapcraft:
|
||||||
|
description: build and push snapcraft
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202104-01
|
||||||
|
resource_class: 2xlarge
|
||||||
|
parameters:
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
default: "edge"
|
||||||
|
description: snapcraft channel
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: install snapcraft
|
||||||
|
command: sudo snap install snapcraft --classic
|
||||||
|
- run:
|
||||||
|
name: create snapcraft config file
|
||||||
|
command: |
|
||||||
|
mkdir -p ~/.config/snapcraft
|
||||||
|
echo "$SNAPCRAFT_LOGIN_FILE" | base64 -d > ~/.config/snapcraft/snapcraft.cfg
|
||||||
|
- run:
|
||||||
|
name: build snap
|
||||||
|
command: snapcraft --use-lxd
|
||||||
|
- run:
|
||||||
|
name: publish snap
|
||||||
|
command: snapcraft push *.snap --release << parameters.channel >>
|
||||||
|
|
||||||
|
build-and-push-image:
|
||||||
|
description: build and push docker images to public AWS ECR registry
|
||||||
|
executor: aws-cli/default
|
||||||
|
parameters:
|
||||||
|
profile-name:
|
||||||
|
type: string
|
||||||
|
default: "default"
|
||||||
|
description: AWS profile name to be configured.
|
||||||
|
|
||||||
|
aws-access-key-id:
|
||||||
|
type: env_var_name
|
||||||
|
default: AWS_ACCESS_KEY_ID
|
||||||
|
description: >
|
||||||
|
AWS access key id for IAM role. Set this to the name of
|
||||||
|
the environment variable you will set to hold this
|
||||||
|
value, i.e. AWS_ACCESS_KEY.
|
||||||
|
|
||||||
|
aws-secret-access-key:
|
||||||
|
type: env_var_name
|
||||||
|
default: AWS_SECRET_ACCESS_KEY
|
||||||
|
description: >
|
||||||
|
AWS secret key for IAM role. Set this to the name of
|
||||||
|
the environment variable you will set to hold this
|
||||||
|
value, i.e. AWS_SECRET_ACCESS_KEY.
|
||||||
|
|
||||||
|
region:
|
||||||
|
type: env_var_name
|
||||||
|
default: AWS_REGION
|
||||||
|
description: >
|
||||||
|
Name of env var storing your AWS region information,
|
||||||
|
defaults to AWS_REGION
|
||||||
|
|
||||||
|
account-url:
|
||||||
|
type: env_var_name
|
||||||
|
default: AWS_ECR_ACCOUNT_URL
|
||||||
|
description: >
|
||||||
|
Env var storing Amazon ECR account URL that maps to an AWS account,
|
||||||
|
e.g. {awsAccountNum}.dkr.ecr.us-west-2.amazonaws.com
|
||||||
|
defaults to AWS_ECR_ACCOUNT_URL
|
||||||
|
|
||||||
|
dockerfile:
|
||||||
|
type: string
|
||||||
|
default: Dockerfile
|
||||||
|
description: Name of dockerfile to use. Defaults to Dockerfile.
|
||||||
|
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
default: .
|
||||||
|
description: Path to the directory containing your Dockerfile and build context. Defaults to . (working directory).
|
||||||
|
|
||||||
|
extra-build-args:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: >
|
||||||
|
Extra flags to pass to docker build. For examples, see
|
||||||
|
https://docs.docker.com/engine/reference/commandline/build
|
||||||
|
|
||||||
|
repo:
|
||||||
|
type: string
|
||||||
|
description: Name of an Amazon ECR repository
|
||||||
|
|
||||||
|
tag:
|
||||||
|
type: string
|
||||||
|
default: "latest"
|
||||||
|
description: A comma-separated string containing docker image tags to build and push (default = latest)
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Confirm that environment variables are set
|
||||||
|
command: |
|
||||||
|
if [ -z "$AWS_ACCESS_KEY_ID" ]; then
|
||||||
|
echo "No AWS_ACCESS_KEY_ID is set. Skipping build-and-push job ..."
|
||||||
|
circleci-agent step halt
|
||||||
|
fi
|
||||||
|
|
||||||
|
- aws-cli/setup:
|
||||||
|
profile-name: <<parameters.profile-name>>
|
||||||
|
aws-access-key-id: <<parameters.aws-access-key-id>>
|
||||||
|
aws-secret-access-key: <<parameters.aws-secret-access-key>>
|
||||||
|
aws-region: <<parameters.region>>
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Log into Amazon ECR
|
||||||
|
command: |
|
||||||
|
aws ecr-public get-login-password --region $<<parameters.region>> --profile <<parameters.profile-name>> | docker login --username AWS --password-stdin $<<parameters.account-url>>
|
||||||
|
|
||||||
|
- checkout
|
||||||
|
|
||||||
|
- setup_remote_docker:
|
||||||
|
version: 19.03.13
|
||||||
|
docker_layer_caching: false
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Build docker image
|
||||||
|
command: |
|
||||||
|
registry_id=$(echo $<<parameters.account-url>> | sed "s;\..*;;g")
|
||||||
|
|
||||||
|
docker_tag_args=""
|
||||||
|
IFS="," read -ra DOCKER_TAGS \<<< "<< parameters.tag >>"
|
||||||
|
for tag in "${DOCKER_TAGS[@]}"; do
|
||||||
|
docker_tag_args="$docker_tag_args -t $<<parameters.account-url>>/<<parameters.repo>>:$tag"
|
||||||
|
done
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
<<#parameters.extra-build-args>><<parameters.extra-build-args>><</parameters.extra-build-args>> \
|
||||||
|
-f <<parameters.path>>/<<parameters.dockerfile>> \
|
||||||
|
$docker_tag_args \
|
||||||
|
<<parameters.path>>
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Push image to Amazon ECR
|
||||||
|
command: |
|
||||||
|
IFS="," read -ra DOCKER_TAGS \<<< "<< parameters.tag >>"
|
||||||
|
for tag in "${DOCKER_TAGS[@]}"; do
|
||||||
|
docker push $<<parameters.account-url>>/<<parameters.repo>>:${tag}
|
||||||
|
done
|
||||||
|
|
||||||
|
publish-packer-mainnet:
|
||||||
|
description: build and push AWS IAM and DigitalOcean droplet.
|
||||||
|
executor:
|
||||||
|
name: packer/default
|
||||||
|
packer-version: 1.6.6
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- packer/build:
|
||||||
|
template: tools/packer/lotus.pkr.hcl
|
||||||
|
args: "-var ci_workspace_bins=./linux -var lotus_network=mainnet -var git_tag=$CIRCLE_TAG"
|
||||||
|
publish-packer-calibrationnet:
|
||||||
|
description: build and push AWS IAM and DigitalOcean droplet.
|
||||||
|
executor:
|
||||||
|
name: packer/default
|
||||||
|
packer-version: 1.6.6
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- packer/build:
|
||||||
|
template: tools/packer/lotus.pkr.hcl
|
||||||
|
args: "-var ci_workspace_bins=./linux-calibrationnet -var lotus_network=calibrationnet -var git_tag=$CIRCLE_TAG"
|
||||||
|
publish-packer-butterflynet:
|
||||||
|
description: build and push AWS IAM and DigitalOcean droplet.
|
||||||
|
executor:
|
||||||
|
name: packer/default
|
||||||
|
packer-version: 1.6.6
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- packer/build:
|
||||||
|
template: tools/packer/lotus.pkr.hcl
|
||||||
|
args: "-var ci_workspace_bins=./linux-butterflynet -var lotus_network=butterflynet -var git_tag=$CIRCLE_TAG"
|
||||||
|
publish-packer-nerpanet:
|
||||||
|
description: build and push AWS IAM and DigitalOcean droplet.
|
||||||
|
executor:
|
||||||
|
name: packer/default
|
||||||
|
packer-version: 1.6.6
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: "."
|
||||||
|
- packer/build:
|
||||||
|
template: tools/packer/lotus.pkr.hcl
|
||||||
|
args: "-var ci_workspace_bins=./linux-nerpanet -var lotus_network=nerpanet -var git_tag=$CIRCLE_TAG"
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2.1
|
||||||
|
ci:
|
||||||
|
jobs:
|
||||||
|
- lint-all:
|
||||||
|
concurrency: "16" # expend all docker 2xlarge CPUs.
|
||||||
|
- mod-tidy-check
|
||||||
|
- gofmt
|
||||||
|
- gen-check
|
||||||
|
- docs-check
|
||||||
|
|
||||||
|
[[- range $file := .ItestFiles -]]
|
||||||
|
[[ with $name := $file | stripSuffix ]]
|
||||||
|
- test:
|
||||||
|
name: test-itest-[[ $name ]]
|
||||||
|
suite: itest-[[ $name ]]
|
||||||
|
target: "./itests/[[ $file ]]"
|
||||||
|
[[ end ]]
|
||||||
|
[[- end -]]
|
||||||
|
|
||||||
|
[[range $suite, $pkgs := .UnitSuites]]
|
||||||
|
- test:
|
||||||
|
name: test-[[ $suite ]]
|
||||||
|
suite: utest-[[ $suite ]]
|
||||||
|
target: "[[ $pkgs ]]"
|
||||||
|
[[- end]]
|
||||||
|
- test:
|
||||||
|
go-test-flags: "-run=TestMulticoreSDR"
|
||||||
|
suite: multicore-sdr-check
|
||||||
|
target: "./extern/sector-storage/ffiwrapper"
|
||||||
|
proofs-log-test: "1"
|
||||||
|
- test-conformance:
|
||||||
|
suite: conformance
|
||||||
|
codecov-upload: false
|
||||||
|
target: "./conformance"
|
||||||
|
- test-conformance:
|
||||||
|
name: test-conformance-bleeding-edge
|
||||||
|
codecov-upload: false
|
||||||
|
suite: conformance-bleeding-edge
|
||||||
|
target: "./conformance"
|
||||||
|
vectors-branch: master
|
||||||
|
- trigger-testplans:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- build-debug
|
||||||
|
- build-all:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-ntwk-calibration:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-ntwk-butterfly:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-ntwk-nerpa:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-lotus-soup
|
||||||
|
- build-macos:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-appimage:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- publish:
|
||||||
|
requires:
|
||||||
|
- build-all
|
||||||
|
- build-macos
|
||||||
|
- build-appimage
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- build-and-push-image:
|
||||||
|
dockerfile: Dockerfile.lotus
|
||||||
|
path: .
|
||||||
|
repo: lotus-dev
|
||||||
|
tag: '${CIRCLE_SHA1:0:8}'
|
||||||
|
- publish-packer-mainnet:
|
||||||
|
requires:
|
||||||
|
- build-all
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- publish-packer-calibrationnet:
|
||||||
|
requires:
|
||||||
|
- build-ntwk-calibration
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- publish-packer-butterflynet:
|
||||||
|
requires:
|
||||||
|
- build-ntwk-butterfly
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- publish-packer-nerpanet:
|
||||||
|
requires:
|
||||||
|
- build-ntwk-nerpa
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
- publish-snapcraft:
|
||||||
|
name: publish-snapcraft-stable
|
||||||
|
channel: stable
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- /.*/
|
||||||
|
tags:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||||
|
|
||||||
|
nightly:
|
||||||
|
triggers:
|
||||||
|
- schedule:
|
||||||
|
cron: "0 0 * * *"
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
- publish-snapcraft:
|
||||||
|
name: publish-snapcraft-nightly
|
||||||
|
channel: edge
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@
|
|||||||
/lotus-health
|
/lotus-health
|
||||||
/lotus-chainwatch
|
/lotus-chainwatch
|
||||||
/lotus-shed
|
/lotus-shed
|
||||||
|
/lotus-sim
|
||||||
/lotus-pond
|
/lotus-pond
|
||||||
/lotus-townhall
|
/lotus-townhall
|
||||||
/lotus-fountain
|
/lotus-fountain
|
||||||
|
138
CHANGELOG.md
138
CHANGELOG.md
@ -1,5 +1,143 @@
|
|||||||
# Lotus changelog
|
# Lotus changelog
|
||||||
|
|
||||||
|
# 1.10.0 / 2021-06-23
|
||||||
|
|
||||||
|
This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The
|
||||||
|
Filecoin mainnet will upgrade, which is epoch 892800, on 2021-06-30T22:00:00Z. The network upgrade introduces the
|
||||||
|
following FIPs:
|
||||||
|
|
||||||
|
- [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method
|
||||||
|
- [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults
|
||||||
|
- [FIP-0012](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0012.md): DataCap Top up for FIL+ Client Addresses
|
||||||
|
- [FIP-0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md): Add ProveCommitSectorAggregated method to reduce on-chain congestion
|
||||||
|
- [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md): Revert FIP-0009(Exempt Window PoSts from BaseFee burn)
|
||||||
|
|
||||||
|
Note that this release is built on top of Lotus v1.9.0. Enterprising users can use the `master` branch of Lotus to get the latest functionality, including all changes in this release candidate.
|
||||||
|
|
||||||
|
## Proof batching and aggregation
|
||||||
|
|
||||||
|
FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md) and [0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md) combine to allow for a significant increase in the rate of onboarding storage on the Filecoin network. This aims to lead to more useful data being stored on the network, reduced network congestion, and lower network base fee.
|
||||||
|
|
||||||
|
**Check out the documentation [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details on the new Lotus miner sealing config options, [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#fees-section) for fee config options, and explanations of the new features.**
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- We recommend to keep `PreCommitSectorsBatch` as 1.
|
||||||
|
- We recommend miners to set `PreCommitBatchWait` lower than 30 hours.
|
||||||
|
- We recommend miners to set a longer `CommitBatchSlack` and `PreCommitBatchSlack` to prevent message failures
|
||||||
|
due to expirations.
|
||||||
|
|
||||||
|
### Projected state tree growth
|
||||||
|
|
||||||
|
In order to validate the Hyperdrive changes, we wrote a simulation to seal as many sectors as quickly as possible, assuming the same number and mix of 32GiB and 64GiB miners as the current network.
|
||||||
|
|
||||||
|
Given these assumptions:
|
||||||
|
|
||||||
|
- We'd expect a network storage growth rate of around 530PiB per day. 😳 🎉 🥳 😅
|
||||||
|
- We'd expect network bandwidth dedicated to `SubmitWindowedPoSt` to grow by about 0.02% per day.
|
||||||
|
- We'd expect the [state-tree](https://spec.filecoin.io/#section-systems.filecoin_vm.state_tree) (and therefore [snapshot](https://docs.filecoin.io/get-started/lotus/chain/#lightweight-snapshot)) size to grow by 1.16GiB per day.
|
||||||
|
- Nearly all of the state-tree growth is expected to come from new sector metadata.
|
||||||
|
- We'd expect the daily lotus datastore growth rate to increase by about 10-15% (from current ~21GiB/day).
|
||||||
|
- Most "growth" of the lotus datastore is due to "churn", historical data that's no longer referenced by the latest state-tree.
|
||||||
|
|
||||||
|
### Future improvements
|
||||||
|
|
||||||
|
Various Lotus improvements are planned moving forward to mitigate the effects of the growing state tree size. The primary improvement is the [Lotus splitstore](https://github.com/filecoin-project/lotus/discussions/5788), which will soon be enabled by default. The feature allows for [online garbage collection](https://github.com/filecoin-project/lotus/issues/6577) for nodes that do not seek to maintain full chain and state history, thus eliminating the need for users to delete their datastores and sync from snapshots.
|
||||||
|
|
||||||
|
Other improvements including better compressed snapshots, faster pre-migrations, and improved chain exports are in the roadmap.
|
||||||
|
|
||||||
|
## WindowPost base fee burn
|
||||||
|
|
||||||
|
Included in the HyperDrive upgrade is [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md) which eliminates the special-case gas treatment of `SubmitWindowedPoSt` messages that was introduced in [FIP-0009](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0009.md). Although `SubmitWindowedPoSt` messages will be relatively cheap, thanks to the introduction of optimistic acceptance of these proofs in [FIP-0010](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0010.md), storage providers should pay attention to their `MaxWindowPoStGasFee` config option: too low and PoSts may not land on chain; too high and they may cost an exorbitant amount!
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
- Implement FIP-0015 ([filecoin-project/lotus#6361](https://github.com/filecoin-project/lotus/pull/6361))
|
||||||
|
- Integrate FIP0013 and FIP0008 ([filecoin-project/lotus#6235](https://github.com/filecoin-project/lotus/pull/6235))
|
||||||
|
- [Configuration docs and cli examples](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch)
|
||||||
|
- [cli docs](https://github.com/filecoin-project/lotus/blob/master/documentation/en/cli-lotus-miner.md#lotus-miner-sectors-batching)
|
||||||
|
- Introduce gas prices for aggregate verifications ([filecoin-project/lotus#6347](https://github.com/filecoin-project/lotus/pull/6347))
|
||||||
|
- Introduce v5 actors ([filecoin-project/lotus#6195](https://github.com/filecoin-project/lotus/pull/6195))
|
||||||
|
- Robustify commit batcher ([filecoin-project/lotus#6367](https://github.com/filecoin-project/lotus/pull/6367))
|
||||||
|
- Always flush when timer goes off ([filecoin-project/lotus#6563](https://github.com/filecoin-project/lotus/pull/6563))
|
||||||
|
- Update default fees for aggregates ([filecoin-project/lotus#6548](https://github.com/filecoin-project/lotus/pull/6548))
|
||||||
|
- sealing: Early finalization option ([filecoin-project/lotus#6452](https://github.com/filecoin-project/lotus/pull/6452))
|
||||||
|
- `./lotus-miner/config.toml/[Sealing.FinalizeEarly]`: default to false. Enable if you want to FinalizeSector before commiting
|
||||||
|
- Add filplus utils to CLI ([filecoin-project/lotus#6351](https://github.com/filecoin-project/lotus/pull/6351))
|
||||||
|
- cli doc can be found [here](https://github.com/filecoin-project/lotus/blob/master/documentation/en/cli-lotus.md#lotus-filplus)
|
||||||
|
- Add miner-side MaxDealStartDelay config ([filecoin-project/lotus#6576](https://github.com/filecoin-project/lotus/pull/6576))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- chainstore: Don't take heaviestLk with backlogged reorgCh ([filecoin-project/lotus#6526](https://github.com/filecoin-project/lotus/pull/6526))
|
||||||
|
- Backport #6041 - storagefsm: Fix batch deal packing behavior ([filecoin-project/lotus#6519](https://github.com/filecoin-project/lotus/pull/6519))
|
||||||
|
- backport: pick the correct partitions-per-post limit ([filecoin-project/lotus#6503](https://github.com/filecoin-project/lotus/pull/6503))
|
||||||
|
- failed sectors should be added into res correctly ([filecoin-project/lotus#6472](https://github.com/filecoin-project/lotus/pull/6472))
|
||||||
|
- sealing: Fix restartSectors race ([filecoin-project/lotus#6491](https://github.com/filecoin-project/lotus/pull/6491))
|
||||||
|
- Fund miners with the aggregate fee when ProveCommitting ([filecoin-project/lotus#6428](https://github.com/filecoin-project/lotus/pull/6428))
|
||||||
|
- Commit and Precommit batcher cannot share a getSectorDeadline method ([filecoin-project/lotus#6416](https://github.com/filecoin-project/lotus/pull/6416))
|
||||||
|
- Fix supported proof type manipulations for v5 actors ([filecoin-project/lotus#6366](https://github.com/filecoin-project/lotus/pull/6366))
|
||||||
|
- events: Fix handling of multiple matched events per epoch ([filecoin-project/lotus#6362](https://github.com/filecoin-project/lotus/pull/6362))
|
||||||
|
- Fix randomness fetching around null blocks ([filecoin-project/lotus#6240](https://github.com/filecoin-project/lotus/pull/6240))
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- Appimage v1.10.0 rc3 ([filecoin-project/lotus#6492](https://github.com/filecoin-project/lotus/pull/6492))
|
||||||
|
- Expand on Drand change testing ([filecoin-project/lotus#6500](https://github.com/filecoin-project/lotus/pull/6500))
|
||||||
|
- Backport Fix logging around mineOne ([filecoin-project/lotus#6499](https://github.com/filecoin-project/lotus/pull/6499))
|
||||||
|
- mpool: Add more metrics ([filecoin-project/lotus#6453](https://github.com/filecoin-project/lotus/pull/6453))
|
||||||
|
- Merge backported PRs into v1.10 release branch ([filecoin-project/lotus#6436](https://github.com/filecoin-project/lotus/pull/6436))
|
||||||
|
- Fix tests ([filecoin-project/lotus#6371](https://github.com/filecoin-project/lotus/pull/6371))
|
||||||
|
- Extend the default deal start epoch delay ([filecoin-project/lotus#6350](https://github.com/filecoin-project/lotus/pull/6350))
|
||||||
|
- sealing: Wire up context to batchers ([filecoin-project/lotus#6497](https://github.com/filecoin-project/lotus/pull/6497))
|
||||||
|
- Improve address resolution for messages ([filecoin-project/lotus#6364](https://github.com/filecoin-project/lotus/pull/6364))
|
||||||
|
|
||||||
|
### Dependency Updates
|
||||||
|
- Proofs v8.0.2 ([filecoin-project/lotus#6524](https://github.com/filecoin-project/lotus/pull/6524))
|
||||||
|
- Update to fixed Bellperson ([filecoin-project/lotus#6480](https://github.com/filecoin-project/lotus/pull/6480))
|
||||||
|
- Update to go-praamfetch with fslocks ([filecoin-project/lotus#6473](https://github.com/filecoin-project/lotus/pull/6473))
|
||||||
|
- Update ffi with fixed multicore sdr support ([filecoin-project/lotus#6471](https://github.com/filecoin-project/lotus/pull/6471))
|
||||||
|
- github.com/filecoin-project/go-paramfetch (v0.0.2-0.20200701152213-3e0f0afdc261 -> v0.0.2-0.20210614165157-25a6c7769498)
|
||||||
|
- github.com/filecoin-project/specs-actors/v5 (v5.0.0-20210512015452-4fe3889fff57 -> v5.0.0)
|
||||||
|
- github.com/filecoin-project/go-hamt-ipld/v3 (v3.0.1 -> v3.1.0)
|
||||||
|
- github.com/ipfs/go-log/v2 (v2.1.2-0.20200626104915-0016c0b4b3e4 -> v2.1.3)
|
||||||
|
- github.com/filecoin-project/go-amt-ipld/v3 (v3.0.0 -> v3.1.0)
|
||||||
|
|
||||||
|
### Network Version v13 HyperDrive Upgrade
|
||||||
|
- Set HyperDrive upgrade epoch ([filecoin-project/lotus#6565](https://github.com/filecoin-project/lotus/pull/6565))
|
||||||
|
- version bump to lotus v1.10.0-rc6 ([filecoin-project/lotus#6529](https://github.com/filecoin-project/lotus/pull/6529))
|
||||||
|
- Upgrade epochs for calibration reset ([filecoin-project/lotus#6528](https://github.com/filecoin-project/lotus/pull/6528))
|
||||||
|
- Lotus version 1.10.0-rc5 ([filecoin-project/lotus#6504](https://github.com/filecoin-project/lotus/pull/6504))
|
||||||
|
- Merge releases into v1.10 release ([filecoin-project/lotus#6494](https://github.com/filecoin-project/lotus/pull/6494))
|
||||||
|
- update lotus to v1.10.0-rc3 ([filecoin-project/lotus#6481](https://github.com/filecoin-project/lotus/pull/6481))
|
||||||
|
- updated configuration comments for docs
|
||||||
|
- Lotus version 1.10.0-rc2 ([filecoin-project/lotus#6443](https://github.com/filecoin-project/lotus/pull/6443))
|
||||||
|
- Set ntwk v13 HyperDrive Calibration upgrade epoch ([filecoin-project/lotus#6442](https://github.com/filecoin-project/lotus/pull/6442))
|
||||||
|
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
💙Thank you to all the contributors!
|
||||||
|
|
||||||
|
| Contributor | Commits | Lines ± | Files Changed |
|
||||||
|
|--------------------|---------|-------------|---------------|
|
||||||
|
| @magik6k | 81 | +9606/-1536 | 361 |
|
||||||
|
| @arajasek | 41 | +6543/-679 | 189 |
|
||||||
|
| @ZenGround0 | 11 | +4074/-727 | 110 |
|
||||||
|
| @anorth | 10 | +2035/-1177 | 55 |
|
||||||
|
| @iand | 1 | +779/-12 | 5 |
|
||||||
|
| @frrist | 2 | +722/-6 | 6 |
|
||||||
|
| @Stebalien | 6 | +368/-24 | 15 |
|
||||||
|
| @jennijuju | 11 | +204/-111 | 19 |
|
||||||
|
| @vyzo | 6 | +155/-66 | 13 |
|
||||||
|
| @coryschwartz | 10 | +171/-27 | 14 |
|
||||||
|
| @Kubuxu | 4 | +177/-13 | 7 |
|
||||||
|
| @ribasushi | 4 | +65/-42 | 5 |
|
||||||
|
| @travisperson | 2 | +11/-11 | 4 |
|
||||||
|
| @kirk-baird | 1 | +1/-5 | 1 |
|
||||||
|
| @wangchao | 2 | +3/-2 | 2 |
|
||||||
|
|
||||||
|
|
||||||
# 1.9.0 / 2021-05-17
|
# 1.9.0 / 2021-05-17
|
||||||
|
|
||||||
This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes.
|
This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes.
|
||||||
|
11
Makefile
11
Makefile
@ -234,6 +234,12 @@ BINS+=tvx
|
|||||||
install-chainwatch: lotus-chainwatch
|
install-chainwatch: lotus-chainwatch
|
||||||
install -C ./lotus-chainwatch /usr/local/bin/lotus-chainwatch
|
install -C ./lotus-chainwatch /usr/local/bin/lotus-chainwatch
|
||||||
|
|
||||||
|
lotus-sim: $(BUILD_DEPS)
|
||||||
|
rm -f lotus-sim
|
||||||
|
go build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim
|
||||||
|
.PHONY: lotus-sim
|
||||||
|
BINS+=lotus-sim
|
||||||
|
|
||||||
# SYSTEMD
|
# SYSTEMD
|
||||||
|
|
||||||
install-daemon-service: install-daemon
|
install-daemon-service: install-daemon
|
||||||
@ -364,7 +370,7 @@ docsgen-openrpc-worker: docsgen-openrpc-bin
|
|||||||
|
|
||||||
.PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin
|
.PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin
|
||||||
|
|
||||||
gen: actors-gen type-gen method-gen docsgen api-gen
|
gen: actors-gen type-gen method-gen docsgen api-gen circleci
|
||||||
@echo ">>> IF YOU'VE MODIFIED THE CLI, REMEMBER TO ALSO MAKE docsgen-cli"
|
@echo ">>> IF YOU'VE MODIFIED THE CLI, REMEMBER TO ALSO MAKE docsgen-cli"
|
||||||
.PHONY: gen
|
.PHONY: gen
|
||||||
|
|
||||||
@ -379,3 +385,6 @@ docsgen-cli: lotus lotus-miner lotus-worker
|
|||||||
|
|
||||||
print-%:
|
print-%:
|
||||||
@echo $*=$($*)
|
@echo $*=$($*)
|
||||||
|
|
||||||
|
circleci:
|
||||||
|
go generate -x ./.circleci
|
@ -58,4 +58,5 @@ type Gateway interface {
|
|||||||
StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*MsgLookup, error)
|
StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*MsgLookup, error)
|
||||||
StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64, limit abi.ChainEpoch, allowReplaced bool) (*MsgLookup, error)
|
StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64, limit abi.ChainEpoch, allowReplaced bool) (*MsgLookup, error)
|
||||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||||
|
Version(context.Context) (APIVersion, error)
|
||||||
}
|
}
|
||||||
|
@ -531,6 +531,8 @@ type GatewayStruct struct {
|
|||||||
|
|
||||||
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) ``
|
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) ``
|
||||||
|
|
||||||
|
Version func(p0 context.Context) (APIVersion, error) ``
|
||||||
|
|
||||||
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
|
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2683,6 +2685,14 @@ func (s *GatewayStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3
|
|||||||
return nil, xerrors.New("method not supported")
|
return nil, xerrors.New("method not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GatewayStruct) Version(p0 context.Context) (APIVersion, error) {
|
||||||
|
return s.Internal.Version(p0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GatewayStub) Version(p0 context.Context) (APIVersion, error) {
|
||||||
|
return *new(APIVersion), xerrors.New("method not supported")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GatewayStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {
|
func (s *GatewayStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {
|
||||||
return s.Internal.WalletBalance(p0, p1)
|
return s.Internal.WalletBalance(p0, p1)
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ type Gateway interface {
|
|||||||
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
|
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
|
||||||
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
|
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
|
||||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||||
|
Version(context.Context) (api.APIVersion, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Gateway = *new(FullNode)
|
var _ Gateway = *new(FullNode)
|
||||||
|
@ -449,6 +449,8 @@ type GatewayStruct struct {
|
|||||||
|
|
||||||
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) ``
|
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) ``
|
||||||
|
|
||||||
|
Version func(p0 context.Context) (api.APIVersion, error) ``
|
||||||
|
|
||||||
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
|
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2096,6 +2098,14 @@ func (s *GatewayStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64) (*
|
|||||||
return nil, xerrors.New("method not supported")
|
return nil, xerrors.New("method not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GatewayStruct) Version(p0 context.Context) (api.APIVersion, error) {
|
||||||
|
return s.Internal.Version(p0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GatewayStub) Version(p0 context.Context) (api.APIVersion, error) {
|
||||||
|
return *new(api.APIVersion), xerrors.New("method not supported")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GatewayStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {
|
func (s *GatewayStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {
|
||||||
return s.Internal.WalletBalance(p0, p1)
|
return s.Internal.WalletBalance(p0, p1)
|
||||||
}
|
}
|
||||||
|
21
api/wrap.go
21
api/wrap.go
@ -26,6 +26,27 @@ func Wrap(proxyT, wrapperT, impl interface{}) interface{} {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 0; i < proxy.Elem().NumField(); i++ {
|
||||||
|
if proxy.Elem().Type().Field(i).Name == "Internal" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
subProxy := proxy.Elem().Field(i).FieldByName("Internal")
|
||||||
|
for i := 0; i < ri.NumMethod(); i++ {
|
||||||
|
mt := ri.Type().Method(i)
|
||||||
|
if subProxy.FieldByName(mt.Name).Kind() == reflect.Invalid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := ri.Method(i)
|
||||||
|
of := subProxy.FieldByName(mt.Name)
|
||||||
|
|
||||||
|
subProxy.FieldByName(mt.Name).Set(reflect.MakeFunc(of.Type(), func(args []reflect.Value) (results []reflect.Value) {
|
||||||
|
return fn.Call(args)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wp := reflect.New(reflect.TypeOf(wrapperT).Elem())
|
wp := reflect.New(reflect.TypeOf(wrapperT).Elem())
|
||||||
wp.Elem().Field(0).Set(proxy)
|
wp.Elem().Field(0).Set(proxy)
|
||||||
return wp.Interface()
|
return wp.Interface()
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -62,8 +62,8 @@ const UpgradeNorwegianHeight = 665280
|
|||||||
// 2021-04-29T06:00:00Z
|
// 2021-04-29T06:00:00Z
|
||||||
const UpgradeTurboHeight = 712320
|
const UpgradeTurboHeight = 712320
|
||||||
|
|
||||||
// ???
|
// 2021-06-30T22:00:00Z
|
||||||
var UpgradeHyperdriveHeight = abi.ChainEpoch(9999999)
|
var UpgradeHyperdriveHeight = abi.ChainEpoch(892800)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {
|
if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {
|
||||||
|
@ -34,7 +34,7 @@ func buildType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildVersion is the local build version, set by build system
|
// BuildVersion is the local build version, set by build system
|
||||||
const BuildVersion = "1.11.0-dev"
|
const BuildVersion = "1.11.1-dev"
|
||||||
|
|
||||||
func UserVersion() string {
|
func UserVersion() string {
|
||||||
if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" {
|
if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||||
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
||||||
miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner"
|
miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner"
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
{{range .versions}}
|
{{range .versions}}
|
||||||
builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"
|
builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -97,9 +98,13 @@ type State interface {
|
|||||||
FindSector(abi.SectorNumber) (*SectorLocation, error)
|
FindSector(abi.SectorNumber) (*SectorLocation, error)
|
||||||
GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error)
|
GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error)
|
||||||
GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error)
|
GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error)
|
||||||
|
ForEachPrecommittedSector(func(SectorPreCommitOnChainInfo) error) error
|
||||||
LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error)
|
LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error)
|
||||||
NumLiveSectors() (uint64, error)
|
NumLiveSectors() (uint64, error)
|
||||||
IsAllocated(abi.SectorNumber) (bool, error)
|
IsAllocated(abi.SectorNumber) (bool, error)
|
||||||
|
// UnallocatedSectorNumbers returns up to count unallocated sector numbers (or less than
|
||||||
|
// count if there aren't enough).
|
||||||
|
UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error)
|
||||||
|
|
||||||
// Note that ProvingPeriodStart is deprecated and will be renamed / removed in a future version of actors
|
// Note that ProvingPeriodStart is deprecated and will be renamed / removed in a future version of actors
|
||||||
GetProvingPeriodStart() (abi.ChainEpoch, error)
|
GetProvingPeriodStart() (abi.ChainEpoch, error)
|
||||||
@ -176,6 +181,7 @@ type DeclareFaultsRecoveredParams = miner0.DeclareFaultsRecoveredParams
|
|||||||
type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams
|
type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams
|
||||||
type ProveCommitSectorParams = miner0.ProveCommitSectorParams
|
type ProveCommitSectorParams = miner0.ProveCommitSectorParams
|
||||||
type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams
|
type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams
|
||||||
|
type ProveCommitAggregateParams = miner5.ProveCommitAggregateParams
|
||||||
|
|
||||||
func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
|
func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
|
||||||
// We added support for the new proofs in network version 7, and removed support for the old
|
// We added support for the new proofs in network version 7, and removed support for the old
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||||
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
||||||
miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner"
|
miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner"
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
|
|
||||||
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
||||||
|
|
||||||
@ -156,9 +157,13 @@ type State interface {
|
|||||||
FindSector(abi.SectorNumber) (*SectorLocation, error)
|
FindSector(abi.SectorNumber) (*SectorLocation, error)
|
||||||
GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error)
|
GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error)
|
||||||
GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error)
|
GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error)
|
||||||
|
ForEachPrecommittedSector(func(SectorPreCommitOnChainInfo) error) error
|
||||||
LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error)
|
LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error)
|
||||||
NumLiveSectors() (uint64, error)
|
NumLiveSectors() (uint64, error)
|
||||||
IsAllocated(abi.SectorNumber) (bool, error)
|
IsAllocated(abi.SectorNumber) (bool, error)
|
||||||
|
// UnallocatedSectorNumbers returns up to count unallocated sector numbers (or less than
|
||||||
|
// count if there aren't enough).
|
||||||
|
UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error)
|
||||||
|
|
||||||
// Note that ProvingPeriodStart is deprecated and will be renamed / removed in a future version of actors
|
// Note that ProvingPeriodStart is deprecated and will be renamed / removed in a future version of actors
|
||||||
GetProvingPeriodStart() (abi.ChainEpoch, error)
|
GetProvingPeriodStart() (abi.ChainEpoch, error)
|
||||||
@ -235,6 +240,7 @@ type DeclareFaultsRecoveredParams = miner0.DeclareFaultsRecoveredParams
|
|||||||
type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams
|
type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams
|
||||||
type ProveCommitSectorParams = miner0.ProveCommitSectorParams
|
type ProveCommitSectorParams = miner0.ProveCommitSectorParams
|
||||||
type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams
|
type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams
|
||||||
|
type ProveCommitAggregateParams = miner5.ProveCommitAggregateParams
|
||||||
|
|
||||||
func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
|
func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) {
|
||||||
// We added support for the new proofs in network version 7, and removed support for the old
|
// We added support for the new proofs in network version 7, and removed support for the old
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
{{end}}
|
{{end}}
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -209,6 +210,26 @@ func (s *state{{.v}}) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCom
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state{{.v}}) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
{{if (ge .v 3) -}}
|
||||||
|
precommitted, err := adt{{.v}}.AsMap(s.store, s.State.PreCommittedSectors, builtin{{.v}}.DefaultHamtBitwidth)
|
||||||
|
{{- else -}}
|
||||||
|
precommitted, err := adt{{.v}}.AsMap(s.store, s.State.PreCommittedSectors)
|
||||||
|
{{- end}}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner{{.v}}.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV{{.v}}SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state{{.v}}) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state{{.v}}) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner{{.v}}.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner{{.v}}.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -242,9 +263,15 @@ func (s *state{{.v}}) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state{{.v}}) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state{{.v}}) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state{{.v}}) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,6 +282,42 @@ func (s *state{{.v}}) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state{{.v}}) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{ {Val: true, Len: abi.MaxSectorNumber} }},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state{{.v}}) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state{{.v}}) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -206,6 +207,22 @@ func (s *state0) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state0) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
precommitted, err := adt0.AsMap(s.store, s.State.PreCommittedSectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner0.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV0SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state0) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state0) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner0.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner0.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -239,9 +256,15 @@ func (s *state0) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state0) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state0) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state0) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +275,42 @@ func (s *state0) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state0) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state0) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state0) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -204,6 +205,22 @@ func (s *state2) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state2) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
precommitted, err := adt2.AsMap(s.store, s.State.PreCommittedSectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner2.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV2SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state2) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state2) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner2.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner2.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -237,9 +254,15 @@ func (s *state2) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state2) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state2) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state2) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,6 +273,42 @@ func (s *state2) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state2) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state2) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state2) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -206,6 +207,22 @@ func (s *state3) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state3) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
precommitted, err := adt3.AsMap(s.store, s.State.PreCommittedSectors, builtin3.DefaultHamtBitwidth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner3.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV3SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner3.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner3.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -239,9 +256,15 @@ func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state3) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state3) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state3) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +275,42 @@ func (s *state3) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state3) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state3) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state3) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -206,6 +207,22 @@ func (s *state4) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state4) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
precommitted, err := adt4.AsMap(s.store, s.State.PreCommittedSectors, builtin4.DefaultHamtBitwidth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner4.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV4SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner4.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner4.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -239,9 +256,15 @@ func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state4) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state4) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state4) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +275,42 @@ func (s *state4) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state4) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state4) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state4) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
rle "github.com/filecoin-project/go-bitfield/rle"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/dline"
|
"github.com/filecoin-project/go-state-types/dline"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -206,6 +207,22 @@ func (s *state5) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn
|
|||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state5) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error {
|
||||||
|
precommitted, err := adt5.AsMap(s.store, s.State.PreCommittedSectors, builtin5.DefaultHamtBitwidth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info miner5.SectorPreCommitOnChainInfo
|
||||||
|
if err := precommitted.ForEach(&info, func(_ string) error {
|
||||||
|
return cb(fromV5SectorPreCommitOnChainInfo(info))
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state5) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
func (s *state5) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) {
|
||||||
sectors, err := miner5.LoadSectors(s.store, s.State.Sectors)
|
sectors, err := miner5.LoadSectors(s.store, s.State.Sectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -239,9 +256,15 @@ func (s *state5) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state5) IsAllocated(num abi.SectorNumber) (bool, error) {
|
func (s *state5) loadAllocatedSectorNumbers() (bitfield.BitField, error) {
|
||||||
var allocatedSectors bitfield.BitField
|
var allocatedSectors bitfield.BitField
|
||||||
if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil {
|
err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors)
|
||||||
|
return allocatedSectors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state5) IsAllocated(num abi.SectorNumber) (bool, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +275,42 @@ func (s *state5) GetProvingPeriodStart() (abi.ChainEpoch, error) {
|
|||||||
return s.State.ProvingPeriodStart, nil
|
return s.State.ProvingPeriodStart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *state5) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) {
|
||||||
|
allocatedSectors, err := s.loadAllocatedSectorNumbers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedRuns, err := allocatedSectors.RunIterator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unallocatedRuns, err := rle.Subtract(
|
||||||
|
&rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}},
|
||||||
|
allocatedRuns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := rle.BitsFromRuns(unallocatedRuns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := make([]abi.SectorNumber, 0, count)
|
||||||
|
for iter.HasNext() && len(sectors) < count {
|
||||||
|
nextNo, err := iter.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sectors = append(sectors, abi.SectorNumber(nextNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state5) LoadDeadline(idx uint64) (Deadline, error) {
|
func (s *state5) LoadDeadline(idx uint64) (Deadline, error) {
|
||||||
dls, err := s.State.LoadDeadlines(s.store)
|
dls, err := s.State.LoadDeadlines(s.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -115,6 +115,7 @@ type MessageBuilder interface {
|
|||||||
type ProposalHashData = msig{{.latestVersion}}.ProposalHashData
|
type ProposalHashData = msig{{.latestVersion}}.ProposalHashData
|
||||||
type ProposeReturn = msig{{.latestVersion}}.ProposeReturn
|
type ProposeReturn = msig{{.latestVersion}}.ProposeReturn
|
||||||
type ProposeParams = msig{{.latestVersion}}.ProposeParams
|
type ProposeParams = msig{{.latestVersion}}.ProposeParams
|
||||||
|
type ApproveReturn = msig{{.latestVersion}}.ApproveReturn
|
||||||
|
|
||||||
func txnParams(id uint64, data *ProposalHashData) ([]byte, error) {
|
func txnParams(id uint64, data *ProposalHashData) ([]byte, error) {
|
||||||
params := msig{{.latestVersion}}.TxnIDParams{ID: msig{{.latestVersion}}.TxnID(id)}
|
params := msig{{.latestVersion}}.TxnIDParams{ID: msig{{.latestVersion}}.TxnID(id)}
|
||||||
|
@ -186,6 +186,7 @@ type MessageBuilder interface {
|
|||||||
type ProposalHashData = msig5.ProposalHashData
|
type ProposalHashData = msig5.ProposalHashData
|
||||||
type ProposeReturn = msig5.ProposeReturn
|
type ProposeReturn = msig5.ProposeReturn
|
||||||
type ProposeParams = msig5.ProposeParams
|
type ProposeParams = msig5.ProposeParams
|
||||||
|
type ApproveReturn = msig5.ApproveReturn
|
||||||
|
|
||||||
func txnParams(id uint64, data *ProposalHashData) ([]byte, error) {
|
func txnParams(id uint64, data *ProposalHashData) ([]byte, error) {
|
||||||
params := msig5.TxnIDParams{ID: msig5.TxnID(id)}
|
params := msig5.TxnIDParams{ID: msig5.TxnID(id)}
|
||||||
|
@ -3,6 +3,7 @@ package genesis
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/system"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/system"
|
||||||
@ -32,8 +33,9 @@ func SetupSystemActor(ctx context.Context, bs bstore.Blockstore, av actors.Versi
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return act, nil
|
return act, nil
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
|
||||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||||
|
|
||||||
@ -181,8 +182,9 @@ func SetupInitActor(ctx context.Context, bs bstore.Blockstore, netname string, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return counter, act, keyToId, nil
|
return counter, act, keyToId, nil
|
||||||
|
@ -3,6 +3,7 @@ package genesis
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/cron"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/cron"
|
||||||
@ -31,8 +32,9 @@ func SetupCronActor(ctx context.Context, bs bstore.Blockstore, av actors.Version
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return act, nil
|
return act, nil
|
||||||
|
@ -3,6 +3,7 @@ package genesis
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
@ -33,8 +34,9 @@ func SetupStoragePowerActor(ctx context.Context, bs bstore.Blockstore, av actors
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return act, nil
|
return act, nil
|
||||||
|
@ -3,6 +3,7 @@ package genesis
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||||
@ -31,8 +32,9 @@ func SetupStorageMarketActor(ctx context.Context, bs bstore.Blockstore, av actor
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return act, nil
|
return act, nil
|
||||||
|
@ -3,6 +3,7 @@ package genesis
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
@ -46,8 +47,9 @@ func SetupVerifiedRegistryActor(ctx context.Context, bs bstore.Blockstore, av ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
act := &types.Actor{
|
act := &types.Actor{
|
||||||
Code: actcid,
|
Code: actcid,
|
||||||
Head: statecid,
|
Head: statecid,
|
||||||
|
Balance: big.Zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return act, nil
|
return act, nil
|
||||||
|
@ -313,11 +313,10 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
|||||||
|
|
||||||
totalFilAllocated := big.Zero()
|
totalFilAllocated := big.Zero()
|
||||||
|
|
||||||
// flush as ForEach works on the HAMT
|
|
||||||
if _, err := state.Flush(ctx); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
err = state.ForEach(func(addr address.Address, act *types.Actor) error {
|
err = state.ForEach(func(addr address.Address, act *types.Actor) error {
|
||||||
|
if act.Balance.Nil() {
|
||||||
|
panic(fmt.Sprintf("actor %s (%s) has nil balance", addr, builtin.ActorNameByCode(act.Code)))
|
||||||
|
}
|
||||||
totalFilAllocated = big.Add(totalFilAllocated, act.Balance)
|
totalFilAllocated = big.Add(totalFilAllocated, act.Balance)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -504,6 +504,26 @@ func (st *StateTree) MutateActor(addr address.Address, f func(*types.Actor) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error {
|
func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error {
|
||||||
|
// Walk through layers, if any.
|
||||||
|
seen := make(map[address.Address]struct{})
|
||||||
|
for i := len(st.snaps.layers) - 1; i >= 0; i-- {
|
||||||
|
for addr, op := range st.snaps.layers[i].actors {
|
||||||
|
if _, ok := seen[addr]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[addr] = struct{}{}
|
||||||
|
if op.Delete {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
act := op.Act // copy
|
||||||
|
if err := f(addr, &act); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now walk through the saved actors.
|
||||||
var act types.Actor
|
var act types.Actor
|
||||||
return st.root.ForEach(&act, func(k string) error {
|
return st.root.ForEach(&act, func(k string) error {
|
||||||
act := act // copy
|
act := act // copy
|
||||||
@ -512,6 +532,12 @@ func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error
|
|||||||
return xerrors.Errorf("invalid address (%x) found in state tree key: %w", []byte(k), err)
|
return xerrors.Errorf("invalid address (%x) found in state tree key: %w", []byte(k), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no need to record anything here, there are no duplicates in the actors HAMT
|
||||||
|
// iself.
|
||||||
|
if _, ok := seen[addr]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return f(addr, &act)
|
return f(addr, &act)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -439,6 +439,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
|||||||
},
|
},
|
||||||
GasCosts: &gasOutputs,
|
GasCosts: &gasOutputs,
|
||||||
Duration: time.Since(start),
|
Duration: time.Since(start),
|
||||||
|
ActorErr: aerrors.Newf(exitcode.SysErrOutOfGas,
|
||||||
|
"message gas limit does not cover on-chain gas costs"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,6 +671,12 @@ func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
|
|||||||
return root, nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the buffered blockstore associated with the VM. This includes any temporary blocks produced
|
||||||
|
// during this VM's execution.
|
||||||
|
func (vm *VM) ActorStore(ctx context.Context) adt.Store {
|
||||||
|
return adt.WrapStore(ctx, vm.cst)
|
||||||
|
}
|
||||||
|
|
||||||
func linksForObj(blk block.Block, cb func(cid.Cid)) error {
|
func linksForObj(blk block.Block, cb func(cid.Cid)) error {
|
||||||
switch blk.Cid().Prefix().Codec {
|
switch blk.Cid().Prefix().Codec {
|
||||||
case cid.DagCBOR:
|
case cid.DagCBOR:
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
"go.uber.org/multierr"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
@ -56,18 +57,18 @@ func nonNil(wallets ...getif) []api.Wallet {
|
|||||||
func (m MultiWallet) find(ctx context.Context, address address.Address, wallets ...getif) (api.Wallet, error) {
|
func (m MultiWallet) find(ctx context.Context, address address.Address, wallets ...getif) (api.Wallet, error) {
|
||||||
ws := nonNil(wallets...)
|
ws := nonNil(wallets...)
|
||||||
|
|
||||||
|
var merr error
|
||||||
|
|
||||||
for _, w := range ws {
|
for _, w := range ws {
|
||||||
have, err := w.WalletHas(ctx, address)
|
have, err := w.WalletHas(ctx, address)
|
||||||
if err != nil {
|
merr = multierr.Append(merr, err)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if have {
|
if err == nil && have {
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, merr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MultiWallet) WalletNew(ctx context.Context, keyType types.KeyType) (address.Address, error) {
|
func (m MultiWallet) WalletNew(ctx context.Context, keyType types.KeyType) (address.Address, error) {
|
||||||
|
@ -308,8 +308,8 @@ var clientDealCmd = &cli.Command{
|
|||||||
Description: `Make a deal with a miner.
|
Description: `Make a deal with a miner.
|
||||||
dataCid comes from running 'lotus client import'.
|
dataCid comes from running 'lotus client import'.
|
||||||
miner is the address of the miner you wish to make a deal with.
|
miner is the address of the miner you wish to make a deal with.
|
||||||
price is measured in FIL/GB/Epoch. Miners usually don't accept a bid
|
price is measured in FIL/Epoch. Miners usually don't accept a bid
|
||||||
lower than their advertised ask. You can check a miners listed price
|
lower than their advertised ask (which is in FIL/GiB/Epoch). You can check a miners listed price
|
||||||
with 'lotus client query-ask <miner address>'.
|
with 'lotus client query-ask <miner address>'.
|
||||||
duration is how long the miner should store the data for, in blocks.
|
duration is how long the miner should store the data for, in blocks.
|
||||||
The minimum value is 518400 (6 months).`,
|
The minimum value is 518400 (6 months).`,
|
||||||
@ -1759,7 +1759,7 @@ var clientQueryAskCmd = &cli.Command{
|
|||||||
return xerrors.Errorf("failed to get peerID for miner: %w", err)
|
return xerrors.Errorf("failed to get peerID for miner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *mi.PeerId == peer.ID("SETME") {
|
if mi.PeerId == nil || *mi.PeerId == peer.ID("SETME") {
|
||||||
return fmt.Errorf("the miner hasn't initialized yet")
|
return fmt.Errorf("the miner hasn't initialized yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1428,7 +1428,7 @@ var msigLockCancelCmd = &cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(cctx *cli.Context) error {
|
Action: func(cctx *cli.Context) error {
|
||||||
if cctx.Args().Len() != 6 {
|
if cctx.Args().Len() != 5 {
|
||||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount"))
|
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mathCmd = &cli.Command{
|
var mathCmd = &cli.Command{
|
||||||
@ -17,6 +19,7 @@ var mathCmd = &cli.Command{
|
|||||||
Usage: "utility commands around doing math on a list of numbers",
|
Usage: "utility commands around doing math on a list of numbers",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
mathSumCmd,
|
mathSumCmd,
|
||||||
|
mathAggFeesCmd,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,3 +104,30 @@ var mathSumCmd = &cli.Command{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mathAggFeesCmd = &cli.Command{
|
||||||
|
Name: "agg-fees",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "size",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "base-fee",
|
||||||
|
Usage: "baseFee aFIL",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
as := cctx.Int("size")
|
||||||
|
|
||||||
|
bf, err := types.BigFromString(cctx.String("base-fee"))
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("parsing basefee: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(types.FIL(miner5.AggregateNetworkFee(as, bf)))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
28
cmd/lotus-sim/copy.go
Normal file
28
cmd/lotus-sim/copy.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var copySimCommand = &cli.Command{
|
||||||
|
Name: "copy",
|
||||||
|
ArgsUsage: "<new-name>",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if cctx.NArg() != 1 {
|
||||||
|
return fmt.Errorf("expected 1 argument")
|
||||||
|
}
|
||||||
|
name := cctx.Args().First()
|
||||||
|
return node.CopySim(cctx.Context, cctx.String("simulation"), name)
|
||||||
|
},
|
||||||
|
}
|
49
cmd/lotus-sim/create.go
Normal file
49
cmd/lotus-sim/create.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
lcli "github.com/filecoin-project/lotus/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var createSimCommand = &cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
ArgsUsage: "[tipset]",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var ts *types.TipSet
|
||||||
|
switch cctx.NArg() {
|
||||||
|
case 0:
|
||||||
|
if err := node.Chainstore.Load(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ts = node.Chainstore.GetHeaviestTipSet()
|
||||||
|
case 1:
|
||||||
|
cids, err := lcli.ParseTipSetString(cctx.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tsk := types.NewTipSetKey(cids...)
|
||||||
|
ts, err = node.Chainstore.LoadTipSet(tsk)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("expected 0 or 1 arguments")
|
||||||
|
}
|
||||||
|
_, err = node.CreateSim(cctx.Context, cctx.String("simulation"), ts)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
22
cmd/lotus-sim/delete.go
Normal file
22
cmd/lotus-sim/delete.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deleteSimCommand = &cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return node.DeleteSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
},
|
||||||
|
}
|
110
cmd/lotus-sim/info.go
Normal file
110
cmd/lotus-sim/info.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTotalPower(ctx context.Context, sm *stmgr.StateManager, ts *types.TipSet) (power.Claim, error) {
|
||||||
|
actor, err := sm.LoadActor(ctx, power.Address, ts)
|
||||||
|
if err != nil {
|
||||||
|
return power.Claim{}, err
|
||||||
|
}
|
||||||
|
state, err := power.Load(sm.ChainStore().ActorStore(ctx), actor)
|
||||||
|
if err != nil {
|
||||||
|
return power.Claim{}, err
|
||||||
|
}
|
||||||
|
return state.TotalPower()
|
||||||
|
}
|
||||||
|
|
||||||
|
func printInfo(ctx context.Context, sim *simulation.Simulation, out io.Writer) error {
|
||||||
|
head := sim.GetHead()
|
||||||
|
start := sim.GetStart()
|
||||||
|
|
||||||
|
powerNow, err := getTotalPower(ctx, sim.StateManager, head)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
powerLookbackEpoch := head.Height() - builtin.EpochsInDay*2
|
||||||
|
if powerLookbackEpoch < start.Height() {
|
||||||
|
powerLookbackEpoch = start.Height()
|
||||||
|
}
|
||||||
|
lookbackTs, err := sim.Node.Chainstore.GetTipsetByHeight(ctx, powerLookbackEpoch, head, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
powerLookback, err := getTotalPower(ctx, sim.StateManager, lookbackTs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// growth rate in size/day
|
||||||
|
growthRate := big.Div(
|
||||||
|
big.Mul(big.Sub(powerNow.RawBytePower, powerLookback.RawBytePower),
|
||||||
|
big.NewInt(builtin.EpochsInDay)),
|
||||||
|
big.NewInt(int64(head.Height()-lookbackTs.Height())),
|
||||||
|
)
|
||||||
|
|
||||||
|
tw := tabwriter.NewWriter(out, 8, 8, 1, ' ', 0)
|
||||||
|
|
||||||
|
headEpoch := head.Height()
|
||||||
|
firstEpoch := start.Height() + 1
|
||||||
|
|
||||||
|
headTime := time.Unix(int64(head.MinTimestamp()), 0)
|
||||||
|
startTime := time.Unix(int64(start.MinTimestamp()), 0)
|
||||||
|
duration := headTime.Sub(startTime)
|
||||||
|
|
||||||
|
fmt.Fprintf(tw, "Name:\t%s\n", sim.Name())
|
||||||
|
fmt.Fprintf(tw, "Head:\t%s\n", head)
|
||||||
|
fmt.Fprintf(tw, "Start Epoch:\t%d\n", firstEpoch)
|
||||||
|
fmt.Fprintf(tw, "End Epoch:\t%d\n", headEpoch)
|
||||||
|
fmt.Fprintf(tw, "Length:\t%d\n", headEpoch-firstEpoch)
|
||||||
|
fmt.Fprintf(tw, "Start Date:\t%s\n", startTime)
|
||||||
|
fmt.Fprintf(tw, "End Date:\t%s\n", headTime)
|
||||||
|
fmt.Fprintf(tw, "Duration:\t%.2f day(s)\n", duration.Hours()/24)
|
||||||
|
fmt.Fprintf(tw, "Capacity:\t%s\n", types.SizeStr(powerNow.RawBytePower))
|
||||||
|
fmt.Fprintf(tw, "Daily Capacity Growth:\t%s/day\n", types.SizeStr(growthRate))
|
||||||
|
fmt.Fprintf(tw, "Network Version:\t%d\n", sim.GetNetworkVersion())
|
||||||
|
return tw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
var infoSimCommand = &cli.Command{
|
||||||
|
Name: "info",
|
||||||
|
Description: "Output information about the simulation.",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
infoCommitGasSimCommand,
|
||||||
|
infoMessageSizeSimCommand,
|
||||||
|
infoWindowPostBandwidthSimCommand,
|
||||||
|
infoCapacityGrowthSimCommand,
|
||||||
|
infoStateGrowthSimCommand,
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return printInfo(cctx.Context, sim, cctx.App.Writer)
|
||||||
|
},
|
||||||
|
}
|
67
cmd/lotus-sim/info_capacity.go
Normal file
67
cmd/lotus-sim/info_capacity.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var infoCapacityGrowthSimCommand = &cli.Command{
|
||||||
|
Name: "capacity-growth",
|
||||||
|
Description: "List daily capacity growth over the course of the simulation starting at the end.",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
firstEpoch := sim.GetStart().Height()
|
||||||
|
ts := sim.GetHead()
|
||||||
|
lastPower, err := getTotalPower(cctx.Context, sim.StateManager, ts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastHeight := ts.Height()
|
||||||
|
|
||||||
|
for ts.Height() > firstEpoch && cctx.Err() == nil {
|
||||||
|
ts, err = sim.Node.Chainstore.LoadTipSet(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newEpoch := ts.Height()
|
||||||
|
if newEpoch != firstEpoch && newEpoch+builtin.EpochsInDay > lastHeight {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newPower, err := getTotalPower(cctx.Context, sim.StateManager, ts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
growthRate := big.Div(
|
||||||
|
big.Mul(big.Sub(lastPower.RawBytePower, newPower.RawBytePower),
|
||||||
|
big.NewInt(builtin.EpochsInDay)),
|
||||||
|
big.NewInt(int64(lastHeight-newEpoch)),
|
||||||
|
)
|
||||||
|
lastPower = newPower
|
||||||
|
lastHeight = newEpoch
|
||||||
|
fmt.Fprintf(cctx.App.Writer, "%s/day\n", types.SizeStr(growthRate))
|
||||||
|
}
|
||||||
|
return cctx.Err()
|
||||||
|
},
|
||||||
|
}
|
148
cmd/lotus-sim/info_commit.go
Normal file
148
cmd/lotus-sim/info_commit.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/streadway/quantile"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation"
|
||||||
|
"github.com/filecoin-project/lotus/lib/stati"
|
||||||
|
)
|
||||||
|
|
||||||
|
var infoCommitGasSimCommand = &cli.Command{
|
||||||
|
Name: "commit-gas",
|
||||||
|
Description: "Output information about the gas for commits",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.Int64Flag{
|
||||||
|
Name: "lookback",
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
log := func(f string, i ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, f, i...)
|
||||||
|
}
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go profileOnSignal(cctx, syscall.SIGUSR2)
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var gasAgg, proofsAgg uint64
|
||||||
|
var gasAggMax, proofsAggMax uint64
|
||||||
|
var gasSingle, proofsSingle uint64
|
||||||
|
|
||||||
|
qpoints := []struct{ q, tol float64 }{
|
||||||
|
{0.01, 0.0005},
|
||||||
|
{0.05, 0.001},
|
||||||
|
{0.20, 0.01},
|
||||||
|
{0.25, 0.01},
|
||||||
|
{0.30, 0.01},
|
||||||
|
{0.40, 0.01},
|
||||||
|
{0.45, 0.01},
|
||||||
|
{0.50, 0.01},
|
||||||
|
{0.60, 0.01},
|
||||||
|
{0.80, 0.01},
|
||||||
|
{0.95, 0.001},
|
||||||
|
{0.99, 0.0005},
|
||||||
|
}
|
||||||
|
estims := make([]quantile.Estimate, len(qpoints))
|
||||||
|
for i, p := range qpoints {
|
||||||
|
estims[i] = quantile.Known(p.q, p.tol)
|
||||||
|
}
|
||||||
|
qua := quantile.New(estims...)
|
||||||
|
hist, err := stati.NewHistogram([]float64{
|
||||||
|
1, 3, 5, 7, 15, 30, 50, 100, 200, 400, 600, 700, 819})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sim.Walk(cctx.Context, cctx.Int64("lookback"), func(
|
||||||
|
sm *stmgr.StateManager, ts *types.TipSet, stCid cid.Cid,
|
||||||
|
messages []*simulation.AppliedMessage,
|
||||||
|
) error {
|
||||||
|
for _, m := range messages {
|
||||||
|
if m.ExitCode != exitcode.Ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m.Method == miner.Methods.ProveCommitAggregate {
|
||||||
|
param := miner.ProveCommitAggregateParams{}
|
||||||
|
err := param.UnmarshalCBOR(bytes.NewReader(m.Params))
|
||||||
|
if err != nil {
|
||||||
|
log("failed to decode params: %+v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c, err := param.SectorNumbers.Count()
|
||||||
|
if err != nil {
|
||||||
|
log("failed to count sectors")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
gasAgg += uint64(m.GasUsed)
|
||||||
|
proofsAgg += c
|
||||||
|
if c == 819 {
|
||||||
|
gasAggMax += uint64(m.GasUsed)
|
||||||
|
proofsAggMax += c
|
||||||
|
}
|
||||||
|
for i := uint64(0); i < c; i++ {
|
||||||
|
qua.Add(float64(c))
|
||||||
|
}
|
||||||
|
hist.Observe(float64(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Method == miner.Methods.ProveCommitSector {
|
||||||
|
gasSingle += uint64(m.GasUsed)
|
||||||
|
proofsSingle++
|
||||||
|
qua.Add(1)
|
||||||
|
hist.Observe(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
idealGassUsed := float64(gasAggMax) / float64(proofsAggMax) * float64(proofsAgg+proofsSingle)
|
||||||
|
|
||||||
|
fmt.Printf("Gas usage efficiency in comparison to all 819: %f%%\n", 100*idealGassUsed/float64(gasAgg+gasSingle))
|
||||||
|
|
||||||
|
fmt.Printf("Proofs in singles: %d\n", proofsSingle)
|
||||||
|
fmt.Printf("Proofs in Aggs: %d\n", proofsAgg)
|
||||||
|
fmt.Printf("Proofs in Aggs(819): %d\n", proofsAggMax)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Quantiles of proofs in given aggregate size:")
|
||||||
|
for _, p := range qpoints {
|
||||||
|
fmt.Printf("%.0f%%\t%.0f\n", p.q*100, qua.Get(p.q))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Histogram of messages:")
|
||||||
|
fmt.Printf("Total\t%d\n", hist.Total())
|
||||||
|
for i, b := range hist.Buckets[1:] {
|
||||||
|
fmt.Printf("%.0f\t%d\n", b, hist.Get(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
95
cmd/lotus-sim/info_message.go
Normal file
95
cmd/lotus-sim/info_message.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation"
|
||||||
|
"github.com/filecoin-project/lotus/lib/stati"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/streadway/quantile"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var infoMessageSizeSimCommand = &cli.Command{
|
||||||
|
Name: "message-size",
|
||||||
|
Description: "Output information about message size distribution",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.Int64Flag{
|
||||||
|
Name: "lookback",
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go profileOnSignal(cctx, syscall.SIGUSR2)
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
qpoints := []struct{ q, tol float64 }{
|
||||||
|
{0.30, 0.01},
|
||||||
|
{0.40, 0.01},
|
||||||
|
{0.60, 0.01},
|
||||||
|
{0.70, 0.01},
|
||||||
|
{0.80, 0.01},
|
||||||
|
{0.85, 0.01},
|
||||||
|
{0.90, 0.01},
|
||||||
|
{0.95, 0.001},
|
||||||
|
{0.99, 0.0005},
|
||||||
|
{0.999, 0.0001},
|
||||||
|
}
|
||||||
|
estims := make([]quantile.Estimate, len(qpoints))
|
||||||
|
for i, p := range qpoints {
|
||||||
|
estims[i] = quantile.Known(p.q, p.tol)
|
||||||
|
}
|
||||||
|
qua := quantile.New(estims...)
|
||||||
|
hist, err := stati.NewHistogram([]float64{
|
||||||
|
1 << 8, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sim.Walk(cctx.Context, cctx.Int64("lookback"), func(
|
||||||
|
sm *stmgr.StateManager, ts *types.TipSet, stCid cid.Cid,
|
||||||
|
messages []*simulation.AppliedMessage,
|
||||||
|
) error {
|
||||||
|
for _, m := range messages {
|
||||||
|
msgSize := float64(m.ChainLength())
|
||||||
|
qua.Add(msgSize)
|
||||||
|
hist.Observe(msgSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Quantiles of message sizes:")
|
||||||
|
for _, p := range qpoints {
|
||||||
|
fmt.Printf("%.1f%%\t%.0f\n", p.q*100, qua.Get(p.q))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Histogram of message sizes:")
|
||||||
|
fmt.Printf("Total\t%d\n", hist.Total())
|
||||||
|
for i, b := range hist.Buckets[1:] {
|
||||||
|
fmt.Printf("%.0f\t%d\t%.1f%%\n", b, hist.Get(i), 100*hist.GetRatio(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
141
cmd/lotus-sim/info_state.go
Normal file
141
cmd/lotus-sim/info_state.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var infoStateGrowthSimCommand = &cli.Command{
|
||||||
|
Name: "state-size",
|
||||||
|
Description: "List daily state size over the course of the simulation starting at the end.",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This code is entirely read-bound.
|
||||||
|
store := node.Chainstore.StateBlockstore()
|
||||||
|
stateSize := func(ctx context.Context, c cid.Cid) (uint64, error) {
|
||||||
|
seen := cid.NewSet()
|
||||||
|
sema := make(chan struct{}, 40)
|
||||||
|
var lock sync.Mutex
|
||||||
|
var recSize func(cid.Cid) (uint64, error)
|
||||||
|
recSize = func(c cid.Cid) (uint64, error) {
|
||||||
|
// Not a part of the chain state.
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.Lock()
|
||||||
|
visit := seen.Visit(c)
|
||||||
|
lock.Unlock()
|
||||||
|
// Already seen?
|
||||||
|
if !visit {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var links []cid.Cid
|
||||||
|
var totalSize uint64
|
||||||
|
if err := store.View(c, func(data []byte) error {
|
||||||
|
totalSize += uint64(len(data))
|
||||||
|
return cbg.ScanForLinks(bytes.NewReader(data), func(c cid.Cid) {
|
||||||
|
if c.Prefix().Codec != cid.DagCBOR {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
links = append(links, c)
|
||||||
|
})
|
||||||
|
}); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
cb := func(c cid.Cid) {
|
||||||
|
size, err := recSize(c)
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case errCh <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
atomic.AddUint64(&totalSize, size)
|
||||||
|
}
|
||||||
|
asyncCb := func(c cid.Cid) {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() { <-sema }()
|
||||||
|
cb(c)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for _, link := range links {
|
||||||
|
select {
|
||||||
|
case sema <- struct{}{}:
|
||||||
|
asyncCb(link)
|
||||||
|
default:
|
||||||
|
cb(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
return 0, err
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSize, nil
|
||||||
|
}
|
||||||
|
return recSize(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
firstEpoch := sim.GetStart().Height()
|
||||||
|
ts := sim.GetHead()
|
||||||
|
lastHeight := abi.ChainEpoch(math.MaxInt64)
|
||||||
|
for ts.Height() > firstEpoch && cctx.Err() == nil {
|
||||||
|
if ts.Height()+builtin.EpochsInDay <= lastHeight {
|
||||||
|
lastHeight = ts.Height()
|
||||||
|
|
||||||
|
parentStateSize, err := stateSize(cctx.Context, ts.ParentState())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(cctx.App.Writer, "%d: %s\n", ts.Height(), types.SizeStr(types.NewInt(parentStateSize)))
|
||||||
|
}
|
||||||
|
|
||||||
|
ts, err = sim.Node.Chainstore.LoadTipSet(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cctx.Err()
|
||||||
|
},
|
||||||
|
}
|
69
cmd/lotus-sim/info_wdpost.go
Normal file
69
cmd/lotus-sim/info_wdpost.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation"
|
||||||
|
)
|
||||||
|
|
||||||
|
var infoWindowPostBandwidthSimCommand = &cli.Command{
|
||||||
|
Name: "post-bandwidth",
|
||||||
|
Description: "List average chain bandwidth used by window posts for each day of the simulation.",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var postGas, totalGas int64
|
||||||
|
printStats := func() {
|
||||||
|
fmt.Fprintf(cctx.App.Writer, "%.4f%%\n", float64(100*postGas)/float64(totalGas))
|
||||||
|
}
|
||||||
|
idx := 0
|
||||||
|
err = sim.Walk(cctx.Context, 0, func(
|
||||||
|
sm *stmgr.StateManager, ts *types.TipSet, stCid cid.Cid,
|
||||||
|
messages []*simulation.AppliedMessage,
|
||||||
|
) error {
|
||||||
|
for _, m := range messages {
|
||||||
|
totalGas += m.GasUsed
|
||||||
|
if m.ExitCode != exitcode.Ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m.Method == miner.Methods.SubmitWindowedPoSt {
|
||||||
|
postGas += m.GasUsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
idx %= builtin.EpochsInDay
|
||||||
|
if idx == 0 {
|
||||||
|
printStats()
|
||||||
|
postGas = 0
|
||||||
|
totalGas = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if idx > 0 {
|
||||||
|
printStats()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
38
cmd/lotus-sim/list.go
Normal file
38
cmd/lotus-sim/list.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var listSimCommand = &cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
list, err := node.ListSims(cctx.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tw := tabwriter.NewWriter(cctx.App.Writer, 8, 8, 0, ' ', 0)
|
||||||
|
for _, name := range list {
|
||||||
|
sim, err := node.LoadSim(cctx.Context, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
head := sim.GetHead()
|
||||||
|
fmt.Fprintf(tw, "%s\t%s\t%s\n", name, head.Height(), head.Key())
|
||||||
|
}
|
||||||
|
return tw.Flush()
|
||||||
|
},
|
||||||
|
}
|
63
cmd/lotus-sim/main.go
Normal file
63
cmd/lotus-sim/main.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var root []*cli.Command = []*cli.Command{
|
||||||
|
createSimCommand,
|
||||||
|
deleteSimCommand,
|
||||||
|
copySimCommand,
|
||||||
|
renameSimCommand,
|
||||||
|
listSimCommand,
|
||||||
|
|
||||||
|
runSimCommand,
|
||||||
|
infoSimCommand,
|
||||||
|
upgradeCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if _, set := os.LookupEnv("GOLOG_LOG_LEVEL"); !set {
|
||||||
|
_ = logging.SetLogLevel("simulation", "DEBUG")
|
||||||
|
_ = logging.SetLogLevel("simulation-mock", "DEBUG")
|
||||||
|
}
|
||||||
|
app := &cli.App{
|
||||||
|
Name: "lotus-sim",
|
||||||
|
Usage: "A tool to simulate a network.",
|
||||||
|
Commands: root,
|
||||||
|
Writer: os.Stdout,
|
||||||
|
ErrWriter: os.Stderr,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "repo",
|
||||||
|
EnvVars: []string{"LOTUS_PATH"},
|
||||||
|
Hidden: true,
|
||||||
|
Value: "~/.lotus",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "simulation",
|
||||||
|
Aliases: []string{"sim"},
|
||||||
|
EnvVars: []string{"LOTUS_SIMULATION"},
|
||||||
|
Value: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(),
|
||||||
|
syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := app.RunContext(ctx, os.Args); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
94
cmd/lotus-sim/profile.go
Normal file
94
cmd/lotus-sim/profile.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func takeProfiles(ctx context.Context) (fname string, _err error) {
|
||||||
|
dir, err := os.MkdirTemp(".", ".profiles-temp*")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeProfiles(ctx, dir); err != nil {
|
||||||
|
_ = os.RemoveAll(dir)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fname = fmt.Sprintf("pprof-simulation-%s", time.Now().Format(time.RFC3339))
|
||||||
|
if err := os.Rename(dir, fname); err != nil {
|
||||||
|
_ = os.RemoveAll(dir)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fname, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProfiles(ctx context.Context, dir string) error {
|
||||||
|
for _, profile := range pprof.Profiles() {
|
||||||
|
file, err := os.Create(filepath.Join(dir, profile.Name()+".pprof.gz"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := profile.WriteTo(file, 0); err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(filepath.Join(dir, "cpu.pprof.gz"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pprof.StartCPUProfile(file); err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(30 * time.Second):
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
err = file.Close()
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func profileOnSignal(cctx *cli.Context, signals ...os.Signal) {
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, signals...)
|
||||||
|
defer signal.Stop(ch)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
fname, err := takeProfiles(cctx.Context)
|
||||||
|
switch err {
|
||||||
|
case context.Canceled:
|
||||||
|
return
|
||||||
|
case nil:
|
||||||
|
fmt.Fprintf(cctx.App.ErrWriter, "Wrote profile to %q\n", fname)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(cctx.App.ErrWriter, "ERROR: failed to write profile: %s\n", err)
|
||||||
|
}
|
||||||
|
case <-cctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
cmd/lotus-sim/rename.go
Normal file
29
cmd/lotus-sim/rename.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var renameSimCommand = &cli.Command{
|
||||||
|
Name: "rename",
|
||||||
|
ArgsUsage: "<new-name>",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if cctx.NArg() != 1 {
|
||||||
|
return fmt.Errorf("expected 1 argument")
|
||||||
|
}
|
||||||
|
name := cctx.Args().First()
|
||||||
|
return node.RenameSim(cctx.Context, cctx.String("simulation"), name)
|
||||||
|
},
|
||||||
|
}
|
72
cmd/lotus-sim/run.go
Normal file
72
cmd/lotus-sim/run.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var runSimCommand = &cli.Command{
|
||||||
|
Name: "run",
|
||||||
|
Description: `Run the simulation.
|
||||||
|
|
||||||
|
Signals:
|
||||||
|
- SIGUSR1: Print information about the current simulation (equivalent to 'lotus-sim info').
|
||||||
|
- SIGUSR2: Write pprof profiles to ./pprof-simulation-$DATE/`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "epochs",
|
||||||
|
Usage: "Advance the given number of epochs then stop.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go profileOnSignal(cctx, syscall.SIGUSR2)
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetEpochs := cctx.Int("epochs")
|
||||||
|
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, syscall.SIGUSR1)
|
||||||
|
defer signal.Stop(ch)
|
||||||
|
|
||||||
|
for i := 0; targetEpochs == 0 || i < targetEpochs; i++ {
|
||||||
|
ts, err := sim.Step(cctx.Context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(cctx.App.Writer, "advanced to %d %s\n", ts.Height(), ts.Key())
|
||||||
|
|
||||||
|
// Print
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
fmt.Fprintln(cctx.App.Writer, "---------------------")
|
||||||
|
if err := printInfo(cctx.Context, sim, cctx.App.Writer); err != nil {
|
||||||
|
fmt.Fprintf(cctx.App.ErrWriter, "ERROR: failed to print info: %s\n", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(cctx.App.Writer, "---------------------")
|
||||||
|
case <-cctx.Context.Done():
|
||||||
|
return cctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintln(cctx.App.Writer, "simulation done")
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
93
cmd/lotus-sim/simulation/block.go
Normal file
93
cmd/lotus-sim/simulation/block.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const beaconPrefix = "mockbeacon:"
|
||||||
|
|
||||||
|
// nextBeaconEntries returns a fake beacon entries for the next block.
|
||||||
|
func (sim *Simulation) nextBeaconEntries() []types.BeaconEntry {
|
||||||
|
parentBeacons := sim.head.Blocks()[0].BeaconEntries
|
||||||
|
lastBeacon := parentBeacons[len(parentBeacons)-1]
|
||||||
|
beaconRound := lastBeacon.Round + 1
|
||||||
|
|
||||||
|
buf := make([]byte, len(beaconPrefix)+8)
|
||||||
|
copy(buf, beaconPrefix)
|
||||||
|
binary.BigEndian.PutUint64(buf[len(beaconPrefix):], beaconRound)
|
||||||
|
beaconRand := sha256.Sum256(buf)
|
||||||
|
return []types.BeaconEntry{{
|
||||||
|
Round: beaconRound,
|
||||||
|
Data: beaconRand[:],
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextTicket returns a fake ticket for the next block.
|
||||||
|
func (sim *Simulation) nextTicket() *types.Ticket {
|
||||||
|
newProof := sha256.Sum256(sim.head.MinTicket().VRFProof)
|
||||||
|
return &types.Ticket{
|
||||||
|
VRFProof: newProof[:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeTipSet generates and executes the next tipset from the given messages. This method:
|
||||||
|
//
|
||||||
|
// 1. Stores the given messages in the Chainstore.
|
||||||
|
// 2. Creates and persists a single block mined by the same miner as the parent.
|
||||||
|
// 3. Creates a tipset from this block and executes it.
|
||||||
|
// 4. Returns the resulting tipset.
|
||||||
|
//
|
||||||
|
// This method does _not_ mutate local state (although it does add blocks to the datastore).
|
||||||
|
func (sim *Simulation) makeTipSet(ctx context.Context, messages []*types.Message) (*types.TipSet, error) {
|
||||||
|
parentTs := sim.head
|
||||||
|
parentState, parentRec, err := sim.StateManager.TipSetState(ctx, parentTs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to compute parent tipset: %w", err)
|
||||||
|
}
|
||||||
|
msgsCid, err := sim.storeMessages(ctx, messages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to store block messages: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uts := parentTs.MinTimestamp() + build.BlockDelaySecs
|
||||||
|
|
||||||
|
blks := []*types.BlockHeader{{
|
||||||
|
Miner: parentTs.MinTicketBlock().Miner, // keep reusing the same miner.
|
||||||
|
Ticket: sim.nextTicket(),
|
||||||
|
BeaconEntries: sim.nextBeaconEntries(),
|
||||||
|
Parents: parentTs.Cids(),
|
||||||
|
Height: parentTs.Height() + 1,
|
||||||
|
ParentStateRoot: parentState,
|
||||||
|
ParentMessageReceipts: parentRec,
|
||||||
|
Messages: msgsCid,
|
||||||
|
ParentBaseFee: abi.NewTokenAmount(0),
|
||||||
|
Timestamp: uts,
|
||||||
|
ElectionProof: &types.ElectionProof{WinCount: 1},
|
||||||
|
}}
|
||||||
|
err = sim.Node.Chainstore.PersistBlockHeaders(blks...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to persist block headers: %w", err)
|
||||||
|
}
|
||||||
|
newTipSet, err := types.NewTipSet(blks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to create new tipset: %w", err)
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
_, _, err = sim.StateManager.TipSetState(ctx, newTipSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to compute new tipset: %w", err)
|
||||||
|
}
|
||||||
|
duration := time.Since(now)
|
||||||
|
log.Infow("computed tipset", "duration", duration, "height", newTipSet.Height())
|
||||||
|
|
||||||
|
return newTipSet, nil
|
||||||
|
}
|
280
cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go
Normal file
280
cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
package blockbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/account"
|
||||||
|
"github.com/filecoin-project/lotus/chain/state"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 0.25 is the default, but the number below is from the network.
|
||||||
|
gasOverestimation = 1.0 / 0.808
|
||||||
|
// The number of expected blocks in a tipset. We use this to determine how much gas a tipset
|
||||||
|
// has.
|
||||||
|
// 5 per tipset, but we effectively get 4 blocks worth of messages.
|
||||||
|
expectedBlocks = 4
|
||||||
|
// TODO: This will produce invalid blocks but it will accurately model the amount of gas
|
||||||
|
// we're willing to use per-tipset.
|
||||||
|
// A more correct approach would be to produce 5 blocks. We can do that later.
|
||||||
|
targetGas = build.BlockGasTarget * expectedBlocks
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockBuilder struct {
|
||||||
|
ctx context.Context
|
||||||
|
logger *zap.SugaredLogger
|
||||||
|
|
||||||
|
parentTs *types.TipSet
|
||||||
|
parentSt *state.StateTree
|
||||||
|
vm *vm.VM
|
||||||
|
sm *stmgr.StateManager
|
||||||
|
|
||||||
|
gasTotal int64
|
||||||
|
messages []*types.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockBuilder constructs a new block builder from the parent state. Use this to pack a block
|
||||||
|
// with messages.
|
||||||
|
//
|
||||||
|
// NOTE: The context applies to the life of the block builder itself (but does not need to be canceled).
|
||||||
|
func NewBlockBuilder(ctx context.Context, logger *zap.SugaredLogger, sm *stmgr.StateManager, parentTs *types.TipSet) (*BlockBuilder, error) {
|
||||||
|
parentState, _, err := sm.TipSetState(ctx, parentTs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parentSt, err := sm.StateTree(parentState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bb := &BlockBuilder{
|
||||||
|
ctx: ctx,
|
||||||
|
logger: logger.With("epoch", parentTs.Height()+1),
|
||||||
|
sm: sm,
|
||||||
|
parentTs: parentTs,
|
||||||
|
parentSt: parentSt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we construct a VM to execute messages for gas estimation.
|
||||||
|
//
|
||||||
|
// Most parts of this VM are "real" except:
|
||||||
|
// 1. We don't charge a fee.
|
||||||
|
// 2. The runtime has "fake" proof logic.
|
||||||
|
// 3. We don't actually save any of the results.
|
||||||
|
r := store.NewChainRand(sm.ChainStore(), parentTs.Cids())
|
||||||
|
vmopt := &vm.VMOpts{
|
||||||
|
StateBase: parentState,
|
||||||
|
Epoch: parentTs.Height() + 1,
|
||||||
|
Rand: r,
|
||||||
|
Bstore: sm.ChainStore().StateBlockstore(),
|
||||||
|
Syscalls: sm.ChainStore().VMSys(),
|
||||||
|
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||||
|
NtwkVersion: sm.GetNtwkVersion,
|
||||||
|
BaseFee: abi.NewTokenAmount(0),
|
||||||
|
LookbackState: stmgr.LookbackStateGetterForTipset(sm, parentTs),
|
||||||
|
}
|
||||||
|
bb.vm, err = vm.NewVM(bb.ctx, vmopt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushMessages tries to push the specified message into the block.
|
||||||
|
//
|
||||||
|
// 1. All messages will be executed in-order.
|
||||||
|
// 2. Gas computation & nonce selection will be handled internally.
|
||||||
|
// 3. The base-fee is 0 so the sender does not need funds.
|
||||||
|
// 4. As usual, the sender must be an account (any account).
|
||||||
|
// 5. If the message fails to execute, this method will fail.
|
||||||
|
//
|
||||||
|
// Returns ErrOutOfGas when out of gas. Check BlockBuilder.GasRemaining and try pushing a cheaper
|
||||||
|
// message.
|
||||||
|
func (bb *BlockBuilder) PushMessage(msg *types.Message) (*types.MessageReceipt, error) {
|
||||||
|
if bb.gasTotal >= targetGas {
|
||||||
|
return nil, new(ErrOutOfGas)
|
||||||
|
}
|
||||||
|
|
||||||
|
st := bb.StateTree()
|
||||||
|
store := bb.ActorStore()
|
||||||
|
|
||||||
|
// Copy the message before we start mutating it.
|
||||||
|
msgCpy := *msg
|
||||||
|
msg = &msgCpy
|
||||||
|
|
||||||
|
actor, err := st.GetActor(msg.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !builtin.IsAccountActor(actor.Code) {
|
||||||
|
return nil, xerrors.Errorf(
|
||||||
|
"messags may only be sent from account actors, got message from %s (%s)",
|
||||||
|
msg.From, builtin.ActorNameByCode(actor.Code),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
msg.Nonce = actor.Nonce
|
||||||
|
if msg.From.Protocol() == address.ID {
|
||||||
|
state, err := account.Load(store, actor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.From, err = state.PubkeyAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Our gas estimation is broken for payment channels due to horrible hacks in
|
||||||
|
// gasEstimateGasLimit.
|
||||||
|
if msg.Value == types.EmptyInt {
|
||||||
|
msg.Value = abi.NewTokenAmount(0)
|
||||||
|
}
|
||||||
|
msg.GasPremium = abi.NewTokenAmount(0)
|
||||||
|
msg.GasFeeCap = abi.NewTokenAmount(0)
|
||||||
|
msg.GasLimit = build.BlockGasTarget
|
||||||
|
|
||||||
|
// We manually snapshot so we can revert nonce changes, etc. on failure.
|
||||||
|
err = st.Snapshot(bb.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to take a snapshot while estimating message gas: %w", err)
|
||||||
|
}
|
||||||
|
defer st.ClearSnapshot()
|
||||||
|
|
||||||
|
ret, err := bb.vm.ApplyMessage(bb.ctx, msg)
|
||||||
|
if err != nil {
|
||||||
|
_ = st.Revert()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.ActorErr != nil {
|
||||||
|
_ = st.Revert()
|
||||||
|
return nil, ret.ActorErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes there are bugs. Let's catch them.
|
||||||
|
if ret.GasUsed == 0 {
|
||||||
|
_ = st.Revert()
|
||||||
|
return nil, xerrors.Errorf("used no gas %v -> %v", msg, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the gas limit taking overestimation into account.
|
||||||
|
msg.GasLimit = int64(math.Ceil(float64(ret.GasUsed) * gasOverestimation))
|
||||||
|
|
||||||
|
// Did we go over? Yes, revert.
|
||||||
|
newTotal := bb.gasTotal + msg.GasLimit
|
||||||
|
if newTotal > targetGas {
|
||||||
|
_ = st.Revert()
|
||||||
|
return nil, &ErrOutOfGas{Available: targetGas - bb.gasTotal, Required: msg.GasLimit}
|
||||||
|
}
|
||||||
|
bb.gasTotal = newTotal
|
||||||
|
|
||||||
|
bb.messages = append(bb.messages, msg)
|
||||||
|
return &ret.MessageReceipt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActorStore returns the VM's current (pending) blockstore.
|
||||||
|
func (bb *BlockBuilder) ActorStore() adt.Store {
|
||||||
|
return bb.vm.ActorStore(bb.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateTree returns the VM's current (pending) state-tree. This includes any changes made by
|
||||||
|
// successfully pushed messages.
|
||||||
|
//
|
||||||
|
// You probably want ParentStateTree
|
||||||
|
func (bb *BlockBuilder) StateTree() *state.StateTree {
|
||||||
|
return bb.vm.StateTree().(*state.StateTree)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentStateTree returns the parent state-tree (not the paren't tipset's parent state-tree).
|
||||||
|
func (bb *BlockBuilder) ParentStateTree() *state.StateTree {
|
||||||
|
return bb.parentSt
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateTreeByHeight will return a state-tree up through and including the current in-progress
|
||||||
|
// epoch.
|
||||||
|
//
|
||||||
|
// NOTE: This will return the state after the given epoch, not the parent state for the epoch.
|
||||||
|
func (bb *BlockBuilder) StateTreeByHeight(epoch abi.ChainEpoch) (*state.StateTree, error) {
|
||||||
|
now := bb.Height()
|
||||||
|
if epoch > now {
|
||||||
|
return nil, xerrors.Errorf(
|
||||||
|
"cannot load state-tree from future: %d > %d", epoch, bb.Height(),
|
||||||
|
)
|
||||||
|
} else if epoch <= 0 {
|
||||||
|
return nil, xerrors.Errorf(
|
||||||
|
"cannot load state-tree: epoch %d <= 0", epoch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually handle "now" and "previous".
|
||||||
|
switch epoch {
|
||||||
|
case now:
|
||||||
|
return bb.StateTree(), nil
|
||||||
|
case now - 1:
|
||||||
|
return bb.ParentStateTree(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tipset of the block _after_ the target epoch so we can use its parent state.
|
||||||
|
targetTs, err := bb.sm.ChainStore().GetTipsetByHeight(bb.ctx, epoch+1, bb.parentTs, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bb.sm.StateTree(targetTs.ParentState())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages returns all messages currently packed into the next block.
|
||||||
|
// 1. DO NOT modify the slice, copy it.
|
||||||
|
// 2. DO NOT retain the slice, copy it.
|
||||||
|
func (bb *BlockBuilder) Messages() []*types.Message {
|
||||||
|
return bb.messages
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasRemaining returns the amount of remaining gas in the next block.
|
||||||
|
func (bb *BlockBuilder) GasRemaining() int64 {
|
||||||
|
return targetGas - bb.gasTotal
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentTipSet returns the parent tipset.
|
||||||
|
func (bb *BlockBuilder) ParentTipSet() *types.TipSet {
|
||||||
|
return bb.parentTs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height returns the epoch for the target block.
|
||||||
|
func (bb *BlockBuilder) Height() abi.ChainEpoch {
|
||||||
|
return bb.parentTs.Height() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkVersion returns the network version for the target block.
|
||||||
|
func (bb *BlockBuilder) NetworkVersion() network.Version {
|
||||||
|
return bb.sm.GetNtwkVersion(bb.ctx, bb.Height())
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateManager returns the stmgr.StateManager.
|
||||||
|
func (bb *BlockBuilder) StateManager() *stmgr.StateManager {
|
||||||
|
return bb.sm
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActorsVersion returns the actors version for the target block.
|
||||||
|
func (bb *BlockBuilder) ActorsVersion() actors.Version {
|
||||||
|
return actors.VersionForNetwork(bb.NetworkVersion())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bb *BlockBuilder) L() *zap.SugaredLogger {
|
||||||
|
return bb.logger
|
||||||
|
}
|
25
cmd/lotus-sim/simulation/blockbuilder/errors.go
Normal file
25
cmd/lotus-sim/simulation/blockbuilder/errors.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package blockbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrOutOfGas is returned from BlockBuilder.PushMessage when the block does not have enough gas to
|
||||||
|
// fit the given message.
|
||||||
|
type ErrOutOfGas struct {
|
||||||
|
Available, Required int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrOutOfGas) Error() string {
|
||||||
|
if e.Available == 0 {
|
||||||
|
return "out of gas: block full"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("out of gas: %d < %d", e.Required, e.Available)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOutOfGas returns true if the error is an "out of gas" error.
|
||||||
|
func IsOutOfGas(err error) bool {
|
||||||
|
var oog *ErrOutOfGas
|
||||||
|
return errors.As(err, &oog)
|
||||||
|
}
|
58
cmd/lotus-sim/simulation/messages.go
Normal file
58
cmd/lotus-sim/simulation/messages.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
|
||||||
|
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toArray converts the given set of CIDs to an AMT. This is usually used to pack messages into blocks.
|
||||||
|
func toArray(store blockadt.Store, cids []cid.Cid) (cid.Cid, error) {
|
||||||
|
arr := blockadt.MakeEmptyArray(store)
|
||||||
|
for i, c := range cids {
|
||||||
|
oc := cbg.CborCid(c)
|
||||||
|
if err := arr.Set(uint64(i), &oc); err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr.Root()
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeMessages packs a set of messages into a types.MsgMeta and returns the resulting CID. The
|
||||||
|
// resulting CID is valid for the BlocKHeader's Messages field.
|
||||||
|
func (sim *Simulation) storeMessages(ctx context.Context, messages []*types.Message) (cid.Cid, error) {
|
||||||
|
// We store all messages as "bls" messages so they're executed in-order. This ensures
|
||||||
|
// accurate gas accounting. It also ensures we don't, e.g., try to fund a miner after we
|
||||||
|
// fail a pre-commit...
|
||||||
|
var msgCids []cid.Cid
|
||||||
|
for _, msg := range messages {
|
||||||
|
c, err := sim.Node.Chainstore.PutMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
msgCids = append(msgCids, c)
|
||||||
|
}
|
||||||
|
adtStore := sim.Node.Chainstore.ActorStore(ctx)
|
||||||
|
blsMsgArr, err := toArray(adtStore, msgCids)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
sekpMsgArr, err := toArray(adtStore, nil)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgsCid, err := adtStore.Put(adtStore.Context(), &types.MsgMeta{
|
||||||
|
BlsMessages: blsMsgArr,
|
||||||
|
SecpkMessages: sekpMsgArr,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
return msgsCid, nil
|
||||||
|
}
|
179
cmd/lotus-sim/simulation/mock/mock.go
Normal file
179
cmd/lotus-sim/simulation/mock/mock.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
|
proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof"
|
||||||
|
tutils "github.com/filecoin-project/specs-actors/v5/support/testing"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ideally, we'd use extern/sector-storage/mock. Unfortunately, those mocks are a bit _too_ accurate
|
||||||
|
// and would force us to load sector info for window post proofs.
|
||||||
|
|
||||||
|
const (
|
||||||
|
mockSealProofPrefix = "valid seal proof:"
|
||||||
|
mockAggregateSealProofPrefix = "valid aggregate seal proof:"
|
||||||
|
mockPoStProofPrefix = "valid post proof:"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logging.Logger("simulation-mock")
|
||||||
|
|
||||||
|
// mockVerifier is a simple mock for verifying "fake" proofs.
|
||||||
|
type mockVerifier struct{}
|
||||||
|
|
||||||
|
var Verifier ffiwrapper.Verifier = mockVerifier{}
|
||||||
|
|
||||||
|
func (mockVerifier) VerifySeal(proof proof5.SealVerifyInfo) (bool, error) {
|
||||||
|
addr, err := address.NewIDAddress(uint64(proof.Miner))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
mockProof, err := MockSealProof(proof.SealProof, addr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(proof.Proof, mockProof) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
log.Debugw("invalid seal proof", "expected", mockProof, "actual", proof.Proof, "miner", addr)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mockVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) {
|
||||||
|
addr, err := address.NewIDAddress(uint64(aggregate.Miner))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
mockProof, err := MockAggregateSealProof(aggregate.SealProof, addr, len(aggregate.Infos))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(aggregate.Proof, mockProof) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
log.Debugw("invalid aggregate seal proof",
|
||||||
|
"expected", mockProof,
|
||||||
|
"actual", aggregate.Proof,
|
||||||
|
"count", len(aggregate.Infos),
|
||||||
|
"miner", addr,
|
||||||
|
)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
func (mockVerifier) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) {
|
||||||
|
panic("should not be called")
|
||||||
|
}
|
||||||
|
func (mockVerifier) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) {
|
||||||
|
if len(info.Proofs) != 1 {
|
||||||
|
return false, fmt.Errorf("expected exactly one proof")
|
||||||
|
}
|
||||||
|
proof := info.Proofs[0]
|
||||||
|
addr, err := address.NewIDAddress(uint64(info.Prover))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
mockProof, err := MockWindowPoStProof(proof.PoStProof, addr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(proof.ProofBytes, mockProof) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugw("invalid window post proof",
|
||||||
|
"expected", mockProof,
|
||||||
|
"actual", info.Proofs[0],
|
||||||
|
"miner", addr,
|
||||||
|
)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mockVerifier) GenerateWinningPoStSectorChallenge(context.Context, abi.RegisteredPoStProof, abi.ActorID, abi.PoStRandomness, uint64) ([]uint64, error) {
|
||||||
|
panic("should not be called")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSealProof generates a mock "seal" proof tied to the specified proof type and the given miner.
|
||||||
|
func MockSealProof(proofType abi.RegisteredSealProof, minerAddr address.Address) ([]byte, error) {
|
||||||
|
plen, err := proofType.ProofSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
proof := make([]byte, plen)
|
||||||
|
i := copy(proof, mockSealProofPrefix)
|
||||||
|
binary.BigEndian.PutUint64(proof[i:], uint64(proofType))
|
||||||
|
i += 8
|
||||||
|
i += copy(proof[i:], minerAddr.Bytes())
|
||||||
|
return proof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockAggregateSealProof generates a mock "seal" aggregate proof tied to the specified proof type,
|
||||||
|
// the given miner, and the number of proven sectors.
|
||||||
|
func MockAggregateSealProof(proofType abi.RegisteredSealProof, minerAddr address.Address, count int) ([]byte, error) {
|
||||||
|
proof := make([]byte, aggProofLen(count))
|
||||||
|
i := copy(proof, mockAggregateSealProofPrefix)
|
||||||
|
binary.BigEndian.PutUint64(proof[i:], uint64(proofType))
|
||||||
|
i += 8
|
||||||
|
binary.BigEndian.PutUint64(proof[i:], uint64(count))
|
||||||
|
i += 8
|
||||||
|
i += copy(proof[i:], minerAddr.Bytes())
|
||||||
|
|
||||||
|
return proof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWindowPoStProof generates a mock "window post" proof tied to the specified proof type, and the
|
||||||
|
// given miner.
|
||||||
|
func MockWindowPoStProof(proofType abi.RegisteredPoStProof, minerAddr address.Address) ([]byte, error) {
|
||||||
|
plen, err := proofType.ProofSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
proof := make([]byte, plen)
|
||||||
|
i := copy(proof, mockPoStProofPrefix)
|
||||||
|
i += copy(proof[i:], minerAddr.Bytes())
|
||||||
|
return proof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeCommR generates a "fake" but valid CommR for a sector. It is unique for the given sector/miner.
|
||||||
|
func MockCommR(minerAddr address.Address, sno abi.SectorNumber) cid.Cid {
|
||||||
|
return tutils.MakeCID(fmt.Sprintf("%s:%d", minerAddr, sno), &miner5.SealedCIDPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: dedup
|
||||||
|
func aggProofLen(nproofs int) int {
|
||||||
|
switch {
|
||||||
|
case nproofs <= 8:
|
||||||
|
return 11220
|
||||||
|
case nproofs <= 16:
|
||||||
|
return 14196
|
||||||
|
case nproofs <= 32:
|
||||||
|
return 17172
|
||||||
|
case nproofs <= 64:
|
||||||
|
return 20148
|
||||||
|
case nproofs <= 128:
|
||||||
|
return 23124
|
||||||
|
case nproofs <= 256:
|
||||||
|
return 26100
|
||||||
|
case nproofs <= 512:
|
||||||
|
return 29076
|
||||||
|
case nproofs <= 1024:
|
||||||
|
return 32052
|
||||||
|
case nproofs <= 2048:
|
||||||
|
return 35028
|
||||||
|
case nproofs <= 4096:
|
||||||
|
return 38004
|
||||||
|
case nproofs <= 8192:
|
||||||
|
return 40980
|
||||||
|
default:
|
||||||
|
panic("too many proofs")
|
||||||
|
}
|
||||||
|
}
|
241
cmd/lotus-sim/simulation/node.go
Normal file
241
cmd/lotus-sim/simulation/node.go
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/multierr"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
"github.com/ipfs/go-datastore/query"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/blockstore"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/mock"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/stages"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Node represents the local lotus node, or at least the part of it we care about.
|
||||||
|
type Node struct {
|
||||||
|
repo repo.LockedRepo
|
||||||
|
Blockstore blockstore.Blockstore
|
||||||
|
MetadataDS datastore.Batching
|
||||||
|
Chainstore *store.ChainStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenNode opens the local lotus node for writing. This will fail if the node is online.
|
||||||
|
func OpenNode(ctx context.Context, path string) (*Node, error) {
|
||||||
|
r, err := repo.NewFS(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewNode(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNode constructs a new node from the given repo.
|
||||||
|
func NewNode(ctx context.Context, r repo.Repo) (nd *Node, _err error) {
|
||||||
|
lr, err := r.Lock(repo.FullNode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
_ = lr.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
bs, err := lr.Blockstore(ctx, repo.UniversalBlockstore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds, err := lr.Datastore(ctx, "/metadata")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Node{
|
||||||
|
repo: lr,
|
||||||
|
Chainstore: store.NewChainStore(bs, bs, ds, vm.Syscalls(mock.Verifier), nil),
|
||||||
|
MetadataDS: ds,
|
||||||
|
Blockstore: bs,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleanly close the repo. Please call this on shutdown to make sure everything is flushed.
|
||||||
|
func (nd *Node) Close() error {
|
||||||
|
if nd.repo != nil {
|
||||||
|
return nd.repo.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadSim loads
|
||||||
|
func (nd *Node) LoadSim(ctx context.Context, name string) (*Simulation, error) {
|
||||||
|
stages, err := stages.DefaultPipeline()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sim := &Simulation{
|
||||||
|
Node: nd,
|
||||||
|
name: name,
|
||||||
|
stages: stages,
|
||||||
|
}
|
||||||
|
|
||||||
|
sim.head, err = sim.loadNamedTipSet("head")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sim.start, err = sim.loadNamedTipSet("start")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sim.loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load config for simulation %s: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
us, err := sim.config.upgradeSchedule()
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to create upgrade schedule for simulation %s: %w", name, err)
|
||||||
|
}
|
||||||
|
sim.StateManager, err = stmgr.NewStateManagerWithUpgradeSchedule(nd.Chainstore, us)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to create state manager for simulation %s: %w", name, err)
|
||||||
|
}
|
||||||
|
return sim, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new simulation.
|
||||||
|
//
|
||||||
|
// - This will fail if a simulation already exists with the given name.
|
||||||
|
// - Name must not contain a '/'.
|
||||||
|
func (nd *Node) CreateSim(ctx context.Context, name string, head *types.TipSet) (*Simulation, error) {
|
||||||
|
if strings.Contains(name, "/") {
|
||||||
|
return nil, xerrors.Errorf("simulation name %q cannot contain a '/'", name)
|
||||||
|
}
|
||||||
|
stages, err := stages.DefaultPipeline()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sim := &Simulation{
|
||||||
|
name: name,
|
||||||
|
Node: nd,
|
||||||
|
StateManager: stmgr.NewStateManager(nd.Chainstore),
|
||||||
|
stages: stages,
|
||||||
|
}
|
||||||
|
if has, err := nd.MetadataDS.Has(sim.key("head")); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if has {
|
||||||
|
return nil, xerrors.Errorf("simulation named %s already exists", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sim.storeNamedTipSet("start", head); err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to set simulation start: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sim.SetHead(head); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sim, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSims lists all simulations.
|
||||||
|
func (nd *Node) ListSims(ctx context.Context) ([]string, error) {
|
||||||
|
prefix := simulationPrefix.ChildString("head").String()
|
||||||
|
items, err := nd.MetadataDS.Query(query.Query{
|
||||||
|
Prefix: prefix,
|
||||||
|
KeysOnly: true,
|
||||||
|
Orders: []query.Order{query.OrderByKey{}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to list simulations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = items.Close() }()
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result, ok := <-items.Next():
|
||||||
|
if !ok {
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to retrieve next simulation: %w", result.Error)
|
||||||
|
}
|
||||||
|
names = append(names, strings.TrimPrefix(result.Key, prefix+"/"))
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var simFields = []string{"head", "start", "config"}
|
||||||
|
|
||||||
|
// DeleteSim deletes a simulation and all related metadata.
|
||||||
|
//
|
||||||
|
// NOTE: This function does not delete associated messages, blocks, or chain state.
|
||||||
|
func (nd *Node) DeleteSim(ctx context.Context, name string) error {
|
||||||
|
var err error
|
||||||
|
for _, field := range simFields {
|
||||||
|
key := simulationPrefix.ChildString(field).ChildString(name)
|
||||||
|
err = multierr.Append(err, nd.MetadataDS.Delete(key))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopySim copies a simulation.
|
||||||
|
func (nd *Node) CopySim(ctx context.Context, oldName, newName string) error {
|
||||||
|
if strings.Contains(newName, "/") {
|
||||||
|
return xerrors.Errorf("simulation name %q cannot contain a '/'", newName)
|
||||||
|
}
|
||||||
|
if strings.Contains(oldName, "/") {
|
||||||
|
return xerrors.Errorf("simulation name %q cannot contain a '/'", oldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
values := make(map[string][]byte)
|
||||||
|
for _, field := range simFields {
|
||||||
|
key := simulationPrefix.ChildString(field).ChildString(oldName)
|
||||||
|
value, err := nd.MetadataDS.Get(key)
|
||||||
|
if err == datastore.ErrNotFound {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
values[field] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := values["head"]; !ok {
|
||||||
|
return xerrors.Errorf("simulation named %s not found", oldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range simFields {
|
||||||
|
key := simulationPrefix.ChildString(field).ChildString(newName)
|
||||||
|
var err error
|
||||||
|
if value, ok := values[field]; ok {
|
||||||
|
err = nd.MetadataDS.Put(key, value)
|
||||||
|
} else {
|
||||||
|
err = nd.MetadataDS.Delete(key)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameSim renames a simulation.
|
||||||
|
func (nd *Node) RenameSim(ctx context.Context, oldName, newName string) error {
|
||||||
|
if err := nd.CopySim(ctx, oldName, newName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nd.DeleteSim(ctx, oldName)
|
||||||
|
}
|
408
cmd/lotus-sim/simulation/simulation.go
Normal file
408
cmd/lotus-sim/simulation/simulation.go
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
|
||||||
|
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/stages"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logging.Logger("simulation")
|
||||||
|
|
||||||
|
// config is the simulation's config, persisted to the local metadata store and loaded on start.
|
||||||
|
//
|
||||||
|
// See Simulation.loadConfig and Simulation.saveConfig.
|
||||||
|
type config struct {
|
||||||
|
Upgrades map[network.Version]abi.ChainEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradeSchedule constructs an stmgr.StateManager upgrade schedule, overriding any network upgrade
|
||||||
|
// epochs as specified in the config.
|
||||||
|
func (c *config) upgradeSchedule() (stmgr.UpgradeSchedule, error) {
|
||||||
|
upgradeSchedule := stmgr.DefaultUpgradeSchedule()
|
||||||
|
expected := make(map[network.Version]struct{}, len(c.Upgrades))
|
||||||
|
for nv := range c.Upgrades {
|
||||||
|
expected[nv] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update network upgrade epochs.
|
||||||
|
newUpgradeSchedule := upgradeSchedule[:0]
|
||||||
|
for _, upgrade := range upgradeSchedule {
|
||||||
|
if height, ok := c.Upgrades[upgrade.Network]; ok {
|
||||||
|
delete(expected, upgrade.Network)
|
||||||
|
if height < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
upgrade.Height = height
|
||||||
|
}
|
||||||
|
newUpgradeSchedule = append(newUpgradeSchedule, upgrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we didn't try to configure an unknown network version.
|
||||||
|
if len(expected) > 0 {
|
||||||
|
missing := make([]network.Version, 0, len(expected))
|
||||||
|
for nv := range expected {
|
||||||
|
missing = append(missing, nv)
|
||||||
|
}
|
||||||
|
return nil, xerrors.Errorf("unknown network versions %v in config", missing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, validate it. This ensures we don't change the order of the upgrade or anything
|
||||||
|
// like that.
|
||||||
|
if err := newUpgradeSchedule.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newUpgradeSchedule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulation specifies a lotus-sim simulation.
|
||||||
|
type Simulation struct {
|
||||||
|
Node *Node
|
||||||
|
StateManager *stmgr.StateManager
|
||||||
|
|
||||||
|
name string
|
||||||
|
config config
|
||||||
|
start *types.TipSet
|
||||||
|
|
||||||
|
// head
|
||||||
|
head *types.TipSet
|
||||||
|
|
||||||
|
stages []stages.Stage
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadConfig loads a simulation's config from the datastore. This must be called on startup and may
|
||||||
|
// be called to restore the config from-disk.
|
||||||
|
func (sim *Simulation) loadConfig() error {
|
||||||
|
configBytes, err := sim.Node.MetadataDS.Get(sim.key("config"))
|
||||||
|
if err == nil {
|
||||||
|
err = json.Unmarshal(configBytes, &sim.config)
|
||||||
|
}
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
case datastore.ErrNotFound:
|
||||||
|
sim.config = config{}
|
||||||
|
default:
|
||||||
|
return xerrors.Errorf("failed to load config: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveConfig saves the current config to the datastore. This must be called whenever the config is
|
||||||
|
// changed.
|
||||||
|
func (sim *Simulation) saveConfig() error {
|
||||||
|
buf, err := json.Marshal(sim.config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sim.Node.MetadataDS.Put(sim.key("config"), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var simulationPrefix = datastore.NewKey("/simulation")
|
||||||
|
|
||||||
|
// key returns the the key in the form /simulation/<subkey>/<simulation-name>. For example,
|
||||||
|
// /simulation/head/default.
|
||||||
|
func (sim *Simulation) key(subkey string) datastore.Key {
|
||||||
|
return simulationPrefix.ChildString(subkey).ChildString(sim.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadNamedTipSet the tipset with the given name (for this simulation)
|
||||||
|
func (sim *Simulation) loadNamedTipSet(name string) (*types.TipSet, error) {
|
||||||
|
tskBytes, err := sim.Node.MetadataDS.Get(sim.key(name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load tipset %s/%s: %w", sim.name, name, err)
|
||||||
|
}
|
||||||
|
tsk, err := types.TipSetKeyFromBytes(tskBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to parse tipste %v (%s/%s): %w", tskBytes, sim.name, name, err)
|
||||||
|
}
|
||||||
|
ts, err := sim.Node.Chainstore.LoadTipSet(tsk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load tipset %s (%s/%s): %w", tsk, sim.name, name, err)
|
||||||
|
}
|
||||||
|
return ts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeNamedTipSet stores the tipset at name (relative to the simulation).
|
||||||
|
func (sim *Simulation) storeNamedTipSet(name string, ts *types.TipSet) error {
|
||||||
|
if err := sim.Node.MetadataDS.Put(sim.key(name), ts.Key().Bytes()); err != nil {
|
||||||
|
return xerrors.Errorf("failed to store tipset (%s/%s): %w", sim.name, name, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHead returns the current simulation head.
|
||||||
|
func (sim *Simulation) GetHead() *types.TipSet {
|
||||||
|
return sim.head
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStart returns simulation's parent tipset.
|
||||||
|
func (sim *Simulation) GetStart() *types.TipSet {
|
||||||
|
return sim.start
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkVersion returns the current network version for the simulation.
|
||||||
|
func (sim *Simulation) GetNetworkVersion() network.Version {
|
||||||
|
return sim.StateManager.GetNtwkVersion(context.TODO(), sim.head.Height())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHead updates the current head of the simulation and stores it in the metadata store. This is
|
||||||
|
// called for every Simulation.Step.
|
||||||
|
func (sim *Simulation) SetHead(head *types.TipSet) error {
|
||||||
|
if err := sim.storeNamedTipSet("head", head); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sim.head = head
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the simulation's name.
|
||||||
|
func (sim *Simulation) Name() string {
|
||||||
|
return sim.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpgradeHeight sets the height of the given network version change (and saves the config).
|
||||||
|
//
|
||||||
|
// This fails if the specified epoch has already passed or the new upgrade schedule is invalid.
|
||||||
|
func (sim *Simulation) SetUpgradeHeight(nv network.Version, epoch abi.ChainEpoch) (_err error) {
|
||||||
|
if epoch <= sim.head.Height() {
|
||||||
|
return xerrors.Errorf("cannot set upgrade height in the past (%d <= %d)", epoch, sim.head.Height())
|
||||||
|
}
|
||||||
|
|
||||||
|
if sim.config.Upgrades == nil {
|
||||||
|
sim.config.Upgrades = make(map[network.Version]abi.ChainEpoch, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
sim.config.Upgrades[nv] = epoch
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
// try to restore the old config on error.
|
||||||
|
_ = sim.loadConfig()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
newUpgradeSchedule, err := sim.config.upgradeSchedule()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(sim.Node.Chainstore, newUpgradeSchedule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = sim.saveConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sim.StateManager = sm
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUpgrades returns any future network upgrades.
|
||||||
|
func (sim *Simulation) ListUpgrades() (stmgr.UpgradeSchedule, error) {
|
||||||
|
upgrades, err := sim.config.upgradeSchedule()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var pending stmgr.UpgradeSchedule
|
||||||
|
for _, upgrade := range upgrades {
|
||||||
|
if upgrade.Height < sim.head.Height() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pending = append(pending, upgrade)
|
||||||
|
}
|
||||||
|
return pending, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppliedMessage struct {
|
||||||
|
types.Message
|
||||||
|
types.MessageReceipt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk walks the simulation's chain from the current head back to the first tipset.
|
||||||
|
func (sim *Simulation) Walk(
|
||||||
|
ctx context.Context,
|
||||||
|
lookback int64,
|
||||||
|
cb func(sm *stmgr.StateManager,
|
||||||
|
ts *types.TipSet,
|
||||||
|
stCid cid.Cid,
|
||||||
|
messages []*AppliedMessage) error,
|
||||||
|
) error {
|
||||||
|
store := sim.Node.Chainstore.ActorStore(ctx)
|
||||||
|
minEpoch := sim.start.Height()
|
||||||
|
if lookback != 0 {
|
||||||
|
minEpoch = sim.head.Height() - abi.ChainEpoch(lookback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given tha loading messages and receipts can be a little bit slow, we do this in parallel.
|
||||||
|
//
|
||||||
|
// 1. We spin up some number of workers.
|
||||||
|
// 2. We hand tipsets to workers in round-robin order.
|
||||||
|
// 3. We pull "resolved" tipsets in the same round-robin order.
|
||||||
|
// 4. We serially call the callback in reverse-chain order.
|
||||||
|
//
|
||||||
|
// We have a buffer of size 1 for both resolved tipsets and unresolved tipsets. This should
|
||||||
|
// ensure that we never block unecessarily.
|
||||||
|
|
||||||
|
type work struct {
|
||||||
|
ts *types.TipSet
|
||||||
|
stCid cid.Cid
|
||||||
|
recCid cid.Cid
|
||||||
|
}
|
||||||
|
type result struct {
|
||||||
|
ts *types.TipSet
|
||||||
|
stCid cid.Cid
|
||||||
|
messages []*AppliedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is more disk bound than CPU bound, but eh...
|
||||||
|
workerCount := runtime.NumCPU() * 2
|
||||||
|
|
||||||
|
workQs := make([]chan *work, workerCount)
|
||||||
|
resultQs := make([]chan *result, workerCount)
|
||||||
|
|
||||||
|
for i := range workQs {
|
||||||
|
workQs[i] = make(chan *work, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range resultQs {
|
||||||
|
resultQs[i] = make(chan *result, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
grp, ctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
|
// Walk the chain and fire off work items.
|
||||||
|
grp.Go(func() error {
|
||||||
|
ts := sim.head
|
||||||
|
stCid, recCid, err := sim.StateManager.TipSetState(ctx, ts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for ts.Height() > minEpoch {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case workQs[i] <- &work{ts, stCid, recCid}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
stCid = ts.MinTicketBlock().ParentStateRoot
|
||||||
|
recCid = ts.MinTicketBlock().ParentMessageReceipts
|
||||||
|
ts, err = sim.Node.Chainstore.LoadTipSet(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("loading parent: %w", err)
|
||||||
|
}
|
||||||
|
i = (i + 1) % workerCount
|
||||||
|
}
|
||||||
|
for _, q := range workQs {
|
||||||
|
close(q)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Spin up one worker per queue pair.
|
||||||
|
for i := 0; i < workerCount; i++ {
|
||||||
|
workQ := workQs[i]
|
||||||
|
resultQ := resultQs[i]
|
||||||
|
grp.Go(func() error {
|
||||||
|
for {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
var job *work
|
||||||
|
var ok bool
|
||||||
|
select {
|
||||||
|
case job, ok = <-workQ:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs, err := sim.Node.Chainstore.MessagesForTipset(job.ts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
recs, err := blockadt.AsArray(store, job.recCid)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("amt load: %w", err)
|
||||||
|
}
|
||||||
|
applied := make([]*AppliedMessage, len(msgs))
|
||||||
|
var rec types.MessageReceipt
|
||||||
|
err = recs.ForEach(&rec, func(i int64) error {
|
||||||
|
applied[i] = &AppliedMessage{
|
||||||
|
Message: *msgs[i].VMMessage(),
|
||||||
|
MessageReceipt: rec,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case resultQ <- &result{
|
||||||
|
ts: job.ts,
|
||||||
|
stCid: job.stCid,
|
||||||
|
messages: applied,
|
||||||
|
}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(resultQ)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process results in the same order we enqueued them.
|
||||||
|
grp.Go(func() error {
|
||||||
|
qs := resultQs
|
||||||
|
for len(qs) > 0 {
|
||||||
|
newQs := qs[:0]
|
||||||
|
for _, q := range qs {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case r, ok := <-q:
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := cb(sim.StateManager, r.ts, r.stCid, r.messages)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
newQs = append(newQs, q)
|
||||||
|
}
|
||||||
|
qs = newQs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for everything to finish.
|
||||||
|
return grp.Wait()
|
||||||
|
}
|
38
cmd/lotus-sim/simulation/stages/actor_iter.go
Normal file
38
cmd/lotus-sim/simulation/stages/actor_iter.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// actorIter is a simple persistent iterator that loops over a set of actors.
|
||||||
|
type actorIter struct {
|
||||||
|
actors []address.Address
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
// shuffle randomly permutes the set of actors.
|
||||||
|
func (p *actorIter) shuffle() {
|
||||||
|
rand.Shuffle(len(p.actors), func(i, j int) {
|
||||||
|
p.actors[i], p.actors[j] = p.actors[j], p.actors[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// next returns the next actor's address and advances the iterator.
|
||||||
|
func (p *actorIter) next() address.Address {
|
||||||
|
next := p.actors[p.offset]
|
||||||
|
p.offset++
|
||||||
|
p.offset %= len(p.actors)
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// add adds a new actor to the iterator.
|
||||||
|
func (p *actorIter) add(addr address.Address) {
|
||||||
|
p.actors = append(p.actors, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// len returns the number of actors in the iterator.
|
||||||
|
func (p *actorIter) len() int {
|
||||||
|
return len(p.actors)
|
||||||
|
}
|
200
cmd/lotus-sim/simulation/stages/commit_queue.go
Normal file
200
cmd/lotus-sim/simulation/stages/commit_queue.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pendingCommitTracker tracks pending commits per-miner for a single epoch.
|
||||||
|
type pendingCommitTracker map[address.Address]minerPendingCommits
|
||||||
|
|
||||||
|
// minerPendingCommits tracks a miner's pending commits during a single epoch (grouped by seal proof type).
|
||||||
|
type minerPendingCommits map[abi.RegisteredSealProof][]abi.SectorNumber
|
||||||
|
|
||||||
|
// finish marks count sectors of the given proof type as "prove-committed".
|
||||||
|
func (m minerPendingCommits) finish(proof abi.RegisteredSealProof, count int) {
|
||||||
|
snos := m[proof]
|
||||||
|
if len(snos) < count {
|
||||||
|
panic("not enough sector numbers to finish")
|
||||||
|
} else if len(snos) == count {
|
||||||
|
delete(m, proof)
|
||||||
|
} else {
|
||||||
|
m[proof] = snos[count:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty returns true if there are no pending commits.
|
||||||
|
func (m minerPendingCommits) empty() bool {
|
||||||
|
return len(m) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// count returns the number of pending commits.
|
||||||
|
func (m minerPendingCommits) count() int {
|
||||||
|
count := 0
|
||||||
|
for _, snos := range m {
|
||||||
|
count += len(snos)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// commitQueue is used to track pending prove-commits.
|
||||||
|
//
|
||||||
|
// Miners are processed in round-robin where _all_ commits from a given miner are finished before
|
||||||
|
// moving on to the next. This is designed to maximize batching.
|
||||||
|
type commitQueue struct {
|
||||||
|
minerQueue []address.Address
|
||||||
|
queue []pendingCommitTracker
|
||||||
|
offset abi.ChainEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
// ready returns the number of prove-commits ready to be proven at the current epoch. Useful for logging.
|
||||||
|
func (q *commitQueue) ready() int {
|
||||||
|
if len(q.queue) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for _, pending := range q.queue[0] {
|
||||||
|
count += pending.count()
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextMiner returns the next miner to be proved and the set of pending prove commits for that
|
||||||
|
// miner. When some number of sectors have successfully been proven, call "finish" so we don't try
|
||||||
|
// to prove them again.
|
||||||
|
func (q *commitQueue) nextMiner() (address.Address, minerPendingCommits, bool) {
|
||||||
|
if len(q.queue) == 0 {
|
||||||
|
return address.Undef, nil, false
|
||||||
|
}
|
||||||
|
next := q.queue[0]
|
||||||
|
|
||||||
|
// Go through the queue and find the first non-empty batch.
|
||||||
|
for len(q.minerQueue) > 0 {
|
||||||
|
addr := q.minerQueue[0]
|
||||||
|
q.minerQueue = q.minerQueue[1:]
|
||||||
|
pending := next[addr]
|
||||||
|
if !pending.empty() {
|
||||||
|
return addr, pending, true
|
||||||
|
}
|
||||||
|
delete(next, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return address.Undef, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// advanceEpoch will advance to the next epoch. If some sectors were left unproven in the current
|
||||||
|
// epoch, they will be "prepended" into the next epochs sector set.
|
||||||
|
func (q *commitQueue) advanceEpoch(epoch abi.ChainEpoch) {
|
||||||
|
if epoch < q.offset {
|
||||||
|
panic("cannot roll epoch backwards")
|
||||||
|
}
|
||||||
|
// Now we "roll forwards", merging each epoch we advance over with the next.
|
||||||
|
for len(q.queue) > 1 && q.offset < epoch {
|
||||||
|
curr := q.queue[0]
|
||||||
|
q.queue[0] = nil
|
||||||
|
q.queue = q.queue[1:]
|
||||||
|
q.offset++
|
||||||
|
|
||||||
|
next := q.queue[0]
|
||||||
|
|
||||||
|
// Cleanup empty entries.
|
||||||
|
for addr, pending := range curr {
|
||||||
|
if pending.empty() {
|
||||||
|
delete(curr, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entire level is actually empty, just skip to the next one.
|
||||||
|
if len(curr) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, merge the next into the current.
|
||||||
|
for addr, nextPending := range next {
|
||||||
|
currPending := curr[addr]
|
||||||
|
if currPending.empty() {
|
||||||
|
curr[addr] = nextPending
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for ty, nextSnos := range nextPending {
|
||||||
|
currSnos := currPending[ty]
|
||||||
|
if len(currSnos) == 0 {
|
||||||
|
currPending[ty] = nextSnos
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currPending[ty] = append(currSnos, nextSnos...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now replace next with the merged curr.
|
||||||
|
q.queue[0] = curr
|
||||||
|
}
|
||||||
|
q.offset = epoch
|
||||||
|
if len(q.queue) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next := q.queue[0]
|
||||||
|
seenMiners := make(map[address.Address]struct{}, len(q.minerQueue))
|
||||||
|
for _, addr := range q.minerQueue {
|
||||||
|
seenMiners[addr] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the new miners not already in the queue.
|
||||||
|
offset := len(q.minerQueue)
|
||||||
|
for addr, pending := range next {
|
||||||
|
if pending.empty() {
|
||||||
|
delete(next, addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seenMiners[addr]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q.minerQueue = append(q.minerQueue, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the new miners only.
|
||||||
|
newMiners := q.minerQueue[offset:]
|
||||||
|
sort.Slice(newMiners, func(i, j int) bool {
|
||||||
|
// eh, escape analysis should be fine here...
|
||||||
|
return string(newMiners[i].Bytes()) < string(newMiners[j].Bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// enquueProveCommit enqueues prove-commit for the given pre-commit for the given miner.
|
||||||
|
func (q *commitQueue) enqueueProveCommit(addr address.Address, preCommitEpoch abi.ChainEpoch, info miner.SectorPreCommitInfo) error {
|
||||||
|
// Compute the epoch at which we can start trying to commit.
|
||||||
|
preCommitDelay := policy.GetPreCommitChallengeDelay()
|
||||||
|
minCommitEpoch := preCommitEpoch + preCommitDelay + 1
|
||||||
|
|
||||||
|
// Figure out the offset in the queue.
|
||||||
|
i := int(minCommitEpoch - q.offset)
|
||||||
|
if i < 0 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand capacity and insert.
|
||||||
|
if cap(q.queue) <= i {
|
||||||
|
pc := make([]pendingCommitTracker, i+1, preCommitDelay*2)
|
||||||
|
copy(pc, q.queue)
|
||||||
|
q.queue = pc
|
||||||
|
} else if len(q.queue) <= i {
|
||||||
|
q.queue = q.queue[:i+1]
|
||||||
|
}
|
||||||
|
tracker := q.queue[i]
|
||||||
|
if tracker == nil {
|
||||||
|
tracker = make(pendingCommitTracker)
|
||||||
|
q.queue[i] = tracker
|
||||||
|
}
|
||||||
|
minerPending := tracker[addr]
|
||||||
|
if minerPending == nil {
|
||||||
|
minerPending = make(minerPendingCommits)
|
||||||
|
tracker[addr] = minerPending
|
||||||
|
}
|
||||||
|
minerPending[info.SealProof] = append(minerPending[info.SealProof], info.SectorNumber)
|
||||||
|
return nil
|
||||||
|
}
|
128
cmd/lotus-sim/simulation/stages/commit_queue_test.go
Normal file
128
cmd/lotus-sim/simulation/stages/commit_queue_test.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommitQueue(t *testing.T) {
|
||||||
|
var q commitQueue
|
||||||
|
addr1, err := address.NewIDAddress(1000)
|
||||||
|
require.NoError(t, err)
|
||||||
|
proofType := abi.RegisteredSealProof_StackedDrg64GiBV1_1
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 0, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 0,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 0, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 1,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 1, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 2,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 1, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 3,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 3, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 4,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 4, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 5,
|
||||||
|
}))
|
||||||
|
require.NoError(t, q.enqueueProveCommit(addr1, 6, miner.SectorPreCommitInfo{
|
||||||
|
SealProof: proofType,
|
||||||
|
SectorNumber: 6,
|
||||||
|
}))
|
||||||
|
|
||||||
|
epoch := abi.ChainEpoch(0)
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, _, ok := q.nextMiner()
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
epoch += policy.GetPreCommitChallengeDelay()
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, _, ok = q.nextMiner()
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
// 0 : empty + non-empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
addr, sectors, ok := q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 2)
|
||||||
|
require.Equal(t, addr, addr1)
|
||||||
|
sectors.finish(proofType, 1)
|
||||||
|
require.Equal(t, sectors.count(), 1)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{1}, sectors[proofType])
|
||||||
|
|
||||||
|
// 1 : non-empty + non-empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
addr, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, addr, addr1)
|
||||||
|
require.Equal(t, sectors.count(), 3)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{1, 2, 3}, sectors[proofType])
|
||||||
|
sectors.finish(proofType, 3)
|
||||||
|
require.Equal(t, sectors.count(), 0)
|
||||||
|
|
||||||
|
// 2 : empty + empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, _, ok = q.nextMiner()
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
// 3 : empty + non-empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 1)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{4}, sectors[proofType])
|
||||||
|
|
||||||
|
// 4 : non-empty + non-empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 2)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{4, 5}, sectors[proofType])
|
||||||
|
|
||||||
|
// 5 : empty + non-empty
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 2)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{4, 5}, sectors[proofType])
|
||||||
|
sectors.finish(proofType, 1)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{5}, sectors[proofType])
|
||||||
|
|
||||||
|
// 6
|
||||||
|
epoch++
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 2)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{5, 6}, sectors[proofType])
|
||||||
|
|
||||||
|
// 8
|
||||||
|
epoch += 2
|
||||||
|
q.advanceEpoch(epoch)
|
||||||
|
_, sectors, ok = q.nextMiner()
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, sectors.count(), 2)
|
||||||
|
require.EqualValues(t, []abi.SectorNumber{5, 6}, sectors[proofType])
|
||||||
|
}
|
318
cmd/lotus-sim/simulation/stages/funding_stage.go
Normal file
318
cmd/lotus-sim/simulation/stages/funding_stage.go
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TargetFunds = abi.TokenAmount(types.MustParseFIL("1000FIL"))
|
||||||
|
MinimumFunds = abi.TokenAmount(types.MustParseFIL("100FIL"))
|
||||||
|
)
|
||||||
|
|
||||||
|
type FundingStage struct {
|
||||||
|
fundAccount address.Address
|
||||||
|
taxMin abi.TokenAmount
|
||||||
|
minFunds, maxFunds abi.TokenAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFundingStage() (*FundingStage, error) {
|
||||||
|
// TODO: make all this configurable.
|
||||||
|
addr, err := address.NewIDAddress(100)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &FundingStage{
|
||||||
|
fundAccount: addr,
|
||||||
|
taxMin: abi.TokenAmount(types.MustParseFIL("1000FIL")),
|
||||||
|
minFunds: abi.TokenAmount(types.MustParseFIL("1000000FIL")),
|
||||||
|
maxFunds: abi.TokenAmount(types.MustParseFIL("100000000FIL")),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FundingStage) Name() string {
|
||||||
|
return "funding"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FundingStage) Fund(bb *blockbuilder.BlockBuilder, target address.Address) error {
|
||||||
|
return fs.fund(bb, target, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendAndFund "packs" the given message, funding the actor if necessary. It:
|
||||||
|
//
|
||||||
|
// 1. Tries to send the given message.
|
||||||
|
// 2. If that fails, it checks to see if the exit code was ErrInsufficientFunds.
|
||||||
|
// 3. If so, it sends 1K FIL from the "burnt funds actor" (because we need to send it from
|
||||||
|
// somewhere) and re-tries the message.0
|
||||||
|
func (fs *FundingStage) SendAndFund(bb *blockbuilder.BlockBuilder, msg *types.Message) (res *types.MessageReceipt, err error) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
res, err = bb.PushMessage(msg)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
aerr, ok := err.(aerrors.ActorError)
|
||||||
|
if !ok || aerr.RetCode() != exitcode.ErrInsufficientFunds {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, insufficient funds. Let's fund this miner and try again.
|
||||||
|
if err := fs.fund(bb, msg.To, i); err != nil {
|
||||||
|
if !blockbuilder.IsOutOfGas(err) {
|
||||||
|
err = xerrors.Errorf("failed to fund %s: %w", msg.To, err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fund funds the target actor with 'TargetFunds << shift' FIL. The "shift" parameter allows us to
|
||||||
|
// keep doubling the amount until the intended operation succeeds.
|
||||||
|
func (fs *FundingStage) fund(bb *blockbuilder.BlockBuilder, target address.Address, shift int) error {
|
||||||
|
amt := TargetFunds
|
||||||
|
if shift > 0 {
|
||||||
|
if shift >= 8 {
|
||||||
|
shift = 8 // cap
|
||||||
|
}
|
||||||
|
amt = big.Lsh(amt, uint(shift))
|
||||||
|
}
|
||||||
|
_, err := bb.PushMessage(&types.Message{
|
||||||
|
From: fs.fundAccount,
|
||||||
|
To: target,
|
||||||
|
Value: amt,
|
||||||
|
Method: builtin.MethodSend,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FundingStage) PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
st := bb.StateTree()
|
||||||
|
fundAccActor, err := st.GetActor(fs.fundAccount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fs.minFunds.LessThan(fundAccActor.Balance) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, we're going to go fund this thing.
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
type actor struct {
|
||||||
|
types.Actor
|
||||||
|
Address address.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets []*actor
|
||||||
|
err = st.ForEach(func(addr address.Address, act *types.Actor) error {
|
||||||
|
// Don't steal from ourselves!
|
||||||
|
if addr == fs.fundAccount {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if act.Balance.LessThan(fs.taxMin) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !(builtin.IsAccountActor(act.Code) || builtin.IsMultisigActor(act.Code)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targets = append(targets, &actor{*act, addr})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
balance := fundAccActor.Balance.Copy()
|
||||||
|
|
||||||
|
sort.Slice(targets, func(i, j int) bool {
|
||||||
|
return targets[i].Balance.GreaterThan(targets[j].Balance)
|
||||||
|
})
|
||||||
|
|
||||||
|
store := bb.ActorStore()
|
||||||
|
epoch := bb.Height()
|
||||||
|
actorsVersion := bb.ActorsVersion()
|
||||||
|
|
||||||
|
var accounts, multisigs int
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bb.L().Infow("finished funding the simulation",
|
||||||
|
"duration", time.Since(start),
|
||||||
|
"targets", len(targets),
|
||||||
|
"epoch", epoch,
|
||||||
|
"new-balance", types.FIL(balance),
|
||||||
|
"old-balance", types.FIL(fundAccActor.Balance),
|
||||||
|
"multisigs", multisigs,
|
||||||
|
"accounts", accounts,
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, actor := range targets {
|
||||||
|
switch {
|
||||||
|
case builtin.IsAccountActor(actor.Code):
|
||||||
|
if _, err := bb.PushMessage(&types.Message{
|
||||||
|
From: actor.Address,
|
||||||
|
To: fs.fundAccount,
|
||||||
|
Value: actor.Balance,
|
||||||
|
}); blockbuilder.IsOutOfGas(err) {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accounts++
|
||||||
|
case builtin.IsMultisigActor(actor.Code):
|
||||||
|
msigState, err := multisig.Load(store, &actor.Actor)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
threshold, err := msigState.Threshold()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if threshold > 16 {
|
||||||
|
bb.L().Debugw("ignoring multisig with high threshold",
|
||||||
|
"multisig", actor.Address,
|
||||||
|
"threshold", threshold,
|
||||||
|
"max", 16,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
locked, err := msigState.LockedBalance(epoch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if locked.LessThan(fs.taxMin) {
|
||||||
|
continue // not worth it.
|
||||||
|
}
|
||||||
|
|
||||||
|
allSigners, err := msigState.Signers()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signers := make([]address.Address, 0, threshold)
|
||||||
|
for _, signer := range allSigners {
|
||||||
|
actor, err := st.GetActor(signer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !builtin.IsAccountActor(actor.Code) {
|
||||||
|
// I am so not dealing with this mess.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if uint64(len(signers)) >= threshold {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ok, we're not dealing with this one.
|
||||||
|
if uint64(len(signers)) < threshold {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
available := big.Sub(actor.Balance, locked)
|
||||||
|
|
||||||
|
var txnId uint64
|
||||||
|
{
|
||||||
|
msg, err := multisig.Message(actorsVersion, signers[0]).Propose(
|
||||||
|
actor.Address, fs.fundAccount, available,
|
||||||
|
builtin.MethodSend, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := bb.PushMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
if blockbuilder.IsOutOfGas(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var ret multisig.ProposeReturn
|
||||||
|
err = ret.UnmarshalCBOR(bytes.NewReader(res.Return))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Applied {
|
||||||
|
if !ret.Code.IsSuccess() {
|
||||||
|
bb.L().Errorw("failed to tax multisig",
|
||||||
|
"multisig", actor.Address,
|
||||||
|
"exitcode", ret.Code,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
txnId = uint64(ret.TxnID)
|
||||||
|
}
|
||||||
|
var ret multisig.ProposeReturn
|
||||||
|
for _, signer := range signers[1:] {
|
||||||
|
msg, err := multisig.Message(actorsVersion, signer).Approve(actor.Address, txnId, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := bb.PushMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
if blockbuilder.IsOutOfGas(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var ret multisig.ProposeReturn
|
||||||
|
err = ret.UnmarshalCBOR(bytes.NewReader(res.Return))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A bit redundant, but nice.
|
||||||
|
if ret.Applied {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if !ret.Applied {
|
||||||
|
bb.L().Errorw("failed to apply multisig transaction",
|
||||||
|
"multisig", actor.Address,
|
||||||
|
"txnid", txnId,
|
||||||
|
"signers", len(signers),
|
||||||
|
"threshold", threshold,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ret.Code.IsSuccess() {
|
||||||
|
bb.L().Errorw("failed to tax multisig",
|
||||||
|
"multisig", actor.Address,
|
||||||
|
"txnid", txnId,
|
||||||
|
"exitcode", ret.Code,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
multisigs++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("impossible case")
|
||||||
|
}
|
||||||
|
balance = big.Int{Int: balance.Add(balance.Int, actor.Balance.Int)}
|
||||||
|
if balance.GreaterThanEqual(fs.maxFunds) {
|
||||||
|
// There's no need to get greedy.
|
||||||
|
// Well, really, we're trying to avoid messing with state _too_ much.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
27
cmd/lotus-sim/simulation/stages/interface.go
Normal file
27
cmd/lotus-sim/simulation/stages/interface.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stage is a stage of the simulation. It's asked to pack messages for every block.
|
||||||
|
type Stage interface {
|
||||||
|
Name() string
|
||||||
|
PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Funding interface {
|
||||||
|
SendAndFund(*blockbuilder.BlockBuilder, *types.Message) (*types.MessageReceipt, error)
|
||||||
|
Fund(*blockbuilder.BlockBuilder, address.Address) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Committer interface {
|
||||||
|
EnqueueProveCommit(addr address.Address, preCommitEpoch abi.ChainEpoch, info miner.SectorPreCommitInfo) error
|
||||||
|
}
|
31
cmd/lotus-sim/simulation/stages/pipeline.go
Normal file
31
cmd/lotus-sim/simulation/stages/pipeline.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
// DefaultPipeline returns the default stage pipeline. This pipeline.
|
||||||
|
//
|
||||||
|
// 1. Funds a "funding" actor, if necessary.
|
||||||
|
// 2. Submits any ready window posts.
|
||||||
|
// 3. Submits any ready prove commits.
|
||||||
|
// 4. Submits pre-commits with the remaining gas.
|
||||||
|
func DefaultPipeline() ([]Stage, error) {
|
||||||
|
// TODO: make this configurable. E.g., through DI?
|
||||||
|
// Ideally, we'd also be able to change priority, limit throughput (by limiting gas in the
|
||||||
|
// block builder, etc.
|
||||||
|
funding, err := NewFundingStage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wdpost, err := NewWindowPoStStage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
provecommit, err := NewProveCommitStage(funding)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
precommit, err := NewPreCommitStage(funding, provecommit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Stage{funding, wdpost, provecommit, precommit}, nil
|
||||||
|
}
|
347
cmd/lotus-sim/simulation/stages/precommit_stage.go
Normal file
347
cmd/lotus-sim/simulation/stages/precommit_stage.go
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minPreCommitBatchSize = 1
|
||||||
|
maxPreCommitBatchSize = miner5.PreCommitSectorBatchMaxSize
|
||||||
|
)
|
||||||
|
|
||||||
|
type PreCommitStage struct {
|
||||||
|
funding Funding
|
||||||
|
committer Committer
|
||||||
|
|
||||||
|
// The tiers represent the top 1%, top 10%, and everyone else. When sealing sectors, we seal
|
||||||
|
// a group of sectors for the top 1%, a group (half that size) for the top 10%, and one
|
||||||
|
// sector for everyone else. We determine these rates by looking at two power tables.
|
||||||
|
// TODO Ideally we'd "learn" this distribution from the network. But this is good enough for
|
||||||
|
// now.
|
||||||
|
top1, top10, rest actorIter
|
||||||
|
initialized bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPreCommitStage(funding Funding, committer Committer) (*PreCommitStage, error) {
|
||||||
|
return &PreCommitStage{
|
||||||
|
funding: funding,
|
||||||
|
committer: committer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PreCommitStage) Name() string {
|
||||||
|
return "pre-commit"
|
||||||
|
}
|
||||||
|
|
||||||
|
// packPreCommits packs pre-commit messages until the block is full.
|
||||||
|
func (stage *PreCommitStage) PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
if !stage.initialized {
|
||||||
|
if err := stage.load(ctx, bb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
full bool
|
||||||
|
top1Count, top10Count, restCount int
|
||||||
|
)
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bb.L().Debugw("packed pre commits",
|
||||||
|
"done", top1Count+top10Count+restCount,
|
||||||
|
"top1", top1Count,
|
||||||
|
"top10", top10Count,
|
||||||
|
"rest", restCount,
|
||||||
|
"filled-block", full,
|
||||||
|
"duration", time.Since(start),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var top1Miners, top10Miners, restMiners int
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
var (
|
||||||
|
minerAddr address.Address
|
||||||
|
count *int
|
||||||
|
)
|
||||||
|
|
||||||
|
// We pre-commit for the top 1%, 10%, and the of the network 1/3rd of the time each.
|
||||||
|
// This won't yield the most accurate distribution... but it'll give us a good
|
||||||
|
// enough distribution.
|
||||||
|
switch {
|
||||||
|
case (i%3) <= 0 && top1Miners < stage.top1.len():
|
||||||
|
count = &top1Count
|
||||||
|
minerAddr = stage.top1.next()
|
||||||
|
top1Miners++
|
||||||
|
case (i%3) <= 1 && top10Miners < stage.top10.len():
|
||||||
|
count = &top10Count
|
||||||
|
minerAddr = stage.top10.next()
|
||||||
|
top10Miners++
|
||||||
|
case (i%3) <= 2 && restMiners < stage.rest.len():
|
||||||
|
count = &restCount
|
||||||
|
minerAddr = stage.rest.next()
|
||||||
|
restMiners++
|
||||||
|
default:
|
||||||
|
// Well, we've run through all miners.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
added int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
added, full, err = stage.packMiner(ctx, bb, minerAddr, maxProveCommitBatchSize)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to pack precommits for miner %s: %w", minerAddr, err)
|
||||||
|
}
|
||||||
|
*count += added
|
||||||
|
if full {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// packPreCommitsMiner packs count pre-commits for the given miner.
|
||||||
|
func (stage *PreCommitStage) packMiner(
|
||||||
|
ctx context.Context, bb *blockbuilder.BlockBuilder,
|
||||||
|
minerAddr address.Address, count int,
|
||||||
|
) (int, bool, error) {
|
||||||
|
log := bb.L().With("miner", minerAddr)
|
||||||
|
epoch := bb.Height()
|
||||||
|
nv := bb.NetworkVersion()
|
||||||
|
|
||||||
|
minerActor, err := bb.StateTree().GetActor(minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
minerState, err := miner.Load(bb.ActorStore(), minerActor)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
minerInfo, err := minerState.Info()
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the miner is funded.
|
||||||
|
minerBalance, err := minerState.AvailableBalance(minerActor.Balance)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if big.Cmp(minerBalance, MinimumFunds) < 0 {
|
||||||
|
err := stage.funding.Fund(bb, minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
if blockbuilder.IsOutOfGas(err) {
|
||||||
|
return 0, true, nil
|
||||||
|
}
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate pre-commits.
|
||||||
|
sealType, err := miner.PreferredSealProofTypeFromWindowPoStType(
|
||||||
|
nv, minerInfo.WindowPoStProofType,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectorNos, err := minerState.UnallocatedSectorNumbers(count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expiration := epoch + policy.GetMaxSectorExpirationExtension()
|
||||||
|
infos := make([]miner.SectorPreCommitInfo, len(sectorNos))
|
||||||
|
for i, sno := range sectorNos {
|
||||||
|
infos[i] = miner.SectorPreCommitInfo{
|
||||||
|
SealProof: sealType,
|
||||||
|
SectorNumber: sno,
|
||||||
|
SealedCID: mock.MockCommR(minerAddr, sno),
|
||||||
|
SealRandEpoch: epoch - 1,
|
||||||
|
Expiration: expiration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the pre-commits.
|
||||||
|
added := 0
|
||||||
|
if nv >= network.Version13 {
|
||||||
|
targetBatchSize := maxPreCommitBatchSize
|
||||||
|
for targetBatchSize >= minPreCommitBatchSize && len(infos) >= minPreCommitBatchSize {
|
||||||
|
batch := infos
|
||||||
|
if len(batch) > targetBatchSize {
|
||||||
|
batch = batch[:targetBatchSize]
|
||||||
|
}
|
||||||
|
params := miner5.PreCommitSectorBatchParams{
|
||||||
|
Sectors: batch,
|
||||||
|
}
|
||||||
|
enc, err := actors.SerializeParams(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return added, false, err
|
||||||
|
}
|
||||||
|
// NOTE: just in-case, sendAndFund will "fund" and re-try for any message
|
||||||
|
// that fails due to "insufficient funds".
|
||||||
|
if _, err := stage.funding.SendAndFund(bb, &types.Message{
|
||||||
|
To: minerAddr,
|
||||||
|
From: minerInfo.Worker,
|
||||||
|
Value: abi.NewTokenAmount(0),
|
||||||
|
Method: miner.Methods.PreCommitSectorBatch,
|
||||||
|
Params: enc,
|
||||||
|
}); blockbuilder.IsOutOfGas(err) {
|
||||||
|
// try again with a smaller batch.
|
||||||
|
targetBatchSize /= 2
|
||||||
|
continue
|
||||||
|
} else if aerr, ok := err.(aerrors.ActorError); ok && !aerr.IsFatal() {
|
||||||
|
// Log the error and move on. No reason to stop.
|
||||||
|
log.Errorw("failed to pre-commit for unknown reasons",
|
||||||
|
"error", aerr,
|
||||||
|
"sectors", batch,
|
||||||
|
)
|
||||||
|
return added, false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return added, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range batch {
|
||||||
|
if err := stage.committer.EnqueueProveCommit(minerAddr, epoch, info); err != nil {
|
||||||
|
return added, false, err
|
||||||
|
}
|
||||||
|
added++
|
||||||
|
}
|
||||||
|
infos = infos[len(batch):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, info := range infos {
|
||||||
|
enc, err := actors.SerializeParams(&info) //nolint
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
if _, err := stage.funding.SendAndFund(bb, &types.Message{
|
||||||
|
To: minerAddr,
|
||||||
|
From: minerInfo.Worker,
|
||||||
|
Value: abi.NewTokenAmount(0),
|
||||||
|
Method: miner.Methods.PreCommitSector,
|
||||||
|
Params: enc,
|
||||||
|
}); blockbuilder.IsOutOfGas(err) {
|
||||||
|
return added, true, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return added, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stage.committer.EnqueueProveCommit(minerAddr, epoch, info); err != nil {
|
||||||
|
return added, false, err
|
||||||
|
}
|
||||||
|
added++
|
||||||
|
}
|
||||||
|
return added, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *PreCommitStage) load(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
bb.L().Infow("loading miner power for pre-commits")
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bb.L().Infow("loaded miner power for pre-commits",
|
||||||
|
"duration", time.Since(start),
|
||||||
|
"top1", stage.top1.len(),
|
||||||
|
"top10", stage.top10.len(),
|
||||||
|
"rest", stage.rest.len(),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
store := bb.ActorStore()
|
||||||
|
st := bb.ParentStateTree()
|
||||||
|
powerState, err := loadPower(store, st)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to power actor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type onboardingInfo struct {
|
||||||
|
addr address.Address
|
||||||
|
sectorCount uint64
|
||||||
|
}
|
||||||
|
var sealList []onboardingInfo
|
||||||
|
err = powerState.ForEachClaim(func(addr address.Address, claim power.Claim) error {
|
||||||
|
if claim.RawBytePower.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
minerState, err := loadMiner(store, st, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := minerState.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectorCount := sectorsFromClaim(info.SectorSize, claim)
|
||||||
|
|
||||||
|
if sectorCount > 0 {
|
||||||
|
sealList = append(sealList, onboardingInfo{addr, uint64(sectorCount)})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sealList) == 0 {
|
||||||
|
return xerrors.Errorf("simulation has no miners")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have a list of sealing miners, sort them into percentiles.
|
||||||
|
sort.Slice(sealList, func(i, j int) bool {
|
||||||
|
return sealList[i].sectorCount < sealList[j].sectorCount
|
||||||
|
})
|
||||||
|
|
||||||
|
// reset, just in case.
|
||||||
|
stage.top1 = actorIter{}
|
||||||
|
stage.top10 = actorIter{}
|
||||||
|
stage.rest = actorIter{}
|
||||||
|
|
||||||
|
for i, oi := range sealList {
|
||||||
|
var dist *actorIter
|
||||||
|
if i < len(sealList)/100 {
|
||||||
|
dist = &stage.top1
|
||||||
|
} else if i < len(sealList)/10 {
|
||||||
|
dist = &stage.top10
|
||||||
|
} else {
|
||||||
|
dist = &stage.rest
|
||||||
|
}
|
||||||
|
dist.add(oi.addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.top1.shuffle()
|
||||||
|
stage.top10.shuffle()
|
||||||
|
stage.rest.shuffle()
|
||||||
|
|
||||||
|
stage.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
372
cmd/lotus-sim/simulation/stages/provecommit_stage.go
Normal file
372
cmd/lotus-sim/simulation/stages/provecommit_stage.go
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-bitfield"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
|
||||||
|
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
|
||||||
|
power5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/power"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minProveCommitBatchSize = 4
|
||||||
|
maxProveCommitBatchSize = miner5.MaxAggregatedSectors
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProveCommitStage struct {
|
||||||
|
funding Funding
|
||||||
|
// We track the set of pending commits. On simulation load, and when a new pre-commit is
|
||||||
|
// added to the chain, we put the commit in this queue. advanceEpoch(currentEpoch) should be
|
||||||
|
// called on this queue at every epoch before using it.
|
||||||
|
commitQueue commitQueue
|
||||||
|
initialized bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProveCommitStage(funding Funding) (*ProveCommitStage, error) {
|
||||||
|
return &ProveCommitStage{
|
||||||
|
funding: funding,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ProveCommitStage) Name() string {
|
||||||
|
return "prove-commit"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *ProveCommitStage) EnqueueProveCommit(
|
||||||
|
minerAddr address.Address, preCommitEpoch abi.ChainEpoch, info miner.SectorPreCommitInfo,
|
||||||
|
) error {
|
||||||
|
return stage.commitQueue.enqueueProveCommit(minerAddr, preCommitEpoch, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// packProveCommits packs all prove-commits for all "ready to be proven" sectors until it fills the
|
||||||
|
// block or runs out.
|
||||||
|
func (stage *ProveCommitStage) PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
if !stage.initialized {
|
||||||
|
if err := stage.load(ctx, bb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Roll the commitQueue forward.
|
||||||
|
stage.commitQueue.advanceEpoch(bb.Height())
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
var failed, done, unbatched, count int
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remaining := stage.commitQueue.ready()
|
||||||
|
bb.L().Debugw("packed prove commits",
|
||||||
|
"remaining", remaining,
|
||||||
|
"done", done,
|
||||||
|
"failed", failed,
|
||||||
|
"unbatched", unbatched,
|
||||||
|
"miners-processed", count,
|
||||||
|
"duration", time.Since(start),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
addr, pending, ok := stage.commitQueue.nextMiner()
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := stage.packProveCommitsMiner(ctx, bb, addr, pending)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
failed += res.failed
|
||||||
|
done += res.done
|
||||||
|
unbatched += res.unbatched
|
||||||
|
count++
|
||||||
|
if res.full {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type proveCommitResult struct {
|
||||||
|
done, failed, unbatched int
|
||||||
|
full bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// packProveCommitsMiner enqueues a prove commits from the given miner until it runs out of
|
||||||
|
// available prove-commits, batching as much as possible.
|
||||||
|
//
|
||||||
|
// This function will fund as necessary from the "burnt funds actor" (look, it's convenient).
|
||||||
|
func (stage *ProveCommitStage) packProveCommitsMiner(
|
||||||
|
ctx context.Context, bb *blockbuilder.BlockBuilder, minerAddr address.Address,
|
||||||
|
pending minerPendingCommits,
|
||||||
|
) (res proveCommitResult, _err error) {
|
||||||
|
minerActor, err := bb.StateTree().GetActor(minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
minerState, err := miner.Load(bb.ActorStore(), minerActor)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
info, err := minerState.Info()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log := bb.L().With("miner", minerAddr)
|
||||||
|
|
||||||
|
nv := bb.NetworkVersion()
|
||||||
|
for sealType, snos := range pending {
|
||||||
|
if nv >= network.Version13 {
|
||||||
|
for len(snos) > minProveCommitBatchSize {
|
||||||
|
batchSize := maxProveCommitBatchSize
|
||||||
|
if len(snos) < batchSize {
|
||||||
|
batchSize = len(snos)
|
||||||
|
}
|
||||||
|
batch := snos[:batchSize]
|
||||||
|
|
||||||
|
proof, err := mock.MockAggregateSealProof(sealType, minerAddr, batchSize)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := miner5.ProveCommitAggregateParams{
|
||||||
|
SectorNumbers: bitfield.New(),
|
||||||
|
AggregateProof: proof,
|
||||||
|
}
|
||||||
|
for _, sno := range batch {
|
||||||
|
params.SectorNumbers.Set(uint64(sno))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc, err := actors.SerializeParams(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := stage.funding.SendAndFund(bb, &types.Message{
|
||||||
|
From: info.Worker,
|
||||||
|
To: minerAddr,
|
||||||
|
Value: abi.NewTokenAmount(0),
|
||||||
|
Method: miner.Methods.ProveCommitAggregate,
|
||||||
|
Params: enc,
|
||||||
|
}); err == nil {
|
||||||
|
res.done += len(batch)
|
||||||
|
} else if blockbuilder.IsOutOfGas(err) {
|
||||||
|
res.full = true
|
||||||
|
return res, nil
|
||||||
|
} else if aerr, ok := err.(aerrors.ActorError); !ok || aerr.IsFatal() {
|
||||||
|
// If we get a random error, or a fatal actor error, bail.
|
||||||
|
return res, err
|
||||||
|
} else if aerr.RetCode() == exitcode.ErrNotFound || aerr.RetCode() == exitcode.ErrIllegalArgument {
|
||||||
|
// If we get a "not-found" or illegal argument error, try to
|
||||||
|
// remove any missing prove-commits and continue. This can
|
||||||
|
// happen either because:
|
||||||
|
//
|
||||||
|
// 1. The pre-commit failed on execution (but not when
|
||||||
|
// packing). This shouldn't happen, but we might as well
|
||||||
|
// gracefully handle it.
|
||||||
|
// 2. The pre-commit has expired. We'd have to be really
|
||||||
|
// backloged to hit this case, but we might as well handle
|
||||||
|
// it.
|
||||||
|
// First, split into "good" and "missing"
|
||||||
|
good, err := stage.filterProveCommits(ctx, bb, minerAddr, batch)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorw("failed to filter prove commits", "error", err)
|
||||||
|
// fail with the original error.
|
||||||
|
return res, aerr
|
||||||
|
}
|
||||||
|
removed := len(batch) - len(good)
|
||||||
|
if removed == 0 {
|
||||||
|
log.Errorw("failed to prove-commit for unknown reasons",
|
||||||
|
"error", aerr,
|
||||||
|
"sectors", batch,
|
||||||
|
)
|
||||||
|
res.failed += len(batch)
|
||||||
|
} else if len(good) == 0 {
|
||||||
|
log.Errorw("failed to prove commit missing pre-commits",
|
||||||
|
"error", aerr,
|
||||||
|
"discarded", removed,
|
||||||
|
)
|
||||||
|
res.failed += len(batch)
|
||||||
|
} else {
|
||||||
|
// update the pending sector numbers in-place to remove the expired ones.
|
||||||
|
snos = snos[removed:]
|
||||||
|
copy(snos, good)
|
||||||
|
pending.finish(sealType, removed)
|
||||||
|
|
||||||
|
log.Errorw("failed to prove commit expired/missing pre-commits",
|
||||||
|
"error", aerr,
|
||||||
|
"discarded", removed,
|
||||||
|
"kept", len(good),
|
||||||
|
)
|
||||||
|
res.failed += removed
|
||||||
|
|
||||||
|
// Then try again.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Errorw("failed to prove commit sector(s)",
|
||||||
|
"error", err,
|
||||||
|
"sectors", batch,
|
||||||
|
)
|
||||||
|
res.failed += len(batch)
|
||||||
|
}
|
||||||
|
pending.finish(sealType, len(batch))
|
||||||
|
snos = snos[len(batch):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for len(snos) > 0 && res.unbatched < power5.MaxMinerProveCommitsPerEpoch {
|
||||||
|
sno := snos[0]
|
||||||
|
snos = snos[1:]
|
||||||
|
|
||||||
|
proof, err := mock.MockSealProof(sealType, minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
params := miner.ProveCommitSectorParams{
|
||||||
|
SectorNumber: sno,
|
||||||
|
Proof: proof,
|
||||||
|
}
|
||||||
|
enc, err := actors.SerializeParams(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
if _, err := stage.funding.SendAndFund(bb, &types.Message{
|
||||||
|
From: info.Worker,
|
||||||
|
To: minerAddr,
|
||||||
|
Value: abi.NewTokenAmount(0),
|
||||||
|
Method: miner.Methods.ProveCommitSector,
|
||||||
|
Params: enc,
|
||||||
|
}); err == nil {
|
||||||
|
res.unbatched++
|
||||||
|
res.done++
|
||||||
|
} else if blockbuilder.IsOutOfGas(err) {
|
||||||
|
res.full = true
|
||||||
|
return res, nil
|
||||||
|
} else if aerr, ok := err.(aerrors.ActorError); !ok || aerr.IsFatal() {
|
||||||
|
return res, err
|
||||||
|
} else {
|
||||||
|
log.Errorw("failed to prove commit sector(s)",
|
||||||
|
"error", err,
|
||||||
|
"sectors", []abi.SectorNumber{sno},
|
||||||
|
)
|
||||||
|
res.failed++
|
||||||
|
}
|
||||||
|
// mark it as "finished" regardless so we skip it.
|
||||||
|
pending.finish(sealType, 1)
|
||||||
|
}
|
||||||
|
// if we get here, we can't pre-commit anything more.
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadMiner enqueue all pending prove-commits for the given miner. This is called on load to
|
||||||
|
// populate the commitQueue and should not need to be called later.
|
||||||
|
//
|
||||||
|
// It will drop any pre-commits that have already expired.
|
||||||
|
func (stage *ProveCommitStage) loadMiner(ctx context.Context, bb *blockbuilder.BlockBuilder, addr address.Address) error {
|
||||||
|
epoch := bb.Height()
|
||||||
|
av := bb.ActorsVersion()
|
||||||
|
minerState, err := loadMiner(bb.ActorStore(), bb.ParentStateTree(), addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all pending prove commits and group by proof type. Really, there should never
|
||||||
|
// (except during upgrades be more than one type.
|
||||||
|
var total, dropped int
|
||||||
|
err = minerState.ForEachPrecommittedSector(func(info miner.SectorPreCommitOnChainInfo) error {
|
||||||
|
total++
|
||||||
|
msd := policy.GetMaxProveCommitDuration(av, info.Info.SealProof)
|
||||||
|
if epoch > info.PreCommitEpoch+msd {
|
||||||
|
dropped++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return stage.commitQueue.enqueueProveCommit(addr, info.PreCommitEpoch, info.Info)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dropped > 0 {
|
||||||
|
bb.L().Warnw("dropped expired pre-commits on load",
|
||||||
|
"miner", addr,
|
||||||
|
"total", total,
|
||||||
|
"expired", dropped,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterProveCommits filters out expired and/or missing pre-commits.
|
||||||
|
func (stage *ProveCommitStage) filterProveCommits(
|
||||||
|
ctx context.Context, bb *blockbuilder.BlockBuilder,
|
||||||
|
minerAddr address.Address, snos []abi.SectorNumber,
|
||||||
|
) ([]abi.SectorNumber, error) {
|
||||||
|
act, err := bb.StateTree().GetActor(minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
minerState, err := miner.Load(bb.ActorStore(), act)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextEpoch := bb.Height()
|
||||||
|
av := bb.ActorsVersion()
|
||||||
|
|
||||||
|
good := make([]abi.SectorNumber, 0, len(snos))
|
||||||
|
for _, sno := range snos {
|
||||||
|
info, err := minerState.GetPrecommittedSector(sno)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if info == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msd := policy.GetMaxProveCommitDuration(av, info.Info.SealProof)
|
||||||
|
if nextEpoch > info.PreCommitEpoch+msd {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
good = append(good, sno)
|
||||||
|
}
|
||||||
|
return good, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *ProveCommitStage) load(ctx context.Context, bb *blockbuilder.BlockBuilder) error {
|
||||||
|
stage.initialized = false // in case something failes while we're doing this.
|
||||||
|
stage.commitQueue = commitQueue{offset: bb.Height()}
|
||||||
|
powerState, err := loadPower(bb.ActorStore(), bb.ParentStateTree())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = powerState.ForEachClaim(func(minerAddr address.Address, claim power.Claim) error {
|
||||||
|
// TODO: If we want to finish pre-commits for "new" miners, we'll need to change
|
||||||
|
// this.
|
||||||
|
if claim.RawBytePower.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return stage.loadMiner(ctx, bb, minerAddr)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
51
cmd/lotus-sim/simulation/stages/util.go
Normal file
51
cmd/lotus-sim/simulation/stages/util.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/crypto"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadMiner(store adt.Store, st types.StateTree, addr address.Address) (miner.State, error) {
|
||||||
|
minerActor, err := st.GetActor(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return miner.Load(store, minerActor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPower(store adt.Store, st types.StateTree) (power.State, error) {
|
||||||
|
powerActor, err := st.GetActor(power.Address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return power.Load(store, powerActor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the number of sectors a miner has from their power claim.
|
||||||
|
func sectorsFromClaim(sectorSize abi.SectorSize, c power.Claim) int64 {
|
||||||
|
if c.RawBytePower.Int == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
sectorCount := big.Div(c.RawBytePower, big.NewIntUnsigned(uint64(sectorSize)))
|
||||||
|
if !sectorCount.IsInt64() {
|
||||||
|
panic("impossible number of sectors")
|
||||||
|
}
|
||||||
|
return sectorCount.Int64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func postChainCommitInfo(ctx context.Context, bb *blockbuilder.BlockBuilder, epoch abi.ChainEpoch) (abi.Randomness, error) {
|
||||||
|
cs := bb.StateManager().ChainStore()
|
||||||
|
ts := bb.ParentTipSet()
|
||||||
|
commitRand, err := cs.GetChainRandomness(ctx, ts.Cids(), crypto.DomainSeparationTag_PoStChainCommit, epoch, nil, true)
|
||||||
|
return commitRand, err
|
||||||
|
}
|
317
cmd/lotus-sim/simulation/stages/windowpost_stage.go
Normal file
317
cmd/lotus-sim/simulation/stages/windowpost_stage.go
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
package stages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WindowPoStStage struct {
|
||||||
|
// We track the window post periods per miner and assume that no new miners are ever added.
|
||||||
|
|
||||||
|
// We record all pending window post messages, and the epoch up through which we've
|
||||||
|
// generated window post messages.
|
||||||
|
pendingWposts []*types.Message
|
||||||
|
wpostPeriods [][]address.Address // (epoch % (epochs in a deadline)) -> miner
|
||||||
|
nextWpostEpoch abi.ChainEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWindowPoStStage() (*WindowPoStStage, error) {
|
||||||
|
return new(WindowPoStStage), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*WindowPoStStage) Name() string {
|
||||||
|
return "window-post"
|
||||||
|
}
|
||||||
|
|
||||||
|
// packWindowPoSts packs window posts until either the block is full or all healty sectors
|
||||||
|
// have been proven. It does not recover sectors.
|
||||||
|
func (stage *WindowPoStStage) PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
// Push any new window posts into the queue.
|
||||||
|
if err := stage.tick(ctx, bb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
done := 0
|
||||||
|
failed := 0
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bb.L().Debugw("packed window posts",
|
||||||
|
"done", done,
|
||||||
|
"failed", failed,
|
||||||
|
"remaining", len(stage.pendingWposts),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
// Then pack as many as we can.
|
||||||
|
for len(stage.pendingWposts) > 0 {
|
||||||
|
next := stage.pendingWposts[0]
|
||||||
|
if _, err := bb.PushMessage(next); err != nil {
|
||||||
|
if blockbuilder.IsOutOfGas(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if aerr, ok := err.(aerrors.ActorError); !ok || aerr.IsFatal() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bb.L().Errorw("failed to submit windowed post",
|
||||||
|
"error", err,
|
||||||
|
"miner", next.To,
|
||||||
|
)
|
||||||
|
failed++
|
||||||
|
} else {
|
||||||
|
done++
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.pendingWposts = stage.pendingWposts[1:]
|
||||||
|
}
|
||||||
|
stage.pendingWposts = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stepWindowPoStsMiner enqueues all missing window posts for the current epoch for the given miner.
|
||||||
|
func (stage *WindowPoStStage) queueMiner(
|
||||||
|
ctx context.Context, bb *blockbuilder.BlockBuilder,
|
||||||
|
addr address.Address, minerState miner.State,
|
||||||
|
commitEpoch abi.ChainEpoch, commitRand abi.Randomness,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
if active, err := minerState.DeadlineCronActive(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !active {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
minerInfo, err := minerState.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
di, err := minerState.DeadlineInfo(bb.Height())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
di = di.NextNotElapsed()
|
||||||
|
|
||||||
|
dl, err := minerState.LoadDeadline(di.Index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
provenBf, err := dl.PartitionsPoSted()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
proven, err := provenBf.AllMap(math.MaxUint64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
poStBatchSize, err := policy.GetMaxPoStPartitions(bb.NetworkVersion(), minerInfo.WindowPoStProofType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
partitions []miner.PoStPartition
|
||||||
|
partitionGroups [][]miner.PoStPartition
|
||||||
|
)
|
||||||
|
// Only prove partitions with live sectors.
|
||||||
|
err = dl.ForEachPartition(func(idx uint64, part miner.Partition) error {
|
||||||
|
if proven[idx] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// NOTE: We're mimicing the behavior of wdpost_run.go here.
|
||||||
|
if len(partitions) > 0 && idx%uint64(poStBatchSize) == 0 {
|
||||||
|
partitionGroups = append(partitionGroups, partitions)
|
||||||
|
partitions = nil
|
||||||
|
|
||||||
|
}
|
||||||
|
live, err := part.LiveSectors()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
liveCount, err := live.Count()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
faulty, err := part.FaultySectors()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
faultyCount, err := faulty.Count()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if liveCount-faultyCount > 0 {
|
||||||
|
partitions = append(partitions, miner.PoStPartition{Index: idx})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(partitions) > 0 {
|
||||||
|
partitionGroups = append(partitionGroups, partitions)
|
||||||
|
partitions = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
proof, err := mock.MockWindowPoStProof(minerInfo.WindowPoStProofType, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, group := range partitionGroups {
|
||||||
|
params := miner.SubmitWindowedPoStParams{
|
||||||
|
Deadline: di.Index,
|
||||||
|
Partitions: group,
|
||||||
|
Proofs: []proof5.PoStProof{{
|
||||||
|
PoStProof: minerInfo.WindowPoStProofType,
|
||||||
|
ProofBytes: proof,
|
||||||
|
}},
|
||||||
|
ChainCommitEpoch: commitEpoch,
|
||||||
|
ChainCommitRand: commitRand,
|
||||||
|
}
|
||||||
|
enc, aerr := actors.SerializeParams(¶ms)
|
||||||
|
if aerr != nil {
|
||||||
|
return xerrors.Errorf("could not serialize submit window post parameters: %w", aerr)
|
||||||
|
}
|
||||||
|
msg := &types.Message{
|
||||||
|
To: addr,
|
||||||
|
From: minerInfo.Worker,
|
||||||
|
Method: miner.Methods.SubmitWindowedPoSt,
|
||||||
|
Params: enc,
|
||||||
|
Value: types.NewInt(0),
|
||||||
|
}
|
||||||
|
stage.pendingWposts = append(stage.pendingWposts, msg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *WindowPoStStage) load(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) {
|
||||||
|
bb.L().Info("loading window post info")
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bb.L().Infow("loaded window post info", "duration", time.Since(start))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// reset
|
||||||
|
stage.wpostPeriods = make([][]address.Address, miner.WPoStChallengeWindow)
|
||||||
|
stage.pendingWposts = nil
|
||||||
|
stage.nextWpostEpoch = bb.Height() + 1
|
||||||
|
|
||||||
|
st := bb.ParentStateTree()
|
||||||
|
store := bb.ActorStore()
|
||||||
|
|
||||||
|
powerState, err := loadPower(store, st)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
commitEpoch := bb.ParentTipSet().Height()
|
||||||
|
commitRand, err := postChainCommitInfo(ctx, bb, commitEpoch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return powerState.ForEachClaim(func(minerAddr address.Address, claim power.Claim) error {
|
||||||
|
// TODO: If we start recovering power, we'll need to change this.
|
||||||
|
if claim.RawBytePower.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
minerState, err := loadMiner(store, st, minerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shouldn't be necessary if the miner has power, but we might as well be safe.
|
||||||
|
if active, err := minerState.DeadlineCronActive(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !active {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record when we need to prove for this miner.
|
||||||
|
dinfo, err := minerState.DeadlineInfo(bb.Height())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dinfo = dinfo.NextNotElapsed()
|
||||||
|
|
||||||
|
ppOffset := int(dinfo.PeriodStart % miner.WPoStChallengeWindow)
|
||||||
|
stage.wpostPeriods[ppOffset] = append(stage.wpostPeriods[ppOffset], minerAddr)
|
||||||
|
|
||||||
|
return stage.queueMiner(ctx, bb, minerAddr, minerState, commitEpoch, commitRand)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *WindowPoStStage) tick(ctx context.Context, bb *blockbuilder.BlockBuilder) error {
|
||||||
|
// If this is our first time, load from scratch.
|
||||||
|
if stage.wpostPeriods == nil {
|
||||||
|
return stage.load(ctx, bb)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetHeight := bb.Height()
|
||||||
|
now := time.Now()
|
||||||
|
was := len(stage.pendingWposts)
|
||||||
|
count := 0
|
||||||
|
defer func() {
|
||||||
|
bb.L().Debugw("computed window posts",
|
||||||
|
"miners", count,
|
||||||
|
"count", len(stage.pendingWposts)-was,
|
||||||
|
"duration", time.Since(now),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
st := bb.ParentStateTree()
|
||||||
|
store := bb.ActorStore()
|
||||||
|
|
||||||
|
// Perform a bit of catch up. This lets us do things like skip blocks at upgrades then catch
|
||||||
|
// up to make the simulation easier.
|
||||||
|
for ; stage.nextWpostEpoch <= targetHeight; stage.nextWpostEpoch++ {
|
||||||
|
if stage.nextWpostEpoch+miner.WPoStChallengeWindow < targetHeight {
|
||||||
|
bb.L().Warnw("skipping old window post", "deadline-open", stage.nextWpostEpoch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
commitEpoch := stage.nextWpostEpoch - 1
|
||||||
|
commitRand, err := postChainCommitInfo(ctx, bb, commitEpoch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range stage.wpostPeriods[int(stage.nextWpostEpoch%miner.WPoStChallengeWindow)] {
|
||||||
|
minerState, err := loadMiner(store, st, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stage.queueMiner(ctx, bb, addr, minerState, commitEpoch, commitRand); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
71
cmd/lotus-sim/simulation/step.go
Normal file
71
cmd/lotus-sim/simulation/step.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step steps the simulation forward one step. This may move forward by more than one epoch.
|
||||||
|
func (sim *Simulation) Step(ctx context.Context) (*types.TipSet, error) {
|
||||||
|
log.Infow("step", "epoch", sim.head.Height()+1)
|
||||||
|
messages, err := sim.popNextMessages(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to select messages for block: %w", err)
|
||||||
|
}
|
||||||
|
head, err := sim.makeTipSet(ctx, messages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to make tipset: %w", err)
|
||||||
|
}
|
||||||
|
if err := sim.SetHead(head); err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to update head: %w", err)
|
||||||
|
}
|
||||||
|
return head, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// popNextMessages generates/picks a set of messages to be included in the next block.
|
||||||
|
//
|
||||||
|
// - This function is destructive and should only be called once per epoch.
|
||||||
|
// - This function does not store anything in the repo.
|
||||||
|
// - This function handles all gas estimation. The returned messages should all fit in a single
|
||||||
|
// block.
|
||||||
|
func (sim *Simulation) popNextMessages(ctx context.Context) ([]*types.Message, error) {
|
||||||
|
parentTs := sim.head
|
||||||
|
|
||||||
|
// First we make sure we don't have an upgrade at this epoch. If we do, we return no
|
||||||
|
// messages so we can just create an empty block at that epoch.
|
||||||
|
//
|
||||||
|
// This isn't what the network does, but it makes things easier. Otherwise, we'd need to run
|
||||||
|
// migrations before this epoch and I'd rather not deal with that.
|
||||||
|
nextHeight := parentTs.Height() + 1
|
||||||
|
prevVer := sim.StateManager.GetNtwkVersion(ctx, nextHeight-1)
|
||||||
|
nextVer := sim.StateManager.GetNtwkVersion(ctx, nextHeight)
|
||||||
|
if nextVer != prevVer {
|
||||||
|
log.Warnw("packing no messages for version upgrade block",
|
||||||
|
"old", prevVer,
|
||||||
|
"new", nextVer,
|
||||||
|
"epoch", nextHeight,
|
||||||
|
)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bb, err := blockbuilder.NewBlockBuilder(
|
||||||
|
ctx, log.With("simulation", sim.name),
|
||||||
|
sim.StateManager, parentTs,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stage := range sim.stages {
|
||||||
|
// We're intentionally ignoring the "full" signal so we can try to pack a few more
|
||||||
|
// messages.
|
||||||
|
if err := stage.PackMessages(ctx, bb); err != nil && !blockbuilder.IsOutOfGas(err) {
|
||||||
|
return nil, xerrors.Errorf("when packing messages with %s: %w", stage.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bb.Messages(), nil
|
||||||
|
}
|
109
cmd/lotus-sim/upgrade.go
Normal file
109
cmd/lotus-sim/upgrade.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgradeCommand = &cli.Command{
|
||||||
|
Name: "upgrade",
|
||||||
|
Description: "Modifies network upgrade heights.",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
upgradeSetCommand,
|
||||||
|
upgradeList,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgradeList = &cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Description: "Lists all pending upgrades.",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
upgradeSetCommand,
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upgrades, err := sim.ListUpgrades()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tw := tabwriter.NewWriter(cctx.App.Writer, 8, 8, 0, ' ', 0)
|
||||||
|
fmt.Fprintf(tw, "version\theight\tepochs\tmigration\texpensive")
|
||||||
|
epoch := sim.GetHead().Height()
|
||||||
|
for _, upgrade := range upgrades {
|
||||||
|
fmt.Fprintf(
|
||||||
|
tw, "%d\t%d\t%+d\t%t\t%t",
|
||||||
|
upgrade.Network, upgrade.Height, upgrade.Height-epoch,
|
||||||
|
upgrade.Migration != nil,
|
||||||
|
upgrade.Expensive,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgradeSetCommand = &cli.Command{
|
||||||
|
Name: "set",
|
||||||
|
ArgsUsage: "<network-version> [+]<epochs>",
|
||||||
|
Description: "Set a network upgrade height. Prefix with '+' to set it relative to the last epoch.",
|
||||||
|
Action: func(cctx *cli.Context) (err error) {
|
||||||
|
args := cctx.Args()
|
||||||
|
if args.Len() != 2 {
|
||||||
|
return fmt.Errorf("expected 2 arguments")
|
||||||
|
}
|
||||||
|
nvString := args.Get(0)
|
||||||
|
networkVersion, err := strconv.ParseUint(nvString, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse network version %q: %w", nvString, err)
|
||||||
|
}
|
||||||
|
heightString := args.Get(1)
|
||||||
|
relative := false
|
||||||
|
if strings.HasPrefix(heightString, "+") {
|
||||||
|
heightString = heightString[1:]
|
||||||
|
relative = true
|
||||||
|
}
|
||||||
|
height, err := strconv.ParseInt(heightString, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse height version %q: %w", heightString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := open(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cerr := node.Close(); err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if relative {
|
||||||
|
height += int64(sim.GetHead().Height())
|
||||||
|
}
|
||||||
|
return sim.SetUpgradeHeight(network.Version(networkVersion), abi.ChainEpoch(height))
|
||||||
|
},
|
||||||
|
}
|
18
cmd/lotus-sim/util.go
Normal file
18
cmd/lotus-sim/util.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation"
|
||||||
|
"github.com/filecoin-project/lotus/lib/ulimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open(cctx *cli.Context) (*simulation.Node, error) {
|
||||||
|
_, _, err := ulimit.ManageFdLimit()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(cctx.App.ErrWriter, "ERROR: failed to raise ulimit: %s\n", err)
|
||||||
|
}
|
||||||
|
return simulation.OpenNode(cctx.Context, cctx.String("repo"))
|
||||||
|
}
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/lib/ulimit"
|
"github.com/filecoin-project/lotus/lib/ulimit"
|
||||||
"github.com/filecoin-project/lotus/metrics"
|
"github.com/filecoin-project/lotus/metrics"
|
||||||
"github.com/filecoin-project/lotus/node"
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
"github.com/filecoin-project/lotus/node/config"
|
||||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
)
|
)
|
||||||
@ -142,15 +143,30 @@ var runCmd = &cli.Command{
|
|||||||
return xerrors.Errorf("getting API endpoint: %w", err)
|
return xerrors.Errorf("getting API endpoint: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap with full node
|
lr, _ := r.Lock(repo.StorageMiner)
|
||||||
//remoteAddrs, err := nodeApi.NetAddrsListen(ctx)
|
c, _ := lr.Config()
|
||||||
//if err != nil {
|
cfg, ok := c.(*config.StorageMiner)
|
||||||
//return xerrors.Errorf("getting full node libp2p address: %w", err)
|
if !ok {
|
||||||
//}
|
log.Fatalf("invalid config from repo, got: %T", c)
|
||||||
|
}
|
||||||
|
|
||||||
//if err := minerapi.NetConnect(ctx, remoteAddrs); err != nil {
|
if cfg.Subsystems.EnableStorageMarket {
|
||||||
//return xerrors.Errorf("connecting to full node (libp2p): %w", err)
|
log.Infof("Bootstrapping libp2p network with full node")
|
||||||
//}
|
|
||||||
|
// Bootstrap with full node
|
||||||
|
remoteAddrs, err := nodeApi.NetAddrsListen(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting full node libp2p address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := minerapi.NetConnect(ctx, remoteAddrs); err != nil {
|
||||||
|
return xerrors.Errorf("connecting to full node (libp2p): %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("No markets subsystem enabled, so no libp2p network bootstrapping")
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = lr.Close()
|
||||||
|
|
||||||
log.Infof("Remote version %s", v)
|
log.Infof("Remote version %s", v)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ USAGE:
|
|||||||
lotus-miner [global options] command [command options] [arguments...]
|
lotus-miner [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
VERSION:
|
VERSION:
|
||||||
1.11.0-dev
|
1.11.1-dev
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
init Initialize a lotus miner repo
|
init Initialize a lotus miner repo
|
||||||
|
@ -7,7 +7,7 @@ USAGE:
|
|||||||
lotus-worker [global options] command [command options] [arguments...]
|
lotus-worker [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
VERSION:
|
VERSION:
|
||||||
1.11.0-dev
|
1.11.1-dev
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
run Start lotus worker
|
run Start lotus worker
|
||||||
|
@ -7,7 +7,7 @@ USAGE:
|
|||||||
lotus [global options] command [command options] [arguments...]
|
lotus [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
VERSION:
|
VERSION:
|
||||||
1.11.0-dev
|
1.11.1-dev
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
daemon Start a lotus daemon process
|
daemon Start a lotus daemon process
|
||||||
@ -558,8 +558,8 @@ DESCRIPTION:
|
|||||||
Make a deal with a miner.
|
Make a deal with a miner.
|
||||||
dataCid comes from running 'lotus client import'.
|
dataCid comes from running 'lotus client import'.
|
||||||
miner is the address of the miner you wish to make a deal with.
|
miner is the address of the miner you wish to make a deal with.
|
||||||
price is measured in FIL/GB/Epoch. Miners usually don't accept a bid
|
price is measured in FIL/Epoch. Miners usually don't accept a bid
|
||||||
lower than their advertised ask. You can check a miners listed price
|
lower than their advertised ask (which is in FIL/GiB/Epoch). You can check a miners listed price
|
||||||
with 'lotus client query-ask <miner address>'.
|
with 'lotus client query-ask <miner address>'.
|
||||||
duration is how long the miner should store the data for, in blocks.
|
duration is how long the miner should store the data for, in blocks.
|
||||||
The minimum value is 518400 (6 months).
|
The minimum value is 518400 (6 months).
|
||||||
|
26
extern/storage-sealing/commit_batch.go
vendored
26
extern/storage-sealing/commit_batch.go
vendored
@ -32,6 +32,9 @@ import (
|
|||||||
|
|
||||||
const arp = abi.RegisteredAggregationProof_SnarkPackV1
|
const arp = abi.RegisteredAggregationProof_SnarkPackV1
|
||||||
|
|
||||||
|
var aggFeeNum = big.NewInt(110)
|
||||||
|
var aggFeeDen = big.NewInt(100)
|
||||||
|
|
||||||
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_commit_batcher.go -package=mocks . CommitBatcherApi
|
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_commit_batcher.go -package=mocks . CommitBatcherApi
|
||||||
|
|
||||||
type CommitBatcherApi interface {
|
type CommitBatcherApi interface {
|
||||||
@ -110,7 +113,8 @@ func (b *CommitBatcher) run() {
|
|||||||
}
|
}
|
||||||
lastMsg = nil
|
lastMsg = nil
|
||||||
|
|
||||||
var sendAboveMax, sendAboveMin bool
|
// indicates whether we should only start a batch if we have reached or exceeded cfg.MaxCommitBatch
|
||||||
|
var sendAboveMax bool
|
||||||
select {
|
select {
|
||||||
case <-b.stop:
|
case <-b.stop:
|
||||||
close(b.stopped)
|
close(b.stopped)
|
||||||
@ -118,13 +122,13 @@ func (b *CommitBatcher) run() {
|
|||||||
case <-b.notify:
|
case <-b.notify:
|
||||||
sendAboveMax = true
|
sendAboveMax = true
|
||||||
case <-b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack):
|
case <-b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack):
|
||||||
sendAboveMin = true
|
// do nothing
|
||||||
case fr := <-b.force: // user triggered
|
case fr := <-b.force: // user triggered
|
||||||
forceRes = fr
|
forceRes = fr
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
lastMsg, err = b.maybeStartBatch(sendAboveMax, sendAboveMin)
|
lastMsg, err = b.maybeStartBatch(sendAboveMax)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnw("CommitBatcher processBatch error", "error", err)
|
log.Warnw("CommitBatcher processBatch error", "error", err)
|
||||||
}
|
}
|
||||||
@ -172,7 +176,7 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time
|
|||||||
return time.After(wait)
|
return time.After(wait)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBatchRes, error) {
|
func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, error) {
|
||||||
b.lk.Lock()
|
b.lk.Lock()
|
||||||
defer b.lk.Unlock()
|
defer b.lk.Unlock()
|
||||||
|
|
||||||
@ -190,10 +194,6 @@ func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBa
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if after && total < cfg.MinCommitBatch {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var res []sealiface.CommitBatchRes
|
var res []sealiface.CommitBatchRes
|
||||||
|
|
||||||
if total < cfg.MinCommitBatch || total < miner5.MinAggregatedSectors {
|
if total < cfg.MinCommitBatch || total < miner5.MinAggregatedSectors {
|
||||||
@ -308,16 +308,18 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa
|
|||||||
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting network version: %s", err)
|
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting network version: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
aggFee := policy.AggregateNetworkFee(nv, len(infos), bf)
|
aggFee := big.Div(big.Mul(policy.AggregateNetworkFee(nv, len(infos), bf), aggFeeNum), aggFeeDen)
|
||||||
|
|
||||||
goodFunds := big.Add(maxFee, big.Add(collateral, aggFee))
|
needFunds := big.Add(collateral, aggFee)
|
||||||
|
|
||||||
from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral)
|
goodFunds := big.Add(maxFee, needFunds)
|
||||||
|
|
||||||
|
from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, needFunds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err)
|
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, collateral, maxFee, enc.Bytes())
|
mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, needFunds, maxFee, enc.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err)
|
return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err)
|
||||||
}
|
}
|
||||||
|
1
extern/storage-sealing/commit_batch_test.go
vendored
1
extern/storage-sealing/commit_batch_test.go
vendored
@ -48,7 +48,6 @@ func TestCommitBatcher(t *testing.T) {
|
|||||||
AlwaysKeepUnsealedCopy: true,
|
AlwaysKeepUnsealedCopy: true,
|
||||||
|
|
||||||
BatchPreCommits: true,
|
BatchPreCommits: true,
|
||||||
MinPreCommitBatch: 1,
|
|
||||||
MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize,
|
MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize,
|
||||||
PreCommitBatchWait: 24 * time.Hour,
|
PreCommitBatchWait: 24 * time.Hour,
|
||||||
PreCommitBatchSlack: 3 * time.Hour,
|
PreCommitBatchSlack: 3 * time.Hour,
|
||||||
|
12
extern/storage-sealing/precommit_batch.go
vendored
12
extern/storage-sealing/precommit_batch.go
vendored
@ -95,7 +95,7 @@ func (b *PreCommitBatcher) run() {
|
|||||||
}
|
}
|
||||||
lastRes = nil
|
lastRes = nil
|
||||||
|
|
||||||
var sendAboveMax, sendAboveMin bool
|
var sendAboveMax bool
|
||||||
select {
|
select {
|
||||||
case <-b.stop:
|
case <-b.stop:
|
||||||
close(b.stopped)
|
close(b.stopped)
|
||||||
@ -103,13 +103,13 @@ func (b *PreCommitBatcher) run() {
|
|||||||
case <-b.notify:
|
case <-b.notify:
|
||||||
sendAboveMax = true
|
sendAboveMax = true
|
||||||
case <-b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack):
|
case <-b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack):
|
||||||
sendAboveMin = true
|
// do nothing
|
||||||
case fr := <-b.force: // user triggered
|
case fr := <-b.force: // user triggered
|
||||||
forceRes = fr
|
forceRes = fr
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
lastRes, err = b.maybeStartBatch(sendAboveMax, sendAboveMin)
|
lastRes, err = b.maybeStartBatch(sendAboveMax)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnw("PreCommitBatcher processBatch error", "error", err)
|
log.Warnw("PreCommitBatcher processBatch error", "error", err)
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.T
|
|||||||
return time.After(wait)
|
return time.After(wait)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCommitBatchRes, error) {
|
func (b *PreCommitBatcher) maybeStartBatch(notif bool) ([]sealiface.PreCommitBatchRes, error) {
|
||||||
b.lk.Lock()
|
b.lk.Lock()
|
||||||
defer b.lk.Unlock()
|
defer b.lk.Unlock()
|
||||||
|
|
||||||
@ -175,10 +175,6 @@ func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCo
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if after && total < cfg.MinPreCommitBatch {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo support multiple batches
|
// todo support multiple batches
|
||||||
res, err := b.processBatch(cfg)
|
res, err := b.processBatch(cfg)
|
||||||
if err != nil && len(res) == 0 {
|
if err != nil && len(res) == 0 {
|
||||||
|
@ -55,7 +55,6 @@ func TestPrecommitBatcher(t *testing.T) {
|
|||||||
AlwaysKeepUnsealedCopy: true,
|
AlwaysKeepUnsealedCopy: true,
|
||||||
|
|
||||||
BatchPreCommits: true,
|
BatchPreCommits: true,
|
||||||
MinPreCommitBatch: 1,
|
|
||||||
MaxPreCommitBatch: maxBatch,
|
MaxPreCommitBatch: maxBatch,
|
||||||
PreCommitBatchWait: 24 * time.Hour,
|
PreCommitBatchWait: 24 * time.Hour,
|
||||||
PreCommitBatchSlack: 3 * time.Hour,
|
PreCommitBatchSlack: 3 * time.Hour,
|
||||||
|
1
extern/storage-sealing/sealiface/config.go
vendored
1
extern/storage-sealing/sealiface/config.go
vendored
@ -22,7 +22,6 @@ type Config struct {
|
|||||||
|
|
||||||
BatchPreCommits bool
|
BatchPreCommits bool
|
||||||
MaxPreCommitBatch int
|
MaxPreCommitBatch int
|
||||||
MinPreCommitBatch int
|
|
||||||
PreCommitBatchWait time.Duration
|
PreCommitBatchWait time.Duration
|
||||||
PreCommitBatchSlack time.Duration
|
PreCommitBatchSlack time.Duration
|
||||||
|
|
||||||
|
2
extern/storage-sealing/states_sealing.go
vendored
2
extern/storage-sealing/states_sealing.go
vendored
@ -274,7 +274,7 @@ func (m *Sealing) preCommitParams(ctx statemachine.Context, sector SectorInfo) (
|
|||||||
|
|
||||||
msd := policy.GetMaxProveCommitDuration(actors.VersionForNetwork(nv), sector.SectorType)
|
msd := policy.GetMaxProveCommitDuration(actors.VersionForNetwork(nv), sector.SectorType)
|
||||||
|
|
||||||
if minExpiration := height + msd + miner.MinSectorExpiration + 10; expiration < minExpiration {
|
if minExpiration := sector.TicketEpoch + policy.MaxPreCommitRandomnessLookback + msd + miner.MinSectorExpiration; expiration < minExpiration {
|
||||||
expiration = minExpiration
|
expiration = minExpiration
|
||||||
}
|
}
|
||||||
// TODO: enforce a reasonable _maximum_ sector lifetime?
|
// TODO: enforce a reasonable _maximum_ sector lifetime?
|
||||||
|
@ -233,3 +233,19 @@ func (m *mockGatewayDepsAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Ci
|
|||||||
func (m *mockGatewayDepsAPI) StateReadState(ctx context.Context, act address.Address, ts types.TipSetKey) (*api.ActorState, error) {
|
func (m *mockGatewayDepsAPI) StateReadState(ctx context.Context, act address.Address, ts types.TipSetKey) (*api.ActorState, error) {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockGatewayDepsAPI) Version(context.Context) (api.APIVersion, error) {
|
||||||
|
return api.APIVersion{
|
||||||
|
APIVersion: api.FullAPIVersion1,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatewayVersion(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
mock := &mockGatewayDepsAPI{}
|
||||||
|
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit)
|
||||||
|
|
||||||
|
v, err := a.Version(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, api.FullAPIVersion1, v.APIVersion)
|
||||||
|
}
|
||||||
|
5
go.mod
5
go.mod
@ -48,7 +48,7 @@ require (
|
|||||||
github.com/filecoin-project/specs-actors/v2 v2.3.5
|
github.com/filecoin-project/specs-actors/v2 v2.3.5
|
||||||
github.com/filecoin-project/specs-actors/v3 v3.1.1
|
github.com/filecoin-project/specs-actors/v3 v3.1.1
|
||||||
github.com/filecoin-project/specs-actors/v4 v4.0.1
|
github.com/filecoin-project/specs-actors/v4 v4.0.1
|
||||||
github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c
|
github.com/filecoin-project/specs-actors/v5 v5.0.1
|
||||||
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506
|
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506
|
||||||
github.com/filecoin-project/test-vectors/schema v0.0.5
|
github.com/filecoin-project/test-vectors/schema v0.0.5
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||||
@ -133,6 +133,7 @@ require (
|
|||||||
github.com/prometheus/client_golang v1.6.0
|
github.com/prometheus/client_golang v1.6.0
|
||||||
github.com/raulk/clock v1.1.0
|
github.com/raulk/clock v1.1.0
|
||||||
github.com/raulk/go-watchdog v1.0.1
|
github.com/raulk/go-watchdog v1.0.1
|
||||||
|
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25
|
||||||
github.com/stretchr/objx v0.2.0 // indirect
|
github.com/stretchr/objx v0.2.0 // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
@ -160,6 +161,8 @@ require (
|
|||||||
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/libp2p/go-libp2p-yamux => github.com/libp2p/go-libp2p-yamux v0.5.1
|
||||||
|
|
||||||
replace github.com/filecoin-project/lotus => ./
|
replace github.com/filecoin-project/lotus => ./
|
||||||
|
|
||||||
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
|
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
|
||||||
|
41
go.sum
41
go.sum
@ -331,8 +331,8 @@ github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIP
|
|||||||
github.com/filecoin-project/specs-actors/v4 v4.0.1 h1:AiWrtvJZ63MHGe6rn7tPu4nSUY8bA1KDNszqJaD5+Fg=
|
github.com/filecoin-project/specs-actors/v4 v4.0.1 h1:AiWrtvJZ63MHGe6rn7tPu4nSUY8bA1KDNszqJaD5+Fg=
|
||||||
github.com/filecoin-project/specs-actors/v4 v4.0.1/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng=
|
github.com/filecoin-project/specs-actors/v4 v4.0.1/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng=
|
||||||
github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI=
|
github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI=
|
||||||
github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c h1:GnDJ6q3QEm2ytTKjPFQSvczAltgCSb3j9F1FeynwvPA=
|
github.com/filecoin-project/specs-actors/v5 v5.0.1 h1:PrYm5AKdMlJ/55eRW5laWcnaX66gyyDYBWvH38kNAMo=
|
||||||
github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c/go.mod h1:b/btpRl84Q9SeDKlyIoORBQwe2OTmq14POrYrVvBWCM=
|
github.com/filecoin-project/specs-actors/v5 v5.0.1/go.mod h1:74euMDIXorusOBs/QL/LNkYsXZdDpLJwojWw6T03pdE=
|
||||||
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw=
|
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw=
|
||||||
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g=
|
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g=
|
||||||
github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg=
|
github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg=
|
||||||
@ -704,12 +704,13 @@ github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHn
|
|||||||
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
||||||
github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
||||||
github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk=
|
github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk=
|
||||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
|
||||||
github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk=
|
github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk=
|
||||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||||
|
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||||
github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA=
|
github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA=
|
||||||
github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto=
|
github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto=
|
||||||
github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
|
github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
|
||||||
|
github.com/ipfs/go-merkledag v0.2.4/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
|
||||||
github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
|
github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
|
||||||
github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1xY=
|
github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1xY=
|
||||||
github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
|
github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
|
||||||
@ -728,6 +729,7 @@ github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUn
|
|||||||
github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4=
|
github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4=
|
||||||
github.com/ipfs/go-unixfs v0.0.4/go.mod h1:eIo/p9ADu/MFOuyxzwU+Th8D6xoxU//r590vUpWyfz8=
|
github.com/ipfs/go-unixfs v0.0.4/go.mod h1:eIo/p9ADu/MFOuyxzwU+Th8D6xoxU//r590vUpWyfz8=
|
||||||
github.com/ipfs/go-unixfs v0.2.1/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
|
github.com/ipfs/go-unixfs v0.2.1/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
|
||||||
|
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
|
||||||
github.com/ipfs/go-unixfs v0.2.4 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo=
|
github.com/ipfs/go-unixfs v0.2.4 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo=
|
||||||
github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
|
github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
|
||||||
github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
|
github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
|
||||||
@ -738,13 +740,16 @@ github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo=
|
|||||||
github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg=
|
github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg=
|
||||||
github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA=
|
github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA=
|
||||||
github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
|
github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
|
||||||
|
github.com/ipld/go-car v0.1.0/go.mod h1:RCWzaUh2i4mOEkB3W45Vc+9jnS/M6Qay5ooytiBHl3g=
|
||||||
github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw=
|
github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw=
|
||||||
github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d h1:iphSzTuPqyDgH7WUVZsdqUnQNzYgIblsVr1zhVNA33U=
|
github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d h1:iphSzTuPqyDgH7WUVZsdqUnQNzYgIblsVr1zhVNA33U=
|
||||||
github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d/go.mod h1:2Gys8L8MJ6zkh1gktTSXreY63t4UbyvNp5JaudTyxHQ=
|
github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d/go.mod h1:2Gys8L8MJ6zkh1gktTSXreY63t4UbyvNp5JaudTyxHQ=
|
||||||
|
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
|
||||||
github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
|
github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
|
||||||
github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
|
github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
|
||||||
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018 h1:RbRHv8epkmvBYA5cGfz68GUSbOgx5j/7ObLIl4Rsif0=
|
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018 h1:RbRHv8epkmvBYA5cGfz68GUSbOgx5j/7ObLIl4Rsif0=
|
||||||
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
|
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
|
||||||
|
github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5/go.mod h1:gcvzoEDBjwycpXt3LBE061wT9f46szXGHAmj9uoP6fU=
|
||||||
github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs=
|
github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs=
|
||||||
github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0=
|
github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0=
|
||||||
github.com/ipld/go-ipld-prime-proto v0.1.0 h1:j7gjqrfwbT4+gXpHwEx5iMssma3mnctC7YaCimsFP70=
|
github.com/ipld/go-ipld-prime-proto v0.1.0 h1:j7gjqrfwbT4+gXpHwEx5iMssma3mnctC7YaCimsFP70=
|
||||||
@ -1062,18 +1067,8 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSo
|
|||||||
github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
|
github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
|
||||||
github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 h1:4JsnbfJzgZeRS9AWN7B9dPqn/LY/HoQTlO9gtdJTIYM=
|
github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 h1:4JsnbfJzgZeRS9AWN7B9dPqn/LY/HoQTlO9gtdJTIYM=
|
||||||
github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk=
|
github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk=
|
||||||
github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8=
|
github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0=
|
||||||
github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4=
|
github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4=
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.5.4 h1:/UOPtT/6DHPtr3TtKXBHa6g0Le0szYuI33Xc/Xpd7fQ=
|
|
||||||
github.com/libp2p/go-libp2p-yamux v0.5.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE=
|
|
||||||
github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
|
github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
|
||||||
github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
|
github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
|
||||||
github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
|
github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
|
||||||
@ -1144,19 +1139,11 @@ github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1f
|
|||||||
github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
|
github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
|
||||||
github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k=
|
github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k=
|
||||||
github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA=
|
github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA=
|
||||||
github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
|
||||||
github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
|
||||||
github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
||||||
github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
github.com/libp2p/go-yamux v1.3.6 h1:O5qcBXRcfqecvQ/My9NqDNHB3/5t58yuJYqthcKhhgE=
|
||||||
github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
|
||||||
github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
|
||||||
github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
||||||
github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
|
github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU=
|
||||||
github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
|
github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U=
|
||||||
github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI=
|
|
||||||
github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
|
|
||||||
github.com/libp2p/go-yamux/v2 v2.2.0 h1:RwtpYZ2/wVviZ5+3pjC8qdQ4TKnrak0/E01N1UWoAFU=
|
|
||||||
github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ=
|
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||||
@ -1513,6 +1500,8 @@ github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/
|
|||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||||
|
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A=
|
||||||
|
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
|
@ -62,7 +62,10 @@ func runTestCCUpgrade(t *testing.T, upgradeHeight abi.ChainEpoch) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
dh := kit.NewDealHarness(t, client, miner, miner)
|
||||||
deal, res, inPath := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{Rseed: 6})
|
deal, res, inPath := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{
|
||||||
|
Rseed: 6,
|
||||||
|
SuspendUntilCryptoeconStable: true,
|
||||||
|
})
|
||||||
outPath := dh.PerformRetrieval(context.Background(), deal, res.Root, false)
|
outPath := dh.PerformRetrieval(context.Background(), deal, res.Root, false)
|
||||||
kit.AssertFilesEqual(t, inPath, outPath)
|
kit.AssertFilesEqual(t, inPath, outPath)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package itests
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -53,9 +52,7 @@ import (
|
|||||||
// * asserts that miner B loses power
|
// * asserts that miner B loses power
|
||||||
// * asserts that miner D loses power, is inactive
|
// * asserts that miner D loses power, is inactive
|
||||||
func TestDeadlineToggling(t *testing.T) {
|
func TestDeadlineToggling(t *testing.T) {
|
||||||
if os.Getenv("LOTUS_TEST_DEADLINE_TOGGLING") != "1" {
|
kit.Expensive(t)
|
||||||
t.Skip("this takes a few minutes, set LOTUS_TEST_DEADLINE_TOGGLING=1 to run")
|
|
||||||
}
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
174
itests/deals_concurrent_test.go
Normal file
174
itests/deals_concurrent_test.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDealWithMarketAndMinerNode(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
oldDelay := policy.GetPreCommitChallengeDelay()
|
||||||
|
policy.SetPreCommitChallengeDelay(5)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
policy.SetPreCommitChallengeDelay(oldDelay)
|
||||||
|
})
|
||||||
|
|
||||||
|
// For these tests where the block time is artificially short, just use
|
||||||
|
// a deal start epoch that is guaranteed to be far enough in the future
|
||||||
|
// so that the deal starts sealing in time
|
||||||
|
startEpoch := abi.ChainEpoch(2 << 12)
|
||||||
|
|
||||||
|
runTest := func(t *testing.T, n int, fastRetrieval bool, carExport bool) {
|
||||||
|
api.RunningNodeType = api.NodeMiner // TODO(anteva): fix me
|
||||||
|
|
||||||
|
client, main, market, _ := kit.EnsembleWithMinerAndMarketNodes(t, kit.ThroughRPC())
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, client, main, market)
|
||||||
|
|
||||||
|
runConcurrentDeals(t, dh, fullDealCyclesOpts{
|
||||||
|
n: n,
|
||||||
|
fastRetrieval: fastRetrieval,
|
||||||
|
carExport: carExport,
|
||||||
|
startEpoch: startEpoch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add 2, 4, 8, more when this graphsync issue is fixed: https://github.com/ipfs/go-graphsync/issues/175#
|
||||||
|
cycles := []int{1}
|
||||||
|
for _, n := range cycles {
|
||||||
|
n := n
|
||||||
|
ns := fmt.Sprintf("%d", n)
|
||||||
|
t.Run(ns+"-fastretrieval-CAR", func(t *testing.T) { runTest(t, n, true, true) })
|
||||||
|
//t.Run(ns+"-fastretrieval-NoCAR", func(t *testing.T) { runTest(t, n, true, false) })
|
||||||
|
//t.Run(ns+"-stdretrieval-CAR", func(t *testing.T) { runTest(t, n, true, false) })
|
||||||
|
//t.Run(ns+"-stdretrieval-NoCAR", func(t *testing.T) { runTest(t, n, false, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDealCyclesConcurrent(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
blockTime := 10 * time.Millisecond
|
||||||
|
|
||||||
|
// For these tests where the block time is artificially short, just use
|
||||||
|
// a deal start epoch that is guaranteed to be far enough in the future
|
||||||
|
// so that the deal starts sealing in time
|
||||||
|
startEpoch := abi.ChainEpoch(2 << 12)
|
||||||
|
|
||||||
|
runTest := func(t *testing.T, n int, fastRetrieval bool, carExport bool) {
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
||||||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
|
dh := kit.NewDealHarness(t, client, miner, miner)
|
||||||
|
|
||||||
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{
|
||||||
|
N: n,
|
||||||
|
FastRetrieval: fastRetrieval,
|
||||||
|
CarExport: carExport,
|
||||||
|
StartEpoch: startEpoch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add 2, 4, 8, more when this graphsync issue is fixed: https://github.com/ipfs/go-graphsync/issues/175#
|
||||||
|
cycles := []int{1}
|
||||||
|
for _, n := range cycles {
|
||||||
|
n := n
|
||||||
|
ns := fmt.Sprintf("%d", n)
|
||||||
|
t.Run(ns+"-fastretrieval-CAR", func(t *testing.T) { runTest(t, n, true, true) })
|
||||||
|
t.Run(ns+"-fastretrieval-NoCAR", func(t *testing.T) { runTest(t, n, true, false) })
|
||||||
|
t.Run(ns+"-stdretrieval-CAR", func(t *testing.T) { runTest(t, n, true, false) })
|
||||||
|
t.Run(ns+"-stdretrieval-NoCAR", func(t *testing.T) { runTest(t, n, false, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimultenousTransferLimit(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
blockTime := 10 * time.Millisecond
|
||||||
|
|
||||||
|
// For these tests where the block time is artificially short, just use
|
||||||
|
// a deal start epoch that is guaranteed to be far enough in the future
|
||||||
|
// so that the deal starts sealing in time
|
||||||
|
startEpoch := abi.ChainEpoch(2 << 12)
|
||||||
|
|
||||||
|
runTest := func(t *testing.T) {
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ConstructorOpts(
|
||||||
|
node.ApplyIf(node.IsType(repo.StorageMiner), node.Override(new(dtypes.StagingGraphsync), modules.StagingGraphsync(2))),
|
||||||
|
))
|
||||||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
|
dh := kit.NewDealHarness(t, client, miner)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
du, err := miner.MarketDataTransferUpdates(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var maxOngoing int
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ongoing := map[datatransfer.TransferID]struct{}{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case u := <-du:
|
||||||
|
t.Logf("%d - %s", u.TransferID, datatransfer.Statuses[u.Status])
|
||||||
|
if u.Status == datatransfer.Ongoing {
|
||||||
|
ongoing[u.TransferID] = struct{}{}
|
||||||
|
} else {
|
||||||
|
delete(ongoing, u.TransferID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ongoing) > maxOngoing {
|
||||||
|
maxOngoing = len(ongoing)
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{
|
||||||
|
N: 1, // TODO: set to 20 after https://github.com/ipfs/go-graphsync/issues/175 is fixed
|
||||||
|
FastRetrieval: true,
|
||||||
|
StartEpoch: startEpoch,
|
||||||
|
})
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
require.LessOrEqual(t, maxOngoing, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest(t)
|
||||||
|
}
|
101
itests/deals_offline_test.go
Normal file
101
itests/deals_offline_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOfflineDealFlow(t *testing.T) {
|
||||||
|
blocktime := 10 * time.Millisecond
|
||||||
|
|
||||||
|
// For these tests where the block time is artificially short, just use
|
||||||
|
// a deal start epoch that is guaranteed to be far enough in the future
|
||||||
|
// so that the deal starts sealing in time
|
||||||
|
startEpoch := abi.ChainEpoch(2 << 12)
|
||||||
|
|
||||||
|
runTest := func(t *testing.T, fastRet bool) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
||||||
|
ens.InterconnectAll().BeginMining(blocktime)
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, client, miner)
|
||||||
|
|
||||||
|
// Create a random file and import on the client.
|
||||||
|
res, inFile := client.CreateImportFile(ctx, 1, 0)
|
||||||
|
|
||||||
|
// Get the piece size and commP
|
||||||
|
rootCid := res.Root
|
||||||
|
pieceInfo, err := client.ClientDealPieceCID(ctx, rootCid)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Log("FILE CID:", rootCid)
|
||||||
|
|
||||||
|
// Create a storage deal with the miner
|
||||||
|
maddr, err := miner.ActorAddress(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
addr, err := client.WalletDefaultAddress(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Manual storage deal (offline deal)
|
||||||
|
dataRef := &storagemarket.DataRef{
|
||||||
|
TransferType: storagemarket.TTManual,
|
||||||
|
Root: rootCid,
|
||||||
|
PieceCid: &pieceInfo.PieceCID,
|
||||||
|
PieceSize: pieceInfo.PieceSize.Unpadded(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proposalCid, err := client.ClientStartDeal(ctx, &api.StartDealParams{
|
||||||
|
Data: dataRef,
|
||||||
|
Wallet: addr,
|
||||||
|
Miner: maddr,
|
||||||
|
EpochPrice: types.NewInt(1000000),
|
||||||
|
DealStartEpoch: startEpoch,
|
||||||
|
MinBlocksDuration: uint64(build.MinDealDuration),
|
||||||
|
FastRetrieval: fastRet,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Wait for the deal to reach StorageDealCheckForAcceptance on the client
|
||||||
|
cd, err := client.ClientGetDealInfo(ctx, *proposalCid)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Eventually(t, func() bool {
|
||||||
|
cd, _ := client.ClientGetDealInfo(ctx, *proposalCid)
|
||||||
|
return cd.State == storagemarket.StorageDealCheckForAcceptance
|
||||||
|
}, 30*time.Second, 1*time.Second, "actual deal status is %s", storagemarket.DealStates[cd.State])
|
||||||
|
|
||||||
|
// Create a CAR file from the raw file
|
||||||
|
carFileDir := t.TempDir()
|
||||||
|
carFilePath := filepath.Join(carFileDir, "out.car")
|
||||||
|
err = client.ClientGenCar(ctx, api.FileRef{Path: inFile}, carFilePath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Import the CAR file on the miner - this is the equivalent to
|
||||||
|
// transferring the file across the wire in a normal (non-offline) deal
|
||||||
|
err = miner.DealsImportData(ctx, *proposalCid, carFilePath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Wait for the deal to be published
|
||||||
|
dh.WaitDealPublished(ctx, proposalCid)
|
||||||
|
|
||||||
|
t.Logf("deal published, retrieving")
|
||||||
|
|
||||||
|
// Retrieve the deal
|
||||||
|
outFile := dh.PerformRetrieval(ctx, proposalCid, rootCid, false)
|
||||||
|
|
||||||
|
kit.AssertFilesEqual(t, inFile, outFile)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("stdretrieval", func(t *testing.T) { runTest(t, false) })
|
||||||
|
t.Run("fastretrieval", func(t *testing.T) { runTest(t, true) })
|
||||||
|
}
|
61
itests/deals_power_test.go
Normal file
61
itests/deals_power_test.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFirstDealEnablesMining(t *testing.T) {
|
||||||
|
// test making a deal with a fresh miner, and see if it starts to mine.
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
var (
|
||||||
|
client kit.TestFullNode
|
||||||
|
genMiner kit.TestMiner // bootstrap
|
||||||
|
provider kit.TestMiner // no sectors, will need to create one
|
||||||
|
)
|
||||||
|
|
||||||
|
ens := kit.NewEnsemble(t, kit.MockProofs())
|
||||||
|
ens.FullNode(&client)
|
||||||
|
ens.Miner(&genMiner, &client)
|
||||||
|
ens.Miner(&provider, &client, kit.PresealSectors(0))
|
||||||
|
ens.Start().InterconnectAll().BeginMining(50 * time.Millisecond)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, &client, &provider)
|
||||||
|
|
||||||
|
ref, _ := client.CreateImportFile(ctx, 5, 0)
|
||||||
|
|
||||||
|
t.Log("FILE CID:", ref.Root)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// start a goroutine to monitor head changes from the client
|
||||||
|
// once the provider has mined a block, thanks to the power acquired from the deal,
|
||||||
|
// we pass the test.
|
||||||
|
providerMined := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_ = client.WaitTillChain(ctx, kit.BlockMinedBy(provider.ActorAddr))
|
||||||
|
close(providerMined)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// now perform the deal.
|
||||||
|
deal := dh.StartDeal(ctx, ref.Root, false, 0)
|
||||||
|
|
||||||
|
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
dh.WaitDealSealed(ctx, deal, false, false, nil)
|
||||||
|
|
||||||
|
<-providerMined
|
||||||
|
}
|
131
itests/deals_pricing_test.go
Normal file
131
itests/deals_pricing_test.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQuotePriceForUnsealedRetrieval(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
blocktime = time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t)
|
||||||
|
ens.InterconnectAll().BeginMining(blocktime)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ppb = int64(1)
|
||||||
|
unsealPrice = int64(77)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set unsealed price to non-zero
|
||||||
|
ask, err := miner.MarketGetRetrievalAsk(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ask.PricePerByte = abi.NewTokenAmount(ppb)
|
||||||
|
ask.UnsealPrice = abi.NewTokenAmount(unsealPrice)
|
||||||
|
err = miner.MarketSetRetrievalAsk(ctx, ask)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, client, miner)
|
||||||
|
|
||||||
|
deal1, res1, _ := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{Rseed: 6})
|
||||||
|
|
||||||
|
// one more storage deal for the same data
|
||||||
|
_, res2, _ := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{Rseed: 6})
|
||||||
|
require.Equal(t, res1.Root, res2.Root)
|
||||||
|
|
||||||
|
// Retrieval
|
||||||
|
dealInfo, err := client.ClientGetDealInfo(ctx, *deal1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// fetch quote -> zero for unsealed price since unsealed file already exists.
|
||||||
|
offers, err := client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, offers, 2)
|
||||||
|
require.Equal(t, offers[0], offers[1])
|
||||||
|
require.Equal(t, uint64(0), offers[0].UnsealPrice.Uint64())
|
||||||
|
require.Equal(t, dealInfo.Size*uint64(ppb), offers[0].MinPrice.Uint64())
|
||||||
|
|
||||||
|
// remove ONLY one unsealed file
|
||||||
|
ss, err := miner.StorageList(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = miner.SectorsList(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
iLoop:
|
||||||
|
for storeID, sd := range ss {
|
||||||
|
for _, sector := range sd {
|
||||||
|
err := miner.StorageDropSector(ctx, storeID, sector.SectorID, storiface.FTUnsealed)
|
||||||
|
require.NoError(t, err)
|
||||||
|
break iLoop // remove ONLY one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get retrieval quote -> zero for unsealed price as unsealed file exists.
|
||||||
|
offers, err = client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, offers, 2)
|
||||||
|
require.Equal(t, offers[0], offers[1])
|
||||||
|
require.Equal(t, uint64(0), offers[0].UnsealPrice.Uint64())
|
||||||
|
require.Equal(t, dealInfo.Size*uint64(ppb), offers[0].MinPrice.Uint64())
|
||||||
|
|
||||||
|
// remove the other unsealed file as well
|
||||||
|
ss, err = miner.StorageList(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = miner.SectorsList(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for storeID, sd := range ss {
|
||||||
|
for _, sector := range sd {
|
||||||
|
require.NoError(t, miner.StorageDropSector(ctx, storeID, sector.SectorID, storiface.FTUnsealed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch quote -> non-zero for unseal price as we no more unsealed files.
|
||||||
|
offers, err = client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, offers, 2)
|
||||||
|
require.Equal(t, offers[0], offers[1])
|
||||||
|
require.Equal(t, uint64(unsealPrice), offers[0].UnsealPrice.Uint64())
|
||||||
|
total := (dealInfo.Size * uint64(ppb)) + uint64(unsealPrice)
|
||||||
|
require.Equal(t, total, offers[0].MinPrice.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZeroPricePerByteRetrieval(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
var (
|
||||||
|
blockTime = 10 * time.Millisecond
|
||||||
|
startEpoch = abi.ChainEpoch(2 << 12)
|
||||||
|
)
|
||||||
|
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
||||||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
ask, err := miner.MarketGetRetrievalAsk(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ask.PricePerByte = abi.NewTokenAmount(0)
|
||||||
|
err = miner.MarketSetRetrievalAsk(ctx, ask)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, client, miner)
|
||||||
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{
|
||||||
|
N: 1,
|
||||||
|
StartEpoch: startEpoch,
|
||||||
|
})
|
||||||
|
}
|
108
itests/deals_publish_test.go
Normal file
108
itests/deals_publish_test.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/filecoin-project/lotus/markets/storageadapter"
|
||||||
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPublishDealsBatching(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
publishPeriod = 10 * time.Second
|
||||||
|
maxDealsPerMsg = uint64(2) // Set max deals per publish deals message to 2
|
||||||
|
startEpoch = abi.ChainEpoch(2 << 12)
|
||||||
|
)
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
opts := node.Override(new(*storageadapter.DealPublisher),
|
||||||
|
storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{
|
||||||
|
Period: publishPeriod,
|
||||||
|
MaxDealsPerMsg: maxDealsPerMsg,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ConstructorOpts(opts))
|
||||||
|
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, client, miner)
|
||||||
|
|
||||||
|
// Starts a deal and waits until it's published
|
||||||
|
runDealTillPublish := func(rseed int) {
|
||||||
|
res, _ := client.CreateImportFile(ctx, rseed, 0)
|
||||||
|
|
||||||
|
upds, err := client.ClientGetDealUpdates(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dh.StartDeal(ctx, res.Root, false, startEpoch)
|
||||||
|
|
||||||
|
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for upd := range upds {
|
||||||
|
if upd.DataRef.Root == res.Root && upd.State == storagemarket.StorageDealAwaitingPreCommit {
|
||||||
|
done <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run three deals in parallel
|
||||||
|
done := make(chan struct{}, maxDealsPerMsg+1)
|
||||||
|
for rseed := 1; rseed <= 3; rseed++ {
|
||||||
|
rseed := rseed
|
||||||
|
go func() {
|
||||||
|
runDealTillPublish(rseed)
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for two of the deals to be published
|
||||||
|
for i := 0; i < int(maxDealsPerMsg); i++ {
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect a single PublishStorageDeals message that includes the first two deals
|
||||||
|
msgCids, err := client.StateListMessages(ctx, &api.MessageMatch{To: market.Address}, types.EmptyTSK, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
count := 0
|
||||||
|
for _, msgCid := range msgCids {
|
||||||
|
msg, err := client.ChainGetMessage(ctx, msgCid)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if msg.Method == market.Methods.PublishStorageDeals {
|
||||||
|
count++
|
||||||
|
var pubDealsParams market2.PublishStorageDealsParams
|
||||||
|
err = pubDealsParams.UnmarshalCBOR(bytes.NewReader(msg.Params))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, pubDealsParams.Deals, int(maxDealsPerMsg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.Equal(t, 1, count)
|
||||||
|
|
||||||
|
// The third deal should be published once the publish period expires.
|
||||||
|
// Allow a little padding as it takes a moment for the state change to
|
||||||
|
// be noticed by the client.
|
||||||
|
padding := 10 * time.Second
|
||||||
|
select {
|
||||||
|
case <-time.After(publishPeriod + padding):
|
||||||
|
require.Fail(t, "Expected 3rd deal to be published once publish period elapsed")
|
||||||
|
case <-done: // Success
|
||||||
|
}
|
||||||
|
}
|
@ -1,153 +1,13 @@
|
|||||||
package itests
|
package itests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
|
||||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
"github.com/filecoin-project/lotus/markets/storageadapter"
|
|
||||||
"github.com/filecoin-project/lotus/node"
|
|
||||||
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDealWithMarketAndMinerNode(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
|
||||||
|
|
||||||
// For these tests where the block time is artificially short, just use
|
|
||||||
// a deal start epoch that is guaranteed to be far enough in the future
|
|
||||||
// so that the deal starts sealing in time
|
|
||||||
startEpoch := abi.ChainEpoch(2 << 12)
|
|
||||||
|
|
||||||
runTest := func(t *testing.T, n int, fastRetrieval bool, carExport bool) {
|
|
||||||
api.RunningNodeType = api.NodeMiner // TODO(anteva): fix me
|
|
||||||
|
|
||||||
client, main, market, _ := kit.EnsembleWithMinerAndMarketNodes(t, kit.ThroughRPC())
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, main, market)
|
|
||||||
|
|
||||||
runConcurrentDeals(t, dh, fullDealCyclesOpts{
|
|
||||||
n: n,
|
|
||||||
fastRetrieval: fastRetrieval,
|
|
||||||
carExport: carExport,
|
|
||||||
startEpoch: startEpoch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add 2, 4, 8, more when this graphsync issue is fixed: https://github.com/ipfs/go-graphsync/issues/175#
|
|
||||||
cycles := []int{1}
|
|
||||||
for _, n := range cycles {
|
|
||||||
n := n
|
|
||||||
ns := fmt.Sprintf("%d", n)
|
|
||||||
t.Run(ns+"-fastretrieval-CAR", func(t *testing.T) { runTest(t, n, true, true) })
|
|
||||||
//t.Run(ns+"-fastretrieval-NoCAR", func(t *testing.T) { runTest(t, n, true, false) })
|
|
||||||
//t.Run(ns+"-stdretrieval-CAR", func(t *testing.T) { runTest(t, n, true, false) })
|
|
||||||
//t.Run(ns+"-stdretrieval-NoCAR", func(t *testing.T) { runTest(t, n, false, false) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDealCyclesConcurrent(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
|
||||||
|
|
||||||
blockTime := 10 * time.Millisecond
|
|
||||||
|
|
||||||
// For these tests where the block time is artificially short, just use
|
|
||||||
// a deal start epoch that is guaranteed to be far enough in the future
|
|
||||||
// so that the deal starts sealing in time
|
|
||||||
startEpoch := abi.ChainEpoch(2 << 12)
|
|
||||||
|
|
||||||
runTest := func(t *testing.T, n int, fastRetrieval bool, carExport bool) {
|
|
||||||
api.RunningNodeType = api.NodeMiner // TODO(anteva): fix me
|
|
||||||
|
|
||||||
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
|
||||||
ens.InterconnectAll().BeginMining(blockTime)
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
|
||||||
|
|
||||||
runConcurrentDeals(t, dh, fullDealCyclesOpts{
|
|
||||||
n: n,
|
|
||||||
fastRetrieval: fastRetrieval,
|
|
||||||
carExport: carExport,
|
|
||||||
startEpoch: startEpoch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add 2, 4, 8, more when this graphsync issue is fixed: https://github.com/ipfs/go-graphsync/issues/175#
|
|
||||||
cycles := []int{1}
|
|
||||||
for _, n := range cycles {
|
|
||||||
n := n
|
|
||||||
ns := fmt.Sprintf("%d", n)
|
|
||||||
t.Run(ns+"-fastretrieval-CAR", func(t *testing.T) { runTest(t, n, true, true) })
|
|
||||||
t.Run(ns+"-fastretrieval-NoCAR", func(t *testing.T) { runTest(t, n, true, false) })
|
|
||||||
t.Run(ns+"-stdretrieval-CAR", func(t *testing.T) { runTest(t, n, true, false) })
|
|
||||||
t.Run(ns+"-stdretrieval-NoCAR", func(t *testing.T) { runTest(t, n, false, false) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fullDealCyclesOpts struct {
|
|
||||||
n int
|
|
||||||
fastRetrieval bool
|
|
||||||
carExport bool
|
|
||||||
startEpoch abi.ChainEpoch
|
|
||||||
}
|
|
||||||
|
|
||||||
func runConcurrentDeals(t *testing.T, dh *kit.DealHarness, opts fullDealCyclesOpts) {
|
|
||||||
errgrp, _ := errgroup.WithContext(context.Background())
|
|
||||||
for i := 0; i < opts.n; i++ {
|
|
||||||
i := i
|
|
||||||
errgrp.Go(func() (err error) {
|
|
||||||
defer func() {
|
|
||||||
// This is necessary because golang can't deal with test
|
|
||||||
// failures being reported from children goroutines ¯\_(ツ)_/¯
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = fmt.Errorf("deal failed: %s", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
deal, res, inPath := dh.MakeOnlineDeal(context.Background(), kit.MakeFullDealParams{
|
|
||||||
Rseed: 5 + i,
|
|
||||||
FastRet: opts.fastRetrieval,
|
|
||||||
StartEpoch: opts.startEpoch,
|
|
||||||
})
|
|
||||||
outPath := dh.PerformRetrieval(context.Background(), deal, res.Root, opts.carExport)
|
|
||||||
kit.AssertFilesEqual(t, inPath, outPath)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
require.NoError(t, errgrp.Wait())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDealsWithSealingAndRPC(t *testing.T) {
|
func TestDealsWithSealingAndRPC(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping test in short mode")
|
t.Skip("skipping test in short mode")
|
||||||
@ -168,385 +28,15 @@ func TestDealsWithSealingAndRPC(t *testing.T) {
|
|||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
dh := kit.NewDealHarness(t, client, miner, miner)
|
||||||
|
|
||||||
t.Run("stdretrieval", func(t *testing.T) {
|
t.Run("stdretrieval", func(t *testing.T) {
|
||||||
runConcurrentDeals(t, dh, fullDealCyclesOpts{n: 1})
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{N: 1})
|
||||||
})
|
})
|
||||||
|
|
||||||
//t.Run("fastretrieval", func(t *testing.T) {
|
t.Run("fastretrieval", func(t *testing.T) {
|
||||||
//runConcurrentDeals(t, dh, fullDealCyclesOpts{n: 1, fastRetrieval: true})
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{N: 1, FastRetrieval: true})
|
||||||
//})
|
|
||||||
|
|
||||||
//t.Run("fastretrieval-twodeals-sequential", func(t *testing.T) {
|
|
||||||
//runConcurrentDeals(t, dh, fullDealCyclesOpts{n: 1, fastRetrieval: true})
|
|
||||||
//runConcurrentDeals(t, dh, fullDealCyclesOpts{n: 1, fastRetrieval: true})
|
|
||||||
//})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuotePriceForUnsealedRetrieval(t *testing.T) {
|
|
||||||
var (
|
|
||||||
ctx = context.Background()
|
|
||||||
blocktime = time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
|
||||||
|
|
||||||
client, miner, ens := kit.EnsembleMinimal(t)
|
|
||||||
ens.InterconnectAll().BeginMining(blocktime)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ppb = int64(1)
|
|
||||||
unsealPrice = int64(77)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set unsealed price to non-zero
|
|
||||||
ask, err := miner.MarketGetRetrievalAsk(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
ask.PricePerByte = abi.NewTokenAmount(ppb)
|
|
||||||
ask.UnsealPrice = abi.NewTokenAmount(unsealPrice)
|
|
||||||
err = miner.MarketSetRetrievalAsk(ctx, ask)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
|
||||||
|
|
||||||
deal1, res1, _ := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{Rseed: 6})
|
|
||||||
|
|
||||||
// one more storage deal for the same data
|
|
||||||
_, res2, _ := dh.MakeOnlineDeal(ctx, kit.MakeFullDealParams{Rseed: 6})
|
|
||||||
require.Equal(t, res1.Root, res2.Root)
|
|
||||||
|
|
||||||
// Retrieval
|
|
||||||
dealInfo, err := client.ClientGetDealInfo(ctx, *deal1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// fetch quote -> zero for unsealed price since unsealed file already exists.
|
|
||||||
offers, err := client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, offers, 2)
|
|
||||||
require.Equal(t, offers[0], offers[1])
|
|
||||||
require.Equal(t, uint64(0), offers[0].UnsealPrice.Uint64())
|
|
||||||
require.Equal(t, dealInfo.Size*uint64(ppb), offers[0].MinPrice.Uint64())
|
|
||||||
|
|
||||||
// remove ONLY one unsealed file
|
|
||||||
ss, err := miner.StorageList(context.Background())
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, err = miner.SectorsList(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
iLoop:
|
|
||||||
for storeID, sd := range ss {
|
|
||||||
for _, sector := range sd {
|
|
||||||
err := miner.StorageDropSector(ctx, storeID, sector.SectorID, storiface.FTUnsealed)
|
|
||||||
require.NoError(t, err)
|
|
||||||
break iLoop // remove ONLY one
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get retrieval quote -> zero for unsealed price as unsealed file exists.
|
|
||||||
offers, err = client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, offers, 2)
|
|
||||||
require.Equal(t, offers[0], offers[1])
|
|
||||||
require.Equal(t, uint64(0), offers[0].UnsealPrice.Uint64())
|
|
||||||
require.Equal(t, dealInfo.Size*uint64(ppb), offers[0].MinPrice.Uint64())
|
|
||||||
|
|
||||||
// remove the other unsealed file as well
|
|
||||||
ss, err = miner.StorageList(context.Background())
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, err = miner.SectorsList(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
for storeID, sd := range ss {
|
|
||||||
for _, sector := range sd {
|
|
||||||
require.NoError(t, miner.StorageDropSector(ctx, storeID, sector.SectorID, storiface.FTUnsealed))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch quote -> non-zero for unseal price as we no more unsealed files.
|
|
||||||
offers, err = client.ClientFindData(ctx, res1.Root, &dealInfo.PieceCID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, offers, 2)
|
|
||||||
require.Equal(t, offers[0], offers[1])
|
|
||||||
require.Equal(t, uint64(unsealPrice), offers[0].UnsealPrice.Uint64())
|
|
||||||
total := (dealInfo.Size * uint64(ppb)) + uint64(unsealPrice)
|
|
||||||
require.Equal(t, total, offers[0].MinPrice.Uint64())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublishDealsBatching(t *testing.T) {
|
|
||||||
var (
|
|
||||||
ctx = context.Background()
|
|
||||||
publishPeriod = 10 * time.Second
|
|
||||||
maxDealsPerMsg = uint64(2) // Set max deals per publish deals message to 2
|
|
||||||
startEpoch = abi.ChainEpoch(2 << 12)
|
|
||||||
)
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
t.Run("fastretrieval-twodeals-sequential", func(t *testing.T) {
|
||||||
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{N: 1, FastRetrieval: true})
|
||||||
opts := node.Override(new(*storageadapter.DealPublisher),
|
dh.RunConcurrentDeals(kit.RunConcurrentDealsOpts{N: 1, FastRetrieval: true})
|
||||||
storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{
|
|
||||||
Period: publishPeriod,
|
|
||||||
MaxDealsPerMsg: maxDealsPerMsg,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ConstructorOpts(opts))
|
|
||||||
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
|
||||||
|
|
||||||
// Starts a deal and waits until it's published
|
|
||||||
runDealTillPublish := func(rseed int) {
|
|
||||||
res, _ := client.CreateImportFile(ctx, rseed, 0)
|
|
||||||
|
|
||||||
upds, err := client.ClientGetDealUpdates(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
dh.StartDeal(ctx, res.Root, false, startEpoch)
|
|
||||||
|
|
||||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
for upd := range upds {
|
|
||||||
if upd.DataRef.Root == res.Root && upd.State == storagemarket.StorageDealAwaitingPreCommit {
|
|
||||||
done <- struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run three deals in parallel
|
|
||||||
done := make(chan struct{}, maxDealsPerMsg+1)
|
|
||||||
for rseed := 1; rseed <= 3; rseed++ {
|
|
||||||
rseed := rseed
|
|
||||||
go func() {
|
|
||||||
runDealTillPublish(rseed)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for two of the deals to be published
|
|
||||||
for i := 0; i < int(maxDealsPerMsg); i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expect a single PublishStorageDeals message that includes the first two deals
|
|
||||||
msgCids, err := client.StateListMessages(ctx, &api.MessageMatch{To: market.Address}, types.EmptyTSK, 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
count := 0
|
|
||||||
for _, msgCid := range msgCids {
|
|
||||||
msg, err := client.ChainGetMessage(ctx, msgCid)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
if msg.Method == market.Methods.PublishStorageDeals {
|
|
||||||
count++
|
|
||||||
var pubDealsParams market2.PublishStorageDealsParams
|
|
||||||
err = pubDealsParams.UnmarshalCBOR(bytes.NewReader(msg.Params))
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, pubDealsParams.Deals, int(maxDealsPerMsg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.Equal(t, 1, count)
|
|
||||||
|
|
||||||
// The third deal should be published once the publish period expires.
|
|
||||||
// Allow a little padding as it takes a moment for the state change to
|
|
||||||
// be noticed by the client.
|
|
||||||
padding := 10 * time.Second
|
|
||||||
select {
|
|
||||||
case <-time.After(publishPeriod + padding):
|
|
||||||
require.Fail(t, "Expected 3rd deal to be published once publish period elapsed")
|
|
||||||
case <-done: // Success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFirstDealEnablesMining(t *testing.T) {
|
|
||||||
// test making a deal with a fresh miner, and see if it starts to mine.
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
|
||||||
|
|
||||||
var (
|
|
||||||
client kit.TestFullNode
|
|
||||||
genMiner kit.TestMiner // bootstrap
|
|
||||||
provider kit.TestMiner // no sectors, will need to create one
|
|
||||||
)
|
|
||||||
|
|
||||||
ens := kit.NewEnsemble(t, kit.MockProofs())
|
|
||||||
ens.FullNode(&client)
|
|
||||||
ens.Miner(&genMiner, &client, kit.WithAllSubsystems())
|
|
||||||
ens.Miner(&provider, &client, kit.WithAllSubsystems(), kit.PresealSectors(0))
|
|
||||||
ens.Start().InterconnectAll().BeginMining(50 * time.Millisecond)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, &client, &provider, &provider)
|
|
||||||
|
|
||||||
ref, _ := client.CreateImportFile(ctx, 5, 0)
|
|
||||||
|
|
||||||
t.Log("FILE CID:", ref.Root)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// start a goroutine to monitor head changes from the client
|
|
||||||
// once the provider has mined a block, thanks to the power acquired from the deal,
|
|
||||||
// we pass the test.
|
|
||||||
providerMined := make(chan struct{})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
_ = client.WaitTillChain(ctx, kit.BlockMinedBy(provider.ActorAddr))
|
|
||||||
close(providerMined)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// now perform the deal.
|
|
||||||
deal := dh.StartDeal(ctx, ref.Root, false, 0)
|
|
||||||
|
|
||||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
dh.WaitDealSealed(ctx, deal, false, false, nil)
|
|
||||||
|
|
||||||
<-providerMined
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOfflineDealFlow(t *testing.T) {
|
|
||||||
blocktime := 10 * time.Millisecond
|
|
||||||
|
|
||||||
// For these tests where the block time is artificially short, just use
|
|
||||||
// a deal start epoch that is guaranteed to be far enough in the future
|
|
||||||
// so that the deal starts sealing in time
|
|
||||||
startEpoch := abi.ChainEpoch(2 << 12)
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
|
||||||
|
|
||||||
runTest := func(t *testing.T, fastRet bool) {
|
|
||||||
ctx := context.Background()
|
|
||||||
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
|
||||||
ens.InterconnectAll().BeginMining(blocktime)
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
|
||||||
|
|
||||||
// Create a random file and import on the client.
|
|
||||||
res, inFile := client.CreateImportFile(ctx, 1, 0)
|
|
||||||
|
|
||||||
// Get the piece size and commP
|
|
||||||
rootCid := res.Root
|
|
||||||
pieceInfo, err := client.ClientDealPieceCID(ctx, rootCid)
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Log("FILE CID:", rootCid)
|
|
||||||
|
|
||||||
// Create a storage deal with the miner
|
|
||||||
maddr, err := miner.ActorAddress(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
addr, err := client.WalletDefaultAddress(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Manual storage deal (offline deal)
|
|
||||||
dataRef := &storagemarket.DataRef{
|
|
||||||
TransferType: storagemarket.TTManual,
|
|
||||||
Root: rootCid,
|
|
||||||
PieceCid: &pieceInfo.PieceCID,
|
|
||||||
PieceSize: pieceInfo.PieceSize.Unpadded(),
|
|
||||||
}
|
|
||||||
|
|
||||||
proposalCid, err := client.ClientStartDeal(ctx, &api.StartDealParams{
|
|
||||||
Data: dataRef,
|
|
||||||
Wallet: addr,
|
|
||||||
Miner: maddr,
|
|
||||||
EpochPrice: types.NewInt(1000000),
|
|
||||||
DealStartEpoch: startEpoch,
|
|
||||||
MinBlocksDuration: uint64(build.MinDealDuration),
|
|
||||||
FastRetrieval: fastRet,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Wait for the deal to reach StorageDealCheckForAcceptance on the client
|
|
||||||
cd, err := client.ClientGetDealInfo(ctx, *proposalCid)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Eventually(t, func() bool {
|
|
||||||
cd, _ := client.ClientGetDealInfo(ctx, *proposalCid)
|
|
||||||
return cd.State == storagemarket.StorageDealCheckForAcceptance
|
|
||||||
}, 30*time.Second, 1*time.Second, "actual deal status is %s", storagemarket.DealStates[cd.State])
|
|
||||||
|
|
||||||
// Create a CAR file from the raw file
|
|
||||||
carFileDir := t.TempDir()
|
|
||||||
carFilePath := filepath.Join(carFileDir, "out.car")
|
|
||||||
err = client.ClientGenCar(ctx, api.FileRef{Path: inFile}, carFilePath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Import the CAR file on the miner - this is the equivalent to
|
|
||||||
// transferring the file across the wire in a normal (non-offline) deal
|
|
||||||
err = miner.DealsImportData(ctx, *proposalCid, carFilePath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Wait for the deal to be published
|
|
||||||
dh.WaitDealPublished(ctx, proposalCid)
|
|
||||||
|
|
||||||
t.Logf("deal published, retrieving")
|
|
||||||
|
|
||||||
// Retrieve the deal
|
|
||||||
outFile := dh.PerformRetrieval(ctx, proposalCid, rootCid, false)
|
|
||||||
|
|
||||||
kit.AssertFilesEqual(t, inFile, outFile)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("stdretrieval", func(t *testing.T) { runTest(t, false) })
|
|
||||||
t.Run("fastretrieval", func(t *testing.T) { runTest(t, true) })
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestZeroPricePerByteRetrieval(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
|
||||||
policy.SetPreCommitChallengeDelay(5)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
|
||||||
})
|
|
||||||
|
|
||||||
kit.QuietMiningLogs()
|
|
||||||
|
|
||||||
var (
|
|
||||||
blockTime = 10 * time.Millisecond
|
|
||||||
startEpoch = abi.ChainEpoch(2 << 12)
|
|
||||||
)
|
|
||||||
|
|
||||||
client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
|
||||||
ens.InterconnectAll().BeginMining(blockTime)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
ask, err := miner.MarketGetRetrievalAsk(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ask.PricePerByte = abi.NewTokenAmount(0)
|
|
||||||
err = miner.MarketSetRetrievalAsk(ctx, ask)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner, miner)
|
|
||||||
runConcurrentDeals(t, dh, fullDealCyclesOpts{
|
|
||||||
n: 1,
|
|
||||||
startEpoch: startEpoch,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,25 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/xerrors"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-jsonrpc"
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/ipfs/go-cid"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/api/client"
|
"github.com/filecoin-project/lotus/api/client"
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/cli"
|
"github.com/filecoin-project/lotus/cli"
|
||||||
"github.com/filecoin-project/lotus/gateway"
|
"github.com/filecoin-project/lotus/gateway"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/filecoin-project/lotus/itests/multisig"
|
||||||
"github.com/filecoin-project/lotus/node"
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
|
||||||
init2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/init"
|
init2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/init"
|
||||||
multisig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig"
|
multisig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -175,7 +175,7 @@ func TestGatewayMsigCLI(t *testing.T) {
|
|||||||
nodes := startNodesWithFunds(ctx, t, blocktime, maxLookbackCap, maxStateWaitLookbackLimit)
|
nodes := startNodesWithFunds(ctx, t, blocktime, maxLookbackCap, maxStateWaitLookbackLimit)
|
||||||
|
|
||||||
lite := nodes.lite
|
lite := nodes.lite
|
||||||
runMultisigTests(t, lite)
|
multisig.RunMultisigTests(t, lite)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatewayDealFlow(t *testing.T) {
|
func TestGatewayDealFlow(t *testing.T) {
|
||||||
|
@ -2,26 +2,27 @@ package kit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
|
||||||
files "github.com/ipfs/go-ipfs-files"
|
|
||||||
"github.com/ipld/go-car"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"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"
|
||||||
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
files "github.com/ipfs/go-ipfs-files"
|
||||||
ipld "github.com/ipfs/go-ipld-format"
|
ipld "github.com/ipfs/go-ipld-format"
|
||||||
dag "github.com/ipfs/go-merkledag"
|
dag "github.com/ipfs/go-merkledag"
|
||||||
dstest "github.com/ipfs/go-merkledag/test"
|
dstest "github.com/ipfs/go-merkledag/test"
|
||||||
unixfile "github.com/ipfs/go-unixfs/file"
|
unixfile "github.com/ipfs/go-unixfs/file"
|
||||||
|
"github.com/ipld/go-car"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DealHarness struct {
|
type DealHarness struct {
|
||||||
@ -35,6 +36,30 @@ type MakeFullDealParams struct {
|
|||||||
Rseed int
|
Rseed int
|
||||||
FastRet bool
|
FastRet bool
|
||||||
StartEpoch abi.ChainEpoch
|
StartEpoch abi.ChainEpoch
|
||||||
|
|
||||||
|
// SuspendUntilCryptoeconStable suspends deal-making, until cryptoecon
|
||||||
|
// parameters are stabilised. This affects projected collateral, and tests
|
||||||
|
// will fail in network version 13 and higher if deals are started too soon
|
||||||
|
// after network birth.
|
||||||
|
//
|
||||||
|
// The reason is that the formula for collateral calculation takes
|
||||||
|
// circulating supply into account:
|
||||||
|
//
|
||||||
|
// [portion of power this deal will be] * [~1% of tokens].
|
||||||
|
//
|
||||||
|
// In the first epochs after genesis, the total circulating supply is
|
||||||
|
// changing dramatically in percentual terms. Therefore, if the deal is
|
||||||
|
// proposed too soon, by the time it gets published on chain, the quoted
|
||||||
|
// provider collateral will no longer be valid.
|
||||||
|
//
|
||||||
|
// The observation is that deals fail with:
|
||||||
|
//
|
||||||
|
// GasEstimateMessageGas error: estimating gas used: message execution
|
||||||
|
// failed: exit 16, reason: Provider collateral out of bounds. (RetCode=16)
|
||||||
|
//
|
||||||
|
// Enabling this will suspend deal-making until the network has reached a
|
||||||
|
// height of 300.
|
||||||
|
SuspendUntilCryptoeconStable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDealHarness creates a test harness that contains testing utilities for deals.
|
// NewDealHarness creates a test harness that contains testing utilities for deals.
|
||||||
@ -57,6 +82,12 @@ func (dh *DealHarness) MakeOnlineDeal(ctx context.Context, params MakeFullDealPa
|
|||||||
|
|
||||||
dh.t.Logf("FILE CID: %s", res.Root)
|
dh.t.Logf("FILE CID: %s", res.Root)
|
||||||
|
|
||||||
|
if params.SuspendUntilCryptoeconStable {
|
||||||
|
dh.t.Logf("deal-making suspending until cryptecon parameters have stabilised")
|
||||||
|
ts := dh.client.WaitTillChain(ctx, HeightAtLeast(300))
|
||||||
|
dh.t.Logf("deal-making continuing; current height is %d", ts.Height())
|
||||||
|
}
|
||||||
|
|
||||||
deal = dh.StartDeal(ctx, res.Root, params.FastRet, params.StartEpoch)
|
deal = dh.StartDeal(ctx, res.Root, params.FastRet, params.StartEpoch)
|
||||||
|
|
||||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||||
@ -248,3 +279,35 @@ func (dh *DealHarness) ExtractFileFromCAR(ctx context.Context, file *os.File) (o
|
|||||||
|
|
||||||
return tmpfile
|
return tmpfile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RunConcurrentDealsOpts struct {
|
||||||
|
N int
|
||||||
|
FastRetrieval bool
|
||||||
|
CarExport bool
|
||||||
|
StartEpoch abi.ChainEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dh *DealHarness) RunConcurrentDeals(opts RunConcurrentDealsOpts) {
|
||||||
|
errgrp, _ := errgroup.WithContext(context.Background())
|
||||||
|
for i := 0; i < opts.N; i++ {
|
||||||
|
i := i
|
||||||
|
errgrp.Go(func() (err error) {
|
||||||
|
defer func() {
|
||||||
|
// This is necessary because golang can't deal with test
|
||||||
|
// failures being reported from children goroutines ¯\_(ツ)_/¯
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("deal failed: %s", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
deal, res, inPath := dh.MakeOnlineDeal(context.Background(), MakeFullDealParams{
|
||||||
|
Rseed: 5 + i,
|
||||||
|
FastRet: opts.FastRetrieval,
|
||||||
|
StartEpoch: opts.StartEpoch,
|
||||||
|
})
|
||||||
|
outPath := dh.PerformRetrieval(context.Background(), deal, res.Root, opts.CarExport)
|
||||||
|
AssertFilesEqual(dh.t, inPath, outPath)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
require.NoError(dh.t, errgrp.Wait())
|
||||||
|
}
|
||||||
|
20
itests/kit/run.go
Normal file
20
itests/kit/run.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnvRunExpensiveTests is the environment variable that needs to be present
|
||||||
|
// and set to value "1" to enable running expensive tests outside of CI.
|
||||||
|
const EnvRunExpensiveTests = "LOTUS_RUN_EXPENSIVE_TESTS"
|
||||||
|
|
||||||
|
// Expensive marks a test as expensive, skipping it immediately if not running an
|
||||||
|
func Expensive(t *testing.T) {
|
||||||
|
switch {
|
||||||
|
case os.Getenv("CI") == "true":
|
||||||
|
return
|
||||||
|
case os.Getenv(EnvRunExpensiveTests) != "1":
|
||||||
|
t.Skipf("skipping expensive test outside of CI; enable by setting env var %s=1", EnvRunExpensiveTests)
|
||||||
|
}
|
||||||
|
}
|
96
itests/multisig/suite.go
Normal file
96
itests/multisig/suite.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package multisig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/cli"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunMultisigTests(t *testing.T, client *kit.TestFullNode) {
|
||||||
|
// Create mock CLI
|
||||||
|
ctx := context.Background()
|
||||||
|
mockCLI := kit.NewMockCLI(ctx, t, cli.Commands)
|
||||||
|
clientCLI := mockCLI.Client(client.ListenAddr)
|
||||||
|
|
||||||
|
// Create some wallets on the node to use for testing multisig
|
||||||
|
var walletAddrs []address.Address
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
addr, err := client.WalletNew(ctx, types.KTSecp256k1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
walletAddrs = append(walletAddrs, addr)
|
||||||
|
|
||||||
|
kit.SendFunds(ctx, t, client, addr, types.NewInt(1e15))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an msig with three of the addresses and threshold of two sigs
|
||||||
|
// msig create --required=2 --duration=50 --value=1000attofil <addr1> <addr2> <addr3>
|
||||||
|
amtAtto := types.NewInt(1000)
|
||||||
|
threshold := 2
|
||||||
|
paramDuration := "--duration=50"
|
||||||
|
paramRequired := fmt.Sprintf("--required=%d", threshold)
|
||||||
|
paramValue := fmt.Sprintf("--value=%dattofil", amtAtto)
|
||||||
|
out := clientCLI.RunCmd(
|
||||||
|
"msig", "create",
|
||||||
|
paramRequired,
|
||||||
|
paramDuration,
|
||||||
|
paramValue,
|
||||||
|
walletAddrs[0].String(),
|
||||||
|
walletAddrs[1].String(),
|
||||||
|
walletAddrs[2].String(),
|
||||||
|
)
|
||||||
|
fmt.Println(out)
|
||||||
|
|
||||||
|
// Extract msig robust address from output
|
||||||
|
expCreateOutPrefix := "Created new multisig:"
|
||||||
|
require.Regexp(t, regexp.MustCompile(expCreateOutPrefix), out)
|
||||||
|
parts := strings.Split(strings.TrimSpace(strings.Replace(out, expCreateOutPrefix, "", -1)), " ")
|
||||||
|
require.Len(t, parts, 2)
|
||||||
|
msigRobustAddr := parts[1]
|
||||||
|
fmt.Println("msig robust address:", msigRobustAddr)
|
||||||
|
|
||||||
|
// Propose to add a new address to the msig
|
||||||
|
// msig add-propose --from=<addr> <msig> <addr>
|
||||||
|
paramFrom := fmt.Sprintf("--from=%s", walletAddrs[0])
|
||||||
|
out = clientCLI.RunCmd(
|
||||||
|
"msig", "add-propose",
|
||||||
|
paramFrom,
|
||||||
|
msigRobustAddr,
|
||||||
|
walletAddrs[3].String(),
|
||||||
|
)
|
||||||
|
fmt.Println(out)
|
||||||
|
|
||||||
|
// msig inspect <msig>
|
||||||
|
out = clientCLI.RunCmd("msig", "inspect", "--vesting", "--decode-params", msigRobustAddr)
|
||||||
|
fmt.Println(out)
|
||||||
|
|
||||||
|
// Expect correct balance
|
||||||
|
require.Regexp(t, regexp.MustCompile("Balance: 0.000000000000001 FIL"), out)
|
||||||
|
// Expect 1 transaction
|
||||||
|
require.Regexp(t, regexp.MustCompile(`Transactions:\s*1`), out)
|
||||||
|
// Expect transaction to be "AddSigner"
|
||||||
|
require.Regexp(t, regexp.MustCompile(`AddSigner`), out)
|
||||||
|
|
||||||
|
// Approve adding the new address
|
||||||
|
// msig add-approve --from=<addr> <msig> <addr> 0 <addr> false
|
||||||
|
txnID := "0"
|
||||||
|
paramFrom = fmt.Sprintf("--from=%s", walletAddrs[1])
|
||||||
|
out = clientCLI.RunCmd(
|
||||||
|
"msig", "add-approve",
|
||||||
|
paramFrom,
|
||||||
|
msigRobustAddr,
|
||||||
|
walletAddrs[0].String(),
|
||||||
|
txnID,
|
||||||
|
walletAddrs[3].String(),
|
||||||
|
"false",
|
||||||
|
)
|
||||||
|
fmt.Println(out)
|
||||||
|
}
|
@ -1,18 +1,11 @@
|
|||||||
package itests
|
package itests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
|
||||||
"github.com/filecoin-project/lotus/cli"
|
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/filecoin-project/lotus/itests/multisig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMultisig does a basic test to exercise the multisig CLI commands
|
// TestMultisig does a basic test to exercise the multisig CLI commands
|
||||||
@ -23,86 +16,5 @@ func TestMultisig(t *testing.T) {
|
|||||||
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||||||
ens.InterconnectAll().BeginMining(blockTime)
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
|
|
||||||
runMultisigTests(t, client)
|
multisig.RunMultisigTests(t, client)
|
||||||
}
|
|
||||||
|
|
||||||
func runMultisigTests(t *testing.T, clientNode *kit.TestFullNode) {
|
|
||||||
// Create mock CLI
|
|
||||||
ctx := context.Background()
|
|
||||||
mockCLI := kit.NewMockCLI(ctx, t, cli.Commands)
|
|
||||||
clientCLI := mockCLI.Client(clientNode.ListenAddr)
|
|
||||||
|
|
||||||
// Create some wallets on the node to use for testing multisig
|
|
||||||
var walletAddrs []address.Address
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
addr, err := clientNode.WalletNew(ctx, types.KTSecp256k1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
walletAddrs = append(walletAddrs, addr)
|
|
||||||
|
|
||||||
kit.SendFunds(ctx, t, clientNode, addr, types.NewInt(1e15))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an msig with three of the addresses and threshold of two sigs
|
|
||||||
// msig create --required=2 --duration=50 --value=1000attofil <addr1> <addr2> <addr3>
|
|
||||||
amtAtto := types.NewInt(1000)
|
|
||||||
threshold := 2
|
|
||||||
paramDuration := "--duration=50"
|
|
||||||
paramRequired := fmt.Sprintf("--required=%d", threshold)
|
|
||||||
paramValue := fmt.Sprintf("--value=%dattofil", amtAtto)
|
|
||||||
out := clientCLI.RunCmd(
|
|
||||||
"msig", "create",
|
|
||||||
paramRequired,
|
|
||||||
paramDuration,
|
|
||||||
paramValue,
|
|
||||||
walletAddrs[0].String(),
|
|
||||||
walletAddrs[1].String(),
|
|
||||||
walletAddrs[2].String(),
|
|
||||||
)
|
|
||||||
fmt.Println(out)
|
|
||||||
|
|
||||||
// Extract msig robust address from output
|
|
||||||
expCreateOutPrefix := "Created new multisig:"
|
|
||||||
require.Regexp(t, regexp.MustCompile(expCreateOutPrefix), out)
|
|
||||||
parts := strings.Split(strings.TrimSpace(strings.Replace(out, expCreateOutPrefix, "", -1)), " ")
|
|
||||||
require.Len(t, parts, 2)
|
|
||||||
msigRobustAddr := parts[1]
|
|
||||||
fmt.Println("msig robust address:", msigRobustAddr)
|
|
||||||
|
|
||||||
// Propose to add a new address to the msig
|
|
||||||
// msig add-propose --from=<addr> <msig> <addr>
|
|
||||||
paramFrom := fmt.Sprintf("--from=%s", walletAddrs[0])
|
|
||||||
out = clientCLI.RunCmd(
|
|
||||||
"msig", "add-propose",
|
|
||||||
paramFrom,
|
|
||||||
msigRobustAddr,
|
|
||||||
walletAddrs[3].String(),
|
|
||||||
)
|
|
||||||
fmt.Println(out)
|
|
||||||
|
|
||||||
// msig inspect <msig>
|
|
||||||
out = clientCLI.RunCmd("msig", "inspect", "--vesting", "--decode-params", msigRobustAddr)
|
|
||||||
fmt.Println(out)
|
|
||||||
|
|
||||||
// Expect correct balance
|
|
||||||
require.Regexp(t, regexp.MustCompile("Balance: 0.000000000000001 FIL"), out)
|
|
||||||
// Expect 1 transaction
|
|
||||||
require.Regexp(t, regexp.MustCompile(`Transactions:\s*1`), out)
|
|
||||||
// Expect transaction to be "AddSigner"
|
|
||||||
require.Regexp(t, regexp.MustCompile(`AddSigner`), out)
|
|
||||||
|
|
||||||
// Approve adding the new address
|
|
||||||
// msig add-approve --from=<addr> <msig> <addr> 0 <addr> false
|
|
||||||
txnID := "0"
|
|
||||||
paramFrom = fmt.Sprintf("--from=%s", walletAddrs[1])
|
|
||||||
out = clientCLI.RunCmd(
|
|
||||||
"msig", "add-approve",
|
|
||||||
paramFrom,
|
|
||||||
msigRobustAddr,
|
|
||||||
walletAddrs[0].String(),
|
|
||||||
txnID,
|
|
||||||
walletAddrs[3].String(),
|
|
||||||
"false",
|
|
||||||
)
|
|
||||||
fmt.Println(out)
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user