├── .circleci └── config.yml ├── .codecov.yml ├── .github └── CODEOWNERS ├── .gitignore ├── .golangci.yml ├── CONTRIBUTING.md ├── COPYRIGHT ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── SECURITY.md ├── actors ├── builtin │ ├── account │ │ ├── account_actor.go │ │ ├── account_test.go │ │ ├── cbor_gen.go │ │ └── testing.go │ ├── codes.go │ ├── cron │ │ ├── cbor_gen.go │ │ ├── cron_actor.go │ │ ├── cron_state.go │ │ ├── cron_test.go │ │ └── testing.go │ ├── exported │ │ ├── actors.go │ │ └── actors_test.go │ ├── init │ │ ├── cbor_gen.go │ │ ├── init_actor.go │ │ ├── init_actor_state.go │ │ ├── init_test.go │ │ └── testing.go │ ├── manifest │ │ ├── cbor_gen.go │ │ └── manifest.go │ ├── market │ │ ├── cbor_gen.go │ │ ├── deal.go │ │ ├── deal_test.go │ │ ├── market_actor.go │ │ ├── market_balances.go │ │ ├── market_state.go │ │ ├── market_test.go │ │ ├── policy.go │ │ ├── set_multimap.go │ │ ├── testing.go │ │ └── types.go │ ├── methods.go │ ├── miner │ │ ├── bitfield_queue.go │ │ ├── bitfield_queue_test.go │ │ ├── bitfield_test.go │ │ ├── burn_type.go │ │ ├── cbor_gen.go │ │ ├── deadline_assignment.go │ │ ├── deadline_assignment_test.go │ │ ├── deadline_state.go │ │ ├── deadline_state_test.go │ │ ├── deadlines.go │ │ ├── deadlines_helper_test.go │ │ ├── deadlines_test.go │ │ ├── expiration_queue.go │ │ ├── expiration_queue_internal_test.go │ │ ├── expiration_queue_test.go │ │ ├── miner_actor.go │ │ ├── miner_commitment_test.go │ │ ├── miner_internal_test.go │ │ ├── miner_state.go │ │ ├── miner_state_test.go │ │ ├── miner_test.go │ │ ├── monies.go │ │ ├── monies_test.go │ │ ├── partition_state.go │ │ ├── partition_state_test.go │ │ ├── policy.go │ │ ├── policy_test.go │ │ ├── sector_map.go │ │ ├── sector_map_test.go │ │ ├── sectors.go │ │ ├── sectors_test.go │ │ ├── termination.go │ │ ├── termination_test.go │ │ ├── testing.go │ │ └── vesting_state.go │ ├── multisig │ │ ├── cbor_gen.go │ │ ├── multisig_actor.go │ │ ├── multisig_state.go │ │ ├── multisig_test.go │ │ ├── policy.go │ │ └── testing.go │ ├── network.go │ ├── paych │ │ ├── cbor_gen.go │ │ ├── paych_actor.go │ │ ├── paych_state.go │ │ ├── paych_test.go │ │ ├── policy.go │ │ └── testing.go │ ├── power │ │ ├── cbor_gen.go │ │ ├── policy.go │ │ ├── power_actor.go │ │ ├── power_state.go │ │ ├── power_test.go │ │ └── testing.go │ ├── quantize.go │ ├── quantize_test.go │ ├── reward │ │ ├── cbor_gen.go │ │ ├── reward_actor.go │ │ ├── reward_calc.py │ │ ├── reward_logic.go │ │ ├── reward_logic_test.go │ │ ├── reward_state.go │ │ ├── reward_test.go │ │ ├── testdata │ │ │ ├── TestBaselineReward.golden │ │ │ └── TestSimpleReward.golden │ │ └── testing.go │ ├── sector.go │ ├── shared.go │ ├── singletons.go │ ├── system │ │ ├── cbor_gen.go │ │ ├── system_actor.go │ │ ├── system_actor_state.go │ │ └── system_test.go │ ├── testing.go │ ├── testing_test.go │ └── verifreg │ │ ├── cbor_gen.go │ │ ├── testing.go │ │ ├── verified_registry_actor.go │ │ ├── verified_registry_state.go │ │ └── verified_registry_test.go ├── migration │ └── nv16 │ │ ├── market.go │ │ ├── system.go │ │ ├── test │ │ ├── deal_label_migration_test.go │ │ ├── parallel_migration_test.go │ │ └── system_migration_test.go │ │ ├── top.go │ │ └── util.go ├── runtime │ ├── proof │ │ └── verify.go │ ├── runtime.go │ └── types.go ├── states │ ├── check.go │ ├── election.go │ ├── election_test.go │ ├── tree.go │ └── tree_test.go ├── test │ ├── commit_post_test.go │ ├── common_test.go │ ├── extend_sectors_test.go │ ├── market_miner_withdrawal_test.go │ ├── multisig_delete_self_test.go │ ├── multisig_proposal_hash_test.go │ ├── power_scenario_test.go │ ├── publish-deals_test.go │ ├── replica_update_test.go │ ├── terminate_sectors_scenario_test.go │ └── verifreg_remove_datacap_test.go └── util │ ├── adt │ ├── array.go │ ├── array_test.go │ ├── balancetable.go │ ├── balancetable_test.go │ ├── map.go │ ├── multimap.go │ ├── set.go │ └── store.go │ ├── bitfield.go │ ├── bitfield_test.go │ ├── math │ ├── exp.go │ ├── exp_test.go │ ├── expneg.go │ ├── expneg_test.go │ ├── ln.go │ ├── ln_test.go │ ├── parse.go │ ├── polyval.go │ └── testdata │ │ └── TestExpFunction.golden │ └── smoothing │ ├── alpha_beta_filter.go │ ├── alpha_beta_filter_test.go │ └── testing.go ├── docs └── post-mortem-jan-21.md ├── gen └── gen.go ├── go.mod ├── go.sum ├── support ├── agent │ ├── blockstore_util.go │ ├── cases_test.go │ ├── deal_client_agent.go │ ├── math.go │ ├── miner_agent.go │ ├── miner_generator.go │ ├── miner_state.go │ └── sim.go ├── ipld │ ├── cbor.go │ └── store.go ├── mock │ ├── builder.go │ ├── exports.go │ ├── mockrt.go │ └── mockrt_state_test.go ├── testing │ ├── address.go │ ├── adt.go │ ├── cid.go │ └── pid.go ├── tools │ ├── go.mod │ ├── go.sum │ └── tools.go ├── vm │ ├── cbor_gen.go │ ├── invocation_context.go │ ├── price_list.go │ ├── stats.go │ ├── testing.go │ ├── vector.go │ ├── vector_gen.go │ └── vm.go └── vm7Util │ └── utilV7.go └── test-vectors ├── .gitignore ├── README.md ├── determinism-check └── tools └── digest └── main.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | go: gotest/tools@0.0.9 4 | codecov: codecov/codecov@1.0.2 5 | 6 | executors: 7 | golang: 8 | docker: 9 | - image: circleci/golang:1.17.1 10 | 11 | commands: 12 | install-deps: 13 | steps: 14 | - go/install-ssh 15 | - go/install: {package: git} 16 | prepare: 17 | parameters: 18 | linux: 19 | default: true 20 | description: is a linux build environment? 21 | type: boolean 22 | steps: 23 | - checkout 24 | - when: 25 | condition: << parameters.linux >> 26 | steps: 27 | - run: sudo apt-get update 28 | build-all: 29 | 30 | 31 | jobs: 32 | mod-tidy-check: 33 | executor: golang 34 | steps: 35 | - install-deps 36 | - prepare 37 | - go/mod-download 38 | - go/mod-tidy-check 39 | 40 | build-all: 41 | executor: golang 42 | steps: 43 | - install-deps 44 | - prepare 45 | - go/mod-download 46 | - run: 47 | command: make build 48 | - store_artifacts: 49 | path: specs-actors 50 | - store_artifacts: 51 | path: specs-actors 52 | 53 | check-gen: 54 | executor: golang 55 | steps: 56 | - install-deps 57 | - prepare 58 | - go/mod-download 59 | - run: 60 | name: "Install goimports" 61 | command: | 62 | cd / && go get golang.org/x/tools/cmd/goimports 63 | - run: 64 | name: "Ensure we don't need to run 'make gen'" 65 | command: | 66 | make gen && go mod tidy && git diff --exit-code 67 | 68 | check-determinism: 69 | executor: golang 70 | steps: 71 | - install-deps 72 | - prepare 73 | - go/mod-download 74 | - run: 75 | name: "Check for execution determinism" 76 | command: make determinism-check 77 | - store_artifacts: 78 | path: specs-actors 79 | 80 | test-all: 81 | executor: golang 82 | steps: 83 | - install-deps 84 | - prepare 85 | - go/mod-download 86 | - run: 87 | command: | 88 | make test-coverage 89 | mkdir -p /tmp/artifacts 90 | mv coverage.out /tmp/artifacts/coverage.out 91 | make test-migration 92 | - codecov/upload: 93 | file: /tmp/artifacts/coverage.out 94 | - store_artifacts: 95 | path: specs-actors 96 | 97 | lint: &lint 98 | description: | 99 | Run golangci-lint. 100 | parameters: 101 | executor: 102 | type: executor 103 | default: golang 104 | golangci-lint-version: 105 | type: string 106 | default: 1.28.2 107 | concurrency: 108 | type: string 109 | default: '2' 110 | description: | 111 | Concurrency used to run linters. Defaults to 2 because NumCPU is not 112 | aware of container CPU limits. 113 | args: 114 | type: string 115 | default: '' 116 | description: | 117 | Arguments to pass to golangci-lint 118 | executor: << parameters.executor >> 119 | steps: 120 | - install-deps 121 | - prepare 122 | - run: 123 | command: make -j3 support/tools/bin/golangci-lint support/tools/bin/no-map-range.so 124 | - run: 125 | name: Lint 126 | command: | 127 | support/tools/bin/golangci-lint run -v \ 128 | --concurrency << parameters.concurrency >> << parameters.args >> 129 | 130 | lint-all: 131 | <<: *lint 132 | 133 | workflows: 134 | version: 2.1 135 | ci: 136 | jobs: 137 | - lint-all 138 | - mod-tidy-check 139 | - build-all 140 | - test-all 141 | - check-gen 142 | - check-determinism 143 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | # Documentation: 2 | # https://docs.codecov.io/docs/codecovyml-reference 3 | # https://docs.codecov.io/docs/pull-request-comments 4 | # https://gist.github.com/stevepeak/53bee7b2c326b24a9b4a 5 | 6 | coverage: 7 | range: 50..90 8 | round: down 9 | precision: 1 10 | status: 11 | patch: off 12 | 13 | ignore: 14 | - "**/*/cbor_gen.go" 15 | - "support/**/*" 16 | - "gen/**/*" 17 | 18 | comment: 19 | layout: "diff" # "diff, flags, files" 20 | behavior: default 21 | require_changes: false # if true: only post the comment if coverage changes 22 | require_base: true # [yes :: must have a base report to post] 23 | require_head: true # [yes :: must have a head report to post] 24 | branches: [] # branch names that can post comment 25 | 26 | codecov: 27 | notify: 28 | # yes: will delay sending notifications until all ci is finished 29 | # no: will send notifications without checking ci status and wait till "after_n_builds" are uploaded 30 | require_ci_to_pass: false 31 | 32 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Reference 2 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners 3 | 4 | # Global owners 5 | # Ensure maintainers team is a requested reviewer for non-draft PRs 6 | * @filecoin-project/actors-maintainers 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | digest 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | 18 | .idea 19 | 20 | support/tools/bin 21 | gen/command-line-arguments 22 | 23 | digest 24 | test-vectors/determinism/ 25 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | custom: 3 | nomaprange: 4 | path: support/tools/bin/no-map-range.so 5 | description: Checks for range over maps 6 | original-url: github.com/Kubuxu/go-no-map-range 7 | 8 | linters: 9 | enable: 10 | - nomaprange 11 | 12 | run: 13 | skip-dirs-use-default: false 14 | 15 | issues: 16 | exclude-rules: 17 | - path: _test\.go 18 | text: "range iteration over map" 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Please [read the spec process](https://filecoin-project.github.io/specs/#intro__process). Please file PRs on github with fixes. 4 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2020. Protocol Labs, Inc. 2 | 3 | This library is dual-licensed under Apache 2.0 and MIT terms. 4 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2020. Protocol Labs, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2020. Protocol Labs, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO_BIN ?= go 2 | # relative path ../../ is included in path because current working directory of tests is directory of test files 3 | # and all test vector generation comes from a call to go test ./actors/test 4 | TEST_VECTOR_PATH = ../../test-vectors 5 | all: build lint test tidy determinism-check 6 | .PHONY: all 7 | 8 | build: 9 | $(GO_BIN) build ./... 10 | .PHONY: build 11 | 12 | test: 13 | $(GO_BIN) test ./... 14 | $(GO_BIN) test -race ./actors/migration/nv16/test 15 | .PHONY: test 16 | 17 | test-migration: 18 | .PHONY: test-migration 19 | $(GO_BIN) test -race ./actors/migration/nv16/test 20 | 21 | test-coverage: 22 | $(GO_BIN) test -coverprofile=coverage.out ./... 23 | .PHONY: test-coverage 24 | 25 | tidy: 26 | $(GO_BIN) mod tidy 27 | .PHONY: tidy 28 | 29 | gen: 30 | $(GO_BIN) run ./gen/gen.go 31 | .PHONY: gen 32 | 33 | determinism-check: 34 | rm -rf test-vectors/determinism 35 | 36 | SPECS_ACTORS_DETERMINISM="$(TEST_VECTOR_PATH)/determinism" $(GO_BIN) test ./actors/test -count=1 37 | $(GO_BIN) build ./test-vectors/tools/digest 38 | 39 | @if [ "`./digest ./test-vectors/determinism`" != "`cat ./test-vectors/determinism-check`" ]; then \ 40 | echo "test-vectors don't match expected";\ 41 | exit 1;\ 42 | fi 43 | 44 | determinism-gen: 45 | rm -rf test-vectors/determinism 46 | SPECS_ACTORS_DETERMINISM="$(TEST_VECTOR_PATH)/determinism" $(GO_BIN) test ./actors/test -count=1 47 | $(GO_BIN) build ./test-vectors/tools/digest 48 | ./digest ./test-vectors/determinism > ./test-vectors/determinism-check 49 | 50 | conformance-gen: 51 | rm -rf test-vectors/conformance 52 | SPECS_ACTORS_CONFORMANCE="$(TEST_VECTOR_PATH)/conformance" $(GO_BIN) test ./actors/test -count=1 53 | tar -zcf test-vectors/conformance.tar.gz test-vectors/conformance 54 | 55 | # tools 56 | toolspath:=support/tools 57 | 58 | $(toolspath)/bin/golangci-lint: $(toolspath)/go.mod 59 | @mkdir -p $(dir $@) 60 | (cd $(toolspath); go build -tags tools -o $(@:$(toolspath)/%=%) github.com/golangci/golangci-lint/cmd/golangci-lint) 61 | 62 | 63 | $(toolspath)/bin/no-map-range.so: $(toolspath)/go.mod 64 | @mkdir -p $(dir $@) 65 | (cd $(toolspath); go build -tags tools -buildmode=plugin -o $(@:$(toolspath)/%=%) github.com/Kubuxu/go-no-map-range/plugin) 66 | 67 | lint: $(toolspath)/bin/golangci-lint $(toolspath)/bin/no-map-range.so 68 | $(toolspath)/bin/golangci-lint run ./... 69 | .PHONY: lint 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo is deprecated 2 | 3 | To support the transition to FVM the specs-actors logic is now actively developed in the [builtin actors](https://github.com/filecoin-project/builtin-actors) code base. 4 | 5 | (Read only) specs-actors code is still called by historical chain syncing but its logic will never change. 6 | 7 | Issues in this repo may still be relevant for filecoin protocol and builtin actors development. 8 | 9 | # Filecoin actors 10 | [![CircleCI](https://circleci.com/gh/filecoin-project/specs-actors.svg?style=svg)](https://circleci.com/gh/filecoin-project/specs-actors) 11 | [![codecov](https://codecov.io/gh/filecoin-project/specs-actors/branch/master/graph/badge.svg)](https://codecov.io/gh/filecoin-project/specs-actors) 12 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/filecoin-project/specs-actors) 13 | 14 | This repo is the specification of the Filecoin builtin actors, in the form of executable code. 15 | 16 | This is a companion to the rest of the [Filecoin Specification](https://github.com/filecoin-project/specs), 17 | but also directly usable by Go implementations of Filecoin. 18 | 19 | ## Versioning 20 | 21 | Releases of this repo follow semantic versioning rules, with consideration of distributed state machines. 22 | - The major version will remain `0` or `1` for the forseeable future. 23 | We do not bump the major version every time there's a backwards-incompatible change in state machine evaluation, 24 | or actor interfaces, because this interacts very poorly with Go's module resolution, 25 | requiring a change of all import paths. 26 | After `1.0` we may consider using the major version number to version the `Runtime` interface, which is the link between 27 | the actors and the system in which they are embedded. 28 | - A minor version change indicates a backwards-incompatible change in the state machine evaluation, including 29 | actor exported methods or constant values, while retaining compatibility of the `Runtime` interface. 30 | This means that the same sequence of messages might produce different states at two different versions. 31 | In a blockchain, this would usually require a coordinated network upgrade or "hard fork". 32 | After `1.0`, a minor version change may alter behaviour but not exported code or actor interfaces. 33 | - A patch version change may alter state evaluation (but not exported code or actor interfaces). 34 | After `1.0`, a patch version change indicates a backward compatible fix or improvement that doesn't change 35 | state evaluation semantics or exported interfaces. 36 | 37 | ## License 38 | This repository is dual-licensed under Apache 2.0 and MIT terms. 39 | 40 | Copyright 2019-2020. Protocol Labs, Inc. 41 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | For reporting *critical* and *security* bugs, please consult our [Security Policy and Responsible Disclosure Program information](https://github.com/filecoin-project/community/blob/master/SECURITY.md) 6 | 7 | ## Reporting a non security bug 8 | 9 | For non-critical bugs, please simply file a GitHub issue on this repo. 10 | -------------------------------------------------------------------------------- /actors/builtin/account/account_actor.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/cbor" 7 | "github.com/filecoin-project/go-state-types/exitcode" 8 | "github.com/ipfs/go-cid" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 12 | ) 13 | 14 | type Actor struct{} 15 | 16 | func (a Actor) Exports() []interface{} { 17 | return []interface{}{ 18 | 1: a.Constructor, 19 | 2: a.PubkeyAddress, 20 | } 21 | } 22 | 23 | func (a Actor) Code() cid.Cid { 24 | return builtin.AccountActorCodeID 25 | } 26 | 27 | func (a Actor) State() cbor.Er { 28 | return new(State) 29 | } 30 | 31 | var _ runtime.VMActor = Actor{} 32 | 33 | type State struct { 34 | Address addr.Address 35 | } 36 | 37 | func (a Actor) Constructor(rt runtime.Runtime, address *addr.Address) *abi.EmptyValue { 38 | // Account actors are created implicitly by sending a message to a pubkey-style address. 39 | // This constructor is not invoked by the InitActor, but by the system. 40 | rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 41 | switch address.Protocol() { 42 | case addr.SECP256K1: 43 | case addr.BLS: 44 | break // ok 45 | default: 46 | rt.Abortf(exitcode.ErrIllegalArgument, "address must use BLS or SECP protocol, got %v", address.Protocol()) 47 | } 48 | st := State{Address: *address} 49 | rt.StateCreate(&st) 50 | return nil 51 | } 52 | 53 | // Fetches the pubkey-type address from this actor. 54 | func (a Actor) PubkeyAddress(rt runtime.Runtime, _ *abi.EmptyValue) *addr.Address { 55 | rt.ValidateImmediateCallerAcceptAny() 56 | var st State 57 | rt.StateReadonly(&st) 58 | return &st.Address 59 | } 60 | -------------------------------------------------------------------------------- /actors/builtin/account/account_test.go: -------------------------------------------------------------------------------- 1 | package account_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/filecoin-project/go-address" 8 | "github.com/filecoin-project/go-state-types/exitcode" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 13 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/account" 14 | "github.com/filecoin-project/specs-actors/v8/support/mock" 15 | tutil "github.com/filecoin-project/specs-actors/v8/support/testing" 16 | ) 17 | 18 | type constructorTestCase struct { 19 | desc string 20 | addr address.Address 21 | exitCode exitcode.ExitCode 22 | } 23 | 24 | func TestExports(t *testing.T) { 25 | mock.CheckActorExports(t, account.Actor{}) 26 | } 27 | 28 | func TestAccountactor(t *testing.T) { 29 | actor := account.Actor{} 30 | 31 | receiver := tutil.NewIDAddr(t, 100) 32 | builder := mock.NewBuilder(receiver).WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID) 33 | 34 | testCases := []constructorTestCase{ 35 | { 36 | desc: "happy path construct SECP256K1 address", 37 | addr: tutil.NewSECP256K1Addr(t, "secpaddress"), 38 | exitCode: exitcode.Ok, 39 | }, 40 | { 41 | desc: "happy path construct BLS address", 42 | addr: tutil.NewBLSAddr(t, 1), 43 | exitCode: exitcode.Ok, 44 | }, 45 | { 46 | desc: "fail to construct account actor using ID address", 47 | addr: tutil.NewIDAddr(t, 1), 48 | exitCode: exitcode.ErrIllegalArgument, 49 | }, 50 | { 51 | desc: "fail to construct account actor using Actor address", 52 | addr: tutil.NewActorAddr(t, "actoraddress"), 53 | exitCode: exitcode.ErrIllegalArgument, 54 | }, 55 | } 56 | for _, tc := range testCases { 57 | t.Run(tc.desc, func(t *testing.T) { 58 | rt := builder.Build(t) 59 | rt.ExpectValidateCallerAddr(builtin.SystemActorAddr) 60 | 61 | if tc.exitCode.IsSuccess() { 62 | rt.Call(actor.Constructor, &tc.addr) 63 | 64 | var st account.State 65 | rt.GetState(&st) 66 | assert.Equal(t, tc.addr, st.Address) 67 | 68 | rt.ExpectValidateCallerAny() 69 | pubkeyAddress := rt.Call(actor.PubkeyAddress, nil).(*address.Address) 70 | assert.Equal(t, &tc.addr, pubkeyAddress) 71 | 72 | checkState(t, rt) 73 | } else { 74 | rt.ExpectAbort(tc.exitCode, func() { 75 | rt.Call(actor.Constructor, &tc.addr) 76 | }) 77 | } 78 | rt.Verify() 79 | }) 80 | } 81 | } 82 | 83 | func checkState(t *testing.T, rt *mock.Runtime) { 84 | testAddress, err := address.NewIDAddress(1000) 85 | require.NoError(t, err) 86 | var st account.State 87 | rt.GetState(&st) 88 | _, msgs := account.CheckStateInvariants(&st, testAddress) 89 | assert.True(t, msgs.IsEmpty(), strings.Join(msgs.Messages(), "\n")) 90 | } 91 | -------------------------------------------------------------------------------- /actors/builtin/account/cbor_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 | 3 | package account 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | 9 | cbg "github.com/whyrusleeping/cbor-gen" 10 | xerrors "golang.org/x/xerrors" 11 | ) 12 | 13 | var _ = xerrors.Errorf 14 | 15 | var lengthBufState = []byte{129} 16 | 17 | func (t *State) MarshalCBOR(w io.Writer) error { 18 | if t == nil { 19 | _, err := w.Write(cbg.CborNull) 20 | return err 21 | } 22 | if _, err := w.Write(lengthBufState); err != nil { 23 | return err 24 | } 25 | 26 | // t.Address (address.Address) (struct) 27 | if err := t.Address.MarshalCBOR(w); err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | 33 | func (t *State) UnmarshalCBOR(r io.Reader) error { 34 | *t = State{} 35 | 36 | br := cbg.GetPeeker(r) 37 | scratch := make([]byte, 8) 38 | 39 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 40 | if err != nil { 41 | return err 42 | } 43 | if maj != cbg.MajArray { 44 | return fmt.Errorf("cbor input should be of type array") 45 | } 46 | 47 | if extra != 1 { 48 | return fmt.Errorf("cbor input had wrong number of fields") 49 | } 50 | 51 | // t.Address (address.Address) (struct) 52 | 53 | { 54 | 55 | if err := t.Address.UnmarshalCBOR(br); err != nil { 56 | return xerrors.Errorf("unmarshaling t.Address: %w", err) 57 | } 58 | 59 | } 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /actors/builtin/account/testing.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "github.com/filecoin-project/go-address" 5 | 6 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | ) 8 | 9 | type StateSummary struct { 10 | PubKeyAddr address.Address 11 | } 12 | 13 | // Checks internal invariants of account state. 14 | func CheckStateInvariants(st *State, idAddr address.Address) (*StateSummary, *builtin.MessageAccumulator) { 15 | acc := &builtin.MessageAccumulator{} 16 | accountSummary := &StateSummary{ 17 | PubKeyAddr: st.Address, 18 | } 19 | 20 | if id, err := address.IDFromAddress(idAddr); err != nil { 21 | acc.Addf("error extracting actor ID from address: %v", err) 22 | } else if id >= builtin.FirstNonSingletonActorId { 23 | acc.Require(st.Address.Protocol() == address.BLS || st.Address.Protocol() == address.SECP256K1, 24 | "actor address %v must be BLS or SECP256K1 protocol", st.Address) 25 | } 26 | 27 | return accountSummary, acc 28 | } 29 | -------------------------------------------------------------------------------- /actors/builtin/codes.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/ipfs/go-cid" 7 | mh "github.com/multiformats/go-multihash" 8 | ) 9 | 10 | // The built-in actor code IDs 11 | var ( 12 | SystemActorCodeID cid.Cid 13 | InitActorCodeID cid.Cid 14 | CronActorCodeID cid.Cid 15 | AccountActorCodeID cid.Cid 16 | StoragePowerActorCodeID cid.Cid 17 | StorageMinerActorCodeID cid.Cid 18 | StorageMarketActorCodeID cid.Cid 19 | PaymentChannelActorCodeID cid.Cid 20 | MultisigActorCodeID cid.Cid 21 | RewardActorCodeID cid.Cid 22 | VerifiedRegistryActorCodeID cid.Cid 23 | CallerTypesSignable []cid.Cid 24 | ) 25 | 26 | var builtinActors map[cid.Cid]*actorInfo 27 | 28 | type actorInfo struct { 29 | name string 30 | signer bool 31 | } 32 | 33 | func init() { 34 | builder := cid.V1Builder{Codec: cid.Raw, MhType: mh.IDENTITY} 35 | builtinActors = make(map[cid.Cid]*actorInfo) 36 | 37 | // TODO: These will be replaced with the content-addressed CIDs from canonical actors 38 | for id, info := range map[*cid.Cid]*actorInfo{ //nolint:nomaprange 39 | &SystemActorCodeID: {name: "fil/8/system"}, 40 | &InitActorCodeID: {name: "fil/8/init"}, 41 | &CronActorCodeID: {name: "fil/8/cron"}, 42 | &StoragePowerActorCodeID: {name: "fil/8/storagepower"}, 43 | &StorageMinerActorCodeID: {name: "fil/8/storageminer"}, 44 | &StorageMarketActorCodeID: {name: "fil/8/storagemarket"}, 45 | &PaymentChannelActorCodeID: {name: "fil/8/paymentchannel"}, 46 | &RewardActorCodeID: {name: "fil/8/reward"}, 47 | &VerifiedRegistryActorCodeID: {name: "fil/8/verifiedregistry"}, 48 | &AccountActorCodeID: {name: "fil/8/account", signer: true}, 49 | &MultisigActorCodeID: {name: "fil/8/multisig", signer: true}, 50 | } { 51 | c, err := builder.Sum([]byte(info.name)) 52 | if err != nil { 53 | panic(err) 54 | } 55 | *id = c 56 | builtinActors[c] = info 57 | } 58 | 59 | // Set of actor code types that can represent external signing parties. 60 | for id, info := range builtinActors { //nolint:nomaprange 61 | if info.signer { 62 | CallerTypesSignable = append(CallerTypesSignable, id) 63 | } 64 | } 65 | sort.Slice(CallerTypesSignable, func(i, j int) bool { 66 | return CallerTypesSignable[i].KeyString() < CallerTypesSignable[j].KeyString() 67 | }) 68 | 69 | } 70 | 71 | // IsBuiltinActor returns true if the code belongs to an actor defined in this repo. 72 | func IsBuiltinActor(code cid.Cid) bool { 73 | _, isBuiltin := builtinActors[code] 74 | return isBuiltin 75 | } 76 | 77 | // ActorNameByCode returns the (string) name of the actor given a cid code. 78 | func ActorNameByCode(code cid.Cid) string { 79 | if !code.Defined() { 80 | return "" 81 | } 82 | 83 | info, ok := builtinActors[code] 84 | if !ok { 85 | return "" 86 | } 87 | return info.name 88 | } 89 | 90 | // Tests whether a code CID represents an actor that can be an external principal: i.e. an account or multisig. 91 | // We could do something more sophisticated here: https://github.com/filecoin-project/specs-actors/issues/178 92 | func IsPrincipal(code cid.Cid) bool { 93 | info, ok := builtinActors[code] 94 | if !ok { 95 | return false 96 | } 97 | return info.signer 98 | } 99 | -------------------------------------------------------------------------------- /actors/builtin/cron/cbor_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 | 3 | package cron 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | 9 | abi "github.com/filecoin-project/go-state-types/abi" 10 | cbg "github.com/whyrusleeping/cbor-gen" 11 | xerrors "golang.org/x/xerrors" 12 | ) 13 | 14 | var _ = xerrors.Errorf 15 | 16 | var lengthBufState = []byte{129} 17 | 18 | func (t *State) MarshalCBOR(w io.Writer) error { 19 | if t == nil { 20 | _, err := w.Write(cbg.CborNull) 21 | return err 22 | } 23 | if _, err := w.Write(lengthBufState); err != nil { 24 | return err 25 | } 26 | 27 | scratch := make([]byte, 9) 28 | 29 | // t.Entries ([]cron.Entry) (slice) 30 | if len(t.Entries) > cbg.MaxLength { 31 | return xerrors.Errorf("Slice value in field t.Entries was too long") 32 | } 33 | 34 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Entries))); err != nil { 35 | return err 36 | } 37 | for _, v := range t.Entries { 38 | if err := v.MarshalCBOR(w); err != nil { 39 | return err 40 | } 41 | } 42 | return nil 43 | } 44 | 45 | func (t *State) UnmarshalCBOR(r io.Reader) error { 46 | *t = State{} 47 | 48 | br := cbg.GetPeeker(r) 49 | scratch := make([]byte, 8) 50 | 51 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 52 | if err != nil { 53 | return err 54 | } 55 | if maj != cbg.MajArray { 56 | return fmt.Errorf("cbor input should be of type array") 57 | } 58 | 59 | if extra != 1 { 60 | return fmt.Errorf("cbor input had wrong number of fields") 61 | } 62 | 63 | // t.Entries ([]cron.Entry) (slice) 64 | 65 | maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | if extra > cbg.MaxLength { 71 | return fmt.Errorf("t.Entries: array too large (%d)", extra) 72 | } 73 | 74 | if maj != cbg.MajArray { 75 | return fmt.Errorf("expected cbor array") 76 | } 77 | 78 | if extra > 0 { 79 | t.Entries = make([]Entry, extra) 80 | } 81 | 82 | for i := 0; i < int(extra); i++ { 83 | 84 | var v Entry 85 | if err := v.UnmarshalCBOR(br); err != nil { 86 | return err 87 | } 88 | 89 | t.Entries[i] = v 90 | } 91 | 92 | return nil 93 | } 94 | 95 | var lengthBufEntry = []byte{130} 96 | 97 | func (t *Entry) MarshalCBOR(w io.Writer) error { 98 | if t == nil { 99 | _, err := w.Write(cbg.CborNull) 100 | return err 101 | } 102 | if _, err := w.Write(lengthBufEntry); err != nil { 103 | return err 104 | } 105 | 106 | scratch := make([]byte, 9) 107 | 108 | // t.Receiver (address.Address) (struct) 109 | if err := t.Receiver.MarshalCBOR(w); err != nil { 110 | return err 111 | } 112 | 113 | // t.MethodNum (abi.MethodNum) (uint64) 114 | 115 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.MethodNum)); err != nil { 116 | return err 117 | } 118 | 119 | return nil 120 | } 121 | 122 | func (t *Entry) UnmarshalCBOR(r io.Reader) error { 123 | *t = Entry{} 124 | 125 | br := cbg.GetPeeker(r) 126 | scratch := make([]byte, 8) 127 | 128 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 129 | if err != nil { 130 | return err 131 | } 132 | if maj != cbg.MajArray { 133 | return fmt.Errorf("cbor input should be of type array") 134 | } 135 | 136 | if extra != 2 { 137 | return fmt.Errorf("cbor input had wrong number of fields") 138 | } 139 | 140 | // t.Receiver (address.Address) (struct) 141 | 142 | { 143 | 144 | if err := t.Receiver.UnmarshalCBOR(br); err != nil { 145 | return xerrors.Errorf("unmarshaling t.Receiver: %w", err) 146 | } 147 | 148 | } 149 | // t.MethodNum (abi.MethodNum) (uint64) 150 | 151 | { 152 | 153 | maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) 154 | if err != nil { 155 | return err 156 | } 157 | if maj != cbg.MajUnsignedInt { 158 | return fmt.Errorf("wrong type for uint64 field") 159 | } 160 | t.MethodNum = abi.MethodNum(extra) 161 | 162 | } 163 | return nil 164 | } 165 | -------------------------------------------------------------------------------- /actors/builtin/cron/cron_actor.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | "github.com/filecoin-project/go-state-types/cbor" 6 | rtt "github.com/filecoin-project/go-state-types/rt" 7 | cron0 "github.com/filecoin-project/specs-actors/actors/builtin/cron" 8 | "github.com/ipfs/go-cid" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 12 | ) 13 | 14 | // The cron actor is a built-in singleton that sends messages to other registered actors at the end of each epoch. 15 | type Actor struct{} 16 | 17 | func (a Actor) Exports() []interface{} { 18 | return []interface{}{ 19 | builtin.MethodConstructor: a.Constructor, 20 | 2: a.EpochTick, 21 | } 22 | } 23 | 24 | func (a Actor) Code() cid.Cid { 25 | return builtin.CronActorCodeID 26 | } 27 | 28 | func (a Actor) IsSingleton() bool { 29 | return true 30 | } 31 | 32 | func (a Actor) State() cbor.Er { 33 | return new(State) 34 | } 35 | 36 | var _ runtime.VMActor = Actor{} 37 | 38 | //type ConstructorParams struct { 39 | // Entries []Entry 40 | //} 41 | type ConstructorParams = cron0.ConstructorParams 42 | 43 | type EntryParam = cron0.Entry 44 | 45 | func (a Actor) Constructor(rt runtime.Runtime, params *ConstructorParams) *abi.EmptyValue { 46 | rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 47 | entries := make([]Entry, len(params.Entries)) 48 | for i, e := range params.Entries { 49 | entries[i] = Entry(e) // Identical 50 | } 51 | rt.StateCreate(ConstructState(entries)) 52 | return nil 53 | } 54 | 55 | // Invoked by the system after all other messages in the epoch have been processed. 56 | func (a Actor) EpochTick(rt runtime.Runtime, _ *abi.EmptyValue) *abi.EmptyValue { 57 | rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 58 | 59 | var st State 60 | 61 | rt.StateReadonly(&st) 62 | for _, entry := range st.Entries { 63 | code := rt.Send(entry.Receiver, entry.MethodNum, nil, abi.NewTokenAmount(0), &builtin.Discard{}) 64 | // Any error and return value are ignored. 65 | if code.IsError() { 66 | rt.Log(rtt.ERROR, "cron failed to send entry to %s, send error code %d", entry.Receiver, code) 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /actors/builtin/cron/cron_state.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 8 | ) 9 | 10 | type State struct { 11 | Entries []Entry 12 | } 13 | 14 | type Entry struct { 15 | Receiver addr.Address // The actor to call (must be an ID-address) 16 | MethodNum abi.MethodNum // The method number to call (must accept empty parameters) 17 | } 18 | 19 | func ConstructState(entries []Entry) *State { 20 | return &State{Entries: entries} 21 | } 22 | 23 | // The default entries to install in the cron actor's state at genesis. 24 | func BuiltInEntries() []Entry { 25 | return []Entry{ 26 | { 27 | Receiver: builtin.StoragePowerActorAddr, 28 | MethodNum: builtin.MethodsPower.CronTick, 29 | }, 30 | { 31 | Receiver: builtin.StorageMarketActorAddr, 32 | MethodNum: builtin.MethodsMarket.CronTick, 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /actors/builtin/cron/cron_test.go: -------------------------------------------------------------------------------- 1 | package cron_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/filecoin-project/go-state-types/big" 8 | "github.com/filecoin-project/go-state-types/exitcode" 9 | "github.com/stretchr/testify/assert" 10 | 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/cron" 13 | "github.com/filecoin-project/specs-actors/v8/support/mock" 14 | tutil "github.com/filecoin-project/specs-actors/v8/support/testing" 15 | ) 16 | 17 | func TestExports(t *testing.T) { 18 | mock.CheckActorExports(t, cron.Actor{}) 19 | } 20 | 21 | func TestConstructor(t *testing.T) { 22 | actor := cronHarness{cron.Actor{}, t} 23 | 24 | receiver := tutil.NewIDAddr(t, 100) 25 | builder := mock.NewBuilder(receiver).WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID) 26 | 27 | t.Run("construct with empty entries", func(t *testing.T) { 28 | rt := builder.Build(t) 29 | 30 | actor.constructAndVerify(rt) 31 | 32 | var st cron.State 33 | rt.GetState(&st) 34 | var nilCronEntries = []cron.Entry(nil) 35 | assert.Equal(t, nilCronEntries, st.Entries) 36 | 37 | actor.checkState(rt) 38 | }) 39 | 40 | t.Run("construct with non-empty entries", func(t *testing.T) { 41 | rt := builder.Build(t) 42 | 43 | var entryParams = []cron.EntryParam{ 44 | {Receiver: tutil.NewIDAddr(t, 1001), MethodNum: abi.MethodNum(1001)}, 45 | {Receiver: tutil.NewIDAddr(t, 1002), MethodNum: abi.MethodNum(1002)}, 46 | {Receiver: tutil.NewIDAddr(t, 1003), MethodNum: abi.MethodNum(1003)}, 47 | {Receiver: tutil.NewIDAddr(t, 1004), MethodNum: abi.MethodNum(1004)}, 48 | } 49 | actor.constructAndVerify(rt, entryParams...) 50 | 51 | var st cron.State 52 | rt.GetState(&st) 53 | expectedEntries := make([]cron.Entry, len(entryParams)) 54 | for i, e := range entryParams { 55 | expectedEntries[i] = cron.Entry(e) 56 | } 57 | assert.Equal(t, expectedEntries, st.Entries) 58 | 59 | actor.checkState(rt) 60 | }) 61 | } 62 | 63 | func TestEpochTick(t *testing.T) { 64 | actor := cronHarness{cron.Actor{}, t} 65 | 66 | receiver := tutil.NewIDAddr(t, 100) 67 | builder := mock.NewBuilder(receiver).WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID) 68 | 69 | t.Run("epoch tick with empty entries", func(t *testing.T) { 70 | rt := builder.Build(t) 71 | 72 | var nilCronEntries = []cron.EntryParam(nil) 73 | actor.constructAndVerify(rt, nilCronEntries...) 74 | actor.epochTickAndVerify(rt) 75 | actor.checkState(rt) 76 | }) 77 | 78 | t.Run("epoch tick with non-empty entries", func(t *testing.T) { 79 | rt := builder.Build(t) 80 | 81 | entry1 := cron.EntryParam{Receiver: tutil.NewIDAddr(t, 1001), MethodNum: abi.MethodNum(1001)} 82 | entry2 := cron.EntryParam{Receiver: tutil.NewIDAddr(t, 1002), MethodNum: abi.MethodNum(1002)} 83 | entry3 := cron.EntryParam{Receiver: tutil.NewIDAddr(t, 1003), MethodNum: abi.MethodNum(1003)} 84 | entry4 := cron.EntryParam{Receiver: tutil.NewIDAddr(t, 1004), MethodNum: abi.MethodNum(1004)} 85 | 86 | actor.constructAndVerify(rt, entry1, entry2, entry3, entry4) 87 | // exit code should not matter 88 | rt.ExpectSend(entry1.Receiver, entry1.MethodNum, nil, big.Zero(), nil, exitcode.Ok) 89 | rt.ExpectSend(entry2.Receiver, entry2.MethodNum, nil, big.Zero(), nil, exitcode.ErrIllegalArgument) 90 | rt.ExpectSend(entry3.Receiver, entry3.MethodNum, nil, big.Zero(), nil, exitcode.ErrInsufficientFunds) 91 | rt.ExpectSend(entry4.Receiver, entry4.MethodNum, nil, big.Zero(), nil, exitcode.ErrForbidden) 92 | actor.epochTickAndVerify(rt) 93 | 94 | actor.checkState(rt) 95 | }) 96 | 97 | t.Run("built-in entries", func(t *testing.T) { 98 | bie := cron.BuiltInEntries() 99 | assert.True(t, len(bie) > 0) 100 | }) 101 | } 102 | 103 | type cronHarness struct { 104 | cron.Actor 105 | t testing.TB 106 | } 107 | 108 | func (h *cronHarness) constructAndVerify(rt *mock.Runtime, entries ...cron.EntryParam) { 109 | params := cron.ConstructorParams{Entries: entries} 110 | rt.ExpectValidateCallerAddr(builtin.SystemActorAddr) 111 | ret := rt.Call(h.Constructor, ¶ms) 112 | assert.Nil(h.t, ret) 113 | rt.Verify() 114 | } 115 | 116 | func (h *cronHarness) epochTickAndVerify(rt *mock.Runtime) { 117 | rt.ExpectValidateCallerAddr(builtin.SystemActorAddr) 118 | ret := rt.Call(h.EpochTick, nil) 119 | assert.Nil(h.t, ret) 120 | rt.Verify() 121 | } 122 | 123 | func (h *cronHarness) checkState(rt *mock.Runtime) { 124 | var st cron.State 125 | rt.GetState(&st) 126 | _, msgs := cron.CheckStateInvariants(&st, rt.AdtStore()) 127 | assert.True(h.t, msgs.IsEmpty()) 128 | } 129 | -------------------------------------------------------------------------------- /actors/builtin/cron/testing.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 6 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 7 | ) 8 | 9 | type StateSummary struct { 10 | EntryCount int 11 | } 12 | 13 | // Checks internal invariants of cron state. 14 | func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) { 15 | acc := &builtin.MessageAccumulator{} 16 | cronSummary := &StateSummary{ 17 | EntryCount: len(st.Entries), 18 | } 19 | for i, e := range st.Entries { 20 | acc.Require(e.Receiver.Protocol() == address.ID, "entry %d receiver address %v must be ID protocol", i, e.Receiver) 21 | acc.Require(e.MethodNum > 0, "entry %d has invalid method number %d", i, e.MethodNum) 22 | } 23 | return cronSummary, acc 24 | } 25 | -------------------------------------------------------------------------------- /actors/builtin/exported/actors.go: -------------------------------------------------------------------------------- 1 | package exported 2 | 3 | import ( 4 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/account" 5 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/cron" 6 | init_ "github.com/filecoin-project/specs-actors/v8/actors/builtin/init" 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/market" 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 9 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/multisig" 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/paych" 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/power" 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/reward" 13 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/system" 14 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/verifreg" 15 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 16 | ) 17 | 18 | func BuiltinActors() []runtime.VMActor { 19 | return []runtime.VMActor{ 20 | account.Actor{}, 21 | cron.Actor{}, 22 | init_.Actor{}, 23 | market.Actor{}, 24 | miner.Actor{}, 25 | multisig.Actor{}, 26 | paych.Actor{}, 27 | power.Actor{}, 28 | reward.Actor{}, 29 | system.Actor{}, 30 | verifreg.Actor{}, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /actors/builtin/exported/actors_test.go: -------------------------------------------------------------------------------- 1 | package exported 2 | 3 | import ( 4 | "reflect" 5 | goruntime "runtime" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/filecoin-project/go-state-types/abi" 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/account" 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/cron" 13 | init_ "github.com/filecoin-project/specs-actors/v8/actors/builtin/init" 14 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/market" 15 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 16 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/multisig" 17 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/paych" 18 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/power" 19 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/reward" 20 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/system" 21 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/verifreg" 22 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 23 | "github.com/ipfs/go-cid" 24 | 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | func TestKnownActors(t *testing.T) { 29 | // Test all known actors. This ensures we: 30 | // * Export all the right actors. 31 | // * Don't get any method mismatches. 32 | 33 | // We can't test this in the builtin package due to cyclic imports, so 34 | // we test it here. 35 | builtins := BuiltinActors() 36 | actorInfos := []struct { 37 | actor runtime.VMActor 38 | code cid.Cid 39 | methods interface{} 40 | }{ 41 | {account.Actor{}, builtin.AccountActorCodeID, builtin.MethodsAccount}, 42 | {cron.Actor{}, builtin.CronActorCodeID, builtin.MethodsCron}, 43 | {init_.Actor{}, builtin.InitActorCodeID, builtin.MethodsInit}, 44 | {market.Actor{}, builtin.StorageMarketActorCodeID, builtin.MethodsMarket}, 45 | {miner.Actor{}, builtin.StorageMinerActorCodeID, builtin.MethodsMiner}, 46 | {multisig.Actor{}, builtin.MultisigActorCodeID, builtin.MethodsMultisig}, 47 | {paych.Actor{}, builtin.PaymentChannelActorCodeID, builtin.MethodsPaych}, 48 | {power.Actor{}, builtin.StoragePowerActorCodeID, builtin.MethodsPower}, 49 | {reward.Actor{}, builtin.RewardActorCodeID, builtin.MethodsReward}, 50 | {system.Actor{}, builtin.SystemActorCodeID, nil}, 51 | {verifreg.Actor{}, builtin.VerifiedRegistryActorCodeID, builtin.MethodsVerifiedRegistry}, 52 | } 53 | require.Equal(t, len(builtins), len(actorInfos)) 54 | for i, info := range actorInfos { 55 | // check exported actors. 56 | require.Equal(t, info.actor, builtins[i]) 57 | 58 | // check codes. 59 | require.Equal(t, info.code, info.actor.Code()) 60 | 61 | // check methods. 62 | exports := info.actor.Exports() 63 | if info.methods == nil { 64 | continue 65 | } 66 | methodsVal := reflect.ValueOf(info.methods) 67 | methodsTyp := methodsVal.Type() 68 | require.Equal(t, len(exports)-1, methodsVal.NumField()) 69 | require.Nil(t, exports[0]) // send. 70 | for i, m := range exports { 71 | if i == 0 { 72 | // send 73 | require.Nil(t, m) 74 | continue 75 | } 76 | expectedVal := methodsVal.Field(i - 1) 77 | expectedName := methodsTyp.Field(i - 1).Name 78 | 79 | require.Equal(t, expectedVal.Interface().(abi.MethodNum), abi.MethodNum(i)) 80 | 81 | if m == nil { 82 | // not send, must be deprecated. 83 | require.True(t, strings.HasPrefix(expectedName, "Deprecated")) 84 | continue 85 | } 86 | 87 | name := goruntime.FuncForPC(reflect.ValueOf(m).Pointer()).Name() 88 | name = strings.TrimSuffix(name, "-fm") 89 | lastDot := strings.LastIndexByte(name, '.') 90 | name = name[lastDot+1:] 91 | require.Equal(t, expectedName, name) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /actors/builtin/init/cbor_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 | 3 | package init 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | 9 | abi "github.com/filecoin-project/go-state-types/abi" 10 | cbg "github.com/whyrusleeping/cbor-gen" 11 | xerrors "golang.org/x/xerrors" 12 | ) 13 | 14 | var _ = xerrors.Errorf 15 | 16 | var lengthBufState = []byte{131} 17 | 18 | func (t *State) MarshalCBOR(w io.Writer) error { 19 | if t == nil { 20 | _, err := w.Write(cbg.CborNull) 21 | return err 22 | } 23 | if _, err := w.Write(lengthBufState); err != nil { 24 | return err 25 | } 26 | 27 | scratch := make([]byte, 9) 28 | 29 | // t.AddressMap (cid.Cid) (struct) 30 | 31 | if err := cbg.WriteCidBuf(scratch, w, t.AddressMap); err != nil { 32 | return xerrors.Errorf("failed to write cid field t.AddressMap: %w", err) 33 | } 34 | 35 | // t.NextID (abi.ActorID) (uint64) 36 | 37 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.NextID)); err != nil { 38 | return err 39 | } 40 | 41 | // t.NetworkName (string) (string) 42 | if len(t.NetworkName) > cbg.MaxLength { 43 | return xerrors.Errorf("Value in field t.NetworkName was too long") 44 | } 45 | 46 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.NetworkName))); err != nil { 47 | return err 48 | } 49 | if _, err := io.WriteString(w, string(t.NetworkName)); err != nil { 50 | return err 51 | } 52 | return nil 53 | } 54 | 55 | func (t *State) UnmarshalCBOR(r io.Reader) error { 56 | *t = State{} 57 | 58 | br := cbg.GetPeeker(r) 59 | scratch := make([]byte, 8) 60 | 61 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 62 | if err != nil { 63 | return err 64 | } 65 | if maj != cbg.MajArray { 66 | return fmt.Errorf("cbor input should be of type array") 67 | } 68 | 69 | if extra != 3 { 70 | return fmt.Errorf("cbor input had wrong number of fields") 71 | } 72 | 73 | // t.AddressMap (cid.Cid) (struct) 74 | 75 | { 76 | 77 | c, err := cbg.ReadCid(br) 78 | if err != nil { 79 | return xerrors.Errorf("failed to read cid field t.AddressMap: %w", err) 80 | } 81 | 82 | t.AddressMap = c 83 | 84 | } 85 | // t.NextID (abi.ActorID) (uint64) 86 | 87 | { 88 | 89 | maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) 90 | if err != nil { 91 | return err 92 | } 93 | if maj != cbg.MajUnsignedInt { 94 | return fmt.Errorf("wrong type for uint64 field") 95 | } 96 | t.NextID = abi.ActorID(extra) 97 | 98 | } 99 | // t.NetworkName (string) (string) 100 | 101 | { 102 | sval, err := cbg.ReadStringBuf(br, scratch) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | t.NetworkName = string(sval) 108 | } 109 | return nil 110 | } 111 | -------------------------------------------------------------------------------- /actors/builtin/init/init_actor.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/cbor" 7 | "github.com/filecoin-project/go-state-types/exitcode" 8 | init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" 9 | cid "github.com/ipfs/go-cid" 10 | 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 12 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 13 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 14 | ) 15 | 16 | // The init actor uniquely has the power to create new actors. 17 | // It maintains a table resolving pubkey and temporary actor addresses to the canonical ID-addresses. 18 | type Actor struct{} 19 | 20 | func (a Actor) Exports() []interface{} { 21 | return []interface{}{ 22 | builtin.MethodConstructor: a.Constructor, 23 | 2: a.Exec, 24 | } 25 | } 26 | 27 | func (a Actor) Code() cid.Cid { 28 | return builtin.InitActorCodeID 29 | } 30 | 31 | func (a Actor) IsSingleton() bool { 32 | return true 33 | } 34 | 35 | func (a Actor) State() cbor.Er { return new(State) } 36 | 37 | var _ runtime.VMActor = Actor{} 38 | 39 | //type ConstructorParams struct { 40 | // NetworkName string 41 | //} 42 | type ConstructorParams = init0.ConstructorParams 43 | 44 | func (a Actor) Constructor(rt runtime.Runtime, params *ConstructorParams) *abi.EmptyValue { 45 | rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 46 | st, err := ConstructState(adt.AsStore(rt), params.NetworkName) 47 | builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to construct state") 48 | rt.StateCreate(st) 49 | return nil 50 | } 51 | 52 | //type ExecParams struct { 53 | // CodeCID cid.Cid `checked:"true"` // invalid CIDs won't get committed to the state tree 54 | // ConstructorParams []byte 55 | //} 56 | type ExecParams = init0.ExecParams 57 | 58 | //type ExecReturn struct { 59 | // IDAddress addr.Address // The canonical ID-based address for the actor. 60 | // RobustAddress addr.Address // A more expensive but re-org-safe address for the newly created actor. 61 | //} 62 | type ExecReturn = init0.ExecReturn 63 | 64 | func (a Actor) Exec(rt runtime.Runtime, params *ExecParams) *ExecReturn { 65 | rt.ValidateImmediateCallerAcceptAny() 66 | callerCodeCID, ok := rt.GetActorCodeCID(rt.Caller()) 67 | builtin.RequireState(rt, ok, "no code for caller at %s", rt.Caller()) 68 | if !canExec(callerCodeCID, params.CodeCID) { 69 | rt.Abortf(exitcode.ErrForbidden, "caller type %v cannot exec actor type %v", callerCodeCID, params.CodeCID) 70 | } 71 | 72 | // Compute a re-org-stable address. 73 | // This address exists for use by messages coming from outside the system, in order to 74 | // stably address the newly created actor even if a chain re-org causes it to end up with 75 | // a different ID. 76 | uniqueAddress := rt.NewActorAddress() 77 | 78 | // Allocate an ID for this actor. 79 | // Store mapping of pubkey or actor address to actor ID 80 | var st State 81 | var idAddr addr.Address 82 | rt.StateTransaction(&st, func() { 83 | var err error 84 | idAddr, err = st.MapAddressToNewID(adt.AsStore(rt), uniqueAddress) 85 | builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to allocate ID address") 86 | }) 87 | 88 | // Create an empty actor. 89 | rt.CreateActor(params.CodeCID, idAddr) 90 | 91 | // Invoke constructor. 92 | code := rt.Send(idAddr, builtin.MethodConstructor, builtin.CBORBytes(params.ConstructorParams), rt.ValueReceived(), &builtin.Discard{}) 93 | builtin.RequireSuccess(rt, code, "constructor failed") 94 | 95 | return &ExecReturn{IDAddress: idAddr, RobustAddress: uniqueAddress} 96 | } 97 | 98 | func canExec(callerCodeID cid.Cid, execCodeID cid.Cid) bool { 99 | switch execCodeID { 100 | case builtin.StorageMinerActorCodeID: 101 | if callerCodeID == builtin.StoragePowerActorCodeID { 102 | return true 103 | } 104 | return false 105 | case builtin.PaymentChannelActorCodeID, builtin.MultisigActorCodeID: 106 | return true 107 | default: 108 | return false 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /actors/builtin/init/init_actor_state.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | cid "github.com/ipfs/go-cid" 7 | cbg "github.com/whyrusleeping/cbor-gen" 8 | xerrors "golang.org/x/xerrors" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 12 | ) 13 | 14 | type State struct { 15 | AddressMap cid.Cid // HAMT[addr.Address]abi.ActorID 16 | NextID abi.ActorID 17 | NetworkName string 18 | } 19 | 20 | func ConstructState(store adt.Store, networkName string) (*State, error) { 21 | emptyAddressMapCid, err := adt.StoreEmptyMap(store, builtin.DefaultHamtBitwidth) 22 | if err != nil { 23 | return nil, xerrors.Errorf("failed to create empty map: %w", err) 24 | } 25 | 26 | return &State{ 27 | AddressMap: emptyAddressMapCid, 28 | NextID: abi.ActorID(builtin.FirstNonSingletonActorId), 29 | NetworkName: networkName, 30 | }, nil 31 | } 32 | 33 | // ResolveAddress resolves an address to an ID-address, if possible. 34 | // If the provided address is an ID address, it is returned as-is. 35 | // This means that mapped ID-addresses (which should only appear as values, not keys) and 36 | // singleton actor addresses (which are not in the map) pass through unchanged. 37 | // 38 | // Returns an ID-address and `true` if the address was already an ID-address or was resolved in the mapping. 39 | // Returns an undefined address and `false` if the address was not an ID-address and not found in the mapping. 40 | // Returns an error only if state was inconsistent. 41 | func (s *State) ResolveAddress(store adt.Store, address addr.Address) (addr.Address, bool, error) { 42 | // Short-circuit ID address resolution. 43 | if address.Protocol() == addr.ID { 44 | return address, true, nil 45 | } 46 | 47 | // Lookup address. 48 | m, err := adt.AsMap(store, s.AddressMap, builtin.DefaultHamtBitwidth) 49 | if err != nil { 50 | return addr.Undef, false, xerrors.Errorf("failed to load address map: %w", err) 51 | } 52 | 53 | var actorID cbg.CborInt 54 | if found, err := m.Get(abi.AddrKey(address), &actorID); err != nil { 55 | return addr.Undef, false, xerrors.Errorf("failed to get from address map: %w", err) 56 | } else if found { 57 | // Reconstruct address from the ActorID. 58 | idAddr, err := addr.NewIDAddress(uint64(actorID)) 59 | return idAddr, true, err 60 | } else { 61 | return addr.Undef, false, nil 62 | } 63 | } 64 | 65 | // Allocates a new ID address and stores a mapping of the argument address to it. 66 | // Returns the newly-allocated address. 67 | func (s *State) MapAddressToNewID(store adt.Store, address addr.Address) (addr.Address, error) { 68 | actorID := cbg.CborInt(s.NextID) 69 | s.NextID++ 70 | 71 | m, err := adt.AsMap(store, s.AddressMap, builtin.DefaultHamtBitwidth) 72 | if err != nil { 73 | return addr.Undef, xerrors.Errorf("failed to load address map: %w", err) 74 | } 75 | err = m.Put(abi.AddrKey(address), &actorID) 76 | if err != nil { 77 | return addr.Undef, xerrors.Errorf("map address failed to store entry: %w", err) 78 | } 79 | amr, err := m.Root() 80 | if err != nil { 81 | return addr.Undef, xerrors.Errorf("failed to get address map root: %w", err) 82 | } 83 | s.AddressMap = amr 84 | 85 | idAddr, err := addr.NewIDAddress(uint64(actorID)) 86 | return idAddr, err 87 | } 88 | -------------------------------------------------------------------------------- /actors/builtin/init/testing.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | cbg "github.com/whyrusleeping/cbor-gen" 7 | 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 9 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 10 | ) 11 | 12 | type StateSummary struct { 13 | AddrIDs map[addr.Address]abi.ActorID 14 | NextID abi.ActorID 15 | } 16 | 17 | // Checks internal invariants of init state. 18 | func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) { 19 | acc := &builtin.MessageAccumulator{} 20 | 21 | acc.Require(len(st.NetworkName) > 0, "network name is empty") 22 | acc.Require(st.NextID >= builtin.FirstNonSingletonActorId, "next id %d is too low", st.NextID) 23 | 24 | initSummary := &StateSummary{ 25 | AddrIDs: nil, 26 | NextID: st.NextID, 27 | } 28 | 29 | lut, err := adt.AsMap(store, st.AddressMap, builtin.DefaultHamtBitwidth) 30 | if err != nil { 31 | acc.Addf("error loading address map: %v", err) 32 | // Stop here, it's hard to make other useful checks. 33 | return initSummary, acc 34 | } 35 | 36 | initSummary.AddrIDs = map[addr.Address]abi.ActorID{} 37 | reverse := map[abi.ActorID]addr.Address{} 38 | var value cbg.CborInt 39 | err = lut.ForEach(&value, func(key string) error { 40 | actorId := abi.ActorID(value) 41 | keyAddr, err := addr.NewFromBytes([]byte(key)) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | acc.Require(keyAddr.Protocol() != addr.ID, "key %v is an ID address", keyAddr) 47 | acc.Require(keyAddr.Protocol() <= addr.BLS, "unknown address protocol for key %v", keyAddr) 48 | acc.Require(actorId >= builtin.FirstNonSingletonActorId, "unexpected singleton ID value %v", actorId) 49 | 50 | foundAddr, found := reverse[actorId] 51 | acc.Require(!found, "duplicate mapping to ID %v: %v, %v", actorId, keyAddr, foundAddr) 52 | reverse[actorId] = keyAddr 53 | 54 | initSummary.AddrIDs[keyAddr] = actorId 55 | return nil 56 | }) 57 | acc.RequireNoError(err, "error iterating address map") 58 | return initSummary, acc 59 | } 60 | -------------------------------------------------------------------------------- /actors/builtin/manifest/cbor_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 | 3 | package manifest 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | 9 | cbg "github.com/whyrusleeping/cbor-gen" 10 | xerrors "golang.org/x/xerrors" 11 | ) 12 | 13 | var _ = xerrors.Errorf 14 | 15 | var lengthBufManifest = []byte{130} 16 | 17 | func (t *Manifest) MarshalCBOR(w io.Writer) error { 18 | if t == nil { 19 | _, err := w.Write(cbg.CborNull) 20 | return err 21 | } 22 | if _, err := w.Write(lengthBufManifest); err != nil { 23 | return err 24 | } 25 | 26 | scratch := make([]byte, 9) 27 | 28 | // t.Version (uint64) (uint64) 29 | 30 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Version)); err != nil { 31 | return err 32 | } 33 | 34 | // t.Data (cid.Cid) (struct) 35 | 36 | if err := cbg.WriteCidBuf(scratch, w, t.Data); err != nil { 37 | return xerrors.Errorf("failed to write cid field t.Data: %w", err) 38 | } 39 | 40 | return nil 41 | } 42 | 43 | func (t *Manifest) UnmarshalCBOR(r io.Reader) error { 44 | *t = Manifest{} 45 | 46 | br := cbg.GetPeeker(r) 47 | scratch := make([]byte, 8) 48 | 49 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 50 | if err != nil { 51 | return err 52 | } 53 | if maj != cbg.MajArray { 54 | return fmt.Errorf("cbor input should be of type array") 55 | } 56 | 57 | if extra != 2 { 58 | return fmt.Errorf("cbor input had wrong number of fields") 59 | } 60 | 61 | // t.Version (uint64) (uint64) 62 | 63 | { 64 | 65 | maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) 66 | if err != nil { 67 | return err 68 | } 69 | if maj != cbg.MajUnsignedInt { 70 | return fmt.Errorf("wrong type for uint64 field") 71 | } 72 | t.Version = uint64(extra) 73 | 74 | } 75 | // t.Data (cid.Cid) (struct) 76 | 77 | { 78 | 79 | c, err := cbg.ReadCid(br) 80 | if err != nil { 81 | return xerrors.Errorf("failed to read cid field t.Data: %w", err) 82 | } 83 | 84 | t.Data = c 85 | 86 | } 87 | return nil 88 | } 89 | 90 | var lengthBufManifestEntry = []byte{130} 91 | 92 | func (t *ManifestEntry) MarshalCBOR(w io.Writer) error { 93 | if t == nil { 94 | _, err := w.Write(cbg.CborNull) 95 | return err 96 | } 97 | if _, err := w.Write(lengthBufManifestEntry); err != nil { 98 | return err 99 | } 100 | 101 | scratch := make([]byte, 9) 102 | 103 | // t.Name (string) (string) 104 | if len(t.Name) > cbg.MaxLength { 105 | return xerrors.Errorf("Value in field t.Name was too long") 106 | } 107 | 108 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Name))); err != nil { 109 | return err 110 | } 111 | if _, err := io.WriteString(w, string(t.Name)); err != nil { 112 | return err 113 | } 114 | 115 | // t.Code (cid.Cid) (struct) 116 | 117 | if err := cbg.WriteCidBuf(scratch, w, t.Code); err != nil { 118 | return xerrors.Errorf("failed to write cid field t.Code: %w", err) 119 | } 120 | 121 | return nil 122 | } 123 | 124 | func (t *ManifestEntry) UnmarshalCBOR(r io.Reader) error { 125 | *t = ManifestEntry{} 126 | 127 | br := cbg.GetPeeker(r) 128 | scratch := make([]byte, 8) 129 | 130 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 131 | if err != nil { 132 | return err 133 | } 134 | if maj != cbg.MajArray { 135 | return fmt.Errorf("cbor input should be of type array") 136 | } 137 | 138 | if extra != 2 { 139 | return fmt.Errorf("cbor input had wrong number of fields") 140 | } 141 | 142 | // t.Name (string) (string) 143 | 144 | { 145 | sval, err := cbg.ReadStringBuf(br, scratch) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | t.Name = string(sval) 151 | } 152 | // t.Code (cid.Cid) (struct) 153 | 154 | { 155 | 156 | c, err := cbg.ReadCid(br) 157 | if err != nil { 158 | return xerrors.Errorf("failed to read cid field t.Code: %w", err) 159 | } 160 | 161 | t.Code = c 162 | 163 | } 164 | return nil 165 | } 166 | -------------------------------------------------------------------------------- /actors/builtin/manifest/manifest.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | 8 | adt8 "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 9 | 10 | "github.com/ipfs/go-cid" 11 | 12 | cbg "github.com/whyrusleeping/cbor-gen" 13 | ) 14 | 15 | type Manifest struct { 16 | Version uint64 // this is really u32, but cbor-gen can't deal with it 17 | Data cid.Cid 18 | 19 | entries map[string]cid.Cid 20 | } 21 | 22 | type ManifestEntry struct { 23 | Name string 24 | Code cid.Cid 25 | } 26 | 27 | type ManifestData struct { 28 | Entries []ManifestEntry 29 | } 30 | 31 | func (m *Manifest) Load(ctx context.Context, store adt8.Store) error { 32 | if m.Version != 1 { 33 | return fmt.Errorf("unknown manifest version %d", m.Version) 34 | } 35 | 36 | data := ManifestData{} 37 | if err := store.Get(ctx, m.Data, &data); err != nil { 38 | return err 39 | } 40 | 41 | m.entries = make(map[string]cid.Cid) 42 | for _, e := range data.Entries { 43 | m.entries[e.Name] = e.Code 44 | } 45 | 46 | return nil 47 | } 48 | 49 | func (m *Manifest) Get(name string) (cid.Cid, bool) { 50 | c, ok := m.entries[name] 51 | return c, ok 52 | } 53 | 54 | // this is a flat tuple, so we need to write these by hand 55 | func (d *ManifestData) UnmarshalCBOR(r io.Reader) error { 56 | *d = ManifestData{} 57 | 58 | br := cbg.GetPeeker(r) 59 | scratch := make([]byte, 8) 60 | 61 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 62 | if err != nil { 63 | return err 64 | } 65 | if maj != cbg.MajArray { 66 | return fmt.Errorf("cbor input should be of type array") 67 | } 68 | 69 | if extra > cbg.MaxLength { 70 | return fmt.Errorf("too many manifest entries") 71 | } 72 | 73 | entries := int(extra) 74 | d.Entries = make([]ManifestEntry, 0, entries) 75 | 76 | for i := 0; i < entries; i++ { 77 | entry := ManifestEntry{} 78 | if err := entry.UnmarshalCBOR(r); err != nil { 79 | return fmt.Errorf("error unmarsnalling manifest entry: %w", err) 80 | } 81 | 82 | d.Entries = append(d.Entries, entry) 83 | } 84 | 85 | return nil 86 | } 87 | 88 | func (d *ManifestData) MarshalCBOR(w io.Writer) error { 89 | if d == nil { 90 | _, err := w.Write(cbg.CborNull) 91 | return err 92 | } 93 | 94 | scratch := make([]byte, 9) 95 | 96 | if len(d.Entries) > cbg.MaxLength { 97 | return fmt.Errorf("too many manifest entries") 98 | } 99 | 100 | if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(d.Entries))); err != nil { 101 | return err 102 | } 103 | 104 | for _, v := range d.Entries { 105 | if err := v.MarshalCBOR(w); err != nil { 106 | return err 107 | } 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /actors/builtin/market/market_balances.go: -------------------------------------------------------------------------------- 1 | package market 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | "github.com/filecoin-project/go-state-types/exitcode" 8 | "golang.org/x/xerrors" 9 | ) 10 | 11 | func (m *marketStateMutation) lockClientAndProviderBalances(proposal *DealProposal) error { 12 | if err := m.maybeLockBalance(proposal.Client, proposal.ClientBalanceRequirement()); err != nil { 13 | return xerrors.Errorf("failed to lock client funds: %w", err) 14 | } 15 | if err := m.maybeLockBalance(proposal.Provider, proposal.ProviderCollateral); err != nil { 16 | return xerrors.Errorf("failed to lock provider funds: %w", err) 17 | } 18 | 19 | m.totalClientLockedCollateral = big.Add(m.totalClientLockedCollateral, proposal.ClientCollateral) 20 | m.totalClientStorageFee = big.Add(m.totalClientStorageFee, proposal.TotalStorageFee()) 21 | m.totalProviderLockedCollateral = big.Add(m.totalProviderLockedCollateral, proposal.ProviderCollateral) 22 | return nil 23 | } 24 | 25 | func (m *marketStateMutation) unlockBalance(addr addr.Address, amount abi.TokenAmount, lockReason BalanceLockingReason) error { 26 | if amount.LessThan(big.Zero()) { 27 | return xerrors.Errorf("unlock negative amount %v", amount) 28 | } 29 | 30 | err := m.lockedTable.MustSubtract(addr, amount) 31 | if err != nil { 32 | return xerrors.Errorf("subtracting from locked balance: %w", err) 33 | } 34 | 35 | switch lockReason { 36 | case ClientCollateral: 37 | m.totalClientLockedCollateral = big.Sub(m.totalClientLockedCollateral, amount) 38 | case ClientStorageFee: 39 | m.totalClientStorageFee = big.Sub(m.totalClientStorageFee, amount) 40 | case ProviderCollateral: 41 | m.totalProviderLockedCollateral = big.Sub(m.totalProviderLockedCollateral, amount) 42 | } 43 | 44 | return nil 45 | } 46 | 47 | // move funds from locked in client to available in provider 48 | func (m *marketStateMutation) transferBalance(fromAddr addr.Address, toAddr addr.Address, amount abi.TokenAmount) error { 49 | if amount.LessThan(big.Zero()) { 50 | return xerrors.Errorf("transfer negative amount %v", amount) 51 | } 52 | if err := m.escrowTable.MustSubtract(fromAddr, amount); err != nil { 53 | return xerrors.Errorf("subtract from escrow: %w", err) 54 | } 55 | if err := m.unlockBalance(fromAddr, amount, ClientStorageFee); err != nil { 56 | return xerrors.Errorf("subtract from locked: %w", err) 57 | } 58 | if err := m.escrowTable.Add(toAddr, amount); err != nil { 59 | return xerrors.Errorf("add to escrow: %w", err) 60 | } 61 | return nil 62 | } 63 | 64 | func (m *marketStateMutation) slashBalance(addr addr.Address, amount abi.TokenAmount, reason BalanceLockingReason) error { 65 | if amount.LessThan(big.Zero()) { 66 | return xerrors.Errorf("negative amount to slash: %v", amount) 67 | } 68 | 69 | if err := m.escrowTable.MustSubtract(addr, amount); err != nil { 70 | return xerrors.Errorf("subtract from escrow: %v", err) 71 | } 72 | 73 | return m.unlockBalance(addr, amount, reason) 74 | } 75 | 76 | func (m *marketStateMutation) maybeLockBalance(addr addr.Address, amount abi.TokenAmount) error { 77 | if amount.LessThan(big.Zero()) { 78 | return xerrors.Errorf("cannot lock negative amount %v", amount) 79 | } 80 | 81 | prevLocked, err := m.lockedTable.Get(addr) 82 | if err != nil { 83 | return xerrors.Errorf("failed to get locked balance: %w", err) 84 | } 85 | 86 | escrowBalance, err := m.escrowTable.Get(addr) 87 | if err != nil { 88 | return xerrors.Errorf("failed to get escrow balance: %w", err) 89 | } 90 | 91 | if big.Add(prevLocked, amount).GreaterThan(escrowBalance) { 92 | return exitcode.ErrInsufficientFunds.Wrapf("insufficient balance for addr %s: escrow balance %s < locked %s + required %s", 93 | addr, escrowBalance, prevLocked, amount) 94 | } 95 | 96 | if err := m.lockedTable.Add(addr, amount); err != nil { 97 | return xerrors.Errorf("failed to add locked balance: %w", err) 98 | } 99 | return nil 100 | } 101 | 102 | // Return true when the funds in escrow for the input address can cover an additional lockup of amountToLock 103 | func (m *marketStateMutation) balanceCovered(addr addr.Address, amountToLock abi.TokenAmount) (bool, error) { 104 | prevLocked, err := m.lockedTable.Get(addr) 105 | if err != nil { 106 | return false, xerrors.Errorf("failed to get locked balance: %w", err) 107 | } 108 | escrowBalance, err := m.escrowTable.Get(addr) 109 | if err != nil { 110 | return false, xerrors.Errorf("failed to get escrow balance: %w", err) 111 | } 112 | return big.Add(prevLocked, amountToLock).LessThanEqual(escrowBalance), nil 113 | } 114 | -------------------------------------------------------------------------------- /actors/builtin/market/policy.go: -------------------------------------------------------------------------------- 1 | package market 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | "github.com/filecoin-project/go-state-types/big" 6 | 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 8 | ) 9 | 10 | // The number of epochs between payment and other state processing for deals. 11 | const DealUpdatesInterval = builtin.EpochsInDay // PARAM_SPEC 12 | 13 | // The percentage of normalized cirulating 14 | // supply that must be covered by provider collateral in a deal 15 | var ProviderCollateralSupplyTarget = builtin.BigFrac{ 16 | Numerator: big.NewInt(1), // PARAM_SPEC 17 | Denominator: big.NewInt(100), 18 | } 19 | 20 | // Minimum deal duration. 21 | var DealMinDuration = abi.ChainEpoch(180 * builtin.EpochsInDay) // PARAM_SPEC 22 | 23 | // Maximum deal duration 24 | var DealMaxDuration = abi.ChainEpoch(540 * builtin.EpochsInDay) // PARAM_SPEC 25 | 26 | // DealMaxLabelSize is the maximum size of a deal label. 27 | const DealMaxLabelSize = 256 28 | 29 | // Bounds (inclusive) on deal duration 30 | func DealDurationBounds(_ abi.PaddedPieceSize) (min abi.ChainEpoch, max abi.ChainEpoch) { 31 | return DealMinDuration, DealMaxDuration 32 | } 33 | 34 | func DealPricePerEpochBounds(_ abi.PaddedPieceSize, _ abi.ChainEpoch) (min abi.TokenAmount, max abi.TokenAmount) { 35 | return abi.NewTokenAmount(0), builtin.TotalFilecoin 36 | } 37 | 38 | func DealProviderCollateralBounds(pieceSize abi.PaddedPieceSize, verified bool, networkRawPower, networkQAPower, baselinePower abi.StoragePower, 39 | networkCirculatingSupply abi.TokenAmount) (min, max abi.TokenAmount) { 40 | // minimumProviderCollateral = ProviderCollateralSupplyTarget * normalizedCirculatingSupply 41 | // normalizedCirculatingSupply = networkCirculatingSupply * dealPowerShare 42 | // dealPowerShare = dealRawPower / max(BaselinePower(t), NetworkRawPower(t), dealRawPower) 43 | 44 | lockTargetNum := big.Mul(ProviderCollateralSupplyTarget.Numerator, networkCirculatingSupply) 45 | lockTargetDenom := ProviderCollateralSupplyTarget.Denominator 46 | powerShareNum := big.NewIntUnsigned(uint64(pieceSize)) 47 | powerShareDenom := big.Max(big.Max(networkRawPower, baselinePower), powerShareNum) 48 | 49 | num := big.Mul(lockTargetNum, powerShareNum) 50 | denom := big.Mul(lockTargetDenom, powerShareDenom) 51 | minCollateral := big.Div(num, denom) 52 | return minCollateral, builtin.TotalFilecoin 53 | } 54 | 55 | func DealClientCollateralBounds(_ abi.PaddedPieceSize, _ abi.ChainEpoch) (min abi.TokenAmount, max abi.TokenAmount) { 56 | return abi.NewTokenAmount(0), builtin.TotalFilecoin 57 | } 58 | 59 | // Penalty to provider deal collateral if the deadline expires before sector commitment. 60 | func CollateralPenaltyForDealActivationMissed(providerCollateral abi.TokenAmount) abi.TokenAmount { 61 | return providerCollateral 62 | } 63 | 64 | // Computes the weight for a deal proposal, which is a function of its size and duration. 65 | func DealWeight(proposal *DealProposal) abi.DealWeight { 66 | dealDuration := big.NewInt(int64(proposal.Duration())) 67 | dealSize := big.NewIntUnsigned(uint64(proposal.PieceSize)) 68 | dealSpaceTime := big.Mul(dealDuration, dealSize) 69 | return dealSpaceTime 70 | } 71 | -------------------------------------------------------------------------------- /actors/builtin/market/types.go: -------------------------------------------------------------------------------- 1 | package market 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | . "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 6 | 7 | "github.com/ipfs/go-cid" 8 | ) 9 | 10 | // A specialization of a array to deals. 11 | // It is an error to query for a key that doesn't exist. 12 | type DealArray struct { 13 | *Array 14 | } 15 | 16 | // Interprets a store as balance table with root `r`. 17 | func AsDealProposalArray(s Store, r cid.Cid) (*DealArray, error) { 18 | a, err := AsArray(s, r, ProposalsAmtBitwidth) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return &DealArray{a}, nil 23 | } 24 | 25 | // Returns the root cid of underlying AMT. 26 | func (t *DealArray) Root() (cid.Cid, error) { 27 | return t.Array.Root() 28 | } 29 | 30 | // Gets the deal for a key. The entry must have been previously initialized. 31 | func (t *DealArray) Get(id abi.DealID) (*DealProposal, bool, error) { 32 | var value DealProposal 33 | found, err := t.Array.Get(uint64(id), &value) 34 | return &value, found, err 35 | } 36 | 37 | func (t *DealArray) Set(k abi.DealID, value *DealProposal) error { 38 | return t.Array.Set(uint64(k), value) 39 | } 40 | 41 | func (t *DealArray) Delete(id abi.DealID) error { 42 | return t.Array.Delete(uint64(id)) 43 | } 44 | 45 | // A specialization of a array to deals. 46 | // It is an error to query for a key that doesn't exist. 47 | type DealMetaArray struct { 48 | *Array 49 | } 50 | 51 | type DealState struct { 52 | SectorStartEpoch abi.ChainEpoch // -1 if not yet included in proven sector 53 | LastUpdatedEpoch abi.ChainEpoch // -1 if deal state never updated 54 | SlashEpoch abi.ChainEpoch // -1 if deal never slashed 55 | } 56 | 57 | // Interprets a store as balance table with root `r`. 58 | func AsDealStateArray(s Store, r cid.Cid) (*DealMetaArray, error) { 59 | dsa, err := AsArray(s, r, StatesAmtBitwidth) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return &DealMetaArray{dsa}, nil 65 | } 66 | 67 | // Returns the root cid of underlying AMT. 68 | func (t *DealMetaArray) Root() (cid.Cid, error) { 69 | return t.Array.Root() 70 | } 71 | 72 | // Gets the deal for a key. The entry must have been previously initialized. 73 | func (t *DealMetaArray) Get(id abi.DealID) (*DealState, bool, error) { 74 | var value DealState 75 | found, err := t.Array.Get(uint64(id), &value) 76 | if err != nil { 77 | return nil, false, err // The errors from Map carry good information, no need to wrap here. 78 | } 79 | if !found { 80 | return &DealState{ 81 | SectorStartEpoch: EpochUndefined, 82 | LastUpdatedEpoch: EpochUndefined, 83 | SlashEpoch: EpochUndefined, 84 | }, false, nil 85 | } 86 | return &value, true, nil 87 | } 88 | 89 | func (t *DealMetaArray) Set(k abi.DealID, value *DealState) error { 90 | return t.Array.Set(uint64(k), value) 91 | } 92 | 93 | func (t *DealMetaArray) Delete(id abi.DealID) error { 94 | return t.Array.Delete(uint64(id)) 95 | } 96 | -------------------------------------------------------------------------------- /actors/builtin/methods.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" 6 | ) 7 | 8 | const ( 9 | MethodSend = builtin0.MethodSend 10 | MethodConstructor = builtin0.MethodConstructor 11 | ) 12 | 13 | var MethodsAccount = struct { 14 | Constructor abi.MethodNum 15 | PubkeyAddress abi.MethodNum 16 | }{MethodConstructor, 2} 17 | 18 | var MethodsInit = struct { 19 | Constructor abi.MethodNum 20 | Exec abi.MethodNum 21 | }{MethodConstructor, 2} 22 | 23 | var MethodsCron = struct { 24 | Constructor abi.MethodNum 25 | EpochTick abi.MethodNum 26 | }{MethodConstructor, 2} 27 | 28 | var MethodsReward = struct { 29 | Constructor abi.MethodNum 30 | AwardBlockReward abi.MethodNum 31 | ThisEpochReward abi.MethodNum 32 | UpdateNetworkKPI abi.MethodNum 33 | }{MethodConstructor, 2, 3, 4} 34 | 35 | var MethodsMultisig = struct { 36 | Constructor abi.MethodNum 37 | Propose abi.MethodNum 38 | Approve abi.MethodNum 39 | Cancel abi.MethodNum 40 | AddSigner abi.MethodNum 41 | RemoveSigner abi.MethodNum 42 | SwapSigner abi.MethodNum 43 | ChangeNumApprovalsThreshold abi.MethodNum 44 | LockBalance abi.MethodNum 45 | }{MethodConstructor, 2, 3, 4, 5, 6, 7, 8, 9} 46 | 47 | var MethodsPaych = struct { 48 | Constructor abi.MethodNum 49 | UpdateChannelState abi.MethodNum 50 | Settle abi.MethodNum 51 | Collect abi.MethodNum 52 | }{MethodConstructor, 2, 3, 4} 53 | 54 | var MethodsMarket = struct { 55 | Constructor abi.MethodNum 56 | AddBalance abi.MethodNum 57 | WithdrawBalance abi.MethodNum 58 | PublishStorageDeals abi.MethodNum 59 | VerifyDealsForActivation abi.MethodNum 60 | ActivateDeals abi.MethodNum 61 | OnMinerSectorsTerminate abi.MethodNum 62 | ComputeDataCommitment abi.MethodNum 63 | CronTick abi.MethodNum 64 | }{MethodConstructor, 2, 3, 4, 5, 6, 7, 8, 9} 65 | 66 | var MethodsPower = struct { 67 | Constructor abi.MethodNum 68 | CreateMiner abi.MethodNum 69 | UpdateClaimedPower abi.MethodNum 70 | EnrollCronEvent abi.MethodNum 71 | CronTick abi.MethodNum 72 | UpdatePledgeTotal abi.MethodNum 73 | Deprecated1 abi.MethodNum 74 | SubmitPoRepForBulkVerify abi.MethodNum 75 | CurrentTotalPower abi.MethodNum 76 | }{MethodConstructor, 2, 3, 4, 5, 6, 7, 8, 9} 77 | 78 | var MethodsMiner = struct { 79 | Constructor abi.MethodNum 80 | ControlAddresses abi.MethodNum 81 | ChangeWorkerAddress abi.MethodNum 82 | ChangePeerID abi.MethodNum 83 | SubmitWindowedPoSt abi.MethodNum 84 | PreCommitSector abi.MethodNum 85 | ProveCommitSector abi.MethodNum 86 | ExtendSectorExpiration abi.MethodNum 87 | TerminateSectors abi.MethodNum 88 | DeclareFaults abi.MethodNum 89 | DeclareFaultsRecovered abi.MethodNum 90 | OnDeferredCronEvent abi.MethodNum 91 | CheckSectorProven abi.MethodNum 92 | ApplyRewards abi.MethodNum 93 | ReportConsensusFault abi.MethodNum 94 | WithdrawBalance abi.MethodNum 95 | ConfirmSectorProofsValid abi.MethodNum 96 | ChangeMultiaddrs abi.MethodNum 97 | CompactPartitions abi.MethodNum 98 | CompactSectorNumbers abi.MethodNum 99 | ConfirmUpdateWorkerKey abi.MethodNum 100 | RepayDebt abi.MethodNum 101 | ChangeOwnerAddress abi.MethodNum 102 | DisputeWindowedPoSt abi.MethodNum 103 | PreCommitSectorBatch abi.MethodNum 104 | ProveCommitAggregate abi.MethodNum 105 | ProveReplicaUpdates abi.MethodNum 106 | }{MethodConstructor, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27} 107 | 108 | var MethodsVerifiedRegistry = struct { 109 | Constructor abi.MethodNum 110 | AddVerifier abi.MethodNum 111 | RemoveVerifier abi.MethodNum 112 | AddVerifiedClient abi.MethodNum 113 | UseBytes abi.MethodNum 114 | RestoreBytes abi.MethodNum 115 | RemoveVerifiedClientDataCap abi.MethodNum 116 | }{MethodConstructor, 2, 3, 4, 5, 6, 7} 117 | -------------------------------------------------------------------------------- /actors/builtin/miner/bitfield_test.go: -------------------------------------------------------------------------------- 1 | package miner_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-bitfield" 7 | rlepluslazy "github.com/filecoin-project/go-bitfield/rle" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func assertBitfieldsEqual(t *testing.T, expected bitfield.BitField, actual bitfield.BitField) { 13 | const maxDiff = 100 14 | 15 | missing, err := bitfield.SubtractBitField(expected, actual) 16 | require.NoError(t, err) 17 | unexpected, err := bitfield.SubtractBitField(actual, expected) 18 | require.NoError(t, err) 19 | 20 | missingSet, err := missing.All(maxDiff) 21 | require.NoError(t, err, "more than %d missing bits expected", maxDiff) 22 | assert.Empty(t, missingSet, "expected missing bits") 23 | 24 | unexpectedSet, err := unexpected.All(maxDiff) 25 | require.NoError(t, err, "more than %d unexpected bits", maxDiff) 26 | assert.Empty(t, unexpectedSet, "unexpected bits set") 27 | } 28 | 29 | func assertBitfieldEquals(t *testing.T, actual bitfield.BitField, expected ...uint64) { 30 | assertBitfieldsEqual(t, actual, bf(expected...)) 31 | } 32 | 33 | func assertBitfieldEmpty(t *testing.T, bf bitfield.BitField) { 34 | empty, err := bf.IsEmpty() 35 | require.NoError(t, err) 36 | assert.True(t, empty) 37 | } 38 | 39 | // Create a bitfield with count bits set, starting at "start". 40 | func seq(t *testing.T, start, count uint64) bitfield.BitField { 41 | var runs []rlepluslazy.Run 42 | if start > 0 { 43 | runs = append(runs, rlepluslazy.Run{Val: false, Len: start}) 44 | } 45 | runs = append(runs, rlepluslazy.Run{Val: true, Len: count}) 46 | bf, err := bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{Runs: runs}) 47 | require.NoError(t, err) 48 | return bf 49 | } 50 | -------------------------------------------------------------------------------- /actors/builtin/miner/burn_type.go: -------------------------------------------------------------------------------- 1 | package miner 2 | 3 | type BurnMethod string 4 | 5 | const ( 6 | BurnMethodDisputeWindowedPoSt BurnMethod = "DisputeWindowedPoSt" 7 | BurnMethodPreCommitSectorBatch BurnMethod = "PreCommitSectorBatch" 8 | BurnMethodProveCommitAggregate BurnMethod = "ProveCommitAggregate" 9 | BurnMethodDeclareFaultsRecovered BurnMethod = "DeclareFaultsRecovered" 10 | BurnMethodApplyRewards BurnMethod = "ApplyRewards" 11 | BurnMethodReportConsensusFault BurnMethod = "ReportConsensusFault" 12 | BurnMethodWithdrawBalance BurnMethod = "WithdrawBalance " 13 | BurnMethodRepayDebt BurnMethod = "RepayDebt" 14 | BurnMethodProcessEarlyTerminations BurnMethod = "ProcessEarlyTerminations" 15 | BurnMethodHandleProvingDeadline BurnMethod = "HandleProvingDeadline " 16 | ) 17 | -------------------------------------------------------------------------------- /actors/builtin/miner/deadlines_helper_test.go: -------------------------------------------------------------------------------- 1 | package miner 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCompactionWindow(t *testing.T) { 11 | periodStart := abi.ChainEpoch(1024) 12 | dlInfo := NewDeadlineInfo(periodStart, 0, 0) 13 | assert.True(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Open-WPoStChallengeWindow-1), 14 | "compaction is possible up till the blackout period") 15 | assert.False(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Open-WPoStChallengeWindow), 16 | "compaction is not possible during the prior window") 17 | 18 | assert.False(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Open+10), 19 | "compaction is not possible during the window") 20 | 21 | assert.False(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Close), 22 | "compaction is not possible immediately after the window") 23 | 24 | assert.False(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Last()+WPoStDisputeWindow), 25 | "compaction is not possible before the proof challenge period has passed") 26 | 27 | assert.True(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Close+WPoStDisputeWindow), 28 | "compaction is possible after the proof challenge period has passed") 29 | 30 | assert.True(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Open+WPoStProvingPeriod-WPoStChallengeWindow-1), 31 | "compaction remains possible until the next blackout") 32 | assert.False(t, deadlineAvailableForCompaction(periodStart, 0, dlInfo.Open+WPoStProvingPeriod-WPoStChallengeWindow), 33 | "compaction is not possible during the next blackout") 34 | } 35 | 36 | func TestChallengeWindow(t *testing.T) { 37 | periodStart := abi.ChainEpoch(1024) 38 | dlInfo := NewDeadlineInfo(periodStart, 0, 0) 39 | assert.False(t, deadlineAvailableForOptimisticPoStDispute(periodStart, 0, dlInfo.Open), 40 | "proof challenge is not possible while the window is open") 41 | assert.True(t, deadlineAvailableForOptimisticPoStDispute(periodStart, 0, dlInfo.Close), 42 | "proof challenge is possible after the window is closes") 43 | assert.True(t, deadlineAvailableForOptimisticPoStDispute(periodStart, 0, dlInfo.Close+WPoStDisputeWindow-1), 44 | "proof challenge is possible until the proof challenge period has passed") 45 | assert.False(t, deadlineAvailableForOptimisticPoStDispute(periodStart, 0, dlInfo.Close+WPoStDisputeWindow), 46 | "proof challenge is not possible after the proof challenge period has passed") 47 | } 48 | -------------------------------------------------------------------------------- /actors/builtin/miner/deadlines_test.go: -------------------------------------------------------------------------------- 1 | package miner_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 10 | ) 11 | 12 | func TestProvingPeriodDeadlines(t *testing.T) { 13 | 14 | t.Run("quantization spec rounds to the next deadline", func(t *testing.T) { 15 | periodStart := abi.ChainEpoch(2) 16 | curr := periodStart + miner.WPoStProvingPeriod 17 | d := miner.NewDeadlineInfo(periodStart, 10, curr) 18 | quant := miner.QuantSpecForDeadline(d) 19 | assert.Equal(t, d.NextNotElapsed().Last(), quant.QuantizeUp(curr)) 20 | }) 21 | } 22 | 23 | func TestDeadlineInfoFromOffsetAndEpoch(t *testing.T) { 24 | 25 | // All proving periods equivalent mod WPoStProving period should give equivalent 26 | // dlines for a given epoch. Only the offset property should matter 27 | t.Run("Offset and epoch invariant checking", func(t *testing.T) { 28 | pp := abi.ChainEpoch(1972) 29 | ppThree := abi.ChainEpoch(1972 + 2880*3) 30 | ppMillion := abi.ChainEpoch(1972 + 2880*10e6) 31 | 32 | epochs := []abi.ChainEpoch{4, 2000, 400000, 5000000} 33 | for _, epoch := range epochs { 34 | dlineA := miner.NewDeadlineInfoFromOffsetAndEpoch(pp, epoch) 35 | dlineB := miner.NewDeadlineInfoFromOffsetAndEpoch(ppThree, epoch) 36 | dlineC := miner.NewDeadlineInfoFromOffsetAndEpoch(ppMillion, epoch) 37 | 38 | assert.Equal(t, *dlineA, *dlineB) 39 | assert.Equal(t, *dlineB, *dlineC) 40 | } 41 | }) 42 | t.Run("sanity checks", func(t *testing.T) { 43 | offset := abi.ChainEpoch(7) 44 | start := abi.ChainEpoch(2880*103) + offset 45 | // epoch 2880*103 + offset we are in deadline 0, pp start = 2880*103 + offset 46 | dline := miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start) 47 | assert.Equal(t, uint64(0), dline.Index) 48 | assert.Equal(t, start, dline.PeriodStart) 49 | 50 | // epoch 2880*103 + offset + WPoStChallengeWindow - 1 we are in deadline 0 51 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+miner.WPoStChallengeWindow-1) 52 | assert.Equal(t, uint64(0), dline.Index) 53 | assert.Equal(t, start, dline.PeriodStart) 54 | 55 | // epoch 2880*103 + offset + WPoStChallengeWindow we are in deadline 1 56 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+miner.WPoStChallengeWindow) 57 | assert.Equal(t, uint64(1), dline.Index) 58 | assert.Equal(t, start, dline.PeriodStart) 59 | 60 | // epoch 2880*103 + offset + 40*WPoStChallengeWindow we are in deadline 40 61 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow) 62 | assert.Equal(t, uint64(40), dline.Index) 63 | assert.Equal(t, start, dline.PeriodStart) 64 | 65 | // epoch 2880*103 + offset + 40*WPoStChallengeWindow - 1 we are in deadline 39 66 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow-1) 67 | assert.Equal(t, uint64(39), dline.Index) 68 | assert.Equal(t, start, dline.PeriodStart) 69 | 70 | // epoch 2880*103 + offset + 40*WPoStChallengeWindow + 1 we are in deadline 40 71 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow+1) 72 | assert.Equal(t, uint64(40), dline.Index) 73 | assert.Equal(t, start, dline.PeriodStart) 74 | 75 | // epoch 2880*103 + offset + WPoStPeriodDeadlines*WPoStChallengeWindow -1 we are in deadline WPoStPeriodDeadlines - 1 76 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+abi.ChainEpoch(miner.WPoStPeriodDeadlines)*miner.WPoStChallengeWindow-abi.ChainEpoch(1)) 77 | assert.Equal(t, uint64(miner.WPoStPeriodDeadlines-1), dline.Index) 78 | assert.Equal(t, start, dline.PeriodStart) 79 | 80 | // epoch 2880*103 + offset + WPoStPeriodDeadlines*WPoStChallengeWindow + 1 we are in deadline 0, pp start = 2880*104 + offset 81 | dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+abi.ChainEpoch(miner.WPoStPeriodDeadlines)*miner.WPoStChallengeWindow) 82 | assert.Equal(t, uint64(0), dline.Index) 83 | assert.Equal(t, start+miner.WPoStProvingPeriod, dline.PeriodStart) 84 | 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /actors/builtin/miner/expiration_queue_internal_test.go: -------------------------------------------------------------------------------- 1 | package miner 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/filecoin-project/go-state-types/big" 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestExpirations(t *testing.T) { 14 | quant := builtin.NewQuantSpec(10, 3) 15 | sectors := []*SectorOnChainInfo{ 16 | testSector(7, 1, 0, 0, 0), 17 | testSector(8, 2, 0, 0, 0), 18 | testSector(14, 3, 0, 0, 0), 19 | testSector(13, 4, 0, 0, 0), 20 | } 21 | result := groupNewSectorsByDeclaredExpiration(2048, sectors, quant) 22 | expected := []*sectorEpochSet{{ 23 | epoch: 13, 24 | sectors: []uint64{1, 2, 4}, 25 | power: NewPowerPair(big.NewIntUnsigned(2048*3), big.NewIntUnsigned(2048*3)), 26 | pledge: big.Zero(), 27 | }, { 28 | epoch: 23, 29 | sectors: []uint64{3}, 30 | power: NewPowerPair(big.NewIntUnsigned(2048), big.NewIntUnsigned(2048)), 31 | pledge: big.Zero(), 32 | }} 33 | require.Equal(t, len(expected), len(result)) 34 | for i, ex := range expected { 35 | assertSectorSet(t, ex, &result[i]) 36 | } 37 | } 38 | 39 | func TestExpirationsEmpty(t *testing.T) { 40 | sectors := []*SectorOnChainInfo{} 41 | result := groupNewSectorsByDeclaredExpiration(2048, sectors, builtin.NoQuantization) 42 | expected := []sectorEpochSet{} 43 | require.Equal(t, expected, result) 44 | } 45 | 46 | func assertSectorSet(t *testing.T, expected, actual *sectorEpochSet) { 47 | assert.Equal(t, expected.epoch, actual.epoch) 48 | assert.Equal(t, expected.sectors, actual.sectors) 49 | assert.True(t, expected.power.Equals(actual.power), "expected %v, actual %v", expected.power, actual.power) 50 | assert.True(t, expected.pledge.Equals(actual.pledge), "expected %v, actual %v", expected.pledge, actual.pledge) 51 | } 52 | 53 | func testSector(expiration, number, weight, vweight, pledge int64) *SectorOnChainInfo { 54 | return &SectorOnChainInfo{ 55 | Expiration: abi.ChainEpoch(expiration), 56 | SectorNumber: abi.SectorNumber(number), 57 | DealWeight: big.NewInt(weight), 58 | VerifiedDealWeight: big.NewInt(vweight), 59 | InitialPledge: abi.NewTokenAmount(pledge), 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /actors/builtin/miner/sectors_test.go: -------------------------------------------------------------------------------- 1 | package miner_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/filecoin-project/go-state-types/abi" 9 | "github.com/filecoin-project/go-state-types/big" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 13 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 14 | "github.com/filecoin-project/specs-actors/v8/support/ipld" 15 | tutil "github.com/filecoin-project/specs-actors/v8/support/testing" 16 | ) 17 | 18 | func sectorsArr(t *testing.T, store adt.Store, sectors []*miner.SectorOnChainInfo) miner.Sectors { 19 | emptyArray, err := adt.MakeEmptyArray(store, miner.SectorsAmtBitwidth) 20 | require.NoError(t, err) 21 | sectorArr := miner.Sectors{emptyArray} 22 | require.NoError(t, sectorArr.Store(sectors...)) 23 | return sectorArr 24 | } 25 | 26 | func TestSectors(t *testing.T) { 27 | makeSector := func(t *testing.T, i uint64) *miner.SectorOnChainInfo { 28 | return &miner.SectorOnChainInfo{ 29 | SectorNumber: abi.SectorNumber(i), 30 | SealProof: abi.RegisteredSealProof_StackedDrg32GiBV1_1, 31 | SealedCID: tutil.MakeCID(fmt.Sprintf("commR-%d", i), &miner.SealedCIDPrefix), 32 | DealWeight: big.Zero(), 33 | VerifiedDealWeight: big.Zero(), 34 | InitialPledge: big.Zero(), 35 | ExpectedDayReward: big.Zero(), 36 | ExpectedStoragePledge: big.Zero(), 37 | ReplacedDayReward: big.Zero(), 38 | } 39 | } 40 | setupSectors := func(t *testing.T) miner.Sectors { 41 | return sectorsArr(t, ipld.NewADTStore(context.Background()), []*miner.SectorOnChainInfo{ 42 | makeSector(t, 0), makeSector(t, 1), makeSector(t, 5), 43 | }) 44 | } 45 | 46 | t.Run("loads sectors", func(t *testing.T) { 47 | arr := setupSectors(t) 48 | sectors, err := arr.Load(bf(0, 5)) 49 | require.NoError(t, err) 50 | require.Len(t, sectors, 2) 51 | require.Equal(t, makeSector(t, 0), sectors[0]) 52 | require.Equal(t, makeSector(t, 5), sectors[1]) 53 | 54 | _, err = arr.Load(bf(0, 3)) 55 | require.Error(t, err) 56 | }) 57 | 58 | t.Run("stores sectors", func(t *testing.T) { 59 | arr := setupSectors(t) 60 | s0 := makeSector(t, 0) 61 | s1 := makeSector(t, 1) 62 | s1.Activation = 1 63 | s3 := makeSector(t, 3) 64 | s5 := makeSector(t, 5) 65 | 66 | require.NoError(t, arr.Store(s3, s1)) 67 | sectors, err := arr.Load(bf(0, 1, 3, 5)) 68 | require.NoError(t, err) 69 | require.Len(t, sectors, 4) 70 | require.Equal(t, s0, sectors[0]) 71 | require.Equal(t, s1, sectors[1]) 72 | require.Equal(t, s3, sectors[2]) 73 | require.Equal(t, s5, sectors[3]) 74 | }) 75 | 76 | t.Run("loads and stores no sectors", func(t *testing.T) { 77 | arr := setupSectors(t) 78 | 79 | sectors, err := arr.Load(bf()) 80 | require.NoError(t, err) 81 | require.Empty(t, sectors) 82 | 83 | require.NoError(t, arr.Store()) 84 | }) 85 | 86 | t.Run("gets sectors", func(t *testing.T) { 87 | arr := setupSectors(t) 88 | s0, found, err := arr.Get(0) 89 | require.NoError(t, err) 90 | require.True(t, found) 91 | require.Equal(t, makeSector(t, 0), s0) 92 | 93 | _, found, err = arr.Get(3) 94 | require.NoError(t, err) 95 | require.False(t, found) 96 | }) 97 | 98 | t.Run("must get", func(t *testing.T) { 99 | arr := setupSectors(t) 100 | s0, err := arr.MustGet(0) 101 | require.NoError(t, err) 102 | require.Equal(t, makeSector(t, 0), s0) 103 | 104 | _, err = arr.MustGet(3) 105 | require.Error(t, err) 106 | }) 107 | 108 | t.Run("loads for proof with replacement", func(t *testing.T) { 109 | arr := setupSectors(t) 110 | s1 := makeSector(t, 1) 111 | infos, err := arr.LoadForProof(bf(0, 1), bf(0)) 112 | require.NoError(t, err) 113 | require.Equal(t, []*miner.SectorOnChainInfo{s1, s1}, infos) 114 | }) 115 | 116 | t.Run("loads for proof without replacement", func(t *testing.T) { 117 | arr := setupSectors(t) 118 | s0 := makeSector(t, 0) 119 | s1 := makeSector(t, 1) 120 | infos, err := arr.LoadForProof(bf(0, 1), bf()) 121 | require.NoError(t, err) 122 | require.Equal(t, []*miner.SectorOnChainInfo{s0, s1}, infos) 123 | }) 124 | 125 | t.Run("empty proof", func(t *testing.T) { 126 | arr := setupSectors(t) 127 | infos, err := arr.LoadForProof(bf(), bf()) 128 | require.NoError(t, err) 129 | require.Empty(t, infos) 130 | }) 131 | 132 | t.Run("no non-faulty sectors", func(t *testing.T) { 133 | arr := setupSectors(t) 134 | infos, err := arr.LoadForProof(bf(1), bf(1)) 135 | require.NoError(t, err) 136 | require.Empty(t, infos) 137 | }) 138 | } 139 | -------------------------------------------------------------------------------- /actors/builtin/miner/termination.go: -------------------------------------------------------------------------------- 1 | package miner 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/filecoin-project/go-bitfield" 7 | "github.com/filecoin-project/go-state-types/abi" 8 | ) 9 | 10 | type TerminationResult struct { 11 | // Sectors maps epochs at which sectors expired, to bitfields of sector 12 | // numbers. 13 | Sectors map[abi.ChainEpoch]bitfield.BitField 14 | // Counts the number of partitions & sectors processed. 15 | PartitionsProcessed, SectorsProcessed uint64 16 | } 17 | 18 | func (t *TerminationResult) Add(newResult TerminationResult) error { 19 | if t.Sectors == nil { 20 | t.Sectors = make(map[abi.ChainEpoch]bitfield.BitField, len(newResult.Sectors)) 21 | } 22 | t.PartitionsProcessed += newResult.PartitionsProcessed 23 | t.SectorsProcessed += newResult.SectorsProcessed 24 | for epoch, newSectors := range newResult.Sectors { //nolint:nomaprange 25 | if oldSectors, ok := t.Sectors[epoch]; !ok { 26 | t.Sectors[epoch] = newSectors 27 | } else { 28 | var err error 29 | t.Sectors[epoch], err = bitfield.MergeBitFields(oldSectors, newSectors) 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | } 35 | return nil 36 | } 37 | 38 | // Returns true if we're below the partition/sector limit. Returns false if 39 | // we're at (or above) the limit. 40 | func (t *TerminationResult) BelowLimit(maxPartitions, maxSectors uint64) bool { 41 | return t.PartitionsProcessed < maxPartitions && t.SectorsProcessed < maxSectors 42 | } 43 | 44 | func (t *TerminationResult) IsEmpty() bool { 45 | return t.SectorsProcessed == 0 46 | } 47 | 48 | func (t *TerminationResult) ForEach(cb func(epoch abi.ChainEpoch, sectors bitfield.BitField) error) error { 49 | // We're sorting here, so iterating over the map is fine. 50 | epochs := make([]abi.ChainEpoch, 0, len(t.Sectors)) 51 | for epoch := range t.Sectors { //nolint:nomaprange 52 | epochs = append(epochs, epoch) 53 | } 54 | sort.Slice(epochs, func(i, j int) bool { 55 | return epochs[i] < epochs[j] 56 | }) 57 | for _, epoch := range epochs { 58 | err := cb(epoch, t.Sectors[epoch]) 59 | if err != nil { 60 | return err 61 | } 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /actors/builtin/miner/termination_test.go: -------------------------------------------------------------------------------- 1 | package miner_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-bitfield" 7 | "github.com/filecoin-project/go-state-types/abi" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 11 | ) 12 | 13 | func TestTerminationResult(t *testing.T) { 14 | var result miner.TerminationResult 15 | require.True(t, result.IsEmpty()) 16 | err := result.ForEach(func(epoch abi.ChainEpoch, sectors bitfield.BitField) error { 17 | require.FailNow(t, "unreachable") 18 | return nil 19 | }) 20 | require.NoError(t, err) 21 | 22 | resultA := miner.TerminationResult{ 23 | Sectors: map[abi.ChainEpoch]bitfield.BitField{ 24 | 3: bitfield.NewFromSet([]uint64{9}), 25 | 0: bitfield.NewFromSet([]uint64{1, 2, 4}), 26 | 2: bitfield.NewFromSet([]uint64{3, 5, 7}), 27 | }, 28 | PartitionsProcessed: 1, 29 | SectorsProcessed: 7, 30 | } 31 | require.False(t, resultA.IsEmpty()) 32 | resultB := miner.TerminationResult{ 33 | Sectors: map[abi.ChainEpoch]bitfield.BitField{ 34 | 1: bitfield.NewFromSet([]uint64{12}), 35 | 0: bitfield.NewFromSet([]uint64{10}), 36 | }, 37 | PartitionsProcessed: 1, 38 | SectorsProcessed: 2, 39 | } 40 | require.False(t, resultB.IsEmpty()) 41 | require.NoError(t, result.Add(resultA)) 42 | require.NoError(t, result.Add(resultB)) 43 | require.False(t, result.IsEmpty()) 44 | expected := miner.TerminationResult{ 45 | Sectors: map[abi.ChainEpoch]bitfield.BitField{ 46 | 2: bitfield.NewFromSet([]uint64{3, 5, 7}), 47 | 0: bitfield.NewFromSet([]uint64{1, 2, 4, 10}), 48 | 1: bitfield.NewFromSet([]uint64{12}), 49 | 3: bitfield.NewFromSet([]uint64{9}), 50 | }, 51 | PartitionsProcessed: 2, 52 | SectorsProcessed: 9, 53 | } 54 | require.Equal(t, expected.SectorsProcessed, result.SectorsProcessed) 55 | require.Equal(t, expected.PartitionsProcessed, result.PartitionsProcessed) 56 | require.Equal(t, len(expected.Sectors), len(result.Sectors)) 57 | 58 | expectedEpoch := abi.ChainEpoch(0) 59 | err = result.ForEach(func(epoch abi.ChainEpoch, actualBf bitfield.BitField) error { 60 | require.Equal(t, expectedEpoch, epoch) 61 | expectedEpoch++ 62 | expectedBf, ok := expected.Sectors[epoch] 63 | require.True(t, ok) 64 | expectedNos, err := expectedBf.All(1000) 65 | require.NoError(t, err) 66 | actualNos, err := actualBf.All(1000) 67 | require.NoError(t, err) 68 | require.Equal(t, expectedNos, actualNos) 69 | return nil 70 | }) 71 | require.NoError(t, err) 72 | 73 | // partitions = 2, sectors = 9 74 | require.False(t, result.BelowLimit(2, 9)) 75 | require.False(t, result.BelowLimit(3, 9)) 76 | require.False(t, result.BelowLimit(3, 8)) 77 | require.False(t, result.BelowLimit(2, 10)) 78 | require.True(t, result.BelowLimit(3, 10)) 79 | } 80 | -------------------------------------------------------------------------------- /actors/builtin/miner/vesting_state.go: -------------------------------------------------------------------------------- 1 | package miner 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | 8 | "github.com/filecoin-project/go-state-types/abi" 9 | "github.com/filecoin-project/go-state-types/big" 10 | ) 11 | 12 | // VestingFunds represents the vesting table state for the miner. 13 | // It is a slice of (VestingEpoch, VestingAmount). 14 | // The slice will always be sorted by the VestingEpoch. 15 | type VestingFunds struct { 16 | Funds []VestingFund 17 | } 18 | 19 | func (v *VestingFunds) unlockVestedFunds(currEpoch abi.ChainEpoch) abi.TokenAmount { 20 | amountUnlocked := abi.NewTokenAmount(0) 21 | 22 | lastIndexToRemove := -1 23 | for i, vf := range v.Funds { 24 | if vf.Epoch >= currEpoch { 25 | break 26 | } 27 | 28 | amountUnlocked = big.Add(amountUnlocked, vf.Amount) 29 | lastIndexToRemove = i 30 | } 31 | 32 | // remove all entries upto and including lastIndexToRemove 33 | if lastIndexToRemove != -1 { 34 | v.Funds = v.Funds[lastIndexToRemove+1:] 35 | } 36 | 37 | return amountUnlocked 38 | } 39 | 40 | func (v *VestingFunds) addLockedFunds(currEpoch abi.ChainEpoch, vestingSum abi.TokenAmount, 41 | provingPeriodStart abi.ChainEpoch, spec *VestSpec) { 42 | // maps the epochs in VestingFunds to their indices in the slice 43 | epochToIndex := make(map[abi.ChainEpoch]int, len(v.Funds)) 44 | for i, vf := range v.Funds { 45 | epochToIndex[vf.Epoch] = i 46 | } 47 | 48 | // Quantization is aligned with when regular cron will be invoked, in the last epoch of deadlines. 49 | vestBegin := currEpoch + spec.InitialDelay // Nothing unlocks here, this is just the start of the clock. 50 | vestPeriod := big.NewInt(int64(spec.VestPeriod)) 51 | vestedSoFar := big.Zero() 52 | for e := vestBegin + spec.StepDuration; vestedSoFar.LessThan(vestingSum); e += spec.StepDuration { 53 | vestEpoch := builtin.QuantizeUp(e, spec.Quantization, provingPeriodStart) 54 | elapsed := vestEpoch - vestBegin 55 | 56 | targetVest := big.Zero() //nolint:ineffassign 57 | if elapsed < spec.VestPeriod { 58 | // Linear vesting 59 | targetVest = big.Div(big.Mul(vestingSum, big.NewInt(int64(elapsed))), vestPeriod) 60 | } else { 61 | targetVest = vestingSum 62 | } 63 | 64 | vestThisTime := big.Sub(targetVest, vestedSoFar) 65 | vestedSoFar = targetVest 66 | 67 | // epoch already exists. Load existing entry 68 | // and update amount. 69 | if index, ok := epochToIndex[vestEpoch]; ok { 70 | currentAmt := v.Funds[index].Amount 71 | v.Funds[index].Amount = big.Add(currentAmt, vestThisTime) 72 | } else { 73 | // append a new entry -> slice will be sorted by epoch later. 74 | entry := VestingFund{Epoch: vestEpoch, Amount: vestThisTime} 75 | v.Funds = append(v.Funds, entry) 76 | epochToIndex[vestEpoch] = len(v.Funds) - 1 77 | } 78 | } 79 | 80 | // sort slice by epoch 81 | sort.Slice(v.Funds, func(first, second int) bool { 82 | return v.Funds[first].Epoch < v.Funds[second].Epoch 83 | }) 84 | } 85 | 86 | func (v *VestingFunds) unlockUnvestedFunds(currEpoch abi.ChainEpoch, target abi.TokenAmount) abi.TokenAmount { 87 | amountUnlocked := abi.NewTokenAmount(0) 88 | lastIndexToRemove := -1 89 | startIndexForRemove := 0 90 | 91 | // retain funds that should have vested and unlock unvested funds 92 | for i, vf := range v.Funds { 93 | if amountUnlocked.GreaterThanEqual(target) { 94 | break 95 | } 96 | 97 | if vf.Epoch >= currEpoch { 98 | unlockAmount := big.Min(big.Sub(target, amountUnlocked), vf.Amount) 99 | amountUnlocked = big.Add(amountUnlocked, unlockAmount) 100 | newAmount := big.Sub(vf.Amount, unlockAmount) 101 | 102 | if newAmount.IsZero() { 103 | lastIndexToRemove = i 104 | } else { 105 | v.Funds[i].Amount = newAmount 106 | } 107 | } else { 108 | startIndexForRemove = i + 1 109 | } 110 | } 111 | 112 | // remove all entries in [startIndexForRemove, lastIndexToRemove] 113 | if lastIndexToRemove != -1 { 114 | v.Funds = append(v.Funds[0:startIndexForRemove], v.Funds[lastIndexToRemove+1:]...) 115 | } 116 | 117 | return amountUnlocked 118 | } 119 | 120 | // VestingFund represents miner funds that will vest at the given epoch. 121 | type VestingFund struct { 122 | Epoch abi.ChainEpoch 123 | Amount abi.TokenAmount 124 | } 125 | 126 | // ConstructVestingFunds constructs empty VestingFunds state. 127 | func ConstructVestingFunds() *VestingFunds { 128 | v := new(VestingFunds) 129 | v.Funds = nil 130 | return v 131 | } 132 | -------------------------------------------------------------------------------- /actors/builtin/multisig/policy.go: -------------------------------------------------------------------------------- 1 | package multisig 2 | 3 | // SignersMax is the maximum number of signers allowed in a multisig. If more 4 | // are required, please use a combining tree of multisigs. 5 | const SignersMax = 256 6 | -------------------------------------------------------------------------------- /actors/builtin/multisig/testing.go: -------------------------------------------------------------------------------- 1 | package multisig 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/filecoin-project/go-address" 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 8 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 9 | ) 10 | 11 | type StateSummary struct { 12 | PendingTxnCount uint64 13 | NumApprovalsThreshold uint64 14 | SignerCount int 15 | } 16 | 17 | // Checks internal invariants of multisig state. 18 | func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) { 19 | acc := &builtin.MessageAccumulator{} 20 | 21 | // assert invariants involving signers 22 | acc.Require(len(st.Signers) <= SignersMax, "multisig has too many signers: %d", len(st.Signers)) 23 | acc.Require(uint64(len(st.Signers)) >= st.NumApprovalsThreshold, 24 | "multisig has insufficient signers to meet threshold (%d < %d)", len(st.Signers), st.NumApprovalsThreshold) 25 | 26 | if st.UnlockDuration == 0 { // See https://github.com/filecoin-project/specs-actors/issues/1185 27 | acc.Require(st.StartEpoch == 0, "non-zero start epoch %d with zero unlock duration", st.StartEpoch) 28 | acc.Require(st.InitialBalance.IsZero(), "non-zero locked balance %v with zero unlock duration", st.InitialBalance) 29 | } 30 | 31 | // create lookup to test transaction approvals are multisig signers. 32 | signers := make(map[address.Address]struct{}) 33 | for _, a := range st.Signers { 34 | signers[a] = struct{}{} 35 | } 36 | 37 | // test pending transactions 38 | maxTxnID := TxnID(-1) 39 | numPending := uint64(0) 40 | if transactions, err := adt.AsMap(store, st.PendingTxns, builtin.DefaultHamtBitwidth); err != nil { 41 | acc.Addf("error loading transactions: %v", err) 42 | } else { 43 | var txn Transaction 44 | err = transactions.ForEach(&txn, func(txnIDStr string) error { 45 | txnID, err := ParseTxnIDKey(txnIDStr) 46 | if err != nil { 47 | return err 48 | } 49 | if txnID > maxTxnID { 50 | maxTxnID = txnID 51 | } 52 | 53 | seenApprovals := make(map[address.Address]struct{}) 54 | for _, approval := range txn.Approved { 55 | _, found := signers[approval] 56 | acc.Require(found, "approval %v for transaction %d is not in signers list", approval, txnID) 57 | 58 | _, seen := seenApprovals[approval] 59 | acc.Require(!seen, "duplicate approval %v for transaction %d", approval, txnID) 60 | 61 | seenApprovals[approval] = struct{}{} 62 | } 63 | 64 | numPending++ 65 | return nil 66 | }) 67 | acc.RequireNoError(err, "error iterating transactions") 68 | } 69 | 70 | acc.Require(st.NextTxnID > maxTxnID, "next transaction id %d is not greater than pending ids", st.NextTxnID) 71 | return &StateSummary{ 72 | PendingTxnCount: numPending, 73 | NumApprovalsThreshold: st.NumApprovalsThreshold, 74 | SignerCount: len(st.Signers), 75 | }, acc 76 | } 77 | 78 | func ParseTxnIDKey(key string) (TxnID, error) { 79 | id, err := binary.ReadVarint(bytes.NewReader([]byte(key))) 80 | return TxnID(id), err 81 | } 82 | -------------------------------------------------------------------------------- /actors/builtin/network.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/filecoin-project/go-state-types/big" 7 | ) 8 | 9 | // PARAM_SPEC 10 | // The duration of a chain epoch. 11 | // Motivation: It guarantees that a block is propagated and WinningPoSt can be successfully done in time all supported miners. 12 | // Usage: It is used for deriving epoch-denominated periods that are more naturally expressed in clock time. 13 | // TODO: In lieu of a real configuration mechanism for this value, we'd like to make it a var so that implementations 14 | // can override it at runtime. Doing so requires changing all the static references to it in this repo to go through 15 | // late-binding function calls, or they'll see the "wrong" value. 16 | // https://github.com/filecoin-project/specs-actors/issues/353 17 | // If EpochDurationSeconds is changed, update `BaselineExponent`, `lambda`, and // `expLamSubOne` in ./reward/reward_logic.go 18 | // You can re-calculate these constants by changing the epoch duration in ./reward/reward_calc.py and running it. 19 | const EpochDurationSeconds = 30 20 | const SecondsInHour = 60 * 60 21 | const SecondsInDay = 24 * SecondsInHour 22 | const EpochsInHour = SecondsInHour / EpochDurationSeconds 23 | const EpochsInDay = 24 * EpochsInHour 24 | const EpochsInYear = 365 * EpochsInDay 25 | 26 | // PARAM_SPEC 27 | // Expected number of block quality in an epoch (e.g. 1 block with block quality 5, or 5 blocks with quality 1) 28 | // Motivation: It ensures that there is enough on-chain throughput 29 | // Usage: It is used to calculate the block reward. 30 | var ExpectedLeadersPerEpoch = int64(5) 31 | 32 | func init() { 33 | //noinspection GoBoolExpressions 34 | if SecondsInHour%EpochDurationSeconds != 0 { 35 | // This even division is an assumption that other code might unwittingly make. 36 | // Don't rely on it on purpose, though. 37 | // While we're pretty sure everything will still work fine, we're safer maintaining this invariant anyway. 38 | panic(fmt.Sprintf("epoch duration %d does not evenly divide one hour (%d)", EpochDurationSeconds, SecondsInHour)) 39 | } 40 | } 41 | 42 | // Number of token units in an abstract "FIL" token. 43 | // The network works purely in the indivisible token amounts. This constant converts to a fixed decimal with more 44 | // human-friendly scale. 45 | var TokenPrecision = big.NewIntUnsigned(1_000_000_000_000_000_000) 46 | 47 | // The maximum supply of Filecoin that will ever exist (in token units) 48 | var TotalFilecoin = big.Mul(big.NewIntUnsigned(2_000_000_000), TokenPrecision) 49 | 50 | // Quality multiplier for committed capacity (no deals) in a sector 51 | var QualityBaseMultiplier = big.NewInt(10) 52 | 53 | // Quality multiplier for unverified deals in a sector 54 | var DealWeightMultiplier = big.NewInt(10) 55 | 56 | // Quality multiplier for verified deals in a sector 57 | var VerifiedDealWeightMultiplier = big.NewInt(100) 58 | 59 | // Precision used for making QA power calculations 60 | const SectorQualityPrecision = 20 61 | 62 | // 1 NanoFIL 63 | var OneNanoFIL = big.NewInt(1_000_000_000) 64 | -------------------------------------------------------------------------------- /actors/builtin/paych/paych_state.go: -------------------------------------------------------------------------------- 1 | package paych 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | "github.com/ipfs/go-cid" 8 | ) 9 | 10 | // A given payment channel actor is established by From 11 | // to enable off-chain microtransactions to To to be reconciled 12 | // and tallied on chain. 13 | type State struct { 14 | // Channel owner, who has funded the actor 15 | From addr.Address 16 | // Recipient of payouts from channel 17 | To addr.Address 18 | 19 | // Amount successfully redeemed through the payment channel, paid out on `Collect()` 20 | ToSend abi.TokenAmount 21 | 22 | // Height at which the channel can be `Collected` 23 | SettlingAt abi.ChainEpoch 24 | // Height before which the channel `ToSend` cannot be collected 25 | MinSettleHeight abi.ChainEpoch 26 | 27 | // Collections of lane states for the channel, maintained in ID order. 28 | LaneStates cid.Cid // AMT 29 | } 30 | 31 | // The Lane state tracks the latest (highest) voucher nonce used to merge the lane 32 | // as well as the amount it has already redeemed. 33 | type LaneState struct { 34 | Redeemed big.Int 35 | Nonce uint64 36 | } 37 | 38 | const LaneStatesAmtBitwidth = 3 39 | 40 | func ConstructState(from addr.Address, to addr.Address, emptyArrCid cid.Cid) *State { 41 | return &State{ 42 | From: from, 43 | To: to, 44 | ToSend: big.Zero(), 45 | SettlingAt: 0, 46 | MinSettleHeight: 0, 47 | LaneStates: emptyArrCid, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /actors/builtin/paych/policy.go: -------------------------------------------------------------------------------- 1 | package paych 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | ) 8 | 9 | // Maximum number of lanes in a channel. 10 | const MaxLane = math.MaxInt64 11 | 12 | const SettleDelay = builtin.EpochsInHour * 12 13 | 14 | // Maximum size of a secret that can be submitted with a payment channel update (in bytes). 15 | const MaxSecretSize = 256 16 | -------------------------------------------------------------------------------- /actors/builtin/paych/testing.go: -------------------------------------------------------------------------------- 1 | package paych 2 | 3 | import ( 4 | "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 8 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 9 | ) 10 | 11 | type StateSummary struct { 12 | Redeemed abi.TokenAmount 13 | } 14 | 15 | // Checks internal invariants of paych state. 16 | func CheckStateInvariants(st *State, store adt.Store, balance abi.TokenAmount) (*StateSummary, *builtin.MessageAccumulator) { 17 | acc := &builtin.MessageAccumulator{} 18 | paychSummary := &StateSummary{ 19 | Redeemed: big.Zero(), 20 | } 21 | 22 | acc.Require(st.From.Protocol() == address.ID, "from address is not ID address %v", st.From) 23 | acc.Require(st.To.Protocol() == address.ID, "to address is not ID address %v", st.To) 24 | acc.Require(st.SettlingAt >= st.MinSettleHeight, 25 | "channel is setting at epoch %d before min settle height %d", st.SettlingAt, st.MinSettleHeight) 26 | 27 | if lanes, err := adt.AsArray(store, st.LaneStates, LaneStatesAmtBitwidth); err != nil { 28 | acc.Addf("error loading lanes: %v", err) 29 | } else { 30 | var lane LaneState 31 | err = lanes.ForEach(&lane, func(i int64) error { 32 | acc.Require(lane.Redeemed.GreaterThan(big.Zero()), "land %d redeemed is not greater than zero %v", i, lane.Redeemed) 33 | paychSummary.Redeemed = big.Add(paychSummary.Redeemed, lane.Redeemed) 34 | return nil 35 | }) 36 | acc.RequireNoError(err, "error iterating lanes") 37 | } 38 | 39 | acc.Require(balance.GreaterThanEqual(st.ToSend), 40 | "channel has insufficient funds to send (%v < %v)", balance, st.ToSend) 41 | 42 | return paychSummary, acc 43 | } 44 | -------------------------------------------------------------------------------- /actors/builtin/power/policy.go: -------------------------------------------------------------------------------- 1 | package power 2 | 3 | // The number of miners that must meet the consensus minimum miner power before that minimum power is enforced 4 | // as a condition of leader election. 5 | // This ensures a network still functions before any miners reach that threshold. 6 | const ConsensusMinerMinMiners = 4 // PARAM_SPEC 7 | 8 | // Maximum number of prove-commits each miner can submit in one epoch. 9 | // 10 | // This limits the number of proof partitions we may need to load in the cron call path. 11 | // Onboarding 1EiB/year requires at least 32 prove-commits per epoch. 12 | const MaxMinerProveCommitsPerEpoch = 200 // PARAM_SPEC 13 | -------------------------------------------------------------------------------- /actors/builtin/quantize.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import "github.com/filecoin-project/go-state-types/abi" 4 | 5 | // A spec for quantization. 6 | type QuantSpec struct { 7 | unit abi.ChainEpoch // The unit of quantization 8 | offset abi.ChainEpoch // The offset from zero from which to base the modulus 9 | } 10 | 11 | func NewQuantSpec(unit, offset abi.ChainEpoch) QuantSpec { 12 | return QuantSpec{unit: unit, offset: offset} 13 | } 14 | 15 | func (q QuantSpec) QuantizeUp(e abi.ChainEpoch) abi.ChainEpoch { 16 | return QuantizeUp(e, q.unit, q.offset) 17 | } 18 | 19 | func (q QuantSpec) QuantizeDown(e abi.ChainEpoch) abi.ChainEpoch { 20 | next := q.QuantizeUp(e) 21 | // QuantizeDown == QuantizeUp iff e is a fixed point of QuantizeUp 22 | if e == next { 23 | return next 24 | } 25 | return next - q.unit 26 | } 27 | 28 | var NoQuantization = NewQuantSpec(1, 0) 29 | 30 | // Rounds e to the nearest exact multiple of the quantization unit offset by 31 | // offsetSeed % unit, rounding up. 32 | // This function is equivalent to `unit * ceil(e - (offsetSeed % unit) / unit) + (offsetSeed % unit)` 33 | // with the variables/operations are over real numbers instead of ints. 34 | // Precondition: unit >= 0 else behaviour is undefined 35 | func QuantizeUp(e abi.ChainEpoch, unit abi.ChainEpoch, offsetSeed abi.ChainEpoch) abi.ChainEpoch { 36 | offset := offsetSeed % unit 37 | 38 | remainder := (e - offset) % unit 39 | quotient := (e - offset) / unit 40 | // Don't round if epoch falls on a quantization epoch 41 | if remainder == 0 { 42 | return unit*quotient + offset 43 | } 44 | // Negative truncating division rounds up 45 | if e-offset < 0 { 46 | return unit*quotient + offset 47 | } 48 | return unit*(quotient+1) + offset 49 | 50 | } 51 | -------------------------------------------------------------------------------- /actors/builtin/quantize_test.go: -------------------------------------------------------------------------------- 1 | package builtin_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | 8 | "github.com/filecoin-project/go-state-types/abi" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestQuantizeUp(t *testing.T) { 13 | t.Run("no quantization", func(t *testing.T) { 14 | q := NoQuantization 15 | assert.Equal(t, abi.ChainEpoch(0), q.QuantizeUp(0)) 16 | assert.Equal(t, abi.ChainEpoch(1), q.QuantizeUp(1)) 17 | assert.Equal(t, abi.ChainEpoch(2), q.QuantizeUp(2)) 18 | assert.Equal(t, abi.ChainEpoch(123456789), q.QuantizeUp(123456789)) 19 | }) 20 | t.Run("zero offset", func(t *testing.T) { 21 | assert.Equal(t, abi.ChainEpoch(50), QuantizeUp(42, 10, 0)) 22 | assert.Equal(t, abi.ChainEpoch(16000), QuantizeUp(16000, 100, 0)) 23 | assert.Equal(t, abi.ChainEpoch(0), QuantizeUp(-5, 10, 0)) 24 | assert.Equal(t, abi.ChainEpoch(-50), QuantizeUp(-50, 10, 0)) 25 | assert.Equal(t, abi.ChainEpoch(-50), QuantizeUp(-53, 10, 0)) 26 | }) 27 | 28 | t.Run("non zero offset", func(t *testing.T) { 29 | assert.Equal(t, abi.ChainEpoch(6), QuantizeUp(4, 5, 1)) 30 | assert.Equal(t, abi.ChainEpoch(1), QuantizeUp(0, 5, 1)) 31 | assert.Equal(t, abi.ChainEpoch(-4), QuantizeUp(-6, 5, 1)) 32 | assert.Equal(t, abi.ChainEpoch(4), QuantizeUp(2, 10, 4)) 33 | }) 34 | 35 | t.Run("offset seed bigger than unit is normalized", func(t *testing.T) { 36 | assert.Equal(t, abi.ChainEpoch(13), QuantizeUp(9, 5, 28)) // offset should be 3 37 | assert.Equal(t, abi.ChainEpoch(10000), QuantizeUp(10000, 100, 2000000)) 38 | }) 39 | } 40 | 41 | func TestQuantizeDown(t *testing.T) { 42 | t.Run("no quantization", func(t *testing.T) { 43 | q := NoQuantization 44 | assert.Equal(t, abi.ChainEpoch(0), q.QuantizeDown(0)) 45 | assert.Equal(t, abi.ChainEpoch(1), q.QuantizeDown(1)) 46 | assert.Equal(t, abi.ChainEpoch(1337), q.QuantizeDown(1337)) 47 | }) 48 | t.Run("zero offset", func(t *testing.T) { 49 | q := NewQuantSpec(abi.ChainEpoch(10), abi.ChainEpoch(0)) 50 | assert.Equal(t, abi.ChainEpoch(6660), q.QuantizeDown(6666)) 51 | assert.Equal(t, abi.ChainEpoch(50), q.QuantizeDown(50)) 52 | assert.Equal(t, abi.ChainEpoch(50), q.QuantizeDown(59)) 53 | }) 54 | 55 | t.Run("non zero offset", func(t *testing.T) { 56 | q := NewQuantSpec(abi.ChainEpoch(10), abi.ChainEpoch(1)) 57 | assert.Equal(t, abi.ChainEpoch(11), q.QuantizeDown(20)) 58 | assert.Equal(t, abi.ChainEpoch(11), q.QuantizeDown(11)) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /actors/builtin/reward/reward_calc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # This file can be used to recalculate the reward constants in the code when 4 | # changing the reward factor and/or the epoch duration in seconds. 5 | 6 | from decimal import Decimal 7 | import decimal 8 | 9 | # Number of seconds per epoch. 10 | EPOCH_DURATION_SECONDS = 30 11 | 12 | # Total Filecoin supply 13 | SIMPLE_SUPPLY_TOTAL=330000000 14 | 15 | # Growth factor per year. Currently 100%. 16 | GROWTH_FACTOR = 1.0 17 | 18 | # Seconds in a year, according to filecoin. This is actually slightly shorter 19 | # than a year, but it's close enough. 20 | SECONDS_PER_YEAR=60*60*365*24 21 | 22 | # Precision factor. 23 | Q128 = 2**128 24 | 25 | 26 | # Set the precision to enough digits to store (Q128 * Q128). 27 | # 28 | # This gives us wolfram-alpha level precision. 29 | decimal.getcontext().prec=int(Decimal(Q128**2).log10().to_integral_value(decimal.ROUND_CEILING)) 30 | 31 | def epochs_in_year() -> Decimal: 32 | return Decimal(SECONDS_PER_YEAR)/Decimal(EPOCH_DURATION_SECONDS) 33 | 34 | def q128(val) -> str: 35 | return str((Q128 * val).to_integral_value(decimal.ROUND_DOWN)) 36 | 37 | def atto(val) -> str: 38 | return str((10**18 * val).to_integral_value(decimal.ROUND_DOWN)) 39 | 40 | # exp(ln[1 + 200%] / epochsInYear) 41 | def baseline_exponent() -> Decimal: 42 | return (Decimal(1 + GROWTH_FACTOR).ln() / epochs_in_year()).exp() 43 | 44 | # ln(2) / (6 * epochsInYear) 45 | def reward_lambda() -> Decimal: 46 | # 2 is a constant such that the half life is 6 years. 47 | return Decimal(2).ln() / (6 * epochs_in_year()) 48 | 49 | # exp(lambda) - 1 50 | def reward_lambda_prime() -> Decimal: 51 | return reward_lambda().exp() - 1 52 | 53 | # exp(-lambda) - 1 54 | def initial_reward_veolocity_estimate() -> Decimal: 55 | return reward_lambda().copy_negate().exp() - 1 56 | 57 | def initial_reward_position_estimate() -> Decimal: 58 | return (1 - reward_lambda().copy_negate().exp())*SIMPLE_SUPPLY_TOTAL 59 | 60 | def main(): 61 | print("BaselineExponent: ", q128(baseline_exponent())) 62 | print("lambda: ", q128(reward_lambda())) 63 | print("expLamSubOne: ", q128(reward_lambda_prime())) 64 | print("InitialRewardVelocityEstimate: ", atto(initial_reward_veolocity_estimate())) 65 | print("InitialRewardPositionEstimate: ", atto(initial_reward_position_estimate())) 66 | 67 | if __name__ == "__main__": 68 | main() 69 | -------------------------------------------------------------------------------- /actors/builtin/reward/testing.go: -------------------------------------------------------------------------------- 1 | package reward 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | "github.com/filecoin-project/go-state-types/big" 6 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 8 | ) 9 | 10 | type StateSummary struct{} 11 | 12 | var FIL = big.NewInt(1e18) 13 | var StorageMiningAllocationCheck = big.Mul(big.NewInt(1_100_000_000), FIL) 14 | 15 | func CheckStateInvariants(st *State, store adt.Store, priorEpoch abi.ChainEpoch, balance abi.TokenAmount) (*StateSummary, *builtin.MessageAccumulator) { 16 | acc := &builtin.MessageAccumulator{} 17 | 18 | // Can't assert equality because anyone can send funds to reward actor (and already have on mainnet) 19 | acc.Require(big.Add(st.TotalStoragePowerReward, balance).GreaterThanEqual(StorageMiningAllocationCheck), "reward given %v + reward left %v < storage mining allocation %v", st.TotalStoragePowerReward, balance, StorageMiningAllocationCheck) 20 | 21 | acc.Require(st.Epoch == priorEpoch+1, "reward state epoch %d does not match priorEpoch+1 %d", st.Epoch, priorEpoch+1) 22 | acc.Require(st.EffectiveNetworkTime <= st.Epoch, "effective network time greater than state epoch") 23 | 24 | acc.Require(st.CumsumRealized.LessThanEqual(st.CumsumBaseline), "cumsum realized > cumsum baseline") 25 | acc.Require(st.CumsumRealized.GreaterThanEqual(big.Zero()), "cumsum realized < 0") 26 | acc.Require(st.EffectiveBaselinePower.LessThanEqual(st.ThisEpochBaselinePower), "effective baseline power > baseline power") 27 | 28 | return &StateSummary{}, acc 29 | } 30 | -------------------------------------------------------------------------------- /actors/builtin/singletons.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | ) 6 | 7 | // Addresses for singleton system actors. 8 | var ( 9 | // Distinguished AccountActor that is the source of system implicit messages. 10 | SystemActorAddr = mustMakeAddress(0) 11 | InitActorAddr = mustMakeAddress(1) 12 | RewardActorAddr = mustMakeAddress(2) 13 | CronActorAddr = mustMakeAddress(3) 14 | StoragePowerActorAddr = mustMakeAddress(4) 15 | StorageMarketActorAddr = mustMakeAddress(5) 16 | VerifiedRegistryActorAddr = mustMakeAddress(6) 17 | // Distinguished AccountActor that is the destination of all burnt funds. 18 | BurntFundsActorAddr = mustMakeAddress(99) 19 | ) 20 | 21 | const FirstNonSingletonActorId = 100 22 | 23 | func mustMakeAddress(id uint64) addr.Address { 24 | address, err := addr.NewIDAddress(id) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return address 29 | } 30 | -------------------------------------------------------------------------------- /actors/builtin/system/cbor_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 | 3 | package system 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | 9 | cbg "github.com/whyrusleeping/cbor-gen" 10 | xerrors "golang.org/x/xerrors" 11 | ) 12 | 13 | var _ = xerrors.Errorf 14 | 15 | var lengthBufState = []byte{129} 16 | 17 | func (t *State) MarshalCBOR(w io.Writer) error { 18 | if t == nil { 19 | _, err := w.Write(cbg.CborNull) 20 | return err 21 | } 22 | if _, err := w.Write(lengthBufState); err != nil { 23 | return err 24 | } 25 | 26 | scratch := make([]byte, 9) 27 | 28 | // t.BuiltinActors (cid.Cid) (struct) 29 | 30 | if err := cbg.WriteCidBuf(scratch, w, t.BuiltinActors); err != nil { 31 | return xerrors.Errorf("failed to write cid field t.BuiltinActors: %w", err) 32 | } 33 | 34 | return nil 35 | } 36 | 37 | func (t *State) UnmarshalCBOR(r io.Reader) error { 38 | *t = State{} 39 | 40 | br := cbg.GetPeeker(r) 41 | scratch := make([]byte, 8) 42 | 43 | maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) 44 | if err != nil { 45 | return err 46 | } 47 | if maj != cbg.MajArray { 48 | return fmt.Errorf("cbor input should be of type array") 49 | } 50 | 51 | if extra != 1 { 52 | return fmt.Errorf("cbor input had wrong number of fields") 53 | } 54 | 55 | // t.BuiltinActors (cid.Cid) (struct) 56 | 57 | { 58 | 59 | c, err := cbg.ReadCid(br) 60 | if err != nil { 61 | return xerrors.Errorf("failed to read cid field t.BuiltinActors: %w", err) 62 | } 63 | 64 | t.BuiltinActors = c 65 | 66 | } 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /actors/builtin/system/system_actor.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | "github.com/filecoin-project/go-state-types/cbor" 6 | "github.com/filecoin-project/go-state-types/exitcode" 7 | "github.com/ipfs/go-cid" 8 | 9 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 10 | "github.com/filecoin-project/specs-actors/v8/actors/runtime" 11 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 12 | ) 13 | 14 | type Actor struct{} 15 | 16 | func (a Actor) Exports() []interface{} { 17 | return []interface{}{ 18 | builtin.MethodConstructor: a.Constructor, 19 | } 20 | } 21 | 22 | func (a Actor) Code() cid.Cid { 23 | return builtin.SystemActorCodeID 24 | } 25 | 26 | func (a Actor) IsSingleton() bool { 27 | return true 28 | } 29 | 30 | func (a Actor) State() cbor.Er { 31 | return new(State) 32 | } 33 | 34 | var _ runtime.VMActor = Actor{} 35 | 36 | func (a Actor) Constructor(rt runtime.Runtime, _ *abi.EmptyValue) *abi.EmptyValue { 37 | rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 38 | st, err := ConstructState(adt.AsStore(rt)) 39 | builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to construct state") 40 | rt.StateCreate(st) 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /actors/builtin/system/system_actor_state.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "context" 5 | 6 | cid "github.com/ipfs/go-cid" 7 | xerrors "golang.org/x/xerrors" 8 | 9 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/manifest" 10 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 11 | ) 12 | 13 | type State struct { 14 | BuiltinActors cid.Cid // ManifestData 15 | } 16 | 17 | func ConstructState(store adt.Store) (*State, error) { 18 | empty, err := store.Put(context.TODO(), &manifest.ManifestData{}) 19 | if err != nil { 20 | return nil, xerrors.Errorf("failed to create empty manifest: %w", err) 21 | } 22 | 23 | return &State{BuiltinActors: empty}, nil 24 | } 25 | -------------------------------------------------------------------------------- /actors/builtin/system/system_test.go: -------------------------------------------------------------------------------- 1 | package system_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 7 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/manifest" 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/system" 9 | "github.com/filecoin-project/specs-actors/v8/support/mock" 10 | ) 11 | 12 | func TestExports(t *testing.T) { 13 | mock.CheckActorExports(t, system.Actor{}) 14 | } 15 | 16 | func TestConstruction(t *testing.T) { 17 | rt := mock.NewBuilder(builtin.SystemActorAddr).Build(t) 18 | a := system.Actor{} 19 | 20 | rt.ExpectValidateCallerAddr(builtin.SystemActorAddr) 21 | rt.SetCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID) 22 | rt.Call(a.Constructor, nil) 23 | rt.Verify() 24 | 25 | var st system.State 26 | rt.GetState(&st) 27 | 28 | var manifestData manifest.ManifestData 29 | if ok := rt.StoreGet(st.BuiltinActors, &manifestData); !ok { 30 | t.Fatal("missing manifest data") 31 | } 32 | 33 | if len(manifestData.Entries) != 0 { 34 | t.Fatal("expected empty manifest data") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /actors/builtin/testing.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Accumulates a sequence of messages (e.g. validation failures). 8 | type MessageAccumulator struct { 9 | // Accumulated messages. 10 | // This is a pointer to support accumulators derived from `WithPrefix()` accumulating to 11 | // the same underlying collection. 12 | msgs *[]string 13 | // Optional prefix to all new messages, e.g. describing higher level context. 14 | prefix string 15 | } 16 | 17 | // Returns a new accumulator backed by the same collection, that will prefix each new message with 18 | // a formatted string. 19 | func (ma *MessageAccumulator) WithPrefix(format string, args ...interface{}) *MessageAccumulator { 20 | ma.initialize() 21 | return &MessageAccumulator{ 22 | msgs: ma.msgs, 23 | prefix: ma.prefix + fmt.Sprintf(format, args...), 24 | } 25 | } 26 | 27 | func (ma *MessageAccumulator) IsEmpty() bool { 28 | return ma.msgs == nil || len(*ma.msgs) == 0 29 | } 30 | 31 | func (ma *MessageAccumulator) Messages() []string { 32 | if ma.msgs == nil { 33 | return nil 34 | } 35 | return (*ma.msgs)[:] 36 | } 37 | 38 | // Adds messages to the accumulator. 39 | func (ma *MessageAccumulator) Add(msg string) { 40 | ma.initialize() 41 | *ma.msgs = append(*ma.msgs, ma.prefix+msg) 42 | } 43 | 44 | // Adds a message to the accumulator 45 | func (ma *MessageAccumulator) Addf(format string, args ...interface{}) { 46 | ma.Add(fmt.Sprintf(format, args...)) 47 | } 48 | 49 | // Adds messages from another accumulator to this one. 50 | func (ma *MessageAccumulator) AddAll(other *MessageAccumulator) { 51 | if other.msgs == nil { 52 | return 53 | } 54 | for _, msg := range *other.msgs { 55 | ma.Add(msg) 56 | } 57 | } 58 | 59 | // Adds a message if predicate is false. 60 | func (ma *MessageAccumulator) Require(predicate bool, msg string, args ...interface{}) { 61 | if !predicate { 62 | ma.Add(fmt.Sprintf(msg, args...)) 63 | } 64 | } 65 | 66 | func (ma *MessageAccumulator) RequireNoError(err error, msg string, args ...interface{}) { 67 | if err != nil { 68 | msg = msg + ": %v" 69 | args = append(args, err) 70 | ma.Addf(msg, args...) 71 | } 72 | } 73 | 74 | func (ma *MessageAccumulator) initialize() { 75 | if ma.msgs == nil { 76 | ma.msgs = &[]string{} 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /actors/builtin/testing_test.go: -------------------------------------------------------------------------------- 1 | package builtin_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 9 | ) 10 | 11 | func TestMessageAccumulator(t *testing.T) { 12 | t.Run("basics", func(t *testing.T) { 13 | acc := &builtin.MessageAccumulator{} 14 | assert.Equal(t, []string(nil), acc.Messages()) 15 | assert.True(t, acc.Messages() == nil) 16 | assert.True(t, acc.IsEmpty()) 17 | 18 | acc.Add("one") 19 | assert.False(t, acc.IsEmpty()) 20 | acc.Addf("tw%s", "o") 21 | assert.False(t, acc.IsEmpty()) 22 | assert.Equal(t, []string{"one", "two"}, acc.Messages()) 23 | }) 24 | 25 | t.Run("prefix", func(t *testing.T) { 26 | acc := &builtin.MessageAccumulator{} 27 | accA := acc.WithPrefix("A") 28 | 29 | accA.Add("aa") 30 | assert.Equal(t, []string{"Aaa"}, acc.Messages()) 31 | assert.Equal(t, []string{"Aaa"}, accA.Messages()) 32 | 33 | accAB := accA.WithPrefix("B") 34 | accAB.Add("bb") 35 | assert.Equal(t, []string{"Aaa", "ABbb"}, acc.Messages()) 36 | assert.Equal(t, []string{"Aaa", "ABbb"}, accA.Messages()) 37 | assert.Equal(t, []string{"Aaa", "ABbb"}, accA.Messages()) 38 | 39 | func() { 40 | acc := acc.WithPrefix("C") // Shadow 41 | acc.Add("cc") 42 | assert.Equal(t, []string{"Aaa", "ABbb", "Ccc"}, acc.Messages()) 43 | }() 44 | assert.Equal(t, []string{"Aaa", "ABbb", "Ccc"}, acc.Messages()) 45 | }) 46 | 47 | t.Run("merge", func(t *testing.T) { 48 | acc := &builtin.MessageAccumulator{} 49 | acc1 := &builtin.MessageAccumulator{} 50 | 51 | acc1.WithPrefix("A").Add("a1") 52 | acc1.WithPrefix("A").Add("a2") 53 | 54 | acc.AddAll(acc1) 55 | acc.WithPrefix("B").AddAll(acc1) 56 | 57 | assert.Equal(t, []string{"Aa1", "Aa2", "BAa1", "BAa2"}, acc.Messages()) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /actors/builtin/verifreg/testing.go: -------------------------------------------------------------------------------- 1 | package verifreg 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 9 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 10 | ) 11 | 12 | type StateSummary struct { 13 | Verifiers map[addr.Address]DataCap 14 | Clients map[addr.Address]DataCap 15 | } 16 | 17 | // Checks internal invariants of verified registry state. 18 | func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) { 19 | acc := &builtin.MessageAccumulator{} 20 | acc.Require(st.RootKey.Protocol() == addr.ID, "root key %v should have ID protocol", st.RootKey) 21 | 22 | // Check verifiers 23 | allVerifiers := map[addr.Address]DataCap{} 24 | if verifiers, err := adt.AsMap(store, st.Verifiers, builtin.DefaultHamtBitwidth); err != nil { 25 | acc.Addf("error loading verifiers: %v", err) 26 | } else { 27 | var vcap abi.StoragePower 28 | err = verifiers.ForEach(&vcap, func(key string) error { 29 | verifier, err := addr.NewFromBytes([]byte(key)) 30 | if err != nil { 31 | return err 32 | } 33 | acc.Require(verifier.Protocol() == addr.ID, "verifier %v should have ID protocol", verifier) 34 | acc.Require(vcap.GreaterThanEqual(big.Zero()), "verifier %v cap %v is negative", verifier, vcap) 35 | allVerifiers[verifier] = vcap.Copy() 36 | return nil 37 | }) 38 | acc.RequireNoError(err, "error iterating verifiers") 39 | } 40 | 41 | // Check clients 42 | allClients := map[addr.Address]DataCap{} 43 | if clients, err := adt.AsMap(store, st.VerifiedClients, builtin.DefaultHamtBitwidth); err != nil { 44 | acc.Addf("error loading clients: %v", err) 45 | } else { 46 | var ccap abi.StoragePower 47 | err = clients.ForEach(&ccap, func(key string) error { 48 | client, err := addr.NewFromBytes([]byte(key)) 49 | if err != nil { 50 | return err 51 | } 52 | acc.Require(client.Protocol() == addr.ID, "client %v should have ID protocol", client) 53 | acc.Require(ccap.GreaterThanEqual(big.Zero()), "client %v cap %v is negative", client, ccap) 54 | allClients[client] = ccap.Copy() 55 | return nil 56 | }) 57 | acc.RequireNoError(err, "error iterating clients") 58 | } 59 | 60 | // Check verifiers and clients are disjoint. 61 | for v := range allVerifiers { //nolint:nomaprange 62 | _, found := allClients[v] 63 | acc.Require(!found, "verifier %v is also a client", v) 64 | } 65 | // No need to iterate all clients; any overlap must have been one of all verifiers. 66 | 67 | return &StateSummary{ 68 | Verifiers: allVerifiers, 69 | Clients: allClients, 70 | }, acc 71 | } 72 | -------------------------------------------------------------------------------- /actors/migration/nv16/system.go: -------------------------------------------------------------------------------- 1 | package nv16 2 | 3 | import ( 4 | "context" 5 | 6 | system8 "github.com/filecoin-project/specs-actors/v8/actors/builtin/system" 7 | "github.com/ipfs/go-cid" 8 | cbor "github.com/ipfs/go-ipld-cbor" 9 | ) 10 | 11 | // System Actor migrator 12 | type systemActorMigrator struct { 13 | OutCodeCID cid.Cid 14 | ManifestData cid.Cid 15 | } 16 | 17 | func (m systemActorMigrator) migrateState(ctx context.Context, store cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) { 18 | // The ManifestData itself is already in the blockstore 19 | state := system8.State{BuiltinActors: m.ManifestData} 20 | stateHead, err := store.Put(ctx, &state) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | return &actorMigrationResult{ 26 | newCodeCID: m.OutCodeCID, 27 | newHead: stateHead, 28 | }, nil 29 | } 30 | -------------------------------------------------------------------------------- /actors/migration/nv16/test/parallel_migration_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | vm7 "github.com/filecoin-project/specs-actors/v7/support/vm" 8 | 9 | "github.com/filecoin-project/go-state-types/abi" 10 | ipld2 "github.com/filecoin-project/specs-actors/v2/support/ipld" 11 | adt7 "github.com/filecoin-project/specs-actors/v7/actors/util/adt" 12 | "github.com/filecoin-project/specs-actors/v8/actors/migration/nv16" 13 | cbor "github.com/ipfs/go-ipld-cbor" 14 | 15 | "github.com/ipfs/go-cid" 16 | "github.com/stretchr/testify/assert" 17 | "github.com/stretchr/testify/require" 18 | "golang.org/x/sync/errgroup" 19 | ) 20 | 21 | func TestParallelMigrationCalls(t *testing.T) { 22 | // Construct simple prior state tree over a synchronized store 23 | ctx := context.Background() 24 | log := nv16.TestLogger{TB: t} 25 | bs := ipld2.NewSyncBlockStoreInMemory() 26 | vm := vm7.NewVMWithSingletons(ctx, t, bs) 27 | 28 | // Run migration 29 | adtStore := adt7.WrapStore(ctx, cbor.NewCborStore(bs)) 30 | manifestCid := makeTestManifest(t, adtStore) 31 | startRoot := vm.StateRoot() 32 | endRootSerial, err := nv16.MigrateStateTree(ctx, adtStore, manifestCid, startRoot, abi.ChainEpoch(0), nv16.Config{MaxWorkers: 1}, log, nv16.NewMemMigrationCache()) 33 | require.NoError(t, err) 34 | 35 | // Migrate in parallel 36 | var endRootParallel1, endRootParallel2 cid.Cid 37 | grp, ctx := errgroup.WithContext(ctx) 38 | grp.Go(func() error { 39 | var err1 error 40 | endRootParallel1, err1 = nv16.MigrateStateTree(ctx, adtStore, manifestCid, startRoot, abi.ChainEpoch(0), nv16.Config{MaxWorkers: 2}, log, nv16.NewMemMigrationCache()) 41 | return err1 42 | }) 43 | grp.Go(func() error { 44 | var err2 error 45 | endRootParallel2, err2 = nv16.MigrateStateTree(ctx, adtStore, manifestCid, startRoot, abi.ChainEpoch(0), nv16.Config{MaxWorkers: 2}, log, nv16.NewMemMigrationCache()) 46 | return err2 47 | }) 48 | require.NoError(t, grp.Wait()) 49 | assert.Equal(t, endRootSerial, endRootParallel1) 50 | assert.Equal(t, endRootParallel1, endRootParallel2) 51 | } 52 | -------------------------------------------------------------------------------- /actors/migration/nv16/test/system_migration_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | vm7 "github.com/filecoin-project/specs-actors/v7/support/vm" 9 | "github.com/filecoin-project/specs-actors/v8/support/vm7Util" 10 | 11 | "github.com/filecoin-project/go-state-types/rt" 12 | 13 | "github.com/filecoin-project/specs-actors/v7/actors/util/adt" 14 | "github.com/filecoin-project/specs-actors/v8/actors/states" 15 | 16 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 17 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/exported" 18 | manifest8 "github.com/filecoin-project/specs-actors/v8/actors/builtin/manifest" 19 | system8 "github.com/filecoin-project/specs-actors/v8/actors/builtin/system" 20 | "github.com/filecoin-project/specs-actors/v8/actors/migration/nv16" 21 | "github.com/filecoin-project/specs-actors/v8/support/ipld" 22 | vm8 "github.com/filecoin-project/specs-actors/v8/support/vm" 23 | "testing" 24 | 25 | "github.com/ipfs/go-cid" 26 | cbor "github.com/ipfs/go-ipld-cbor" 27 | mh "github.com/multiformats/go-multihash" 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | func makeTestManifest(t *testing.T, store cbor.IpldStore) cid.Cid { 32 | adtStore := adt.WrapStore(context.Background(), store) 33 | builder := cid.V1Builder{Codec: cid.Raw, MhType: mh.IDENTITY} 34 | 35 | manifestData := manifest8.ManifestData{} 36 | for _, name := range []string{"system", "init", "cron", "account", "storagepower", "storageminer", "storagemarket", "paymentchannel", "multisig", "reward", "verifiedregistry"} { 37 | codeCid, err := builder.Sum([]byte(fmt.Sprintf("fil/8/%s", name))) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | manifestData.Entries = append(manifestData.Entries, 43 | manifest8.ManifestEntry{ 44 | Name: name, 45 | Code: codeCid, 46 | }) 47 | } 48 | 49 | manifestDataCid, err := adtStore.Put(context.Background(), &manifestData) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | manifest := manifest8.Manifest{ 55 | Version: 1, 56 | Data: manifestDataCid, 57 | } 58 | 59 | manifestCid, err := adtStore.Put(context.Background(), &manifest) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | 64 | return manifestCid 65 | } 66 | 67 | func TestNv16Migration(t *testing.T) { 68 | ctx := context.Background() 69 | bs := ipld.NewBlockStoreInMemory() 70 | v := vm7.NewVMWithSingletons(ctx, t, bs) 71 | ctxStore := adt.WrapBlockStore(ctx, bs) 72 | manifestCid := makeTestManifest(t, ctxStore) 73 | log := nv16.TestLogger{TB: t} 74 | 75 | v = vm7Util.AdvanceToEpochWithCron(t, v, 200) 76 | 77 | startRoot := v.StateRoot() 78 | cache := nv16.NewMemMigrationCache() 79 | _, err := nv16.MigrateStateTree(ctx, ctxStore, manifestCid, startRoot, v.GetEpoch(), nv16.Config{MaxWorkers: 1}, log, cache) 80 | require.NoError(t, err) 81 | 82 | cacheRoot, err := nv16.MigrateStateTree(ctx, ctxStore, manifestCid, v.StateRoot(), v.GetEpoch(), nv16.Config{MaxWorkers: 1}, log, cache) 83 | require.NoError(t, err) 84 | 85 | noCacheRoot, err := nv16.MigrateStateTree(ctx, ctxStore, manifestCid, v.StateRoot(), v.GetEpoch(), nv16.Config{MaxWorkers: 1}, log, nv16.NewMemMigrationCache()) 86 | require.NoError(t, err) 87 | require.True(t, cacheRoot.Equals(noCacheRoot)) 88 | 89 | lookup := map[cid.Cid]rt.VMActor{} 90 | for _, ba := range exported.BuiltinActors() { 91 | lookup[ba.Code()] = ba 92 | } 93 | 94 | v8, err := vm8.NewVMAtEpoch(ctx, lookup, ctxStore, noCacheRoot, v.GetEpoch()) 95 | require.NoError(t, err) 96 | 97 | stateTree, err := v8.GetStateTree() 98 | require.NoError(t, err) 99 | totalBalance, err := v8.GetTotalActorBalance() 100 | require.NoError(t, err) 101 | acc, err := states.CheckStateInvariants(stateTree, totalBalance, v8.GetEpoch()-1) 102 | require.NoError(t, err) 103 | require.True(t, acc.IsEmpty(), strings.Join(acc.Messages(), "\n")) 104 | 105 | actor, found, err := stateTree.GetActor(builtin.SystemActorAddr) 106 | require.NoError(t, err) 107 | require.True(t, found, "system actor not found") 108 | 109 | var manifest manifest8.Manifest 110 | err = ctxStore.Get(ctx, manifestCid, &manifest) 111 | require.NoError(t, err) 112 | 113 | err = manifest.Load(ctx, ctxStore) 114 | require.NoError(t, err) 115 | 116 | systemActorCodeCid, ok := manifest.Get("system") 117 | require.True(t, ok, "system actor not in manifest") 118 | require.Equal(t, systemActorCodeCid, actor.Code) 119 | 120 | var state system8.State 121 | err = ctxStore.Get(ctx, actor.Head, &state) 122 | require.NoError(t, err) 123 | require.Equal(t, manifest.Data, state.BuiltinActors) 124 | } 125 | -------------------------------------------------------------------------------- /actors/migration/nv16/util.go: -------------------------------------------------------------------------------- 1 | package nv16 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/filecoin-project/go-state-types/rt" 8 | "github.com/ipfs/go-cid" 9 | "golang.org/x/xerrors" 10 | ) 11 | 12 | type MemMigrationCache struct { 13 | MigrationMap sync.Map 14 | } 15 | 16 | func NewMemMigrationCache() *MemMigrationCache { 17 | return new(MemMigrationCache) 18 | } 19 | 20 | func (m *MemMigrationCache) Write(key string, c cid.Cid) error { 21 | m.MigrationMap.Store(key, c) 22 | return nil 23 | } 24 | 25 | func (m *MemMigrationCache) Read(key string) (bool, cid.Cid, error) { 26 | val, found := m.MigrationMap.Load(key) 27 | if !found { 28 | return false, cid.Undef, nil 29 | } 30 | c, ok := val.(cid.Cid) 31 | if !ok { 32 | return false, cid.Undef, xerrors.Errorf("non cid value in cache") 33 | } 34 | 35 | return true, c, nil 36 | } 37 | 38 | func (m *MemMigrationCache) Load(key string, loadFunc func() (cid.Cid, error)) (cid.Cid, error) { 39 | found, c, err := m.Read(key) 40 | if err != nil { 41 | return cid.Undef, err 42 | } 43 | if found { 44 | return c, nil 45 | } 46 | c, err = loadFunc() 47 | if err != nil { 48 | return cid.Undef, err 49 | } 50 | m.MigrationMap.Store(key, c) 51 | return c, nil 52 | } 53 | 54 | func (m *MemMigrationCache) Clone() *MemMigrationCache { 55 | newCache := NewMemMigrationCache() 56 | newCache.Update(m) 57 | return newCache 58 | } 59 | 60 | func (m *MemMigrationCache) Update(other *MemMigrationCache) { 61 | other.MigrationMap.Range(func(key, value interface{}) bool { 62 | m.MigrationMap.Store(key, value) 63 | return true 64 | }) 65 | } 66 | 67 | type TestLogger struct { 68 | TB testing.TB 69 | } 70 | 71 | func (t TestLogger) Log(_ rt.LogLevel, msg string, args ...interface{}) { 72 | t.TB.Logf(msg, args...) 73 | } 74 | -------------------------------------------------------------------------------- /actors/runtime/proof/verify.go: -------------------------------------------------------------------------------- 1 | package proof 2 | 3 | import ( 4 | proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof" 5 | proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" 6 | proof7 "github.com/filecoin-project/specs-actors/v7/actors/runtime/proof" 7 | ) 8 | 9 | /// 10 | /// Sealing 11 | /// 12 | 13 | // Information needed to verify a seal proof. 14 | //type SealVerifyInfo struct { 15 | // SealProof abi.RegisteredSealProof 16 | // abi.SectorID 17 | // DealIDs []abi.DealID 18 | // Randomness abi.SealRandomness 19 | // InteractiveRandomness abi.InteractiveSealRandomness 20 | // ReplicaProof []byte 21 | // 22 | // // Safe because we get those from the miner actor 23 | // SealedCID cid.Cid `checked:"true"` // CommR 24 | // UnsealedCID cid.Cid `checked:"true"` // CommD 25 | //} 26 | type SealVerifyInfo = proof0.SealVerifyInfo 27 | 28 | type AggregateSealVerifyInfo = proof5.AggregateSealVerifyInfo 29 | 30 | type AggregateSealVerifyProofAndInfos = proof5.AggregateSealVerifyProofAndInfos 31 | 32 | /// 33 | /// Replica 34 | /// 35 | 36 | // Information needed to verify a replica update 37 | 38 | type ReplicaUpdateInfo = proof7.ReplicaUpdateInfo 39 | 40 | /// 41 | /// PoSting 42 | /// 43 | 44 | // Information about a proof necessary for PoSt verification. 45 | // type SectorInfo struct { 46 | // SealProof abi.RegisteredSealProof // RegisteredProof used when sealing - needs to be mapped to PoSt registered proof when used to verify a PoSt 47 | // SectorNumber abi.SectorNumber 48 | // SealedCID cid.Cid // CommR 49 | // } 50 | type SectorInfo = proof0.SectorInfo 51 | 52 | type ExtendedSectorInfo = proof7.ExtendedSectorInfo 53 | 54 | //type PoStProof struct { 55 | // PoStProof abi.RegisteredPoStProof 56 | // ProofBytes []byte 57 | //} 58 | type PoStProof = proof0.PoStProof 59 | 60 | // Information needed to verify a Winning PoSt attached to a block header. 61 | // Note: this is not used within the state machine, but by the consensus/election mechanisms. 62 | //type WinningPoStVerifyInfo struct { 63 | // Randomness abi.PoStRandomness 64 | // Proofs []PoStProof 65 | // ChallengedSectors []SectorInfo 66 | // Prover abi.ActorID // used to derive 32-byte prover ID 67 | //} 68 | type WinningPoStVerifyInfo = proof0.WinningPoStVerifyInfo 69 | 70 | // Information needed to verify a Window PoSt submitted directly to a miner actor. 71 | //type WindowPoStVerifyInfo struct { 72 | // Randomness abi.PoStRandomness 73 | // Proofs []PoStProof 74 | // ChallengedSectors []SectorInfo 75 | // Prover abi.ActorID // used to derive 32-byte prover ID 76 | //} 77 | type WindowPoStVerifyInfo = proof0.WindowPoStVerifyInfo 78 | -------------------------------------------------------------------------------- /actors/runtime/types.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/rt" 5 | runtime0 "github.com/filecoin-project/specs-actors/actors/runtime" 6 | ) 7 | 8 | // Concrete types associated with the runtime interface. 9 | 10 | // Result of checking two headers for a consensus fault. 11 | type ConsensusFault = runtime0.ConsensusFault 12 | 13 | //type ConsensusFault struct { 14 | // // Address of the miner at fault (always an ID address). 15 | // Target addr.Address 16 | // // Epoch of the fault, which is the higher epoch of the two blocks causing it. 17 | // Epoch abi.ChainEpoch 18 | // // Type of fault. 19 | // Type ConsensusFaultType 20 | //} 21 | 22 | type ConsensusFaultType = runtime0.ConsensusFaultType 23 | 24 | const ( 25 | ConsensusFaultDoubleForkMining = runtime0.ConsensusFaultDoubleForkMining 26 | ConsensusFaultParentGrinding = runtime0.ConsensusFaultParentGrinding 27 | ConsensusFaultTimeOffsetMining = runtime0.ConsensusFaultTimeOffsetMining 28 | ) 29 | 30 | type VMActor = rt.VMActor 31 | -------------------------------------------------------------------------------- /actors/states/election.go: -------------------------------------------------------------------------------- 1 | package states 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | 8 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" 9 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/power" 10 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 11 | ) 12 | 13 | // Checks for miner election eligibility. 14 | // A miner must satisfy conditions on both the immediate parent state, as well as state at the 15 | // Winning PoSt election lookback. 16 | 17 | // Tests whether a miner is eligible to win an election given the immediately prior state of the 18 | // miner and system actors. 19 | func MinerEligibleForElection(store adt.Store, mstate *miner.State, pstate *power.State, maddr addr.Address, currEpoch abi.ChainEpoch) (bool, error) { 20 | // Non-empty power claim. 21 | if claim, found, err := pstate.GetClaim(store, maddr); err != nil { 22 | return false, err 23 | } else if !found { 24 | return false, err 25 | } else if claim.QualityAdjPower.LessThanEqual(big.Zero()) { 26 | return false, err 27 | } 28 | 29 | // No fee debt. 30 | if !mstate.IsDebtFree() { 31 | return false, nil 32 | } 33 | 34 | // No active consensus faults. 35 | if mInfo, err := mstate.GetInfo(store); err != nil { 36 | return false, err 37 | } else if miner.ConsensusFaultActive(mInfo, currEpoch) { 38 | return false, nil 39 | } 40 | 41 | return true, nil 42 | } 43 | 44 | // Tests whether a miner is eligible for election given a Winning PoSt lookback state. 45 | // The power state must be the state of the power actor at Winning PoSt lookback epoch. 46 | func MinerPoStLookbackEligibleForElection(store adt.Store, pstate *power.State, mAddr addr.Address) (bool, error) { 47 | // Minimum power requirements. 48 | return pstate.MinerNominalPowerMeetsConsensusMinimum(store, mAddr) 49 | } 50 | -------------------------------------------------------------------------------- /actors/states/tree.go: -------------------------------------------------------------------------------- 1 | package states 2 | 3 | import ( 4 | "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | states0 "github.com/filecoin-project/specs-actors/actors/states" 7 | "github.com/ipfs/go-cid" 8 | "golang.org/x/xerrors" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 12 | ) 13 | 14 | // Value type of the top level of the state tree. 15 | // Represents the on-chain state of a single actor. 16 | // type Actor struct { 17 | // Code cid.Cid // CID representing the code associated with the actor 18 | // Head cid.Cid // CID of the head state object for the actor 19 | // CallSeqNum uint64 // CallSeqNum for the next message to be received by the actor (non-zero for accounts only) 20 | // Balance big.Int // Token balance of the actor 21 | // } 22 | type Actor = states0.Actor 23 | 24 | // A specialization of a map of ID-addresses to actor heads. 25 | type Tree struct { 26 | Map *adt.Map 27 | Store adt.Store 28 | } 29 | 30 | // Initializes a new, empty state tree backed by a store. 31 | func NewTree(store adt.Store) (*Tree, error) { 32 | emptyMap, err := adt.MakeEmptyMap(store, builtin.DefaultHamtBitwidth) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &Tree{ 37 | Map: emptyMap, 38 | Store: store, 39 | }, nil 40 | } 41 | 42 | // Loads a tree from a root CID and store. 43 | func LoadTree(s adt.Store, r cid.Cid) (*Tree, error) { 44 | m, err := adt.AsMap(s, r, builtin.DefaultHamtBitwidth) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return &Tree{ 49 | Map: m, 50 | Store: s, 51 | }, nil 52 | } 53 | 54 | // Writes the tree root node to the store, and returns its CID. 55 | func (t *Tree) Flush() (cid.Cid, error) { 56 | return t.Map.Root() 57 | } 58 | 59 | // Loads the state associated with an address. 60 | func (t *Tree) GetActor(addr address.Address) (*Actor, bool, error) { 61 | if addr.Protocol() != address.ID { 62 | return nil, false, xerrors.Errorf("non-ID address %v invalid as actor key", addr) 63 | } 64 | var actor Actor 65 | found, err := t.Map.Get(abi.AddrKey(addr), &actor) 66 | return &actor, found, err 67 | } 68 | 69 | // Sets the state associated with an address, overwriting if it already present. 70 | func (t *Tree) SetActor(addr address.Address, actor *Actor) error { 71 | if addr.Protocol() != address.ID { 72 | return xerrors.Errorf("non-ID address %v invalid as actor key", addr) 73 | } 74 | return t.Map.Put(abi.AddrKey(addr), actor) 75 | } 76 | 77 | // Traverses all entries in the tree. 78 | func (t *Tree) ForEach(fn func(addr address.Address, actor *Actor) error) error { 79 | var val Actor 80 | return t.Map.ForEach(&val, func(key string) error { 81 | addr, err := address.NewFromBytes([]byte(key)) 82 | if err != nil { 83 | return err 84 | } 85 | return fn(addr, &val) 86 | }) 87 | } 88 | 89 | // Traverses all keys in the tree, without decoding the actor states. 90 | func (t *Tree) ForEachKey(fn func(addr address.Address) error) error { 91 | return t.Map.ForEach(nil, func(key string) error { 92 | addr, err := address.NewFromBytes([]byte(key)) 93 | if err != nil { 94 | return err 95 | } 96 | return fn(addr) 97 | }) 98 | } 99 | -------------------------------------------------------------------------------- /actors/test/multisig_proposal_hash_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "testing" 7 | 8 | addr "github.com/filecoin-project/go-address" 9 | "github.com/filecoin-project/go-state-types/abi" 10 | "github.com/filecoin-project/go-state-types/big" 11 | "github.com/filecoin-project/go-state-types/exitcode" 12 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 13 | init_ "github.com/filecoin-project/specs-actors/v8/actors/builtin/init" 14 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/multisig" 15 | "github.com/filecoin-project/specs-actors/v8/support/ipld" 16 | "github.com/filecoin-project/specs-actors/v8/support/vm" 17 | "github.com/minio/blake2b-simd" 18 | "github.com/stretchr/testify/assert" 19 | "github.com/stretchr/testify/require" 20 | ) 21 | 22 | func TestProposalHash(t *testing.T) { 23 | ctx := context.Background() 24 | v := vm.NewVMWithSingletons(ctx, t, ipld.NewBlockStoreInMemory()) 25 | addrs := vm.CreateAccounts(ctx, t, v, 3, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778) 26 | 27 | alice := vm.RequireNormalizeAddress(t, addrs[0], v) 28 | bob := vm.RequireNormalizeAddress(t, addrs[1], v) 29 | 30 | getSysActBal := func() abi.TokenAmount { 31 | act, found, err := v.GetActor(builtin.SystemActorAddr) 32 | require.NoError(t, err) 33 | require.True(t, found) 34 | return act.Balance 35 | } 36 | sysActStartBal := getSysActBal() 37 | 38 | multisigParams := multisig.ConstructorParams{ 39 | Signers: addrs, 40 | NumApprovalsThreshold: 2, 41 | } 42 | 43 | paramBuf := new(bytes.Buffer) 44 | err := multisigParams.MarshalCBOR(paramBuf) 45 | require.NoError(t, err) 46 | 47 | initParam := init_.ExecParams{ 48 | CodeCID: builtin.MultisigActorCodeID, 49 | ConstructorParams: paramBuf.Bytes(), 50 | } 51 | ret := vm.ApplyOk(t, v, addrs[0], builtin.InitActorAddr, big.Zero(), builtin.MethodsInit.Exec, &initParam) 52 | initRet := ret.(*init_.ExecReturn) 53 | assert.NotNil(t, initRet) 54 | multisigAddr := initRet.IDAddress 55 | 56 | // fund msig and propose msig sends funds to system actor 57 | filDelta := big.Mul(big.NewInt(3), builtin.OneNanoFIL) 58 | proposeSendSystemParams := multisig.ProposeParams{ 59 | To: builtin.SystemActorAddr, 60 | Value: filDelta, 61 | Method: builtin.MethodSend, 62 | } 63 | vm.ApplyOk(t, v, alice, multisigAddr, filDelta, builtin.MethodsMultisig.Propose, &proposeSendSystemParams) 64 | 65 | wrongTx := &multisig.Transaction{ 66 | To: builtin.SystemActorAddr, 67 | Value: big.Sub(filDelta, big.NewInt(1)), // incorrect send amount not consistent with proposal 68 | Method: builtin.MethodSend, 69 | Approved: []addr.Address{alice}, 70 | } 71 | wrongHash, err := multisig.ComputeProposalHash(wrongTx, blake2b.Sum256) 72 | require.NoError(t, err) 73 | badApproveParams := multisig.TxnIDParams{ 74 | ID: 0, 75 | ProposalHash: wrongHash, 76 | } 77 | vm.ApplyCode(t, v, bob, multisigAddr, big.Zero(), builtin.MethodsMultisig.Approve, &badApproveParams, exitcode.ErrIllegalArgument) 78 | assert.Equal(t, sysActStartBal, getSysActBal()) 79 | 80 | correctTx := wrongTx 81 | correctTx.Value = filDelta 82 | correctHash, err := multisig.ComputeProposalHash(correctTx, blake2b.Sum256) 83 | require.NoError(t, err) 84 | approveParams := multisig.TxnIDParams{ 85 | ID: 0, 86 | ProposalHash: correctHash, 87 | } 88 | vm.ApplyOk(t, v, bob, multisigAddr, big.Zero(), builtin.MethodsMultisig.Approve, &approveParams) 89 | 90 | assert.Equal(t, big.Add(sysActStartBal, filDelta), getSysActBal()) 91 | } 92 | -------------------------------------------------------------------------------- /actors/util/adt/array_test.go: -------------------------------------------------------------------------------- 1 | package adt_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-address" 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 10 | "github.com/filecoin-project/specs-actors/v8/support/mock" 11 | ) 12 | 13 | func TestArrayNotFound(t *testing.T) { 14 | rt := mock.NewBuilder(address.Undef).Build(t) 15 | store := adt.AsStore(rt) 16 | arr, err := adt.MakeEmptyArray(store, 3) 17 | require.NoError(t, err) 18 | 19 | found, err := arr.Get(7, nil) 20 | require.NoError(t, err) 21 | require.False(t, found) 22 | } 23 | -------------------------------------------------------------------------------- /actors/util/adt/balancetable.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/filecoin-project/go-state-types/abi" 6 | "github.com/filecoin-project/go-state-types/big" 7 | cid "github.com/ipfs/go-cid" 8 | "golang.org/x/xerrors" 9 | ) 10 | 11 | // Bitwidth of balance table HAMTs, determined empirically from mutation 12 | // patterns and projections of mainnet data 13 | const BalanceTableBitwidth = 6 14 | 15 | // A specialization of a map of addresses to (positive) token amounts. 16 | // Absent keys implicitly have a balance of zero. 17 | type BalanceTable Map 18 | 19 | // Interprets a store as balance table with root `r`. 20 | func AsBalanceTable(s Store, r cid.Cid) (*BalanceTable, error) { 21 | m, err := AsMap(s, r, BalanceTableBitwidth) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return &BalanceTable{ 27 | root: m.root, 28 | store: s, 29 | }, nil 30 | } 31 | 32 | // Returns the root cid of underlying HAMT. 33 | func (t *BalanceTable) Root() (cid.Cid, error) { 34 | return (*Map)(t).Root() 35 | } 36 | 37 | // Gets the balance for a key, which is zero if they key has never been added to. 38 | func (t *BalanceTable) Get(key addr.Address) (abi.TokenAmount, error) { 39 | var value abi.TokenAmount 40 | found, err := (*Map)(t).Get(abi.AddrKey(key), &value) 41 | if !found || err != nil { 42 | value = big.Zero() 43 | } 44 | 45 | return value, err 46 | } 47 | 48 | // Adds an amount to a balance, requiring the resulting balance to be non-negative. 49 | func (t *BalanceTable) Add(key addr.Address, value abi.TokenAmount) error { 50 | prev, err := t.Get(key) 51 | if err != nil { 52 | return err 53 | } 54 | sum := big.Add(prev, value) 55 | sign := sum.Sign() 56 | if sign < 0 { 57 | return xerrors.Errorf("adding %v to balance %v would give negative: %v", value, prev, sum) 58 | } else if sign == 0 && !prev.IsZero() { 59 | return (*Map)(t).Delete(abi.AddrKey(key)) 60 | } 61 | return (*Map)(t).Put(abi.AddrKey(key), &sum) 62 | } 63 | 64 | // Subtracts up to the specified amount from a balance, without reducing the balance below some minimum. 65 | // Returns the amount subtracted. 66 | func (t *BalanceTable) SubtractWithMinimum(key addr.Address, req abi.TokenAmount, floor abi.TokenAmount) (abi.TokenAmount, error) { 67 | prev, err := t.Get(key) 68 | if err != nil { 69 | return big.Zero(), err 70 | } 71 | 72 | available := big.Max(big.Zero(), big.Sub(prev, floor)) 73 | sub := big.Min(available, req) 74 | if sub.Sign() > 0 { 75 | err = t.Add(key, sub.Neg()) 76 | if err != nil { 77 | return big.Zero(), err 78 | } 79 | } 80 | return sub, nil 81 | } 82 | 83 | // MustSubtract subtracts the given amount from the account's balance. 84 | // Returns an error if the account has insufficient balance 85 | func (t *BalanceTable) MustSubtract(key addr.Address, req abi.TokenAmount) error { 86 | prev, err := t.Get(key) 87 | if err != nil { 88 | return err 89 | } 90 | if req.GreaterThan(prev) { 91 | return xerrors.New("couldn't subtract the requested amount") 92 | } 93 | return t.Add(key, req.Neg()) 94 | } 95 | 96 | // Returns the total balance held by this BalanceTable 97 | func (t *BalanceTable) Total() (abi.TokenAmount, error) { 98 | total := big.Zero() 99 | var cur abi.TokenAmount 100 | err := (*Map)(t).ForEach(&cur, func(key string) error { 101 | total = big.Add(total, cur) 102 | return nil 103 | }) 104 | return total, err 105 | } 106 | -------------------------------------------------------------------------------- /actors/util/adt/multimap.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | "github.com/filecoin-project/go-state-types/cbor" 6 | cid "github.com/ipfs/go-cid" 7 | cbg "github.com/whyrusleeping/cbor-gen" 8 | "golang.org/x/xerrors" 9 | ) 10 | 11 | // Multimap stores multiple values per key in a HAMT of AMTs. 12 | // The order of insertion of values for each key is retained. 13 | type Multimap struct { 14 | mp *Map 15 | innerBitwidth int 16 | } 17 | 18 | // Interprets a store as a HAMT-based map of AMTs with root `r`. 19 | // The outer map is interpreted with a branching factor of 2^bitwidth. 20 | func AsMultimap(s Store, r cid.Cid, outerBitwidth, innerBitwidth int) (*Multimap, error) { 21 | m, err := AsMap(s, r, outerBitwidth) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return &Multimap{m, innerBitwidth}, nil 27 | } 28 | 29 | // Creates a new map backed by an empty HAMT and flushes it to the store. 30 | // The outer map has a branching factor of 2^bitwidth. 31 | func MakeEmptyMultimap(s Store, outerBitwidth, innerBitwidth int) (*Multimap, error) { 32 | m, err := MakeEmptyMap(s, outerBitwidth) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &Multimap{m, innerBitwidth}, nil 37 | } 38 | 39 | // Creates and stores a new empty multimap, returning its CID. 40 | func StoreEmptyMultimap(store Store, outerBitwidth, innerBitwidth int) (cid.Cid, error) { 41 | mmap, err := MakeEmptyMultimap(store, outerBitwidth, innerBitwidth) 42 | if err != nil { 43 | return cid.Undef, err 44 | } 45 | return mmap.Root() 46 | } 47 | 48 | // Returns the root cid of the underlying HAMT. 49 | func (mm *Multimap) Root() (cid.Cid, error) { 50 | return mm.mp.Root() 51 | } 52 | 53 | // Adds a value for a key. 54 | func (mm *Multimap) Add(key abi.Keyer, value cbor.Marshaler) error { 55 | // Load the array under key, or initialize a new empty one if not found. 56 | array, found, err := mm.Get(key) 57 | if err != nil { 58 | return err 59 | } 60 | if !found { 61 | array, err = MakeEmptyArray(mm.mp.store, mm.innerBitwidth) 62 | if err != nil { 63 | return err 64 | } 65 | } 66 | 67 | // Append to the array. 68 | if err = array.AppendContinuous(value); err != nil { 69 | return xerrors.Errorf("failed to add multimap key %v value %v: %w", key, value, err) 70 | } 71 | 72 | c, err := array.Root() 73 | if err != nil { 74 | return xerrors.Errorf("failed to flush child array: %w", err) 75 | } 76 | 77 | // Store the new array root under key. 78 | newArrayRoot := cbg.CborCid(c) 79 | err = mm.mp.Put(key, &newArrayRoot) 80 | if err != nil { 81 | return xerrors.Errorf("failed to store multimap values: %w", err) 82 | } 83 | return nil 84 | } 85 | 86 | // Removes all values for a key. 87 | func (mm *Multimap) RemoveAll(key abi.Keyer) error { 88 | if _, err := mm.mp.TryDelete(key); err != nil { 89 | return xerrors.Errorf("failed to delete multimap key %v root %v: %w", key, mm.mp.root, err) 90 | } 91 | return nil 92 | } 93 | 94 | // Iterates all entries for a key in the order they were inserted, deserializing each value in turn into `out` and then 95 | // calling a function. 96 | // Iteration halts if the function returns an error. 97 | // If the output parameter is nil, deserialization is skipped. 98 | func (mm *Multimap) ForEach(key abi.Keyer, out cbor.Unmarshaler, fn func(i int64) error) error { 99 | array, found, err := mm.Get(key) 100 | if err != nil { 101 | return err 102 | } 103 | if found { 104 | return array.ForEach(out, fn) 105 | } 106 | return nil 107 | } 108 | 109 | func (mm *Multimap) ForAll(fn func(k string, arr *Array) error) error { 110 | var arrRoot cbg.CborCid 111 | if err := mm.mp.ForEach(&arrRoot, func(k string) error { 112 | arr, err := AsArray(mm.mp.store, cid.Cid(arrRoot), mm.innerBitwidth) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | return fn(k, arr) 118 | }); err != nil { 119 | return err 120 | } 121 | 122 | return nil 123 | } 124 | 125 | func (mm *Multimap) Get(key abi.Keyer) (*Array, bool, error) { 126 | var arrayRoot cbg.CborCid 127 | found, err := mm.mp.Get(key, &arrayRoot) 128 | if err != nil { 129 | return nil, false, xerrors.Errorf("failed to load multimap key %v: %w", key, err) 130 | } 131 | var array *Array 132 | if found { 133 | array, err = AsArray(mm.mp.store, cid.Cid(arrayRoot), mm.innerBitwidth) 134 | if err != nil { 135 | return nil, false, xerrors.Errorf("failed to load value %v as an array: %w", key, err) 136 | } 137 | } 138 | return array, found, nil 139 | } 140 | -------------------------------------------------------------------------------- /actors/util/adt/set.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/abi" 5 | cid "github.com/ipfs/go-cid" 6 | ) 7 | 8 | // Set interprets a Map as a set, storing keys (with empty values) in a HAMT. 9 | type Set struct { 10 | m *Map 11 | } 12 | 13 | // AsSet interprets a store as a HAMT-based set with root `r`. 14 | // The HAMT is interpreted with branching factor 2^bitwidth. 15 | func AsSet(s Store, r cid.Cid, bitwidth int) (*Set, error) { 16 | m, err := AsMap(s, r, bitwidth) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return &Set{ 22 | m: m, 23 | }, nil 24 | } 25 | 26 | // NewSet creates a new HAMT with root `r` and store `s`. 27 | // The HAMT has branching factor 2^bitwidth. 28 | func MakeEmptySet(s Store, bitwidth int) (*Set, error) { 29 | m, err := MakeEmptyMap(s, bitwidth) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return &Set{m}, nil 34 | } 35 | 36 | // Root return the root cid of HAMT. 37 | func (h *Set) Root() (cid.Cid, error) { 38 | return h.m.Root() 39 | } 40 | 41 | // Put adds `k` to the set. 42 | func (h *Set) Put(k abi.Keyer) error { 43 | return h.m.Put(k, nil) 44 | } 45 | 46 | // Has returns true iff `k` is in the set. 47 | func (h *Set) Has(k abi.Keyer) (bool, error) { 48 | return h.m.Get(k, nil) 49 | } 50 | 51 | // Removes `k` from the set, if present. 52 | // Returns whether the key was previously present. 53 | func (h *Set) TryDelete(k abi.Keyer) (bool, error) { 54 | return h.m.TryDelete(k) 55 | } 56 | 57 | // Removes `k` from the set, expecting it to be present. 58 | func (h *Set) Delete(k abi.Keyer) error { 59 | return h.m.Delete(k) 60 | } 61 | 62 | // ForEach iterates over all values in the set, calling the callback for each value. 63 | // Returning error from the callback stops the iteration. 64 | func (h *Set) ForEach(cb func(k string) error) error { 65 | return h.m.ForEach(nil, cb) 66 | } 67 | 68 | // Collects all the keys from the set into a slice of strings. 69 | func (h *Set) CollectKeys() (out []string, err error) { 70 | return h.m.CollectKeys() 71 | } 72 | -------------------------------------------------------------------------------- /actors/util/adt/store.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/filecoin-project/go-state-types/cbor" 7 | "github.com/filecoin-project/go-state-types/exitcode" 8 | adt2 "github.com/filecoin-project/specs-actors/v2/actors/util/adt" 9 | cid "github.com/ipfs/go-cid" 10 | ipldcbor "github.com/ipfs/go-ipld-cbor" 11 | 12 | vmr "github.com/filecoin-project/specs-actors/v8/actors/runtime" 13 | ) 14 | 15 | type Store = adt2.Store 16 | 17 | // Adapts a vanilla IPLD store as an ADT store. 18 | func WrapStore(ctx context.Context, store ipldcbor.IpldStore) Store { 19 | return &wstore{ 20 | ctx: ctx, 21 | IpldStore: store, 22 | } 23 | } 24 | 25 | // Adapts a block store as an ADT store. 26 | func WrapBlockStore(ctx context.Context, bs ipldcbor.IpldBlockstore) Store { 27 | return WrapStore(ctx, ipldcbor.NewCborStore(bs)) 28 | } 29 | 30 | type wstore struct { 31 | ctx context.Context 32 | ipldcbor.IpldStore 33 | } 34 | 35 | var _ Store = &wstore{} 36 | 37 | func (s *wstore) Context() context.Context { 38 | return s.ctx 39 | } 40 | 41 | // Adapter for a Runtime as an ADT Store. 42 | 43 | // Adapts a Runtime as an ADT store. 44 | func AsStore(rt vmr.Runtime) Store { 45 | return rtStore{rt} 46 | } 47 | 48 | type rtStore struct { 49 | vmr.Runtime 50 | } 51 | 52 | var _ Store = &rtStore{} 53 | 54 | func (r rtStore) Context() context.Context { 55 | return r.Runtime.Context() 56 | } 57 | 58 | func (r rtStore) Get(_ context.Context, c cid.Cid, out interface{}) error { 59 | // The Go context is (un/fortunately?) dropped here. 60 | // See https://github.com/filecoin-project/specs-actors/issues/140 61 | if !r.StoreGet(c, out.(cbor.Unmarshaler)) { 62 | r.Abortf(exitcode.ErrNotFound, "not found") 63 | } 64 | return nil 65 | } 66 | 67 | func (r rtStore) Put(_ context.Context, v interface{}) (cid.Cid, error) { 68 | // The Go context is (un/fortunately?) dropped here. 69 | // See https://github.com/filecoin-project/specs-actors/issues/140 70 | return r.StorePut(v.(cbor.Marshaler)), nil 71 | } 72 | -------------------------------------------------------------------------------- /actors/util/bitfield.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/filecoin-project/go-bitfield" 5 | "github.com/filecoin-project/go-bitfield/rle" 6 | ) 7 | 8 | type BitField = bitfield.BitField 9 | 10 | func isEmpty(iter rlepluslazy.RunIterator) (bool, error) { 11 | // Look for the first non-zero bit. 12 | for iter.HasNext() { 13 | r, err := iter.NextRun() 14 | if err != nil { 15 | return false, err 16 | } 17 | if r.Val { 18 | return false, nil 19 | } 20 | } 21 | return true, nil 22 | } 23 | 24 | // Checks whether bitfield `a` contains any bit that is set in bitfield `b`. 25 | func BitFieldContainsAny(a, b BitField) (bool, error) { 26 | aruns, err := a.RunIterator() 27 | if err != nil { 28 | return false, err 29 | } 30 | 31 | bruns, err := b.RunIterator() 32 | if err != nil { 33 | return false, err 34 | } 35 | 36 | // Take the intersection of the two bitfields. 37 | combined, err := rlepluslazy.And(aruns, bruns) 38 | if err != nil { 39 | return false, err 40 | } 41 | 42 | // Look for the first non-zero bit. 43 | empty, err := isEmpty(combined) 44 | if err != nil { 45 | return false, err 46 | } 47 | return !empty, nil 48 | } 49 | 50 | // Checks whether bitfield `a` contains all bits set in bitfield `b`. 51 | func BitFieldContainsAll(a, b BitField) (bool, error) { 52 | aruns, err := a.RunIterator() 53 | if err != nil { 54 | return false, err 55 | } 56 | 57 | bruns, err := b.RunIterator() 58 | if err != nil { 59 | return false, err 60 | } 61 | 62 | // Remove any elements in a from b. If b contains bits not in a, some 63 | // bits will remain. 64 | combined, err := rlepluslazy.Subtract(bruns, aruns) 65 | if err != nil { 66 | return false, err 67 | } 68 | return isEmpty(combined) 69 | } 70 | -------------------------------------------------------------------------------- /actors/util/bitfield_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/filecoin-project/go-bitfield" 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/util" 11 | ) 12 | 13 | func TestBitFieldUnset(t *testing.T) { 14 | bf := bitfield.New() 15 | bf.Set(1) 16 | bf.Set(2) 17 | bf.Set(3) 18 | bf.Set(4) 19 | bf.Set(5) 20 | 21 | bf.Unset(3) 22 | 23 | m, err := bf.AllMap(100) 24 | assert.NoError(t, err) 25 | _, found := m[3] 26 | assert.False(t, found) 27 | 28 | cnt, err := bf.Count() 29 | assert.NoError(t, err) 30 | assert.Equal(t, uint64(4), cnt) 31 | 32 | bf2 := roundtripMarshal(t, bf) 33 | 34 | cnt, err = bf2.Count() 35 | assert.NoError(t, err) 36 | assert.Equal(t, uint64(4), cnt) 37 | 38 | m, err = bf.AllMap(100) 39 | assert.NoError(t, err) 40 | _, found = m[3] 41 | assert.False(t, found) 42 | } 43 | 44 | func roundtripMarshal(t *testing.T, in bitfield.BitField) bitfield.BitField { 45 | buf := new(bytes.Buffer) 46 | err := in.MarshalCBOR(buf) 47 | assert.NoError(t, err) 48 | 49 | bf2 := bitfield.New() 50 | err = bf2.UnmarshalCBOR(buf) 51 | assert.NoError(t, err) 52 | return bf2 53 | } 54 | 55 | func TestBitFieldContains(t *testing.T) { 56 | a := bitfield.New() 57 | a.Set(2) 58 | a.Set(4) 59 | a.Set(5) 60 | 61 | b := bitfield.New() 62 | b.Set(3) 63 | b.Set(4) 64 | 65 | c := bitfield.New() 66 | c.Set(2) 67 | c.Set(5) 68 | 69 | assertContainsAny := func(a, b bitfield.BitField, expected bool) { 70 | t.Helper() 71 | actual, err := util.BitFieldContainsAny(a, b) 72 | assert.NoError(t, err) 73 | assert.Equal(t, expected, actual) 74 | } 75 | 76 | assertContainsAll := func(a, b bitfield.BitField, expected bool) { 77 | t.Helper() 78 | actual, err := util.BitFieldContainsAll(a, b) 79 | assert.NoError(t, err) 80 | assert.Equal(t, expected, actual) 81 | } 82 | 83 | assertContainsAny(a, b, true) 84 | assertContainsAny(b, a, true) 85 | assertContainsAny(a, c, true) 86 | assertContainsAny(c, a, true) 87 | assertContainsAny(b, c, false) 88 | assertContainsAny(c, b, false) 89 | 90 | assertContainsAll(a, b, false) 91 | assertContainsAll(b, a, false) 92 | assertContainsAll(a, c, true) 93 | assertContainsAll(c, a, false) 94 | assertContainsAll(b, c, false) 95 | assertContainsAll(c, b, false) 96 | } 97 | -------------------------------------------------------------------------------- /actors/util/math/exp.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "github.com/filecoin-project/go-state-types/big" 4 | 5 | // ExpBySquaring takes a Q.128 base b and an int64 exponent n and computes n^b 6 | // using the exponentiation by squaring method, returning a Q.128 value. 7 | func ExpBySquaring(base big.Int, n int64) big.Int { 8 | one := big.Lsh(big.NewInt(1), Precision128) 9 | // Base cases 10 | if n == 0 { 11 | return one 12 | } 13 | if n == 1 { 14 | return base 15 | } 16 | 17 | // Recurse 18 | if n < 0 { 19 | inverseBase := big.Div(big.Lsh(one, Precision128), base) // Q.256 / Q.128 => Q.128 20 | return ExpBySquaring(inverseBase, -n) 21 | } 22 | baseSquared := big.Mul(base, base) // Q.128 * Q.128 => Q.256 23 | baseSquared = big.Rsh(baseSquared, Precision128) // Q.256 => Q.128 24 | if n%2 == 0 { 25 | return ExpBySquaring(baseSquared, n/2) 26 | } 27 | result := big.Mul(base, ExpBySquaring(baseSquared, (n-1)/2)) // Q.128 * Q.128 => Q.256 28 | return big.Rsh(result, Precision128) // Q.256 => Q.128 29 | } 30 | -------------------------------------------------------------------------------- /actors/util/math/exp_test.go: -------------------------------------------------------------------------------- 1 | package math_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/filecoin-project/go-state-types/big" 7 | "github.com/filecoin-project/specs-actors/v8/actors/util/math" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestExpBySquaring(t *testing.T) { 12 | one := big.Lsh(big.NewInt(1), math.Precision128) 13 | two := big.Lsh(big.NewInt(2), math.Precision128) 14 | three := big.Lsh(big.NewInt(3), math.Precision128) 15 | five := big.Lsh(big.NewInt(5), math.Precision128) 16 | seven := big.Lsh(big.NewInt(7), math.Precision128) 17 | 18 | assert.Equal(t, one, math.ExpBySquaring(three, int64(0))) 19 | assert.Equal(t, one, math.ExpBySquaring(one, 1)) 20 | assert.Equal(t, three, math.ExpBySquaring(three, 1)) 21 | 22 | assert.Equal(t, 23 | big.Lsh(big.NewInt(70368744177664), math.Precision128), 24 | math.ExpBySquaring(two, 46), 25 | ) 26 | assert.Equal(t, 27 | big.Lsh(big.NewInt(3486784401), math.Precision128), 28 | math.ExpBySquaring(three, 20), 29 | ) 30 | assert.Equal(t, 31 | big.Lsh(big.NewInt(1953125), math.Precision128), 32 | math.ExpBySquaring(five, 9), 33 | ) 34 | assert.Equal(t, 35 | big.Lsh(big.NewInt(117649), math.Precision128), 36 | math.ExpBySquaring(seven, 6), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /actors/util/math/expneg.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math/big" 5 | ) 6 | 7 | var ( 8 | // Coefficents in Q.128 format 9 | expNumCoef []*big.Int 10 | expDenoCoef []*big.Int 11 | ) 12 | 13 | func init() { 14 | 15 | // parameters are in integer format, 16 | // coefficients are *2^-128 of that 17 | // so we can just load them if we treat them as Q.128 18 | num := []string{ 19 | "-648770010757830093818553637600", 20 | "67469480939593786226847644286976", 21 | "-3197587544499098424029388939001856", 22 | "89244641121992890118377641805348864", 23 | "-1579656163641440567800982336819953664", 24 | "17685496037279256458459817590917169152", 25 | "-115682590513835356866803355398940131328", 26 | "340282366920938463463374607431768211456", 27 | } 28 | expNumCoef = Parse(num) 29 | 30 | deno := []string{ 31 | "1225524182432722209606361", 32 | "114095592300906098243859450", 33 | "5665570424063336070530214243", 34 | "194450132448609991765137938448", 35 | "5068267641632683791026134915072", 36 | "104716890604972796896895427629056", 37 | "1748338658439454459487681798864896", 38 | "23704654329841312470660182937960448", 39 | "259380097567996910282699886670381056", 40 | "2250336698853390384720606936038375424", 41 | "14978272436876548034486263159246028800", 42 | "72144088983913131323343765784380833792", 43 | "224599776407103106596571252037123047424", 44 | "340282366920938463463374607431768211456", 45 | } 46 | expDenoCoef = Parse(deno) 47 | } 48 | 49 | // ExpNeg accepts x in Q.128 format and computes e^-x. 50 | // It is most precise within [0, 1.725) range, where error is less than 3.4e-30. 51 | // Over the [0, 5) range its error is less than 4.6e-15. 52 | // Output is in Q.128 format. 53 | func ExpNeg(x *big.Int) *big.Int { 54 | // exp is approximated by rational function 55 | // polynomials of the rational function are evaluated using Horner's method 56 | num := Polyval(expNumCoef, x) // Q.128 57 | deno := Polyval(expDenoCoef, x) // Q.128 58 | 59 | num = num.Lsh(num, Precision128) // Q.256 60 | return num.Div(num, deno) // Q.256 / Q.128 => Q.128 61 | } 62 | -------------------------------------------------------------------------------- /actors/util/math/expneg_test.go: -------------------------------------------------------------------------------- 1 | package math_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | "testing" 8 | 9 | "github.com/xorcare/golden" 10 | 11 | "github.com/filecoin-project/specs-actors/v8/actors/util/math" 12 | ) 13 | 14 | var Res big.Word 15 | 16 | func BenchmarkExpneg(b *testing.B) { 17 | x := new(big.Int).SetUint64(14) 18 | x = x.Lsh(x, math.Precision128-3) // set x to 1.75 19 | dec := new(big.Int) 20 | dec = dec.Div(x, big.NewInt(int64(b.N))) 21 | b.ResetTimer() 22 | b.ReportAllocs() 23 | var res big.Word 24 | 25 | for i := 0; i < b.N; i++ { 26 | r := math.ExpNeg(x) 27 | res += r.Bits()[0] 28 | x.Sub(x, dec) 29 | } 30 | Res += res 31 | } 32 | 33 | func TestExpFunction(t *testing.T) { 34 | const N = 256 35 | 36 | step := big.NewInt(5) 37 | step = step.Lsh(step, math.Precision128) // Q.128 38 | step = step.Div(step, big.NewInt(N-1)) 39 | 40 | x := big.NewInt(0) 41 | b := &bytes.Buffer{} 42 | 43 | b.WriteString("x, y\n") 44 | for i := 0; i < N; i++ { 45 | y := math.ExpNeg(x) 46 | fmt.Fprintf(b, "%s,%s\n", x, y) 47 | x = x.Add(x, step) 48 | } 49 | 50 | golden.Assert(t, b.Bytes()) 51 | } 52 | -------------------------------------------------------------------------------- /actors/util/math/ln.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | gbig "math/big" 5 | 6 | "github.com/filecoin-project/go-state-types/big" 7 | ) 8 | 9 | var ( 10 | // Coefficients in Q.128 format 11 | lnNumCoef []*gbig.Int 12 | lnDenomCoef []*gbig.Int 13 | ln2 big.Int 14 | ) 15 | 16 | func init() { 17 | // ln approximation coefficients 18 | // parameters are in integer format, 19 | // coefficients are *2^-128 of that 20 | // so we can just load them if we treat them as Q.128 21 | num := []string{ 22 | "261417938209272870992496419296200268025", 23 | "7266615505142943436908456158054846846897", 24 | "32458783941900493142649393804518050491988", 25 | "17078670566130897220338060387082146864806", 26 | "-35150353308172866634071793531642638290419", 27 | "-20351202052858059355702509232125230498980", 28 | "-1563932590352680681114104005183375350999", 29 | } 30 | lnNumCoef = Parse(num) 31 | 32 | denom := []string{ 33 | "49928077726659937662124949977867279384", 34 | "2508163877009111928787629628566491583994", 35 | "21757751789594546643737445330202599887121", 36 | "53400635271583923415775576342898617051826", 37 | "41248834748603606604000911015235164348839", 38 | "9015227820322455780436733526367238305537", 39 | "340282366920938463463374607431768211456", 40 | } 41 | lnDenomCoef = Parse(denom) 42 | 43 | constStrs := []string{ 44 | "235865763225513294137944142764154484399", // ln(2) 45 | } 46 | constBigs := Parse(constStrs) 47 | ln2 = big.NewFromGo(constBigs[0]) 48 | } 49 | 50 | // The natural log of Q.128 x. 51 | func Ln(z big.Int) big.Int { 52 | // bitlen - 1 - precision 53 | k := int64(z.BitLen()) - 1 - Precision128 // Q.0 54 | x := big.Zero() // nolint:ineffassign 55 | 56 | if k > 0 { 57 | x = big.Rsh(z, uint(k)) // Q.128 58 | } else { 59 | x = big.Lsh(z, uint(-k)) // Q.128 60 | } 61 | 62 | // ln(z) = ln(x * 2^k) = ln(x) + k * ln2 63 | lnz := big.Mul(big.NewInt(k), ln2) // Q.0 * Q.128 => Q.128 64 | return big.Sum(lnz, lnBetweenOneAndTwo(x)) // Q.128 65 | } 66 | 67 | // The natural log of x, specified in Q.128 format 68 | // Should only use with 1 <= x <= 2 69 | // Output is in Q.128 format. 70 | func lnBetweenOneAndTwo(x big.Int) big.Int { 71 | // ln is approximated by rational function 72 | // polynomials of the rational function are evaluated using Horner's method 73 | num := Polyval(lnNumCoef, x.Int) // Q.128 74 | denom := Polyval(lnDenomCoef, x.Int) // Q.128 75 | 76 | num = num.Lsh(num, Precision128) // Q.128 => Q.256 77 | return big.NewFromGo(num.Div(num, denom)) // Q.256 / Q.128 => Q.128 78 | } 79 | -------------------------------------------------------------------------------- /actors/util/math/ln_test.go: -------------------------------------------------------------------------------- 1 | package math_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/filecoin-project/go-state-types/big" 8 | "github.com/filecoin-project/specs-actors/v8/actors/util/math" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestNaturalLog(t *testing.T) { 14 | lnInputs := math.Parse([]string{ 15 | "340282366920938463463374607431768211456", // Q.128 format of 1 16 | "924990000000000000000000000000000000000", // Q.128 format of e (rounded up in 5th decimal place to handle truncation) 17 | "34028236692093846346337460743176821145600000000000000000000", // Q.128 format of 100e18 18 | "6805647338418769269267492148635364229120000000000000000000000", // Q.128 format of 2e22 19 | "204169000000000000000000000000000000", // Q.128 format of 0.0006 20 | "34028236692093846346337460743", // Q.128 format of 1e-10 21 | }) 22 | 23 | expectedLnOutputs := math.Parse([]string{ 24 | "0", // Q.128 format of 0 = ln(1) 25 | "340282366920938463463374607431768211456", // Q.128 format of 1 = ln(e) 26 | "15670582109617661336106769654068947397831", // Q.128 format of 46.051... = ln(100e18) 27 | "17473506083804940763855390762239996622013", // Q.128 format of 51.35... = ln(2e22) 28 | "-2524410000000000000000000000000000000000", // Q.128 format of -7.41.. = ln(0.0006) 29 | "-7835291054808830668053384827034473698915", // Q.128 format of -23.02.. = ln(1e-10) 30 | }) 31 | fmt.Printf("%v %v\n", lnInputs, expectedLnOutputs) 32 | require.Equal(t, len(lnInputs), len(expectedLnOutputs)) 33 | for i := 0; i < len(lnInputs); i++ { 34 | z := big.NewFromGo(lnInputs[i]) 35 | lnOfZ := math.Ln(z) 36 | expectedZ := big.NewFromGo(expectedLnOutputs[i]) 37 | assert.Equal(t, big.Rsh(expectedZ, math.Precision128), big.Rsh(lnOfZ, math.Precision128), "failed ln of %v", z) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /actors/util/math/parse.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "math/big" 4 | 5 | // Parse a slice of strings (representing integers in decimal) 6 | // Convention: this function is to be applied to strings representing Q.128 fixed-point numbers, and thus returns numbers in binary Q.128 representation 7 | func Parse(coefs []string) []*big.Int { 8 | out := make([]*big.Int, len(coefs)) 9 | for i, coef := range coefs { 10 | c, ok := new(big.Int).SetString(coef, 10) 11 | if !ok { 12 | panic("could not parse q128 parameter") 13 | } 14 | // << 128 (Q.0 to Q.128) >> 128 to transform integer params to coefficients 15 | out[i] = c 16 | } 17 | return out 18 | } 19 | -------------------------------------------------------------------------------- /actors/util/math/polyval.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "math/big" 4 | 5 | // note: all coefficients for which Polyval is used would need to be updated if this precision changes 6 | const Precision128 = 128 7 | 8 | // polyval evaluates a polynomial given by coefficients `p` in Q.128 format 9 | // at point `x` in Q.128 format. Output is in Q.128. 10 | // Coefficients should be ordered from the highest order coefficient to the lowest. 11 | func Polyval(p []*big.Int, x *big.Int) *big.Int { 12 | // evaluation using Horner's method 13 | res := new(big.Int).Set(p[0]) // Q.128 14 | tmp := new(big.Int) // big.Int.Mul doesn't like when input is reused as output 15 | for _, c := range p[1:] { 16 | tmp = tmp.Mul(res, x) // Q.128 * Q.128 => Q.256 17 | res = res.Rsh(tmp, Precision128) // Q.256 >> 128 => Q.128 18 | res = res.Add(res, c) 19 | } 20 | 21 | return res 22 | } 23 | -------------------------------------------------------------------------------- /actors/util/smoothing/testing.go: -------------------------------------------------------------------------------- 1 | package smoothing 2 | 3 | import ( 4 | "github.com/filecoin-project/go-state-types/big" 5 | ) 6 | 7 | // Returns an estimate with position val and velocity 0 8 | func TestingConstantEstimate(val big.Int) FilterEstimate { 9 | return NewEstimate(val, big.Zero()) 10 | } 11 | 12 | // Returns and estimate with postion x and velocity v 13 | func TestingEstimate(x, v big.Int) FilterEstimate { 14 | return NewEstimate(x, v) 15 | } 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/filecoin-project/specs-actors/v8 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/filecoin-project/go-address v0.0.5 7 | github.com/filecoin-project/go-amt-ipld/v4 v4.0.0 8 | github.com/filecoin-project/go-bitfield v0.2.3 9 | github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 10 | github.com/filecoin-project/go-state-types v0.1.3 11 | github.com/filecoin-project/specs-actors v0.9.13 12 | github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb 13 | github.com/filecoin-project/specs-actors/v3 v3.1.0 14 | github.com/filecoin-project/specs-actors/v4 v4.0.0 15 | github.com/filecoin-project/specs-actors/v5 v5.0.4 16 | github.com/filecoin-project/specs-actors/v6 v6.0.1 17 | github.com/filecoin-project/specs-actors/v7 v7.0.0 18 | github.com/ipfs/go-block-format v0.0.3 19 | github.com/ipfs/go-cid v0.0.7 20 | github.com/ipfs/go-ipld-cbor v0.0.5 21 | github.com/ipfs/go-ipld-format v0.0.2 22 | github.com/ipld/go-car v0.1.0 23 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 24 | github.com/minio/sha256-simd v0.1.1 25 | github.com/multiformats/go-multibase v0.0.3 26 | github.com/multiformats/go-multihash v0.0.14 27 | github.com/stretchr/testify v1.7.0 28 | github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2 29 | github.com/xorcare/golden v0.6.0 30 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 31 | golang.org/x/text v0.3.2 32 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 33 | ) 34 | 35 | require ( 36 | github.com/davecgh/go-spew v1.1.1 // indirect 37 | github.com/filecoin-project/go-amt-ipld/v2 v2.1.0 // indirect 38 | github.com/filecoin-project/go-amt-ipld/v3 v3.1.0 // indirect 39 | github.com/filecoin-project/go-hamt-ipld v0.1.5 // indirect 40 | github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 // indirect 41 | github.com/gogo/protobuf v1.3.1 // indirect 42 | github.com/google/uuid v1.1.1 // indirect 43 | github.com/hashicorp/golang-lru v0.5.1 // indirect 44 | github.com/ipfs/bbloom v0.0.1 // indirect 45 | github.com/ipfs/go-blockservice v0.1.0 // indirect 46 | github.com/ipfs/go-datastore v0.0.5 // indirect 47 | github.com/ipfs/go-ipfs-blockstore v0.0.1 // indirect 48 | github.com/ipfs/go-ipfs-ds-help v0.0.1 // indirect 49 | github.com/ipfs/go-ipfs-exchange-interface v0.0.1 // indirect 50 | github.com/ipfs/go-ipfs-util v0.0.2 // indirect 51 | github.com/ipfs/go-log v1.0.4 // indirect 52 | github.com/ipfs/go-log/v2 v2.0.5 // indirect 53 | github.com/ipfs/go-merkledag v0.2.4 // indirect 54 | github.com/ipfs/go-metrics-interface v0.0.1 // indirect 55 | github.com/ipfs/go-verifcid v0.0.1 // indirect 56 | github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785 // indirect 57 | github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5 // indirect 58 | github.com/jbenet/goprocess v0.1.3 // indirect 59 | github.com/mr-tron/base58 v1.1.3 // indirect 60 | github.com/multiformats/go-base32 v0.0.3 // indirect 61 | github.com/multiformats/go-base36 v0.1.0 // indirect 62 | github.com/multiformats/go-varint v0.0.5 // indirect 63 | github.com/opentracing/opentracing-go v1.1.0 // indirect 64 | github.com/pkg/errors v0.9.1 // indirect 65 | github.com/pmezard/go-difflib v1.0.0 // indirect 66 | github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a // indirect 67 | github.com/spaolacci/murmur3 v1.1.0 // indirect 68 | go.uber.org/atomic v1.6.0 // indirect 69 | go.uber.org/multierr v1.5.0 // indirect 70 | go.uber.org/zap v1.14.1 // indirect 71 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect 72 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect 73 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 74 | ) 75 | -------------------------------------------------------------------------------- /support/agent/blockstore_util.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "bytes" 5 | block "github.com/ipfs/go-block-format" 6 | cid "github.com/ipfs/go-cid" 7 | cbor "github.com/ipfs/go-ipld-cbor" 8 | mh "github.com/multiformats/go-multihash" 9 | cbg "github.com/whyrusleeping/cbor-gen" 10 | "golang.org/x/xerrors" 11 | ) 12 | 13 | // extracted from lotus/chain/vm/vm.go 14 | 15 | func BlockstoreCopy(from, to cbor.IpldBlockstore, root cid.Cid) (blocks uint64, copySize uint64, err error) { 16 | var numBlocks uint64 17 | var totalCopySize uint64 18 | 19 | cp := func(blk block.Block) error { 20 | numBlocks++ 21 | totalCopySize += uint64(len(blk.RawData())) 22 | return to.Put(blk) 23 | } 24 | 25 | if err := copyRec(from, to, root, cp); err != nil { 26 | return 0, 0, xerrors.Errorf("copyRec: %w", err) 27 | } 28 | 29 | return numBlocks, totalCopySize, nil 30 | } 31 | 32 | func copyRec(from, to cbor.IpldBlockstore, root cid.Cid, cp func(block.Block) error) error { 33 | if root.Prefix().MhType == 0 { 34 | // identity cid, skip 35 | return nil 36 | } 37 | 38 | blk, err := from.Get(root) 39 | if err != nil { 40 | return xerrors.Errorf("get %s failed: %w", root, err) 41 | } 42 | 43 | var lerr error 44 | err = linksForObj(blk, func(link cid.Cid) { 45 | if lerr != nil { 46 | // Theres no error return on linksForObj callback :( 47 | return 48 | } 49 | 50 | prefix := link.Prefix() 51 | if prefix.Codec == cid.FilCommitmentSealed || prefix.Codec == cid.FilCommitmentUnsealed { 52 | return 53 | } 54 | 55 | // We always have blocks inlined into CIDs, but we may not have their children. 56 | if prefix.MhType == mh.IDENTITY { 57 | // Unless the inlined block has no children. 58 | if prefix.Codec == cid.Raw { 59 | return 60 | } 61 | } 62 | 63 | if err := copyRec(from, to, link, cp); err != nil { 64 | lerr = err 65 | return 66 | } 67 | }) 68 | if err != nil { 69 | return xerrors.Errorf("linksForObj (%x): %w", blk.RawData(), err) 70 | } 71 | if lerr != nil { 72 | return lerr 73 | } 74 | 75 | if err := cp(blk); err != nil { 76 | return xerrors.Errorf("copy: %w", err) 77 | } 78 | return nil 79 | } 80 | 81 | func linksForObj(blk block.Block, cb func(cid.Cid)) error { 82 | switch blk.Cid().Prefix().Codec { 83 | case cid.DagCBOR: 84 | err := cbg.ScanForLinks(bytes.NewReader(blk.RawData()), cb) 85 | if err != nil { 86 | return xerrors.Errorf("cbg.ScanForLinks: %w", err) 87 | } 88 | return nil 89 | case cid.Raw: 90 | // We implicitly have all children of raw blocks. 91 | return nil 92 | default: 93 | return xerrors.Errorf("vm flush copy method only supports dag cbor") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /support/agent/math.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "math" 5 | big2 "math/big" 6 | "math/rand" 7 | 8 | "github.com/filecoin-project/go-state-types/abi" 9 | "github.com/filecoin-project/go-state-types/big" 10 | ) 11 | 12 | var DisbursedAmount = big.Mul(big.NewInt(41e6), big.NewInt(1e18)) 13 | 14 | // RateIterator can be used to model poisson process (a process with discreet events occurring at 15 | // arbitrary times with a specified average rate). It's Tick function must be called at regular 16 | // intervals with a function that will be called zero or more times to produce the event distribution 17 | // at the correct rate. 18 | type RateIterator struct { 19 | rnd *rand.Rand 20 | rate float64 21 | nextOccurrence float64 22 | } 23 | 24 | func NewRateIterator(rate float64, seed int64) *RateIterator { 25 | rnd := rand.New(rand.NewSource(seed)) 26 | next := 1.0 27 | if rate > 0.0 { 28 | next += poissonDelay(rnd.Float64(), rate) 29 | } 30 | 31 | return &RateIterator{ 32 | rnd: rnd, 33 | rate: rate, 34 | 35 | // choose first event in next tick 36 | nextOccurrence: next, 37 | } 38 | } 39 | 40 | // simulate random occurrences by calling the given function once for each event that would land in this epoch. 41 | // The function will be called `rate` times on average, but may be called zero or many times in any Tick. 42 | func (ri *RateIterator) Tick(f func() error) error { 43 | // wait until we have a positive rate before doing anything 44 | if ri.rate <= 0.0 { 45 | return nil 46 | } 47 | 48 | // next tick becomes this tick 49 | ri.nextOccurrence -= 1.0 50 | 51 | // choose events can call function until event occurs in next tick 52 | for ri.nextOccurrence < 1.0 { 53 | err := f() 54 | if err != nil { 55 | return err 56 | } 57 | 58 | // Choose next event 59 | // Note the argument to Log is <= 1, so the right side is always negative and nextOccurrence increases 60 | ri.nextOccurrence += poissonDelay(ri.rnd.Float64(), ri.rate) 61 | } 62 | return nil 63 | } 64 | 65 | // TickWithRate permits a variable rate. 66 | // If the rate has changed, it will compute a new next occurrence before running tick. 67 | // This prevents having to wait a long time to recognize a change from a very slow rate to a higher one. 68 | func (ri *RateIterator) TickWithRate(rate float64, f func() error) error { 69 | // recompute next occurrence if rate has changed 70 | if ri.rate != rate && rate > 0.0 { 71 | ri.nextOccurrence = 1.0 + poissonDelay(ri.rnd.Float64(), rate) 72 | } 73 | ri.rate = rate 74 | 75 | return ri.Tick(f) 76 | } 77 | 78 | // Compute a poisson distributed delay that produces (on average) a given rate. 79 | // The uniformRnd is a real number uniformly distributed in [0, 1). 80 | // The rate is the average number of events expected per epoch and may be greater or less than 1 but not zero. 81 | func poissonDelay(uniformRnd float64, rate float64) float64 { 82 | return -math.Log(1.0-uniformRnd) / rate 83 | } 84 | 85 | /////////////////////////////////////// 86 | // 87 | // Win Count 88 | // 89 | /////////////////////////////////////// 90 | 91 | // This is the Filecoin algorithm for winning a ticket within a block with the tickets replaced 92 | // with random numbers. It lets miners win according to a Poisson distribution with rate 93 | // proportional to the miner's fraction of network power. 94 | func WinCount(minerPower abi.StoragePower, totalPower abi.StoragePower, random float64) uint64 { 95 | E := big2.NewRat(5, 1) 96 | lambdaR := new(big2.Rat) 97 | lambdaR.SetFrac(minerPower.Int, totalPower.Int) 98 | lambdaR.Mul(lambdaR, E) 99 | lambda, _ := lambdaR.Float64() 100 | 101 | rhs := 1 - poissonPMF(lambda, 0) 102 | 103 | winCount := uint64(0) 104 | for rhs > random { 105 | winCount++ 106 | rhs -= poissonPMF(lambda, winCount) 107 | } 108 | return winCount 109 | } 110 | 111 | ////////////////////////////////////////// 112 | // 113 | // Misc 114 | // 115 | ////////////////////////////////////////// 116 | 117 | // this panics if list is empty. 118 | func PopRandom(list []uint64, rnd *rand.Rand) (uint64, []uint64) { 119 | idx := rnd.Int63n(int64(len(list))) 120 | result := list[idx] 121 | list[idx] = list[len(list)-1] 122 | return result, list[:len(list)-1] 123 | } 124 | 125 | func poissonPMF(lambda float64, k uint64) float64 { 126 | fk := float64(k) 127 | return (math.Exp(-lambda) * math.Pow(lambda, fk)) / fact(fk) 128 | } 129 | 130 | func fact(k float64) float64 { 131 | fact := 1.0 132 | for i := 2.0; i <= k; i += 1.0 { 133 | fact *= i 134 | } 135 | return fact 136 | } 137 | -------------------------------------------------------------------------------- /support/agent/miner_generator.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/filecoin-project/go-address" 7 | "github.com/filecoin-project/go-state-types/cbor" 8 | "golang.org/x/xerrors" 9 | 10 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin/power" 12 | ) 13 | 14 | // MinerGenerator adds miner agents to the simulation at a configured rate. 15 | // When triggered to add a new miner, it: 16 | // * Selects the next owner address from the accounts it has been given. 17 | // * Sends a createMiner message from that account 18 | // * Handles the response by creating a MinerAgent with MinerAgentConfig and registering it in the sim. 19 | type MinerGenerator struct { 20 | config MinerAgentConfig // eventually this should become a set of probabilities to support miner differentiation 21 | createMinerEvents *RateIterator 22 | minersCreated int 23 | accounts []address.Address 24 | rnd *rand.Rand 25 | } 26 | 27 | func NewMinerGenerator(accounts []address.Address, config MinerAgentConfig, createMinerRate float64, rndSeed int64) *MinerGenerator { 28 | rnd := rand.New(rand.NewSource(rndSeed)) 29 | return &MinerGenerator{ 30 | config: config, 31 | createMinerEvents: NewRateIterator(createMinerRate, rnd.Int63()), 32 | accounts: accounts, 33 | rnd: rnd, 34 | } 35 | } 36 | 37 | func (mg *MinerGenerator) Tick(s SimState) ([]message, error) { 38 | var msgs []message 39 | if mg.minersCreated >= len(mg.accounts) { 40 | return msgs, nil 41 | } 42 | 43 | err := mg.createMinerEvents.Tick(func() error { 44 | if mg.minersCreated < len(mg.accounts) { 45 | addr := mg.accounts[mg.minersCreated] 46 | mg.minersCreated++ 47 | msg, err := mg.createMiner(addr, mg.config, s) 48 | if err != nil { 49 | return err 50 | } 51 | msgs = append(msgs, msg) 52 | } 53 | return nil 54 | }) 55 | return msgs, err 56 | } 57 | 58 | func (mg *MinerGenerator) createMiner(owner address.Address, cfg MinerAgentConfig, s SimState) (message, error) { 59 | params, err := s.CreateMinerParams(owner, owner, cfg.ProofType) 60 | if err != nil { 61 | return message{}, err 62 | } 63 | return message{ 64 | From: owner, 65 | To: builtin.StoragePowerActorAddr, 66 | Value: mg.config.StartingBalance, // miner gets all account funds 67 | Method: builtin.MethodsPower.CreateMiner, 68 | Params: params, 69 | ReturnHandler: func(s SimState, msg message, ret cbor.Marshaler) error { 70 | createMinerRet, ok := ret.(*power.CreateMinerReturn) 71 | if !ok { 72 | return xerrors.Errorf("create miner return has wrong type: %v", ret) 73 | } 74 | 75 | var worker, owner address.Address 76 | params, ok := msg.Params.(*power.CreateMinerParams) 77 | if !ok { 78 | return xerrors.Errorf("create miner params has wrong type: %v", msg.Params) 79 | } else { 80 | worker = params.Worker 81 | owner = params.Owner 82 | } 83 | 84 | // register agent as both a miner and deal provider 85 | minerAgent := NewMinerAgent(owner, worker, createMinerRet.IDAddress, createMinerRet.RobustAddress, mg.rnd.Int63(), cfg) 86 | s.AddAgent(minerAgent) 87 | s.AddDealProvider(minerAgent) 88 | return nil 89 | }, 90 | }, nil 91 | } 92 | -------------------------------------------------------------------------------- /support/ipld/cbor.go: -------------------------------------------------------------------------------- 1 | package ipld 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/filecoin-project/go-state-types/cbor" 8 | "github.com/ipfs/go-cid" 9 | ) 10 | 11 | // Marshals an object to bytes for storing in state. 12 | func MarshalCBOR(o cbor.Marshaler) (cid.Cid, []byte, error) { 13 | r := bytes.Buffer{} 14 | err := o.MarshalCBOR(&r) 15 | if err != nil { 16 | return cid.Undef, nil, err 17 | } 18 | data := r.Bytes() 19 | key, err := abi.CidBuilder.Sum(data) 20 | if err != nil { 21 | return cid.Undef, nil, err 22 | } 23 | return key, data, nil 24 | } 25 | -------------------------------------------------------------------------------- /support/ipld/store.go: -------------------------------------------------------------------------------- 1 | package ipld 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | 8 | block "github.com/ipfs/go-block-format" 9 | "github.com/ipfs/go-cid" 10 | ipldcbor "github.com/ipfs/go-ipld-cbor" 11 | 12 | "github.com/filecoin-project/specs-actors/v8/actors/util/adt" 13 | ) 14 | 15 | // Creates a new, empty, unsynchronized IPLD store in memory. 16 | // This store is appropriate for most kinds of testing. 17 | func NewADTStore(ctx context.Context) adt.Store { 18 | return adt.WrapBlockStore(ctx, NewBlockStoreInMemory()) 19 | } 20 | 21 | // 22 | // A basic in-memory block store. 23 | // 24 | type BlockStoreInMemory struct { 25 | data map[cid.Cid]block.Block 26 | } 27 | 28 | var _ ipldcbor.IpldBlockstore = (*BlockStoreInMemory)(nil) 29 | 30 | func NewBlockStoreInMemory() *BlockStoreInMemory { 31 | return &BlockStoreInMemory{make(map[cid.Cid]block.Block)} 32 | } 33 | 34 | func (mb *BlockStoreInMemory) Get(c cid.Cid) (block.Block, error) { 35 | d, ok := mb.data[c] 36 | if ok { 37 | return d, nil 38 | } 39 | return nil, fmt.Errorf("not found") 40 | } 41 | 42 | func (mb *BlockStoreInMemory) Put(b block.Block) error { 43 | mb.data[b.Cid()] = b 44 | return nil 45 | } 46 | 47 | // 48 | // Synchronized block store wrapper. 49 | // 50 | type SyncBlockStore struct { 51 | bs ipldcbor.IpldBlockstore 52 | mu sync.Mutex 53 | } 54 | 55 | var _ ipldcbor.IpldBlockstore = (*SyncBlockStore)(nil) 56 | 57 | func NewSyncBlockStore(bs ipldcbor.IpldBlockstore) *SyncBlockStore { 58 | return &SyncBlockStore{ 59 | bs: bs, 60 | } 61 | } 62 | 63 | func (ss *SyncBlockStore) Get(c cid.Cid) (block.Block, error) { 64 | ss.mu.Lock() 65 | defer ss.mu.Unlock() 66 | return ss.bs.Get(c) 67 | } 68 | 69 | func (ss *SyncBlockStore) Put(b block.Block) error { 70 | ss.mu.Lock() 71 | defer ss.mu.Unlock() 72 | return ss.bs.Put(b) 73 | } 74 | 75 | // 76 | // Metric-recording block store wrapper. 77 | // 78 | type MetricsBlockStore struct { 79 | bs ipldcbor.IpldBlockstore 80 | Writes uint64 81 | WriteBytes uint64 82 | Reads uint64 83 | ReadBytes uint64 84 | } 85 | 86 | var _ ipldcbor.IpldBlockstore = (*MetricsBlockStore)(nil) 87 | 88 | func NewMetricsBlockStore(underlying ipldcbor.IpldBlockstore) *MetricsBlockStore { 89 | return &MetricsBlockStore{bs: underlying} 90 | } 91 | 92 | func (ms *MetricsBlockStore) Get(c cid.Cid) (block.Block, error) { 93 | ms.Reads++ 94 | blk, err := ms.bs.Get(c) 95 | if err != nil { 96 | return blk, err 97 | } 98 | ms.ReadBytes += uint64(len(blk.RawData())) 99 | return blk, nil 100 | } 101 | 102 | func (ms *MetricsBlockStore) Put(b block.Block) error { 103 | ms.Writes++ 104 | ms.WriteBytes += uint64(len(b.RawData())) 105 | return ms.bs.Put(b) 106 | } 107 | 108 | func (ms *MetricsBlockStore) ReadCount() uint64 { 109 | return ms.Reads 110 | } 111 | 112 | func (ms *MetricsBlockStore) WriteCount() uint64 { 113 | return ms.Writes 114 | } 115 | 116 | func (ms *MetricsBlockStore) ReadSize() uint64 { 117 | return ms.ReadBytes 118 | } 119 | 120 | func (ms *MetricsBlockStore) WriteSize() uint64 { 121 | return ms.WriteBytes 122 | } 123 | -------------------------------------------------------------------------------- /support/mock/builder.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | addr "github.com/filecoin-project/go-address" 8 | "github.com/filecoin-project/go-state-types/abi" 9 | "github.com/filecoin-project/go-state-types/network" 10 | "github.com/ipfs/go-cid" 11 | "github.com/minio/blake2b-simd" 12 | ) 13 | 14 | // Build for fluent initialization of a mock runtime. 15 | type RuntimeBuilder struct { 16 | options []func(rt *Runtime) 17 | } 18 | 19 | // Initializes a new builder with a receiving actor address. 20 | func NewBuilder(receiver addr.Address) RuntimeBuilder { 21 | var b RuntimeBuilder 22 | b.add(func(rt *Runtime) { 23 | rt.receiver = receiver 24 | }) 25 | return b 26 | } 27 | 28 | // Builds a new runtime object with the configured values. 29 | func (b RuntimeBuilder) Build(t testing.TB) *Runtime { 30 | ctx, cancel := context.WithCancel(context.Background()) 31 | t.Cleanup(cancel) 32 | 33 | m := &Runtime{ 34 | ctx: ctx, 35 | epoch: 0, 36 | networkVersion: network.VersionMax, 37 | caller: addr.Address{}, 38 | callerType: cid.Undef, 39 | miner: addr.Address{}, 40 | idAddresses: make(map[addr.Address]addr.Address), 41 | circulatingSupply: abi.NewTokenAmount(0), 42 | baseFee: abi.NewTokenAmount(0), 43 | 44 | state: cid.Undef, 45 | store: make(map[cid.Cid][]byte), 46 | hashfunc: blake2b.Sum256, 47 | 48 | balance: abi.NewTokenAmount(0), 49 | valueReceived: abi.NewTokenAmount(0), 50 | 51 | actorCodeCIDs: make(map[addr.Address]cid.Cid), 52 | newActorAddr: addr.Undef, 53 | 54 | t: t, 55 | expectValidateCallerAny: false, 56 | expectValidateCallerAddr: nil, 57 | expectValidateCallerType: nil, 58 | expectCreateActor: nil, 59 | 60 | expectSends: make([]*expectedMessage, 0), 61 | expectVerifySigs: make([]*expectVerifySig, 0), 62 | } 63 | for _, opt := range b.options { 64 | opt(m) 65 | } 66 | return m 67 | } 68 | 69 | func (b *RuntimeBuilder) add(cb func(*Runtime)) { 70 | b.options = append(b.options, cb) 71 | } 72 | 73 | func (b RuntimeBuilder) WithEpoch(epoch abi.ChainEpoch) RuntimeBuilder { 74 | b.add(func(rt *Runtime) { 75 | rt.epoch = epoch 76 | }) 77 | return b 78 | } 79 | 80 | func (b RuntimeBuilder) WithCaller(address addr.Address, code cid.Cid) RuntimeBuilder { 81 | b.add(func(rt *Runtime) { 82 | rt.caller = address 83 | rt.callerType = code 84 | }) 85 | return b 86 | } 87 | 88 | func (b RuntimeBuilder) WithMiner(miner addr.Address) RuntimeBuilder { 89 | b.add(func(rt *Runtime) { 90 | rt.miner = miner 91 | }) 92 | return b 93 | } 94 | 95 | func (b RuntimeBuilder) WithBalance(balance, received abi.TokenAmount) RuntimeBuilder { 96 | b.add(func(rt *Runtime) { 97 | rt.balance = balance 98 | rt.valueReceived = received 99 | }) 100 | return b 101 | } 102 | 103 | func (b RuntimeBuilder) WithNetworkVersion(version network.Version) RuntimeBuilder { 104 | b.add(func(rt *Runtime) { 105 | rt.networkVersion = version 106 | }) 107 | return b 108 | } 109 | 110 | func (b RuntimeBuilder) WithActorType(addr addr.Address, code cid.Cid) RuntimeBuilder { 111 | b.add(func(rt *Runtime) { 112 | rt.actorCodeCIDs[addr] = code 113 | }) 114 | return b 115 | } 116 | 117 | func (b RuntimeBuilder) WithHasher(f func(data []byte) [32]byte) RuntimeBuilder { 118 | b.add(func(rt *Runtime) { 119 | rt.hashfunc = f 120 | }) 121 | return b 122 | } 123 | -------------------------------------------------------------------------------- /support/mock/exports.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/ipfs/go-cid" 9 | ) 10 | 11 | func CheckActorExports(t *testing.T, act interface{ Exports() []interface{} }) { 12 | for i, m := range act.Exports() { 13 | if i == 0 { // Send is implicit 14 | continue 15 | } 16 | 17 | if m == nil { 18 | continue 19 | } 20 | 21 | t.Run(fmt.Sprintf("method%d-type", i), func(t *testing.T) { 22 | mrt := Runtime{t: t} 23 | mrt.verifyExportedMethodType(reflect.ValueOf(m)) 24 | }) 25 | 26 | t.Run(fmt.Sprintf("method%d-unsafe-input", i), func(t *testing.T) { 27 | paramsType := reflect.ValueOf(m).Type().In(1) 28 | checkUnsafeInputs(t, paramsType.String(), paramsType) 29 | }) 30 | } 31 | } 32 | 33 | var tCID = reflect.TypeOf(new(cid.Cid)).Elem() 34 | 35 | func checkUnsafeInputs(t *testing.T, name string, typ reflect.Type) { 36 | switch typ.Kind() { 37 | case reflect.Array: 38 | fallthrough 39 | case reflect.Slice: 40 | fallthrough 41 | case reflect.Map: 42 | fallthrough 43 | case reflect.Ptr: 44 | checkUnsafeInputs(t, name+".elem", typ.Elem()) 45 | 46 | case reflect.Struct: 47 | if typ == tCID { 48 | t.Fatal("method has unchecked CID input at ", name) 49 | } 50 | 51 | for i := 0; i < typ.NumField(); i++ { 52 | f := typ.Field(i) 53 | 54 | if f.Tag.Get("checked") == "true" { 55 | if f.Type != tCID { 56 | t.Fatal("expected checked value to be cid.Cid") 57 | } 58 | 59 | continue 60 | } 61 | 62 | checkUnsafeInputs(t, name+"."+f.Name, f.Type) 63 | } 64 | 65 | case reflect.Interface: 66 | t.Fatal("method has unsafe interface{} input parameter") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /support/testing/address.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | addr "github.com/filecoin-project/go-address" 5 | "github.com/stretchr/testify/require" 6 | "math/rand" 7 | "testing" 8 | ) 9 | 10 | func NewIDAddr(t testing.TB, id uint64) addr.Address { 11 | address, err := addr.NewIDAddress(id) 12 | require.NoError(t, err) 13 | return address 14 | } 15 | 16 | func NewSECP256K1Addr(t testing.TB, pubkey string) addr.Address { 17 | // the pubkey of a secp256k1 address is hashed for consistent length. 18 | address, err := addr.NewSecp256k1Address([]byte(pubkey)) 19 | require.NoError(t, err) 20 | return address 21 | } 22 | 23 | func NewBLSAddr(t testing.TB, seed int64) addr.Address { 24 | // the pubkey of a bls address is not hashed and must be the correct length. 25 | buf := make([]byte, 48) 26 | r := rand.New(rand.NewSource(seed)) 27 | r.Read(buf) 28 | 29 | address, err := addr.NewBLSAddress(buf) 30 | require.NoError(t, err) 31 | return address 32 | } 33 | 34 | func NewActorAddr(t testing.TB, data string) addr.Address { 35 | address, err := addr.NewActorAddress([]byte(data)) 36 | require.NoError(t, err) 37 | return address 38 | } 39 | -------------------------------------------------------------------------------- /support/testing/adt.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ipfs/go-cid" 7 | ) 8 | 9 | type rooter interface { 10 | Root() (cid.Cid, error) 11 | } 12 | 13 | func MustRoot(t testing.TB, r rooter) cid.Cid { 14 | t.Helper() 15 | c, err := r.Root() 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | return c 20 | } 21 | -------------------------------------------------------------------------------- /support/testing/cid.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/filecoin-project/go-state-types/abi" 7 | "github.com/ipfs/go-cid" 8 | "github.com/minio/sha256-simd" 9 | mh "github.com/multiformats/go-multihash" 10 | ) 11 | 12 | func MakeCID(input string, prefix *cid.Prefix) cid.Cid { 13 | data := []byte(input) 14 | if prefix == nil { 15 | c, err := abi.CidBuilder.Sum(data) 16 | if err != nil { 17 | panic(err) 18 | } 19 | return c 20 | } 21 | c, err := prefix.Sum(data) 22 | switch { 23 | case errors.Is(err, mh.ErrSumNotSupported): 24 | // multihash library doesn't support this hash function. 25 | // just fake it. 26 | case err == nil: 27 | return c 28 | default: 29 | panic(err) 30 | } 31 | 32 | sum := sha256.Sum256(data) 33 | hash, err := mh.Encode(sum[:], prefix.MhType) 34 | if err != nil { 35 | panic(err) 36 | } 37 | return cid.NewCidV1(prefix.Codec, hash) 38 | } 39 | -------------------------------------------------------------------------------- /support/testing/pid.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import "github.com/filecoin-project/go-state-types/abi" 4 | 5 | func MakePID(input string) abi.PeerID { 6 | return abi.PeerID([]byte(input)) 7 | } 8 | -------------------------------------------------------------------------------- /support/tools/tools.go: -------------------------------------------------------------------------------- 1 | //+build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/Kubuxu/go-no-map-range/pkg/analyzer" 7 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 8 | ) 9 | -------------------------------------------------------------------------------- /support/vm/stats.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | vm2 "github.com/filecoin-project/specs-actors/v2/support/vm" 5 | ) 6 | 7 | // type StatsSource interface { 8 | // WriteCount() uint64 9 | // ReadCount() uint64 10 | // WriteSize() uint64 11 | // ReadSize() uint64 12 | // } 13 | type StatsSource = vm2.StatsSource 14 | 15 | // type StatsByCall map[MethodKey]*CallStats 16 | type StatsByCall = vm2.StatsByCall 17 | 18 | // type MethodKey struct { 19 | // Code cid.Cid 20 | // Method abi.MethodNum 21 | // } 22 | type MethodKey = vm2.MethodKey 23 | 24 | // type CallStats struct { 25 | // Reads uint64 26 | // Writes uint64 27 | // ReadBytes uint64 28 | // WriteBytes uint64 29 | // Calls uint64 30 | // statsSource StatsSource 31 | // SubStats StatsByCall 32 | 33 | // startReads uint64 34 | // startWrites uint64 35 | // startReadBytes uint64 36 | // startWriteBytes uint64 37 | // } 38 | type CallStats = vm2.CallStats 39 | -------------------------------------------------------------------------------- /support/vm/vector_gen.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/filecoin-project/go-address" 10 | abi "github.com/filecoin-project/go-state-types/abi" 11 | "github.com/filecoin-project/specs-actors/v8/actors/builtin" 12 | ) 13 | 14 | // 15 | // Test Vector generation utilities 16 | // 17 | 18 | type vectorGen struct { 19 | conformanceDir string 20 | determinismDir string 21 | vector testVector 22 | } 23 | 24 | func newVectorGen() *vectorGen { 25 | // check environment variables to determine if generation is on 26 | conformanceDir := os.Getenv("SPECS_ACTORS_CONFORMANCE") 27 | determinismDir := os.Getenv("SPECS_ACTORS_DETERMINISM") 28 | return &vectorGen{ 29 | conformanceDir: conformanceDir, 30 | determinismDir: determinismDir, 31 | } 32 | } 33 | 34 | func (g *vectorGen) determinism() bool { 35 | return g.determinismDir != "" 36 | } 37 | 38 | func (g *vectorGen) conformance() bool { 39 | return g.conformanceDir != "" 40 | } 41 | 42 | func (g *vectorGen) before(v *VM, name string) error { 43 | if g.determinism() || g.conformance() { 44 | // Set test vector pre application conditions 45 | startOpts := StartConditions(v, name) 46 | for _, opt := range startOpts { 47 | if err := opt(&(g.vector)); err != nil { 48 | return err 49 | } 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func (g *vectorGen) after(v *VM, from, to address.Address, value abi.TokenAmount, method abi.MethodNum, params interface{}, callSeq uint64, result MessageResult, fakesAccessed bool, name string) error { 56 | if !g.conformance() && !g.determinism() { 57 | return nil 58 | } 59 | // Set test vector message and post application conditions 60 | if err := SetMessage(from, to, callSeq, value, method, params)(&(g.vector)); err != nil { 61 | return err 62 | } 63 | if err := SetEndStateTree(v.StateRoot(), v.store)(&(g.vector)); err != nil { 64 | return err 65 | } 66 | if err := SetReceipt(result)(&(g.vector)); err != nil { 67 | return err 68 | } 69 | 70 | if g.conformance() { 71 | // Set the state for conformance tests, we don't need it to check determinism (we 72 | // just need the roots) 73 | if err := SetState(v.store)(&(g.vector)); err != nil { 74 | return err 75 | } 76 | } 77 | 78 | vectorBytes, err := (&(g.vector)).MarshalJSON() 79 | if err != nil { 80 | return err 81 | } 82 | 83 | fromID, _ := v.NormalizeAddress(from) 84 | toID, _ := v.NormalizeAddress(to) 85 | act, _, _ := v.GetActor(toID) 86 | actName := strings.Split(builtin.ActorNameByCode(act.Code), "/")[2] 87 | 88 | h := sha256.Sum256(vectorBytes) 89 | fname := fmt.Sprintf("%x-%s-%s-%s-%d.json", string(h[:]), fromID, toID, actName, method) 90 | 91 | // Write conformance test-vectors 92 | if g.conformance() && !fakesAccessed { 93 | if err := writeVector(name, fname, vectorBytes, g.conformanceDir); err != nil { 94 | return err 95 | } 96 | } 97 | 98 | // Write determinism test-vectors 99 | if g.determinism() { 100 | if err := writeVector(name, fname, vectorBytes, g.determinismDir); err != nil { 101 | return err 102 | } 103 | } 104 | return nil 105 | } 106 | 107 | // rootDir is the top level directory containing all vectors 108 | // dname is the subdirectory path containing the file to write 109 | // fname is the name of this file 110 | // vectorBytes is the data to write to file 111 | func writeVector(dname, fname string, vectorBytes []byte, rootDir string) error { 112 | dir := rootDir + "/" + dname 113 | exists, err := dirExists(dir) 114 | if err != nil { 115 | return err 116 | } 117 | if !exists { 118 | if err := os.MkdirAll(dir, 0755); err != nil { 119 | return err 120 | } 121 | } 122 | return os.WriteFile(dir+"/"+fname, vectorBytes, 0755) 123 | } 124 | 125 | // dirExists returns whether the given file or directory exists 126 | func dirExists(path string) (bool, error) { 127 | _, err := os.Stat(path) 128 | if err == nil { 129 | return true, nil 130 | } 131 | if os.IsNotExist(err) { 132 | return false, nil 133 | } 134 | return false, err 135 | } 136 | -------------------------------------------------------------------------------- /test-vectors/.gitignore: -------------------------------------------------------------------------------- 1 | determinism/* -------------------------------------------------------------------------------- /test-vectors/README.md: -------------------------------------------------------------------------------- 1 | # specs-actors test-vector generation 2 | 3 | This directory is used to store filecoin test-vectors auto genearted from scenario tests. 4 | 5 | ## Overview 6 | 7 | The structure of the generated files is designed to help with debugging inconsistencies. Vectors live in a directory structure that identifies the name of the test that generated them. Filenames are of the format `----.json`. All test-vectors are of the "message" class and cover the execution of a single message. 8 | 9 | The `determinism` directory is used to hold vectors for checking that multiple runs of the same message results in deterministic output. The `tools` directory contains a tool for taking a collision resistant hash of all vectors to catch non-determinism. The checked-in `determinism-check` file is used to track what this hash should be. This file needs to be updated when merging breaking changes to specs-actors. 10 | 11 | The `conformance` directory is used to hold vectors for conformance tests with other implementations. 12 | 13 | ## Generation workflows 14 | 15 | Three make directives exist for working with these test-vectors. 16 | 17 | ### `make determinism-gen` 18 | 19 | This runs scenario tests to create a test-vector corpus underneath test-vectors/determinism and creates a digest of the corpus in test-vectors/determinism-check by applying the digest tool to the determinism directory 20 | 21 | ### `make determinism-check` 22 | 23 | This removes any existing content in test-vectors/determinism, runs scenario tests to create a test-vector corpus underneath test-vectors/determinism and regenerates the digest to make sure that state transitions in scenario tests match the recorded run. If digests do not match it returns a failing exitcode. This now runs on CI. 24 | 25 | ### `make conformance-gen` 26 | 27 | This runs scenario tests and generates a subset of test-vectors from test state transitions that can serve as valid conformance tests across implementations. It is a subset of the determinism corpus currently because the test-vector format cannot yet handle faking crypto syscalls. The corpus is generated underneath test-vectors/conformance -------------------------------------------------------------------------------- /test-vectors/determinism-check: -------------------------------------------------------------------------------- 1 | - 64f227866918f189dd9dd85d066334d335b8537fb02b68e50f1fd6c3e9842754 2 | -------------------------------------------------------------------------------- /test-vectors/tools/digest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | "golang.org/x/xerrors" 10 | ) 11 | 12 | /* 13 | `digest` does a deterministic traversal of all files in the input directory tree 14 | and hashes the appended string of all filenames. 15 | This only works as an identifier under certain assumptions about filenames. 16 | A sufficient assumption is that filenames contain a collision resistant hash of 17 | file content. 18 | */ 19 | func main() { 20 | if len(os.Args) != 2 { 21 | fmt.Printf("Expected exactly one argument, path of directory to digest") 22 | os.Exit(1) 23 | } 24 | rootDir := os.Args[1] 25 | h := sha256.New() 26 | err := filepath.Walk(rootDir+"/", func(path string, info os.FileInfo, err error) error { 27 | if err != nil { 28 | return err 29 | } 30 | n, err := h.Write([]byte(path)) 31 | if err != nil { 32 | return err 33 | } 34 | if n != len([]byte(path)) { 35 | return xerrors.Errorf("did not write full filename %s to hash, wrote %d bytes, path has %d bytes", path, n, len([]byte(path))) 36 | } 37 | return nil 38 | }) 39 | if err != nil { 40 | fmt.Printf("Error: %s\n", err) 41 | os.Exit(1) 42 | } 43 | fmt.Printf("- %x\n", h.Sum(nil)) 44 | } 45 | --------------------------------------------------------------------------------