Merge pull request #1711 from filecoin-project/testnet/3-cleaned
Interop Staging - Cleaned
This commit is contained in:
commit
d124d10f7c
@ -5,7 +5,7 @@ orbs:
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: circleci/golang:1.13
|
||||
- image: circleci/golang:1.14.2
|
||||
resource_class: 2xlarge
|
||||
ubuntu:
|
||||
docker:
|
||||
@ -42,13 +42,13 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore parameters cache
|
||||
keys:
|
||||
- 'v20-1k-lotus-params'
|
||||
- 'v25-2k-lotus-params'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
- run: ./lotus fetch-params --proving-params 1024
|
||||
- run: ./lotus fetch-params --proving-params 2048
|
||||
- save_cache:
|
||||
name: Save parameters cache
|
||||
key: 'v20-1k-lotus-params'
|
||||
key: 'v25-2k-lotus-params'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
install_ipfs:
|
||||
@ -107,6 +107,18 @@ jobs:
|
||||
paths:
|
||||
- linux
|
||||
|
||||
build-debug:
|
||||
executor: golang
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- restore_cache:
|
||||
name: restore go mod cache
|
||||
key: v1-go-deps-{{ arch }}-{{ checksum "/home/circleci/project/go.mod" }}
|
||||
- run:
|
||||
command: make debug
|
||||
|
||||
test: &test
|
||||
description: |
|
||||
Run tests with gotestsum.
|
||||
@ -200,8 +212,8 @@ jobs:
|
||||
- run:
|
||||
name: Install go
|
||||
command: |
|
||||
curl -O https://dl.google.com/go/go1.13.4.darwin-amd64.pkg && \
|
||||
sudo installer -pkg go1.13.4.darwin-amd64.pkg -target /
|
||||
curl -O https://dl.google.com/go/go1.14.2.darwin-amd64.pkg && \
|
||||
sudo installer -pkg go1.14.2.darwin-amd64.pkg -target /
|
||||
- run:
|
||||
name: Install pkg-config
|
||||
command: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config
|
||||
@ -242,6 +254,15 @@ jobs:
|
||||
- "~/.rustup"
|
||||
- "~/.cargo"
|
||||
|
||||
gofmt:
|
||||
executor: golang
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- run:
|
||||
command: "! go fmt ./... 2>&1 | read"
|
||||
|
||||
lint: &lint
|
||||
description: |
|
||||
Run golangci-lint.
|
||||
@ -251,7 +272,7 @@ jobs:
|
||||
default: golang
|
||||
golangci-lint-version:
|
||||
type: string
|
||||
default: 1.17.1
|
||||
default: 1.23.8
|
||||
concurrency:
|
||||
type: string
|
||||
default: '2'
|
||||
@ -311,16 +332,18 @@ workflows:
|
||||
ci:
|
||||
jobs:
|
||||
- lint-changes:
|
||||
args: "--new-from-rev origin/master"
|
||||
args: "--new-from-rev origin/testnet/3"
|
||||
- test:
|
||||
codecov-upload: true
|
||||
- mod-tidy-check
|
||||
- gofmt
|
||||
- test-short:
|
||||
go-test-flags: "--timeout 10m --short"
|
||||
filters:
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- build-debug
|
||||
- build-all:
|
||||
requires:
|
||||
- test-short
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,7 +15,6 @@
|
||||
/cmd/lotus-townhall/townhall/node_modules
|
||||
/cmd/lotus-townhall/townhall/build
|
||||
extern/filecoin-ffi/rust/target
|
||||
**/*.h
|
||||
**/*.a
|
||||
**/*.pc
|
||||
/**/*/.DS_STORE
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -2,3 +2,6 @@
|
||||
path = extern/filecoin-ffi
|
||||
url = https://github.com/filecoin-project/filecoin-ffi.git
|
||||
branch = master
|
||||
[submodule "extern/serialization-vectors"]
|
||||
path = extern/serialization-vectors
|
||||
url = https://github.com/filecoin-project/serialization-vectors
|
||||
|
33
Makefile
33
Makefile
@ -3,6 +3,8 @@ SHELL=/usr/bin/env bash
|
||||
all: build
|
||||
.PHONY: all
|
||||
|
||||
unexport GOFLAGS
|
||||
|
||||
GOVERSION:=$(shell go version | cut -d' ' -f 3 | cut -d. -f 2)
|
||||
ifeq ($(shell expr $(GOVERSION) \< 13), 1)
|
||||
$(warning Your Golang version is go 1.$(GOVERSION))
|
||||
@ -14,12 +16,19 @@ MODULES:=
|
||||
|
||||
CLEAN:=
|
||||
BINS:=
|
||||
GOFLAGS+=-ldflags=-X="github.com/filecoin-project/lotus/build".CurrentCommit="+git$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))"
|
||||
|
||||
ldflags=-X=github.com/filecoin-project/lotus/build.CurrentCommit='+git$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))'
|
||||
ifneq ($(strip $(LDFLAGS)),)
|
||||
ldflags+=-extldflags=$(LDFLAGS)
|
||||
endif
|
||||
|
||||
GOFLAGS+=-ldflags="$(ldflags)"
|
||||
|
||||
|
||||
## FFI
|
||||
|
||||
FFI_PATH:=extern/filecoin-ffi/
|
||||
FFI_DEPS:=libfilecoin.a filecoin.pc filecoin.h
|
||||
FFI_DEPS:=.install-filcrypto
|
||||
FFI_DEPS:=$(addprefix $(FFI_PATH),$(FFI_DEPS))
|
||||
|
||||
$(FFI_DEPS): build/.filecoin-install ;
|
||||
@ -51,6 +60,9 @@ deps: $(BUILD_DEPS)
|
||||
debug: GOFLAGS+=-tags=debug
|
||||
debug: lotus lotus-storage-miner lotus-seal-worker lotus-seed
|
||||
|
||||
2k: GOFLAGS+=-tags=2k
|
||||
2k: lotus lotus-storage-miner lotus-seal-worker lotus-seed
|
||||
|
||||
lotus: $(BUILD_DEPS)
|
||||
rm -f lotus
|
||||
go build $(GOFLAGS) -o lotus ./cmd/lotus
|
||||
@ -161,6 +173,18 @@ BINS+=health
|
||||
|
||||
buildall: $(BINS)
|
||||
|
||||
completions:
|
||||
./scripts/make-completions.sh lotus
|
||||
./scripts/make-completions.sh lotus-storage-miner
|
||||
.PHONY: completions
|
||||
|
||||
install-completions:
|
||||
mkdir -p /usr/share/bash-completion/completions /usr/local/share/zsh/site-functions/
|
||||
install -C ./scripts/bash-completion/lotus /usr/share/bash-completion/completions/lotus
|
||||
install -C ./scripts/bash-completion/lotus-storage-miner /usr/share/bash-completion/completions/lotus-storage-miner
|
||||
install -C ./scripts/zsh-completion/lotus /usr/local/share/zsh/site-functions/_lotus
|
||||
install -C ./scripts/zsh-completion/lotus-storage-miner /usr/local/share/zsh/site-functions/_lotus-storage-miner
|
||||
|
||||
clean:
|
||||
rm -rf $(CLEAN) $(BINS)
|
||||
-$(MAKE) -C $(FFI_PATH) clean
|
||||
@ -174,5 +198,10 @@ dist-clean:
|
||||
type-gen:
|
||||
go run ./gen/main.go
|
||||
|
||||
method-gen:
|
||||
(cd ./lotuspond/front/src/chain && go run ./methodgen.go)
|
||||
|
||||
gen: type-gen method-gen
|
||||
|
||||
print-%:
|
||||
@echo $*=$($*)
|
||||
|
246
api/api_full.go
246
api/api_full.go
@ -4,43 +4,52 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
// FullNode API is a low-level interface to the Filecoin network full node
|
||||
type FullNode interface {
|
||||
Common
|
||||
|
||||
// TODO: TipSetKeys
|
||||
|
||||
// chain
|
||||
|
||||
// ChainNotify returns channel with chain head updates
|
||||
// First message is guaranteed to be of len == 1, and type == 'current'
|
||||
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
|
||||
ChainNotify(context.Context) (<-chan []*HeadChange, error)
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
|
||||
ChainGetParentReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
|
||||
ChainGetParentMessages(context.Context, cid.Cid) ([]Message, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
ChainStatObj(context.Context, cid.Cid, cid.Cid) (ObjStat, error)
|
||||
ChainSetHead(context.Context, types.TipSetKey) error
|
||||
ChainGetGenesis(context.Context) (*types.TipSet, error)
|
||||
ChainTipSetWeight(context.Context, types.TipSetKey) (types.BigInt, error)
|
||||
ChainGetNode(ctx context.Context, p string) (interface{}, error)
|
||||
ChainGetNode(ctx context.Context, p string) (*IpldObject, error)
|
||||
ChainGetMessage(context.Context, cid.Cid) (*types.Message, error)
|
||||
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error)
|
||||
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error)
|
||||
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
|
||||
|
||||
// syncer
|
||||
@ -56,24 +65,26 @@ type FullNode interface {
|
||||
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error) // get nonce, sign, push
|
||||
MpoolGetNonce(context.Context, address.Address) (uint64, error)
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
MpoolEstimateGasPrice(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// FullNodeStruct
|
||||
|
||||
// miner
|
||||
|
||||
MinerCreateBlock(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error)
|
||||
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error)
|
||||
MinerCreateBlock(context.Context, *BlockTemplate) (*types.BlockMsg, error)
|
||||
|
||||
// // UX ?
|
||||
|
||||
// wallet
|
||||
|
||||
WalletNew(context.Context, string) (address.Address, error)
|
||||
WalletNew(context.Context, crypto.SigType) (address.Address, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
WalletList(context.Context) ([]address.Address, error)
|
||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
WalletSignMessage(context.Context, address.Address, *types.Message) (*types.SignedMessage, error)
|
||||
WalletVerify(context.Context, address.Address, []byte, *types.Signature) bool
|
||||
WalletVerify(context.Context, address.Address, []byte, *crypto.Signature) bool
|
||||
WalletDefaultAddress(context.Context) (address.Address, error)
|
||||
WalletSetDefault(context.Context, address.Address) error
|
||||
WalletExport(context.Context, address.Address) (*types.KeyInfo, error)
|
||||
@ -82,14 +93,16 @@ type FullNode interface {
|
||||
// Other
|
||||
|
||||
// ClientImport imports file under the specified path into filestore
|
||||
ClientImport(ctx context.Context, path string) (cid.Cid, error)
|
||||
ClientStartDeal(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, epochPrice types.BigInt, blocksDuration uint64) (*cid.Cid, error)
|
||||
ClientImport(ctx context.Context, ref FileRef) (cid.Cid, error)
|
||||
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error)
|
||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
|
||||
ClientListDeals(ctx context.Context) ([]DealInfo, error)
|
||||
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
|
||||
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, path string) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref FileRef) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
|
||||
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
|
||||
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
|
||||
|
||||
// ClientUnimport removes references to the specified file from filestore
|
||||
//ClientUnimport(path string)
|
||||
@ -104,33 +117,42 @@ type FullNode interface {
|
||||
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error)
|
||||
StateReadState(ctx context.Context, act *types.Actor, tsk types.TipSetKey) (*ActorState, error)
|
||||
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht uint64) ([]cid.Cid, error)
|
||||
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
|
||||
|
||||
StateMinerSectors(context.Context, address.Address, types.TipSetKey) ([]*ChainSectorInfo, error)
|
||||
StateNetworkName(context.Context) (dtypes.NetworkName, error)
|
||||
StateMinerSectors(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*ChainSectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*ChainSectorInfo, error)
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (MinerPower, error)
|
||||
StateMinerWorker(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
StateMinerPeerID(ctx context.Context, m address.Address, tsk types.TipSetKey) (peer.ID, error)
|
||||
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, tsk types.TipSetKey) (uint64, error)
|
||||
StateMinerSectorSize(context.Context, address.Address, types.TipSetKey) (uint64, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) ([]uint64, error)
|
||||
StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error)
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
|
||||
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
|
||||
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error)
|
||||
StateMinerInitialPledgeCollateral(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error)
|
||||
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
|
||||
StatePledgeCollateral(context.Context, types.TipSetKey) (types.BigInt, error)
|
||||
StateWaitMsg(context.Context, cid.Cid) (*MsgWait, error)
|
||||
StateWaitMsg(context.Context, cid.Cid) (*MsgLookup, error)
|
||||
StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error)
|
||||
StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error)
|
||||
StateListActors(context.Context, types.TipSetKey) ([]address.Address, error)
|
||||
StateMarketBalance(context.Context, address.Address, types.TipSetKey) (actors.StorageParticipantBalance, error)
|
||||
StateMarketParticipants(context.Context, types.TipSetKey) (map[string]actors.StorageParticipantBalance, error)
|
||||
StateMarketDeals(context.Context, types.TipSetKey) (map[string]actors.OnChainDeal, error)
|
||||
StateMarketStorageDeal(context.Context, uint64, types.TipSetKey) (*actors.OnChainDeal, error)
|
||||
StateMarketBalance(context.Context, address.Address, types.TipSetKey) (MarketBalance, error)
|
||||
StateMarketParticipants(context.Context, types.TipSetKey) (map[string]MarketBalance, error)
|
||||
StateMarketDeals(context.Context, types.TipSetKey) (map[string]MarketDeal, error)
|
||||
StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*MarketDeal, error)
|
||||
StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
StateChangedActors(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error)
|
||||
StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error)
|
||||
StateCompute(context.Context, uint64, []*types.Message, types.TipSetKey) (cid.Cid, error)
|
||||
StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*ComputeStateOutput, error)
|
||||
|
||||
MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
MsigCreate(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error)
|
||||
MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
MsigApprove(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
MsigCancel(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
|
||||
MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error
|
||||
MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error)
|
||||
// MarketFreeBalance
|
||||
|
||||
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
|
||||
@ -139,17 +161,22 @@ type FullNode interface {
|
||||
PaychClose(context.Context, address.Address) (cid.Cid, error)
|
||||
PaychAllocateLane(ctx context.Context, ch address.Address) (uint64, error)
|
||||
PaychNewPayment(ctx context.Context, from, to address.Address, vouchers []VoucherSpec) (*PaymentInfo, error)
|
||||
PaychVoucherCheckValid(context.Context, address.Address, *types.SignedVoucher) error
|
||||
PaychVoucherCheckSpendable(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *types.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*types.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error)
|
||||
PaychVoucherCheckValid(context.Context, address.Address, *paych.SignedVoucher) error
|
||||
PaychVoucherCheckSpendable(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*paych.SignedVoucher, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error)
|
||||
}
|
||||
|
||||
type FileRef struct {
|
||||
Path string
|
||||
IsCAR bool
|
||||
}
|
||||
|
||||
type MinerSectors struct {
|
||||
Pset uint64
|
||||
Sset uint64
|
||||
Pset uint64
|
||||
}
|
||||
|
||||
type Import struct {
|
||||
@ -161,19 +188,20 @@ type Import struct {
|
||||
|
||||
type DealInfo struct {
|
||||
ProposalCid cid.Cid
|
||||
State DealState
|
||||
State storagemarket.StorageDealStatus
|
||||
Message string // more information about deal state, particularly errors
|
||||
Provider address.Address
|
||||
|
||||
PieceRef []byte // cid bytes
|
||||
PieceCID cid.Cid
|
||||
Size uint64
|
||||
|
||||
PricePerEpoch types.BigInt
|
||||
Duration uint64
|
||||
|
||||
DealID uint64
|
||||
DealID abi.DealID
|
||||
}
|
||||
|
||||
type MsgWait struct {
|
||||
type MsgLookup struct {
|
||||
Receipt types.MessageReceipt
|
||||
TipSet *types.TipSet
|
||||
}
|
||||
@ -191,9 +219,8 @@ type Message struct {
|
||||
}
|
||||
|
||||
type ChainSectorInfo struct {
|
||||
SectorID uint64
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
Info miner.SectorOnChainInfo
|
||||
ID abi.SectorNumber
|
||||
}
|
||||
|
||||
type ActorState struct {
|
||||
@ -222,20 +249,21 @@ type ChannelInfo struct {
|
||||
type PaymentInfo struct {
|
||||
Channel address.Address
|
||||
ChannelMessage *cid.Cid
|
||||
Vouchers []*types.SignedVoucher
|
||||
Vouchers []*paych.SignedVoucher
|
||||
}
|
||||
|
||||
type VoucherSpec struct {
|
||||
Amount types.BigInt
|
||||
TimeLock uint64
|
||||
MinClose uint64
|
||||
Amount types.BigInt
|
||||
TimeLockMin abi.ChainEpoch
|
||||
TimeLockMax abi.ChainEpoch
|
||||
MinSettle abi.ChainEpoch
|
||||
|
||||
Extra *types.ModVerifyParams
|
||||
Extra *paych.ModVerifyParams
|
||||
}
|
||||
|
||||
type MinerPower struct {
|
||||
MinerPower types.BigInt
|
||||
TotalPower types.BigInt
|
||||
MinerPower power.Claim
|
||||
TotalPower power.Claim
|
||||
}
|
||||
|
||||
type QueryOffer struct {
|
||||
@ -243,43 +271,76 @@ type QueryOffer struct {
|
||||
|
||||
Root cid.Cid
|
||||
|
||||
Size uint64
|
||||
MinPrice types.BigInt
|
||||
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
Size uint64
|
||||
MinPrice types.BigInt
|
||||
PaymentInterval uint64
|
||||
PaymentIntervalIncrease uint64
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
}
|
||||
|
||||
func (o *QueryOffer) Order(client address.Address) RetrievalOrder {
|
||||
return RetrievalOrder{
|
||||
Root: o.Root,
|
||||
Size: o.Size,
|
||||
Total: o.MinPrice,
|
||||
|
||||
Client: client,
|
||||
Root: o.Root,
|
||||
Size: o.Size,
|
||||
Total: o.MinPrice,
|
||||
PaymentInterval: o.PaymentInterval,
|
||||
PaymentIntervalIncrease: o.PaymentIntervalIncrease,
|
||||
Client: client,
|
||||
|
||||
Miner: o.Miner,
|
||||
MinerPeerID: o.MinerPeerID,
|
||||
}
|
||||
}
|
||||
|
||||
type MarketBalance struct {
|
||||
Escrow big.Int
|
||||
Locked big.Int
|
||||
}
|
||||
|
||||
type MarketDeal struct {
|
||||
Proposal market.DealProposal
|
||||
State market.DealState
|
||||
}
|
||||
|
||||
type RetrievalOrder struct {
|
||||
// TODO: make this less unixfs specific
|
||||
Root cid.Cid
|
||||
Size uint64
|
||||
// TODO: support offset
|
||||
Total types.BigInt
|
||||
|
||||
Client address.Address
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
Total types.BigInt
|
||||
PaymentInterval uint64
|
||||
PaymentIntervalIncrease uint64
|
||||
Client address.Address
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
}
|
||||
|
||||
type InvocResult struct {
|
||||
Msg *types.Message
|
||||
MsgRct *types.MessageReceipt
|
||||
InternalExecutions []*vm.ExecutionResult
|
||||
InternalExecutions []*types.ExecutionResult
|
||||
Error string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
type MethodCall struct {
|
||||
types.MessageReceipt
|
||||
Error string
|
||||
}
|
||||
|
||||
type StartDealParams struct {
|
||||
Data *storagemarket.DataRef
|
||||
Wallet address.Address
|
||||
Miner address.Address
|
||||
EpochPrice types.BigInt
|
||||
MinBlocksDuration uint64
|
||||
DealStartEpoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
type IpldObject struct {
|
||||
Cid cid.Cid
|
||||
Obj interface{}
|
||||
}
|
||||
|
||||
type ActiveSync struct {
|
||||
@ -287,7 +348,7 @@ type ActiveSync struct {
|
||||
Target *types.TipSet
|
||||
|
||||
Stage SyncStateStage
|
||||
Height uint64
|
||||
Height abi.ChainEpoch
|
||||
|
||||
Start time.Time
|
||||
End time.Time
|
||||
@ -320,3 +381,46 @@ type MpoolUpdate struct {
|
||||
Type MpoolChange
|
||||
Message *types.SignedMessage
|
||||
}
|
||||
|
||||
type ComputeStateOutput struct {
|
||||
Root cid.Cid
|
||||
Trace []*InvocResult
|
||||
}
|
||||
|
||||
type MiningBaseInfo struct {
|
||||
MinerPower types.BigInt
|
||||
NetworkPower types.BigInt
|
||||
Sectors []abi.SectorInfo
|
||||
WorkerKey address.Address
|
||||
SectorSize abi.SectorSize
|
||||
PrevBeaconEntry types.BeaconEntry
|
||||
BeaconEntries []types.BeaconEntry
|
||||
}
|
||||
|
||||
type BlockTemplate struct {
|
||||
Miner address.Address
|
||||
Parents types.TipSetKey
|
||||
Ticket *types.Ticket
|
||||
Eproof *types.ElectionProof
|
||||
BeaconValues []types.BeaconEntry
|
||||
Messages []*types.SignedMessage
|
||||
Epoch abi.ChainEpoch
|
||||
Timestamp uint64
|
||||
WinningPoStProof []abi.PoStProof
|
||||
}
|
||||
|
||||
type CommPRet struct {
|
||||
Root cid.Cid
|
||||
Size abi.UnpaddedPieceSize
|
||||
}
|
||||
type HeadChange struct {
|
||||
Type string
|
||||
Val *types.TipSet
|
||||
}
|
||||
|
||||
type MsigProposeResponse int
|
||||
|
||||
const (
|
||||
MsigApprove MsigProposeResponse = iota
|
||||
MsigCancel
|
||||
)
|
||||
|
@ -1,112 +1,69 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
// alias because cbor-gen doesn't like non-alias types
|
||||
type SectorState = uint64
|
||||
|
||||
const (
|
||||
UndefinedSectorState SectorState = iota
|
||||
|
||||
// happy path
|
||||
Empty
|
||||
Packing // sector not in sealStore, and not on chain
|
||||
|
||||
Unsealed // sealing / queued
|
||||
PreCommitting // on chain pre-commit
|
||||
WaitSeed // waiting for seed
|
||||
Committing
|
||||
CommitWait // waiting for message to land on chain
|
||||
FinalizeSector
|
||||
Proving
|
||||
_ // reserved
|
||||
_
|
||||
_
|
||||
|
||||
// recovery handling
|
||||
// Reseal
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
|
||||
// error modes
|
||||
FailedUnrecoverable
|
||||
|
||||
SealFailed
|
||||
PreCommitFailed
|
||||
SealCommitFailed
|
||||
CommitFailed
|
||||
PackingFailed
|
||||
_
|
||||
_
|
||||
_
|
||||
|
||||
Faulty // sector is corrupted or gone for some reason
|
||||
FaultReported // sector has been declared as a fault on chain
|
||||
FaultedFinal // fault declared on chain
|
||||
)
|
||||
|
||||
var SectorStates = []string{
|
||||
UndefinedSectorState: "UndefinedSectorState",
|
||||
Empty: "Empty",
|
||||
Packing: "Packing",
|
||||
Unsealed: "Unsealed",
|
||||
PreCommitting: "PreCommitting",
|
||||
WaitSeed: "WaitSeed",
|
||||
Committing: "Committing",
|
||||
CommitWait: "CommitWait",
|
||||
FinalizeSector: "FinalizeSector",
|
||||
Proving: "Proving",
|
||||
|
||||
SealFailed: "SealFailed",
|
||||
PreCommitFailed: "PreCommitFailed",
|
||||
SealCommitFailed: "SealCommitFailed",
|
||||
CommitFailed: "CommitFailed",
|
||||
PackingFailed: "PackingFailed",
|
||||
|
||||
FailedUnrecoverable: "FailedUnrecoverable",
|
||||
|
||||
Faulty: "Faulty",
|
||||
FaultReported: "FaultReported",
|
||||
FaultedFinal: "FaultedFinal",
|
||||
}
|
||||
|
||||
// StorageMiner is a low-level interface to the Filecoin network storage miner node
|
||||
type StorageMiner interface {
|
||||
Common
|
||||
|
||||
ActorAddress(context.Context) (address.Address, error)
|
||||
|
||||
ActorSectorSize(context.Context, address.Address) (uint64, error)
|
||||
ActorSectorSize(context.Context, address.Address) (abi.SectorSize, error)
|
||||
|
||||
MiningBase(context.Context) (*types.TipSet, error)
|
||||
|
||||
// Temp api for testing
|
||||
PledgeSector(context.Context) error
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
SectorsStatus(context.Context, uint64) (SectorInfo, error)
|
||||
SectorsStatus(context.Context, abi.SectorNumber) (SectorInfo, error)
|
||||
|
||||
// List all staged sectors
|
||||
SectorsList(context.Context) ([]uint64, error)
|
||||
SectorsList(context.Context) ([]abi.SectorNumber, error)
|
||||
|
||||
SectorsRefs(context.Context) (map[string][]SealedRef, error)
|
||||
|
||||
SectorsUpdate(context.Context, uint64, SectorState) error
|
||||
SectorsUpdate(context.Context, abi.SectorNumber, SectorState) error
|
||||
|
||||
WorkerStats(context.Context) (sectorbuilder.WorkerStats, error)
|
||||
StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error)
|
||||
StorageLocal(ctx context.Context) (map[stores.ID]string, error)
|
||||
StorageStat(ctx context.Context, id stores.ID) (stores.FsStat, error)
|
||||
|
||||
// WorkerQueue registers a remote worker
|
||||
WorkerQueue(context.Context, sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error)
|
||||
// WorkerConnect tells the node to connect to workers RPC
|
||||
WorkerConnect(context.Context, string) error
|
||||
WorkerStats(context.Context) (map[uint64]storiface.WorkerStats, error)
|
||||
|
||||
WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error
|
||||
stores.SectorIndex
|
||||
|
||||
MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error
|
||||
MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error)
|
||||
MarketSetPrice(context.Context, types.BigInt) error
|
||||
|
||||
DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error
|
||||
DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
|
||||
StorageAddLocal(ctx context.Context, path string) error
|
||||
}
|
||||
|
||||
type SealRes struct {
|
||||
Err string
|
||||
GoErr error `json:"-"`
|
||||
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
type SectorLog struct {
|
||||
@ -119,14 +76,14 @@ type SectorLog struct {
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID uint64
|
||||
SectorID abi.SectorNumber
|
||||
State SectorState
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
CommD *cid.Cid
|
||||
CommR *cid.Cid
|
||||
Proof []byte
|
||||
Deals []uint64
|
||||
Ticket sectorbuilder.SealTicket
|
||||
Seed sectorbuilder.SealSeed
|
||||
Deals []abi.DealID
|
||||
Ticket SealTicket
|
||||
Seed SealSeed
|
||||
Retries uint64
|
||||
|
||||
LastErr string
|
||||
@ -135,11 +92,31 @@ type SectorInfo struct {
|
||||
}
|
||||
|
||||
type SealedRef struct {
|
||||
SectorID uint64
|
||||
SectorID abi.SectorNumber
|
||||
Offset uint64
|
||||
Size uint64
|
||||
Size abi.UnpaddedPieceSize
|
||||
}
|
||||
|
||||
type SealedRefs struct {
|
||||
Refs []SealedRef
|
||||
}
|
||||
|
||||
type SealTicket struct {
|
||||
Value abi.SealRandomness
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
type SealSeed struct {
|
||||
Value abi.InteractiveSealRandomness
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
func (st *SealTicket) Equals(ost *SealTicket) bool {
|
||||
return bytes.Equal(st.Value, ost.Value) && st.Epoch == ost.Epoch
|
||||
}
|
||||
|
||||
func (st *SealSeed) Equals(ost *SealSeed) bool {
|
||||
return bytes.Equal(st.Value, ost.Value) && st.Epoch == ost.Epoch
|
||||
}
|
||||
|
||||
type SectorState string
|
||||
|
34
api/api_test.go
Normal file
34
api/api_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func goCmd() string {
|
||||
var exeSuffix string
|
||||
if runtime.GOOS == "windows" {
|
||||
exeSuffix = ".exe"
|
||||
}
|
||||
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path
|
||||
}
|
||||
return "go"
|
||||
}
|
||||
|
||||
func TestDoesntDependOnFFI(t *testing.T) {
|
||||
deps, err := exec.Command(goCmd(), "list", "-deps", "github.com/filecoin-project/lotus/api").Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, pkg := range strings.Fields(string(deps)) {
|
||||
if pkg == "github.com/filecoin-project/filecoin-ffi" {
|
||||
t.Fatal("api depends on filecoin-ffi")
|
||||
}
|
||||
}
|
||||
}
|
27
api/api_worker.go
Normal file
27
api/api_worker.go
Normal file
@ -0,0 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
type WorkerApi interface {
|
||||
Version(context.Context) (build.Version, error)
|
||||
// TODO: Info() (name, ...) ?
|
||||
|
||||
TaskTypes(context.Context) (map[sealtasks.TaskType]struct{}, error) // TaskType -> Weight
|
||||
Paths(context.Context) ([]stores.StoragePath, error)
|
||||
Info(context.Context) (storiface.WorkerInfo, error)
|
||||
|
||||
storage.Sealer
|
||||
Fetch(context.Context, abi.SectorID, stores.SectorFileType, bool) error
|
||||
|
||||
Closing(context.Context) (<-chan struct{}, error)
|
||||
}
|
@ -18,6 +18,12 @@ type apiBStore struct {
|
||||
api ChainIO
|
||||
}
|
||||
|
||||
func NewAPIBlockstore(cio ChainIO) blockstore.Blockstore {
|
||||
return &apiBStore{
|
||||
api: cio,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *apiBStore) DeleteBlock(cid.Cid) error {
|
||||
return xerrors.New("not supported")
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ func PermissionedFullAPI(a api.FullNode) api.FullNode {
|
||||
return &out
|
||||
}
|
||||
|
||||
func PermissionedWorkerAPI(a api.WorkerApi) api.WorkerApi {
|
||||
var out WorkerStruct
|
||||
permissionedAny(a, &out.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func HasPerm(ctx context.Context, perm api.Permission) bool {
|
||||
callerPerms, ok := ctx.Value(permCtxKey).([]api.Permission)
|
||||
if !ok {
|
||||
|
@ -3,17 +3,26 @@ package apistruct
|
||||
import (
|
||||
"context"
|
||||
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
// All permissions are listed in permissioned.go
|
||||
@ -44,24 +53,25 @@ type FullNodeStruct struct {
|
||||
CommonStruct
|
||||
|
||||
Internal struct {
|
||||
ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"`
|
||||
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"`
|
||||
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
|
||||
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
|
||||
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
||||
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
|
||||
ChainGetTipSetByHeight func(context.Context, uint64, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainHasObj func(context.Context, cid.Cid) (bool, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, types.TipSetKey) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
ChainGetNode func(ctx context.Context, p string) (interface{}, error) `perm:"read"`
|
||||
ChainGetMessage func(context.Context, cid.Cid) (*types.Message, error) `perm:"read"`
|
||||
ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*store.HeadChange, error) `perm:"read"`
|
||||
ChainExport func(context.Context, types.TipSetKey) (<-chan []byte, error) `perm:"read"`
|
||||
ChainNotify func(context.Context) (<-chan []*api.HeadChange, error) `perm:"read"`
|
||||
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetRandomness func(context.Context, types.TipSetKey, crypto.DomainSeparationTag, abi.ChainEpoch, []byte) (abi.Randomness, error) `perm:"read"`
|
||||
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
|
||||
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
|
||||
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
||||
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
|
||||
ChainGetTipSetByHeight func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainHasObj func(context.Context, cid.Cid) (bool, error) `perm:"read"`
|
||||
ChainStatObj func(context.Context, cid.Cid, cid.Cid) (api.ObjStat, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, types.TipSetKey) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
ChainGetNode func(ctx context.Context, p string) (*api.IpldObject, error) `perm:"read"`
|
||||
ChainGetMessage func(context.Context, cid.Cid) (*types.Message, error) `perm:"read"`
|
||||
ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"`
|
||||
ChainExport func(context.Context, types.TipSetKey) (<-chan []byte, error) `perm:"read"`
|
||||
|
||||
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
|
||||
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
||||
@ -69,66 +79,79 @@ type FullNodeStruct struct {
|
||||
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
|
||||
SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"`
|
||||
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
MpoolEstimateGasPrice func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
|
||||
MinerCreateBlock func(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error) `perm:"write"`
|
||||
MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"`
|
||||
MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"`
|
||||
|
||||
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"`
|
||||
WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"`
|
||||
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
|
||||
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
|
||||
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
|
||||
WalletSign func(context.Context, address.Address, []byte) (*types.Signature, error) `perm:"sign"`
|
||||
WalletSign func(context.Context, address.Address, []byte) (*crypto.Signature, error) `perm:"sign"`
|
||||
WalletSignMessage func(context.Context, address.Address, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
WalletVerify func(context.Context, address.Address, []byte, *types.Signature) bool `perm:"read"`
|
||||
WalletVerify func(context.Context, address.Address, []byte, *crypto.Signature) bool `perm:"read"`
|
||||
WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"`
|
||||
WalletSetDefault func(context.Context, address.Address) error `perm:"admin"`
|
||||
WalletExport func(context.Context, address.Address) (*types.KeyInfo, error) `perm:"admin"`
|
||||
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
|
||||
|
||||
ClientImport func(ctx context.Context, path string) (cid.Cid, error) `perm:"admin"`
|
||||
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
|
||||
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
|
||||
ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"`
|
||||
ClientStartDeal func(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, path string) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) `perm:"read"`
|
||||
ClientImport func(ctx context.Context, ref api.FileRef) (cid.Cid, error) `perm:"admin"`
|
||||
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
|
||||
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
|
||||
ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"`
|
||||
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
ClientCalcCommP func(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) `perm:"read"`
|
||||
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
|
||||
|
||||
StateMinerSectors func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (api.MinerPower, error) `perm:"read"`
|
||||
StateMinerWorker func(context.Context, address.Address, types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateMinerPeerID func(ctx context.Context, m address.Address, tsk types.TipSetKey) (peer.ID, error) `perm:"read"`
|
||||
StateMinerElectionPeriodStart func(ctx context.Context, actor address.Address, tsk types.TipSetKey) (uint64, error) `perm:"read"`
|
||||
StateMinerSectorSize func(context.Context, address.Address, types.TipSetKey) (uint64, error) `perm:"read"`
|
||||
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) ([]uint64, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"`
|
||||
StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, *types.Actor, types.TipSetKey) (*api.ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(context.Context, cid.Cid) (*api.MsgWait, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateMarketBalance func(context.Context, address.Address, types.TipSetKey) (actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketParticipants func(context.Context, types.TipSetKey) (map[string]actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketDeals func(context.Context, types.TipSetKey) (map[string]actors.OnChainDeal, error) `perm:"read"`
|
||||
StateMarketStorageDeal func(context.Context, uint64, types.TipSetKey) (*actors.OnChainDeal, error) `perm:"read"`
|
||||
StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"`
|
||||
StateGetReceipt func(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht uint64) ([]cid.Cid, error) `perm:"read"`
|
||||
StateCompute func(context.Context, uint64, []*types.Message, types.TipSetKey) (cid.Cid, error) `perm:"read"`
|
||||
StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"`
|
||||
StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingDeadline func(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"`
|
||||
StateMinerInfo func(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error) `perm:"read"`
|
||||
StateMinerDeadlines func(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) `perm:"read"`
|
||||
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error) `perm:"read"`
|
||||
StateMinerInitialPledgeCollateral func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateMinerAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateSectorPreCommitInfo func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"`
|
||||
StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, *types.Actor, types.TipSetKey) (*api.ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateMarketBalance func(context.Context, address.Address, types.TipSetKey) (api.MarketBalance, error) `perm:"read"`
|
||||
StateMarketParticipants func(context.Context, types.TipSetKey) (map[string]api.MarketBalance, error) `perm:"read"`
|
||||
StateMarketDeals func(context.Context, types.TipSetKey) (map[string]api.MarketDeal, error) `perm:"read"`
|
||||
StateMarketStorageDeal func(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error) `perm:"read"`
|
||||
StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateAccountKey func(context.Context, address.Address, types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"`
|
||||
StateGetReceipt func(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"`
|
||||
StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"`
|
||||
|
||||
MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
MsigCreate func(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
|
||||
MsigPropose func(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
MsigApprove func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
MsigCancel func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
|
||||
MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"`
|
||||
MarketEnsureAvailable func(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
|
||||
|
||||
PaychGet func(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*api.ChannelInfo, error) `perm:"sign"`
|
||||
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
|
||||
@ -136,13 +159,13 @@ type FullNodeStruct struct {
|
||||
PaychClose func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
|
||||
PaychAllocateLane func(context.Context, address.Address) (uint64, error) `perm:"sign"`
|
||||
PaychNewPayment func(ctx context.Context, from, to address.Address, vouchers []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"`
|
||||
PaychVoucherCheck func(context.Context, *types.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckValid func(context.Context, address.Address, *types.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckSpendable func(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
|
||||
PaychVoucherAdd func(context.Context, address.Address, *types.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*types.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error) `perm:"sign"`
|
||||
PaychVoucherCheck func(context.Context, *paych.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckValid func(context.Context, address.Address, *paych.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckSpendable func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
|
||||
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*paych.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error) `perm:"sign"`
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,23 +177,68 @@ type StorageMinerStruct struct {
|
||||
CommonStruct
|
||||
|
||||
Internal struct {
|
||||
ActorAddress func(context.Context) (address.Address, error) `perm:"read"`
|
||||
ActorSectorSize func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
ActorAddress func(context.Context) (address.Address, error) `perm:"read"`
|
||||
ActorSectorSize func(context.Context, address.Address) (abi.SectorSize, error) `perm:"read"`
|
||||
|
||||
MiningBase func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
MarketImportDealData func(context.Context, cid.Cid, string) error `perm:"write"`
|
||||
MarketListDeals func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
|
||||
MarketListIncompleteDeals func(ctx context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"`
|
||||
MarketSetPrice func(context.Context, types.BigInt) error `perm:"admin"`
|
||||
|
||||
PledgeSector func(context.Context) error `perm:"write"`
|
||||
|
||||
SectorsStatus func(context.Context, uint64) (api.SectorInfo, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]uint64, error) `perm:"read"`
|
||||
SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"`
|
||||
SectorsUpdate func(context.Context, uint64, api.SectorState) error `perm:"write"`
|
||||
SectorsStatus func(context.Context, abi.SectorNumber) (api.SectorInfo, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]abi.SectorNumber, error) `perm:"read"`
|
||||
SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"`
|
||||
SectorsUpdate func(context.Context, abi.SectorNumber, api.SectorState) error `perm:"write"`
|
||||
|
||||
WorkerStats func(context.Context) (sectorbuilder.WorkerStats, error) `perm:"read"`
|
||||
WorkerConnect func(context.Context, string) error `perm:"admin"` // TODO: worker perm
|
||||
WorkerStats func(context.Context) (map[uint64]storiface.WorkerStats, error) `perm:"admin"`
|
||||
|
||||
WorkerQueue func(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) `perm:"admin"` // TODO: worker perm
|
||||
WorkerDone func(ctx context.Context, task uint64, res sectorbuilder.SealRes) error `perm:"admin"`
|
||||
StorageList func(context.Context) (map[stores.ID][]stores.Decl, error) `perm:"admin"`
|
||||
StorageLocal func(context.Context) (map[stores.ID]string, error) `perm:"admin"`
|
||||
StorageStat func(context.Context, stores.ID) (stores.FsStat, error) `perm:"admin"`
|
||||
StorageAttach func(context.Context, stores.StorageInfo, stores.FsStat) error `perm:"admin"`
|
||||
StorageDeclareSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType) error `perm:"admin"`
|
||||
StorageDropSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType) error `perm:"admin"`
|
||||
StorageFindSector func(context.Context, abi.SectorID, stores.SectorFileType, bool) ([]stores.StorageInfo, error) `perm:"admin"`
|
||||
StorageInfo func(context.Context, stores.ID) (stores.StorageInfo, error) `perm:"admin"`
|
||||
StorageBestAlloc func(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, sealing bool) ([]stores.StorageInfo, error) `perm:"admin"`
|
||||
StorageReportHealth func(ctx context.Context, id stores.ID, report stores.HealthReport) error `perm:"admin"`
|
||||
|
||||
DealsImportData func(ctx context.Context, dealPropCid cid.Cid, file string) error `perm:"write"`
|
||||
DealsList func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
|
||||
|
||||
StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
type WorkerStruct struct {
|
||||
Internal struct {
|
||||
// TODO: lower perms
|
||||
|
||||
Version func(context.Context) (build.Version, error) `perm:"admin"`
|
||||
|
||||
TaskTypes func(context.Context) (map[sealtasks.TaskType]struct{}, error) `perm:"admin"`
|
||||
Paths func(context.Context) ([]stores.StoragePath, error) `perm:"admin"`
|
||||
Info func(context.Context) (storiface.WorkerInfo, error) `perm:"admin"`
|
||||
|
||||
SealPreCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) `perm:"admin"`
|
||||
SealPreCommit2 func(context.Context, abi.SectorID, storage.PreCommit1Out) (cids storage.SectorCids, err error) `perm:"admin"`
|
||||
SealCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) `perm:"admin"`
|
||||
SealCommit2 func(context.Context, abi.SectorID, storage.Commit1Out) (storage.Proof, error) `perm:"admin"`
|
||||
FinalizeSector func(context.Context, abi.SectorID) error `perm:"admin"`
|
||||
|
||||
Fetch func(context.Context, abi.SectorID, stores.SectorFileType, bool) error `perm:"admin"`
|
||||
|
||||
Closing func(context.Context) (<-chan struct{}, error) `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
// CommonStruct
|
||||
|
||||
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]api.Permission, error) {
|
||||
return c.Internal.AuthVerify(ctx, token)
|
||||
}
|
||||
@ -221,12 +289,14 @@ func (c *CommonStruct) LogSetLevel(ctx context.Context, group, level string) err
|
||||
return c.Internal.LogSetLevel(ctx, group, level)
|
||||
}
|
||||
|
||||
// FullNodeStruct
|
||||
|
||||
func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) {
|
||||
return c.Internal.ClientListImports(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientImport(ctx context.Context, path string) (cid.Cid, error) {
|
||||
return c.Internal.ClientImport(ctx, path)
|
||||
func (c *FullNodeStruct) ClientImport(ctx context.Context, ref api.FileRef) (cid.Cid, error) {
|
||||
return c.Internal.ClientImport(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) {
|
||||
@ -237,8 +307,8 @@ func (c *FullNodeStruct) ClientFindData(ctx context.Context, root cid.Cid) ([]ap
|
||||
return c.Internal.ClientFindData(ctx, root)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) {
|
||||
return c.Internal.ClientStartDeal(ctx, data, addr, miner, price, blocksDuration)
|
||||
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
||||
return c.Internal.ClientStartDeal(ctx, params)
|
||||
}
|
||||
func (c *FullNodeStruct) ClientGetDealInfo(ctx context.Context, deal cid.Cid) (*api.DealInfo, error) {
|
||||
return c.Internal.ClientGetDealInfo(ctx, deal)
|
||||
@ -248,13 +318,20 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
|
||||
return c.Internal.ClientListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, path string) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, path)
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, ref)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) {
|
||||
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) {
|
||||
return c.Internal.ClientQueryAsk(ctx, p, miner)
|
||||
}
|
||||
func (c *FullNodeStruct) ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) {
|
||||
return c.Internal.ClientCalcCommP(ctx, inpath, miner)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientGenCar(ctx context.Context, ref api.FileRef, outpath string) error {
|
||||
return c.Internal.ClientGenCar(ctx, ref, outpath)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*types.SignedMessage, error) {
|
||||
return c.Internal.MpoolPending(ctx, tsk)
|
||||
@ -272,23 +349,31 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate,
|
||||
return c.Internal.MpoolSub(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base types.TipSetKey, ticket *types.Ticket, eproof *types.EPostProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) {
|
||||
return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, msgs, height, ts)
|
||||
func (c *FullNodeStruct) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, limit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.MpoolEstimateGasPrice(ctx, nblocksincl, sender, limit, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
|
||||
return c.Internal.MinerGetBaseInfo(ctx, maddr, epoch, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, bt *api.BlockTemplate) (*types.BlockMsg, error) {
|
||||
return c.Internal.MinerCreateBlock(ctx, bt)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
return c.Internal.ChainHead(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) {
|
||||
return c.Internal.ChainGetRandomness(ctx, pts, round)
|
||||
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
|
||||
return c.Internal.ChainGetRandomness(ctx, tsk, personalization, randEpoch, entropy)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h uint64, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
return c.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ string) (address.Address, error) {
|
||||
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ crypto.SigType) (address.Address, error) {
|
||||
return c.Internal.WalletNew(ctx, typ)
|
||||
}
|
||||
|
||||
@ -304,7 +389,7 @@ func (c *FullNodeStruct) WalletBalance(ctx context.Context, a address.Address) (
|
||||
return c.Internal.WalletBalance(ctx, a)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {
|
||||
func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) {
|
||||
return c.Internal.WalletSign(ctx, k, msg)
|
||||
}
|
||||
|
||||
@ -312,7 +397,7 @@ func (c *FullNodeStruct) WalletSignMessage(ctx context.Context, k address.Addres
|
||||
return c.Internal.WalletSignMessage(ctx, k, msg)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletVerify(ctx context.Context, k address.Address, msg []byte, sig *types.Signature) bool {
|
||||
func (c *FullNodeStruct) WalletVerify(ctx context.Context, k address.Address, msg []byte, sig *crypto.Signature) bool {
|
||||
return c.Internal.WalletVerify(ctx, k, msg, sig)
|
||||
}
|
||||
|
||||
@ -356,7 +441,7 @@ func (c *FullNodeStruct) ChainGetParentMessages(ctx context.Context, b cid.Cid)
|
||||
return c.Internal.ChainGetParentMessages(ctx, b)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainNotify(ctx context.Context) (<-chan []*store.HeadChange, error) {
|
||||
func (c *FullNodeStruct) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, error) {
|
||||
return c.Internal.ChainNotify(ctx)
|
||||
}
|
||||
|
||||
@ -368,6 +453,10 @@ func (c *FullNodeStruct) ChainHasObj(ctx context.Context, o cid.Cid) (bool, erro
|
||||
return c.Internal.ChainHasObj(ctx, o)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainStatObj(ctx context.Context, obj, base cid.Cid) (api.ObjStat, error) {
|
||||
return c.Internal.ChainStatObj(ctx, obj, base)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainSetHead(ctx context.Context, tsk types.TipSetKey) error {
|
||||
return c.Internal.ChainSetHead(ctx, tsk)
|
||||
}
|
||||
@ -380,7 +469,7 @@ func (c *FullNodeStruct) ChainTipSetWeight(ctx context.Context, tsk types.TipSet
|
||||
return c.Internal.ChainTipSetWeight(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetNode(ctx context.Context, p string) (interface{}, error) {
|
||||
func (c *FullNodeStruct) ChainGetNode(ctx context.Context, p string) (*api.IpldObject, error) {
|
||||
return c.Internal.ChainGetNode(ctx, p)
|
||||
}
|
||||
|
||||
@ -388,7 +477,7 @@ func (c *FullNodeStruct) ChainGetMessage(ctx context.Context, mc cid.Cid) (*type
|
||||
return c.Internal.ChainGetMessage(ctx, mc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error) {
|
||||
func (c *FullNodeStruct) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*api.HeadChange, error) {
|
||||
return c.Internal.ChainGetPath(ctx, from, to)
|
||||
}
|
||||
|
||||
@ -416,38 +505,50 @@ func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string
|
||||
return c.Internal.SyncCheckBad(ctx, bcid)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr, tsk)
|
||||
func (c *FullNodeStruct) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) {
|
||||
return c.Internal.StateNetworkName(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address, filter *abi.BitField, filterOut bool, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr, filter, filterOut, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerProvingSet(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address, tsk types.TipSetKey) (api.MinerPower, error) {
|
||||
func (c *FullNodeStruct) StateMinerProvingDeadline(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*miner.DeadlineInfo, error) {
|
||||
return c.Internal.StateMinerProvingDeadline(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address, tsk types.TipSetKey) (*api.MinerPower, error) {
|
||||
return c.Internal.StateMinerPower(ctx, a, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerWorker(ctx context.Context, m address.Address, tsk types.TipSetKey) (address.Address, error) {
|
||||
return c.Internal.StateMinerWorker(ctx, m, tsk)
|
||||
func (c *FullNodeStruct) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
|
||||
return c.Internal.StateMinerInfo(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerPeerID(ctx context.Context, m address.Address, tsk types.TipSetKey) (peer.ID, error) {
|
||||
return c.Internal.StateMinerPeerID(ctx, m, tsk)
|
||||
func (c *FullNodeStruct) StateMinerDeadlines(ctx context.Context, m address.Address, tsk types.TipSetKey) (*miner.Deadlines, error) {
|
||||
return c.Internal.StateMinerDeadlines(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, tsk types.TipSetKey) (uint64, error) {
|
||||
return c.Internal.StateMinerElectionPeriodStart(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address.Address, tsk types.TipSetKey) (uint64, error) {
|
||||
return c.Internal.StateMinerSectorSize(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) ([]uint64, error) {
|
||||
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) ([]abi.SectorNumber, error) {
|
||||
return c.Internal.StateMinerFaults(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerInitialPledgeCollateral(ctx context.Context, maddr address.Address, snum abi.SectorNumber, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StateMinerInitialPledgeCollateral(ctx, maddr, snum, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerAvailableBalance(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StateMinerAvailableBalance(ctx, maddr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) {
|
||||
return c.Internal.StateSectorPreCommitInfo(ctx, maddr, n, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*api.InvocResult, error) {
|
||||
return c.Internal.StateCall(ctx, msg, tsk)
|
||||
}
|
||||
@ -468,9 +569,14 @@ func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, tsk types.Ti
|
||||
return c.Internal.StatePledgeCollateral(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid) (*api.MsgWait, error) {
|
||||
func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) {
|
||||
return c.Internal.StateWaitMsg(ctx, msgc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) {
|
||||
return c.Internal.StateSearchMsg(ctx, msgc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListMiners(ctx context.Context, tsk types.TipSetKey) ([]address.Address, error) {
|
||||
return c.Internal.StateListMiners(ctx, tsk)
|
||||
}
|
||||
@ -479,19 +585,19 @@ func (c *FullNodeStruct) StateListActors(ctx context.Context, tsk types.TipSetKe
|
||||
return c.Internal.StateListActors(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (actors.StorageParticipantBalance, error) {
|
||||
func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error) {
|
||||
return c.Internal.StateMarketBalance(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, tsk types.TipSetKey) (map[string]actors.StorageParticipantBalance, error) {
|
||||
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, tsk types.TipSetKey) (map[string]api.MarketBalance, error) {
|
||||
return c.Internal.StateMarketParticipants(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, tsk types.TipSetKey) (map[string]actors.OnChainDeal, error) {
|
||||
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, tsk types.TipSetKey) (map[string]api.MarketDeal, error) {
|
||||
return c.Internal.StateMarketDeals(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketStorageDeal(ctx context.Context, dealid uint64, tsk types.TipSetKey) (*actors.OnChainDeal, error) {
|
||||
func (c *FullNodeStruct) StateMarketStorageDeal(ctx context.Context, dealid abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error) {
|
||||
return c.Internal.StateMarketStorageDeal(ctx, dealid, tsk)
|
||||
}
|
||||
|
||||
@ -499,6 +605,10 @@ func (c *FullNodeStruct) StateLookupID(ctx context.Context, addr address.Address
|
||||
return c.Internal.StateLookupID(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
|
||||
return c.Internal.StateAccountKey(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateChangedActors(ctx context.Context, olnstate cid.Cid, newstate cid.Cid) (map[string]types.Actor, error) {
|
||||
return c.Internal.StateChangedActors(ctx, olnstate, newstate)
|
||||
}
|
||||
@ -507,11 +617,11 @@ func (c *FullNodeStruct) StateGetReceipt(ctx context.Context, msg cid.Cid, tsk t
|
||||
return c.Internal.StateGetReceipt(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht uint64) ([]cid.Cid, error) {
|
||||
func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
return c.Internal.StateListMessages(ctx, match, tsk, toht)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, tsk types.TipSetKey) (cid.Cid, error) {
|
||||
func (c *FullNodeStruct) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*api.ComputeStateOutput, error) {
|
||||
return c.Internal.StateCompute(ctx, height, msgs, tsk)
|
||||
}
|
||||
|
||||
@ -519,8 +629,24 @@ func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address.
|
||||
return c.Internal.MsigGetAvailableBalance(ctx, a, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, amt)
|
||||
func (c *FullNodeStruct) MsigCreate(ctx context.Context, req int64, addrs []address.Address, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) {
|
||||
return c.Internal.MsigCreate(ctx, req, addrs, val, src, gp)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigPropose(ctx, msig, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigApprove(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigApprove(ctx, msig, txID, proposer, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigCancel(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigCancel(ctx, msig, txID, proposer, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr, wallet address.Address, amt types.BigInt) (cid.Cid, error) {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, wallet, amt)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*api.ChannelInfo, error) {
|
||||
@ -535,23 +661,23 @@ func (c *FullNodeStruct) PaychStatus(ctx context.Context, pch address.Address) (
|
||||
return c.Internal.PaychStatus(ctx, pch)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCheckValid(ctx context.Context, addr address.Address, sv *types.SignedVoucher) error {
|
||||
func (c *FullNodeStruct) PaychVoucherCheckValid(ctx context.Context, addr address.Address, sv *paych.SignedVoucher) error {
|
||||
return c.Internal.PaychVoucherCheckValid(ctx, addr, sv)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCheckSpendable(ctx context.Context, addr address.Address, sv *types.SignedVoucher, secret []byte, proof []byte) (bool, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherCheckSpendable(ctx context.Context, addr address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
|
||||
return c.Internal.PaychVoucherCheckSpendable(ctx, addr, sv, secret, proof)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
|
||||
return c.Internal.PaychVoucherAdd(ctx, addr, sv, proof, minDelta)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*types.SignedVoucher, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*paych.SignedVoucher, error) {
|
||||
return c.Internal.PaychVoucherCreate(ctx, pch, amt, lane)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherList(ctx context.Context, pch address.Address) ([]*types.SignedVoucher, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherList(ctx context.Context, pch address.Address) ([]*paych.SignedVoucher, error) {
|
||||
return c.Internal.PaychVoucherList(ctx, pch)
|
||||
}
|
||||
|
||||
@ -567,15 +693,21 @@ func (c *FullNodeStruct) PaychNewPayment(ctx context.Context, from, to address.A
|
||||
return c.Internal.PaychNewPayment(ctx, from, to, vouchers)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *types.SignedVoucher) (cid.Cid, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (cid.Cid, error) {
|
||||
return c.Internal.PaychVoucherSubmit(ctx, ch, sv)
|
||||
}
|
||||
|
||||
// StorageMinerStruct
|
||||
|
||||
func (c *StorageMinerStruct) ActorAddress(ctx context.Context) (address.Address, error) {
|
||||
return c.Internal.ActorAddress(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) ActorSectorSize(ctx context.Context, addr address.Address) (uint64, error) {
|
||||
func (c *StorageMinerStruct) MiningBase(ctx context.Context) (*types.TipSet, error) {
|
||||
return c.Internal.MiningBase(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) ActorSectorSize(ctx context.Context, addr address.Address) (abi.SectorSize, error) {
|
||||
return c.Internal.ActorSectorSize(ctx, addr)
|
||||
}
|
||||
|
||||
@ -584,12 +716,12 @@ func (c *StorageMinerStruct) PledgeSector(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid uint64) (api.SectorInfo, error) {
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid abi.SectorNumber) (api.SectorInfo, error) {
|
||||
return c.Internal.SectorsStatus(ctx, sid)
|
||||
}
|
||||
|
||||
// List all staged sectors
|
||||
func (c *StorageMinerStruct) SectorsList(ctx context.Context) ([]uint64, error) {
|
||||
func (c *StorageMinerStruct) SectorsList(ctx context.Context) ([]abi.SectorNumber, error) {
|
||||
return c.Internal.SectorsList(ctx)
|
||||
}
|
||||
|
||||
@ -597,22 +729,133 @@ func (c *StorageMinerStruct) SectorsRefs(ctx context.Context) (map[string][]api.
|
||||
return c.Internal.SectorsRefs(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) SectorsUpdate(ctx context.Context, id uint64, state api.SectorState) error {
|
||||
func (c *StorageMinerStruct) SectorsUpdate(ctx context.Context, id abi.SectorNumber, state api.SectorState) error {
|
||||
return c.Internal.SectorsUpdate(ctx, id, state)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerStats(ctx context.Context) (sectorbuilder.WorkerStats, error) {
|
||||
func (c *StorageMinerStruct) WorkerConnect(ctx context.Context, url string) error {
|
||||
return c.Internal.WorkerConnect(ctx, url)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerStats(ctx context.Context) (map[uint64]storiface.WorkerStats, error) {
|
||||
return c.Internal.WorkerStats(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerQueue(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) {
|
||||
return c.Internal.WorkerQueue(ctx, cfg)
|
||||
func (c *StorageMinerStruct) StorageAttach(ctx context.Context, si stores.StorageInfo, st stores.FsStat) error {
|
||||
return c.Internal.StorageAttach(ctx, si, st)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error {
|
||||
return c.Internal.WorkerDone(ctx, task, res)
|
||||
func (c *StorageMinerStruct) StorageDeclareSector(ctx context.Context, storageId stores.ID, s abi.SectorID, ft stores.SectorFileType) error {
|
||||
return c.Internal.StorageDeclareSector(ctx, storageId, s, ft)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageDropSector(ctx context.Context, storageId stores.ID, s abi.SectorID, ft stores.SectorFileType) error {
|
||||
return c.Internal.StorageDropSector(ctx, storageId, s, ft)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageFindSector(ctx context.Context, si abi.SectorID, types stores.SectorFileType, allowFetch bool) ([]stores.StorageInfo, error) {
|
||||
return c.Internal.StorageFindSector(ctx, si, types, allowFetch)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error) {
|
||||
return c.Internal.StorageList(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageLocal(ctx context.Context) (map[stores.ID]string, error) {
|
||||
return c.Internal.StorageLocal(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageStat(ctx context.Context, id stores.ID) (stores.FsStat, error) {
|
||||
return c.Internal.StorageStat(ctx, id)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageInfo(ctx context.Context, id stores.ID) (stores.StorageInfo, error) {
|
||||
return c.Internal.StorageInfo(ctx, id)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageBestAlloc(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, sealing bool) ([]stores.StorageInfo, error) {
|
||||
return c.Internal.StorageBestAlloc(ctx, allocate, spt, sealing)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageReportHealth(ctx context.Context, id stores.ID, report stores.HealthReport) error {
|
||||
return c.Internal.StorageReportHealth(ctx, id, report)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error {
|
||||
return c.Internal.MarketImportDealData(ctx, propcid, path)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error) {
|
||||
return c.Internal.MarketListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error) {
|
||||
return c.Internal.MarketListIncompleteDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketSetPrice(ctx context.Context, p types.BigInt) error {
|
||||
return c.Internal.MarketSetPrice(ctx, p)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error {
|
||||
return c.Internal.DealsImportData(ctx, dealPropCid, file)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error) {
|
||||
return c.Internal.DealsList(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageAddLocal(ctx context.Context, path string) error {
|
||||
return c.Internal.StorageAddLocal(ctx, path)
|
||||
}
|
||||
|
||||
// WorkerStruct
|
||||
|
||||
func (w *WorkerStruct) Version(ctx context.Context) (build.Version, error) {
|
||||
return w.Internal.Version(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) TaskTypes(ctx context.Context) (map[sealtasks.TaskType]struct{}, error) {
|
||||
return w.Internal.TaskTypes(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Paths(ctx context.Context) ([]stores.StoragePath, error) {
|
||||
return w.Internal.Paths(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Info(ctx context.Context) (storiface.WorkerInfo, error) {
|
||||
return w.Internal.Info(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealPreCommit1(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) {
|
||||
return w.Internal.SealPreCommit1(ctx, sector, ticket, pieces)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealPreCommit2(ctx context.Context, sector abi.SectorID, p1o storage.PreCommit1Out) (storage.SectorCids, error) {
|
||||
return w.Internal.SealPreCommit2(ctx, sector, p1o)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealCommit1(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) {
|
||||
return w.Internal.SealCommit1(ctx, sector, ticket, seed, pieces, cids)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealCommit2(ctx context.Context, sector abi.SectorID, c1o storage.Commit1Out) (storage.Proof, error) {
|
||||
return w.Internal.SealCommit2(ctx, sector, c1o)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) FinalizeSector(ctx context.Context, sector abi.SectorID) error {
|
||||
return w.Internal.FinalizeSector(ctx, sector)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Fetch(ctx context.Context, id abi.SectorID, fileType stores.SectorFileType, b bool) error {
|
||||
return w.Internal.Fetch(ctx, id, fileType, b)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
|
||||
return w.Internal.Closing(ctx)
|
||||
}
|
||||
|
||||
var _ api.Common = &CommonStruct{}
|
||||
var _ api.FullNode = &FullNodeStruct{}
|
||||
var _ api.StorageMiner = &StorageMinerStruct{}
|
||||
var _ api.WorkerApi = &WorkerStruct{}
|
||||
|
9
api/apistruct/struct_test.go
Normal file
9
api/apistruct/struct_test.go
Normal file
@ -0,0 +1,9 @@
|
||||
package apistruct
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPermTags(t *testing.T) {
|
||||
_ = PermissionedFullAPI(&FullNodeStruct{})
|
||||
_ = PermissionedStorMinerAPI(&StorageMinerStruct{})
|
||||
_ = PermissionedWorkerAPI(&WorkerStruct{})
|
||||
}
|
433
api/cbor_gen.go
433
api/cbor_gen.go
@ -1,16 +1,17 @@
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
@ -22,15 +23,17 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Channel (address.Address) (struct)
|
||||
if len("Channel") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Channel\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Channel")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Channel"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Channel")); err != nil {
|
||||
if _, err := io.WriteString(w, "Channel"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -43,10 +46,10 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Value in field \"ChannelMessage\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("ChannelMessage")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("ChannelMessage"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("ChannelMessage")); err != nil {
|
||||
if _, err := io.WriteString(w, "ChannelMessage"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -55,20 +58,20 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCid(w, *t.ChannelMessage); err != nil {
|
||||
if err := cbg.WriteCidBuf(scratch, w, *t.ChannelMessage); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ChannelMessage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
// t.Vouchers ([]*paych.SignedVoucher) (slice)
|
||||
if len("Vouchers") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Vouchers\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Vouchers")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Vouchers"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Vouchers")); err != nil {
|
||||
if _, err := io.WriteString(w, "Vouchers"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -76,7 +79,7 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.Vouchers was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Vouchers)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Vouchers))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Vouchers {
|
||||
@ -89,8 +92,9 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -108,7 +112,7 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -123,7 +127,7 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("unmarshaling t.Channel: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
@ -152,10 +156,10 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
}
|
||||
|
||||
}
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
// t.Vouchers ([]*paych.SignedVoucher) (slice)
|
||||
case "Vouchers":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -167,12 +171,14 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Vouchers = make([]*types.SignedVoucher, extra)
|
||||
t.Vouchers = make([]*paych.SignedVoucher, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedVoucher
|
||||
var v paych.SignedVoucher
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -196,19 +202,21 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.SectorID (uint64) (uint64)
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.SectorID (abi.SectorNumber) (uint64)
|
||||
if len("SectorID") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"SectorID\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("SectorID")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("SectorID"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("SectorID")); err != nil {
|
||||
if _, err := io.WriteString(w, "SectorID"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SectorID))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.SectorID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -217,39 +225,41 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Value in field \"Offset\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Offset")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Offset"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Offset")); err != nil {
|
||||
if _, err := io.WriteString(w, "Offset"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Offset))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Offset)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Size (uint64) (uint64)
|
||||
// t.Size (abi.UnpaddedPieceSize) (uint64)
|
||||
if len("Size") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Size\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Size")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Size"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Size")); err != nil {
|
||||
if _, err := io.WriteString(w, "Size"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Size))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -267,7 +277,7 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -276,39 +286,51 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.SectorID (uint64) (uint64)
|
||||
// t.SectorID (abi.SectorNumber) (uint64)
|
||||
case "SectorID":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = abi.SectorNumber(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = uint64(extra)
|
||||
// t.Offset (uint64) (uint64)
|
||||
case "Offset":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
// t.Size (uint64) (uint64)
|
||||
// t.Size (abi.UnpaddedPieceSize) (uint64)
|
||||
case "Size":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Size = abi.UnpaddedPieceSize(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Size = uint64(extra)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
@ -326,15 +348,17 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
if len("Refs") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Refs\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Refs")))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Refs"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Refs")); err != nil {
|
||||
if _, err := io.WriteString(w, "Refs"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -342,7 +366,7 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.Refs was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Refs)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Refs))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Refs {
|
||||
@ -355,8 +379,9 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -374,7 +399,7 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -386,7 +411,7 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
case "Refs":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -398,9 +423,11 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Refs = make([]SealedRef, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v SealedRef
|
||||
@ -418,3 +445,287 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
func (t *SealTicket) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{162}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Value (abi.SealRandomness) (slice)
|
||||
if len("Value") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Value\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Value"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Value"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.Value) > cbg.ByteArrayMaxLen {
|
||||
return xerrors.Errorf("Byte array in field t.Value was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajByteString, uint64(len(t.Value))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
if len("Epoch") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Epoch\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Epoch"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Epoch"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Epoch >= 0 {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Epoch)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Epoch-1)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealTicket) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajMap {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealTicket: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Value (abi.SealRandomness) (slice)
|
||||
case "Value":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.ByteArrayMaxLen {
|
||||
return fmt.Errorf("t.Value: byte array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("expected byte array")
|
||||
}
|
||||
t.Value = make([]byte, extra)
|
||||
if _, err := io.ReadFull(br, t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
case "Epoch":
|
||||
{
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
var extraI int64
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch maj {
|
||||
case cbg.MajUnsignedInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 positive overflow")
|
||||
}
|
||||
case cbg.MajNegativeInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 negative oveflow")
|
||||
}
|
||||
extraI = -1 - extraI
|
||||
default:
|
||||
return fmt.Errorf("wrong type for int64 field: %d", maj)
|
||||
}
|
||||
|
||||
t.Epoch = abi.ChainEpoch(extraI)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (t *SealSeed) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{162}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Value (abi.InteractiveSealRandomness) (slice)
|
||||
if len("Value") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Value\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Value"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Value"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.Value) > cbg.ByteArrayMaxLen {
|
||||
return xerrors.Errorf("Byte array in field t.Value was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajByteString, uint64(len(t.Value))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
if len("Epoch") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Epoch\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Epoch"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Epoch"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Epoch >= 0 {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Epoch)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Epoch-1)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealSeed) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajMap {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealSeed: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Value (abi.InteractiveSealRandomness) (slice)
|
||||
case "Value":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.ByteArrayMaxLen {
|
||||
return fmt.Errorf("t.Value: byte array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("expected byte array")
|
||||
}
|
||||
t.Value = make([]byte, extra)
|
||||
if _, err := io.ReadFull(br, t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
case "Epoch":
|
||||
{
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
var extraI int64
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch maj {
|
||||
case cbg.MajUnsignedInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 positive overflow")
|
||||
}
|
||||
case cbg.MajNegativeInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 negative oveflow")
|
||||
}
|
||||
extraI = -1 - extraI
|
||||
default:
|
||||
return fmt.Errorf("wrong type for int64 field: %d", maj)
|
||||
}
|
||||
|
||||
t.Epoch = abi.ChainEpoch(extraI)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ func NewCommonRPC(addr string, requestHeader http.Header) (api.Common, jsonrpc.C
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
}, requestHeader)
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
@ -38,7 +40,21 @@ func NewStorageMinerRPC(addr string, requestHeader http.Header) (api.StorageMine
|
||||
[]interface{}{
|
||||
&res.CommonStruct.Internal,
|
||||
&res.Internal,
|
||||
}, requestHeader)
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
|
||||
func NewWorkerRPC(addr string, requestHeader http.Header) (api.WorkerApi, jsonrpc.ClientCloser, error) {
|
||||
var res apistruct.WorkerStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
|
@ -11,12 +11,22 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipld/go-car"
|
||||
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
dstest "github.com/ipfs/go-merkledag/test"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -24,11 +34,11 @@ func init() {
|
||||
build.InsecurePoStValidation = true
|
||||
}
|
||||
|
||||
func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport bool) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, []int{0})
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@ -42,21 +52,42 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
data := make([]byte, 1000)
|
||||
rand.New(rand.NewSource(5)).Read(data)
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
makeDeal(t, ctx, 6, client, miner, carExport)
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
maddr, err := miner.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
@ -65,22 +96,67 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx); err != nil {
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
addr, err := client.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deal, err := client.ClientStartDeal(ctx, fcid, addr, maddr, types.NewInt(40000000), 100)
|
||||
|
||||
makeDeal(t, ctx, 6, client, miner, false)
|
||||
makeDeal(t, ctx, 7, client, miner, false)
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
func makeDeal(t *testing.T, ctx context.Context, rseed int, client *impl.FullNodeAPI, miner TestStorageNode, carExport bool) {
|
||||
data := make([]byte, 1600)
|
||||
rand.New(rand.NewSource(int64(rseed))).Read(data)
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
|
||||
deal := startDeal(t, ctx, miner, client, fcid)
|
||||
|
||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||
time.Sleep(time.Second)
|
||||
waitDealSealed(t, ctx, client, deal)
|
||||
|
||||
// Retrieval
|
||||
|
||||
testRetrieval(t, ctx, err, client, fcid, carExport, data)
|
||||
}
|
||||
|
||||
func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client *impl.FullNodeAPI, fcid cid.Cid) *cid.Cid {
|
||||
maddr, err := miner.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr, err := client.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deal, err := client.ClientStartDeal(ctx, &api.StartDealParams{
|
||||
Data: &storagemarket.DataRef{Root: fcid},
|
||||
Wallet: addr,
|
||||
Miner: maddr,
|
||||
EpochPrice: types.NewInt(1000000),
|
||||
MinBlocksDuration: 100,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
return deal
|
||||
}
|
||||
|
||||
func waitDealSealed(t *testing.T, ctx context.Context, client *impl.FullNodeAPI, deal *cid.Cid) {
|
||||
loop:
|
||||
for {
|
||||
di, err := client.ClientGetDealInfo(ctx, *deal)
|
||||
@ -88,22 +164,22 @@ loop:
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch di.State {
|
||||
case api.DealRejected:
|
||||
case storagemarket.StorageDealProposalRejected:
|
||||
t.Fatal("deal rejected")
|
||||
case api.DealFailed:
|
||||
case storagemarket.StorageDealFailing:
|
||||
t.Fatal("deal failed")
|
||||
case api.DealError:
|
||||
t.Fatal("deal errored")
|
||||
case api.DealComplete:
|
||||
case storagemarket.StorageDealError:
|
||||
t.Fatal("deal errored", di.Message)
|
||||
case storagemarket.StorageDealActive:
|
||||
fmt.Println("COMPLETE", di)
|
||||
break loop
|
||||
}
|
||||
fmt.Println("Deal state: ", api.DealStates[di.State])
|
||||
fmt.Println("Deal state: ", storagemarket.DealStates[di.State])
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieval
|
||||
|
||||
func testRetrieval(t *testing.T, ctx context.Context, err error, client *impl.FullNodeAPI, fcid cid.Cid, carExport bool, data []byte) {
|
||||
offers, err := client.ClientFindData(ctx, fcid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -124,7 +200,11 @@ loop:
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = client.ClientRetrieve(ctx, offers[0].Order(caddr), filepath.Join(rpath, "ret"))
|
||||
ref := api.FileRef{
|
||||
Path: filepath.Join(rpath, "ret"),
|
||||
IsCAR: carExport,
|
||||
}
|
||||
err = client.ClientRetrieve(ctx, offers[0].Order(caddr), ref)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
@ -134,11 +214,41 @@ loop:
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if carExport {
|
||||
rdata = extractCarData(t, ctx, rdata, rpath)
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdata, data) {
|
||||
t.Fatal("wrong data retrieved")
|
||||
}
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
func extractCarData(t *testing.T, ctx context.Context, rdata []byte, rpath string) []byte {
|
||||
bserv := dstest.Bserv()
|
||||
ch, err := car.LoadCar(bserv.Blockstore(), bytes.NewReader(rdata))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := bserv.GetBlock(ctx, ch.Roots[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nd, err := ipld.Decode(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dserv := dag.NewDAGService(bserv)
|
||||
fil, err := unixfile.NewUnixfsFile(ctx, dserv, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outPath := filepath.Join(rpath, "retLoadedCAR")
|
||||
if err := files.WriteTo(fil, outPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rdata, err = ioutil.ReadFile(outPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rdata
|
||||
}
|
||||
|
@ -1,31 +1,199 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
var log = logging.Logger("apitest")
|
||||
|
||||
func (ts *testSuite) testMining(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, []int{0})
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
h1, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), h1.Height())
|
||||
require.Equal(t, abi.ChainEpoch(0), h1.Height())
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
require.NoError(t, err)
|
||||
<-newHeads
|
||||
|
||||
err = sn[0].MineOne(ctx)
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), h2.Height())
|
||||
require.Equal(t, abi.ChainEpoch(1), h2.Height())
|
||||
}
|
||||
|
||||
func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
build.InsecurePoStValidation = false
|
||||
defer func() {
|
||||
build.InsecurePoStValidation = true
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
h1, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(0), h1.Height())
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
require.NoError(t, err)
|
||||
<-newHeads
|
||||
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(1), h2.Height())
|
||||
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err = api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(2), h2.Height())
|
||||
}
|
||||
|
||||
func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExport bool) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
// test making a deal with a fresh miner, and see if it starts to mine
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, []StorageMiner{
|
||||
{Full: 0, Preseal: PresealGenesis},
|
||||
{Full: 0, Preseal: 0}, // TODO: Add support for storage miners on non-first full node
|
||||
})
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
provider := sn[1]
|
||||
genesisMiner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := provider.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := genesisMiner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
data := make([]byte, 600)
|
||||
rand.New(rand.NewSource(5)).Read(data)
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
|
||||
var mine int32 = 1
|
||||
done := make(chan struct{})
|
||||
minedTwo := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
|
||||
prevExpect := 0
|
||||
for atomic.LoadInt32(&mine) != 0 {
|
||||
wait := make(chan int, 2)
|
||||
mdone := func(mined bool) {
|
||||
go func() {
|
||||
n := 0
|
||||
if mined {
|
||||
n = 1
|
||||
}
|
||||
wait <- n
|
||||
}()
|
||||
}
|
||||
|
||||
if err := sn[0].MineOne(ctx, mdone); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := sn[1].MineOne(ctx, mdone); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
expect := <-wait
|
||||
expect += <-wait
|
||||
|
||||
time.Sleep(blocktime)
|
||||
|
||||
for {
|
||||
n := 0
|
||||
for i, node := range sn {
|
||||
mb, err := node.MiningBase(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(mb.Cids()) != expect {
|
||||
log.Warnf("node %d mining base not complete (%d, want %d)", i, len(mb.Cids()), expect)
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n == len(sn) {
|
||||
break
|
||||
}
|
||||
time.Sleep(blocktime)
|
||||
}
|
||||
|
||||
if prevExpect == 2 && expect == 2 && minedTwo != nil {
|
||||
close(minedTwo)
|
||||
minedTwo = nil
|
||||
}
|
||||
|
||||
prevExpect = expect
|
||||
}
|
||||
}()
|
||||
|
||||
deal := startDeal(t, ctx, provider, client, fcid)
|
||||
|
||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||
time.Sleep(time.Second)
|
||||
|
||||
waitDealSealed(t, ctx, client, deal)
|
||||
|
||||
<-minedTwo
|
||||
|
||||
atomic.StoreInt32(&mine, 0)
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ type TestNode struct {
|
||||
type TestStorageNode struct {
|
||||
api.StorageMiner
|
||||
|
||||
MineOne func(context.Context) error
|
||||
MineOne func(context.Context, func(bool)) error
|
||||
}
|
||||
|
||||
var PresealGenesis = -1
|
||||
|
||||
type StorageMiner struct {
|
||||
Full int
|
||||
Preseal int
|
||||
}
|
||||
|
||||
// APIBuilder is a function which is invoked in test suite to provide
|
||||
@ -24,7 +31,7 @@ type TestStorageNode struct {
|
||||
//
|
||||
// storage array defines storage nodes, numbers in the array specify full node
|
||||
// index the storage node 'belongs' to
|
||||
type APIBuilder func(t *testing.T, nFull int, storage []int) ([]TestNode, []TestStorageNode)
|
||||
type APIBuilder func(t *testing.T, nFull int, storage []StorageMiner) ([]TestNode, []TestStorageNode)
|
||||
type testSuite struct {
|
||||
makeNodes APIBuilder
|
||||
}
|
||||
@ -39,11 +46,14 @@ func TestApis(t *testing.T, b APIBuilder) {
|
||||
t.Run("id", ts.testID)
|
||||
t.Run("testConnectTwo", ts.testConnectTwo)
|
||||
t.Run("testMining", ts.testMining)
|
||||
t.Run("testMiningReal", ts.testMiningReal)
|
||||
}
|
||||
|
||||
var oneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
|
||||
|
||||
func (ts *testSuite) testVersion(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
v, err := api.Version(ctx)
|
||||
@ -57,7 +67,7 @@ func (ts *testSuite) testVersion(t *testing.T) {
|
||||
|
||||
func (ts *testSuite) testID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
id, err := api.ID(ctx)
|
||||
@ -69,7 +79,7 @@ func (ts *testSuite) testID(t *testing.T) {
|
||||
|
||||
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 2, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 2, oneMiner)
|
||||
|
||||
p, err := apis[0].NetPeers(ctx)
|
||||
if err != nil {
|
||||
|
35
api/types.go
35
api/types.go
@ -6,36 +6,6 @@ import (
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
type DealState = uint64
|
||||
|
||||
const (
|
||||
DealUnknown = DealState(iota)
|
||||
DealRejected // Provider didn't like the proposal
|
||||
DealAccepted // Proposal accepted, data moved
|
||||
DealStaged // Data put into the sector
|
||||
DealSealing // Data in process of being sealed
|
||||
|
||||
DealFailed
|
||||
DealComplete
|
||||
|
||||
// Internal
|
||||
|
||||
DealError // deal failed with an unexpected error
|
||||
|
||||
DealNoUpdate = DealUnknown
|
||||
)
|
||||
|
||||
var DealStates = []string{
|
||||
"DealUnknown",
|
||||
"DealRejected",
|
||||
"DealAccepted",
|
||||
"DealStaged",
|
||||
"DealSealing",
|
||||
"DealFailed",
|
||||
"DealComplete",
|
||||
"DealError",
|
||||
}
|
||||
|
||||
// TODO: check if this exists anywhere else
|
||||
type MultiaddrSlice []ma.Multiaddr
|
||||
|
||||
@ -57,3 +27,8 @@ func (m *MultiaddrSlice) UnmarshalJSON(raw []byte) (err error) {
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = new(MultiaddrSlice)
|
||||
|
||||
type ObjStat struct {
|
||||
Size uint64
|
||||
Links uint64
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
)
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
type SignFunc = func(context.Context, []byte) (*crypto.Signature, error)
|
||||
|
||||
type Signer func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
type Signer func(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
|
||||
type Signable interface {
|
||||
Sign(context.Context, SignFunc) error
|
||||
@ -17,7 +17,7 @@ type Signable interface {
|
||||
|
||||
func SignWith(ctx context.Context, signer Signer, addr address.Address, signable ...Signable) error {
|
||||
for _, s := range signable {
|
||||
err := s.Sign(ctx, func(ctx context.Context, b []byte) (*types.Signature, error) {
|
||||
err := s.Sign(ctx, func(ctx context.Context, b []byte) (*crypto.Signature, error) {
|
||||
return signer(ctx, addr, b)
|
||||
})
|
||||
if err != nil {
|
||||
|
177
bin/dist_get
177
bin/dist_get
@ -1,177 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
GOCC=${GOCC=go}
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
have_binary() {
|
||||
type "$1" > /dev/null 2> /dev/null
|
||||
}
|
||||
|
||||
check_writable() {
|
||||
printf "" > "$1" && rm "$1"
|
||||
}
|
||||
|
||||
try_download() {
|
||||
url="$1"
|
||||
output="$2"
|
||||
command="$3"
|
||||
util_name="$(set -- $command; echo "$1")"
|
||||
|
||||
if ! have_binary "$util_name"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '==> Using %s to download "%s" to "%s"\n' "$util_name" "$url" "$output"
|
||||
if eval "$command"; then
|
||||
echo "==> Download complete!"
|
||||
return
|
||||
else
|
||||
echo "error: couldn't download with $util_name ($?)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
dl_url="$1"
|
||||
dl_output="$2"
|
||||
|
||||
test "$#" -eq "2" || die "download requires exactly two arguments, was given $@"
|
||||
|
||||
if ! check_writable "$dl_output"; then
|
||||
die "download error: cannot write to $dl_output"
|
||||
fi
|
||||
|
||||
try_download "$dl_url" "$dl_output" "wget '$dl_url' -O '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "curl --silent --fail --output '$dl_output' '$dl_url'" && return
|
||||
try_download "$dl_url" "$dl_output" "fetch '$dl_url' -o '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "http '$dl_url' > '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "ftp -o '$dl_output' '$dl_url'" && return
|
||||
|
||||
die "Unable to download $dl_url. exiting."
|
||||
}
|
||||
|
||||
unarchive() {
|
||||
ua_archivetype="$1"
|
||||
ua_infile="$2"
|
||||
ua_outfile="$3"
|
||||
ua_distname="$4"
|
||||
ua_binpostfix=""
|
||||
ua_os=$(uname -o)
|
||||
|
||||
if [ "$ua_os" = "Msys" ] || [ "$ua_os" = "Cygwin" ] ; then
|
||||
ua_binpostfix=".exe"
|
||||
fi
|
||||
ua_outfile="$ua_outfile$ua_binpostfix"
|
||||
|
||||
if ! check_writable "$ua_outfile"; then
|
||||
die "unarchive error: cannot write to $ua_outfile"
|
||||
fi
|
||||
|
||||
case "$ua_archivetype" in
|
||||
tar.gz)
|
||||
if have_binary tar; then
|
||||
echo "==> using 'tar' to extract binary from archive"
|
||||
< "$ua_infile" tar -Ozxf - "$ua_distname/$ua_distname$ua_binpostfix" > "$ua_outfile" \
|
||||
|| die "tar has failed"
|
||||
else
|
||||
die "no binary on system for extracting tar files"
|
||||
fi
|
||||
;;
|
||||
zip)
|
||||
if have_binary unzip; then
|
||||
echo "==> using 'unzip' to extract binary from archive"
|
||||
unzip -p "$ua_infile" "$ua_distname/$ua_distname$ua_binpostfix" > "$ua_outfile" \
|
||||
|| die "unzip has failed"
|
||||
else
|
||||
die "no installed method for extracting .zip archives"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
die "unrecognized archive type '$ua_archivetype'"
|
||||
esac
|
||||
|
||||
chmod +x "$ua_outfile" || die "chmod has failed"
|
||||
}
|
||||
|
||||
get_go_vars() {
|
||||
if [ ! -z "$GOOS" ] && [ ! -z "$GOARCH" ]; then
|
||||
printf "%s-%s" "$GOOS" "$GOARCH"
|
||||
elif have_binary go; then
|
||||
printf "%s-%s" "$($GOCC env GOOS)" "$($GOCC env GOARCH)"
|
||||
else
|
||||
die "no way of determining system GOOS and GOARCH\nPlease manually set GOOS and GOARCH then retry."
|
||||
fi
|
||||
}
|
||||
|
||||
mkurl() {
|
||||
m_root="$1"
|
||||
m_name="$2"
|
||||
m_vers="$3"
|
||||
m_archive="$4"
|
||||
m_govars=$(get_go_vars) || die "could not get go env vars"
|
||||
|
||||
echo "https://ipfs.io$m_root/$m_name/$m_vers/${m_name}_${m_vers}_$m_govars.$m_archive"
|
||||
}
|
||||
|
||||
distroot="$1"
|
||||
distname="$2"
|
||||
outpath="$3"
|
||||
version="$4"
|
||||
|
||||
if [ -z "$distroot" ] || [ -z "$distname" ] || [ -z "$outpath" ] || [ -z "$version" ]; then
|
||||
die "usage: dist_get <distroot> <distname> <outpath> <version>"
|
||||
fi
|
||||
|
||||
case $version in
|
||||
v*)
|
||||
# correct input
|
||||
;;
|
||||
*)
|
||||
echo "invalid version '$version'" >&2
|
||||
die "versions must begin with 'v', for example: v0.4.0"
|
||||
;;
|
||||
esac
|
||||
|
||||
# TODO: don't depend on the go tool being installed to detect this
|
||||
goenv=$(get_go_vars) || die "could not get go env vars"
|
||||
|
||||
case $goenv in
|
||||
linux-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
darwin-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
windows-*)
|
||||
archive="zip"
|
||||
;;
|
||||
freebsd-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
openbsd-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo "unrecognized system environment: $goenv" >&2
|
||||
die "currently only linux, darwin, windows and freebsd are supported by this script"
|
||||
esac
|
||||
|
||||
|
||||
mkdir -p bin/tmp
|
||||
|
||||
url=$(mkurl "$distroot" "$distname" "$version" "$archive")
|
||||
tmpfi="bin/tmp/$distname.$archive"
|
||||
|
||||
download "$url" "$tmpfi"
|
||||
if [ $? -ne 0 ]; then
|
||||
die "failed to download $url to $tmpfi"
|
||||
fi
|
||||
|
||||
unarchive "$archive" "$tmpfi" "$outpath" "$distname"
|
||||
if [ $? -ne 0 ]; then
|
||||
die "failed to extract archive $tmpfi"
|
||||
fi
|
@ -1,6 +1,2 @@
|
||||
/dns4/lotus-bootstrap-0.sin.fil-test.net/tcp/1347/p2p/12D3KooWLZs8BWtEzRTYET4yR4jzDtPamaA1YsyPQJq6cf2RfxBD
|
||||
/dns4/lotus-bootstrap-1.sin.fil-test.net/tcp/1347/p2p/12D3KooWGvrgjWw4Yqo4AFWqYp4g37FpUvUCQBkNWudZVSwR9tY1
|
||||
/dns4/lotus-bootstrap-0.fra.fil-test.net/tcp/1347/p2p/12D3KooWSfNcrD1cs5Cj5eSHbK6nHCqJLffAuPqvRMBRgvUdqQhX
|
||||
/dns4/lotus-bootstrap-1.fra.fil-test.net/tcp/1347/p2p/12D3KooWNkXyVPspUnrHUiSC3VJPMcXvHuNdy3BTCLTPPnDgwwTT
|
||||
/dns4/lotus-bootstrap-0.dfw.fil-test.net/tcp/1347/p2p/12D3KooWSgJWJZK8LTRtCWzPa5FQheCFJjHpficVYgEQWeimcqCu
|
||||
/dns4/lotus-bootstrap-1.dfw.fil-test.net/tcp/1347/p2p/12D3KooWFPaC4dyGpbNXCpVHjZucdJnDwmv4ng9tponPx5GrzJkT
|
||||
/dns4/t01000.miner.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf
|
||||
/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior
|
||||
|
@ -1,9 +1 @@
|
||||
package build
|
||||
|
||||
const ForkBlizzardHeight = 6288
|
||||
|
||||
const ForkFrigidHeight = 7950
|
||||
|
||||
const ForkBootyBayHeight = 11000
|
||||
|
||||
const ForkMissingSnowballs = 34000
|
||||
|
Binary file not shown.
31
build/params_2k.go
Normal file
31
build/params_2k.go
Normal file
@ -0,0 +1,31 @@
|
||||
// +build debug 2k
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 2
|
||||
|
||||
const PropagationDelay = 3
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 20
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
@ -6,30 +6,4 @@ func init() {
|
||||
InsecurePoStValidation = true
|
||||
}
|
||||
|
||||
var SectorSizes = []uint64{1024}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 6
|
||||
|
||||
const PropagationDelay = 3
|
||||
|
||||
// FallbackPoStDelay is the number of epochs the miner needs to wait after
|
||||
// ElectionPeriodStart before starting fallback post computation
|
||||
//
|
||||
// Epochs
|
||||
const FallbackPoStDelay = 10
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 20
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepDelay = 2
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
||||
|
||||
// Bytes
|
||||
var MinimumMinerPower uint64 = 2 << 10 // 2KiB
|
||||
// NOTE: Also includes settings from params_2k
|
||||
|
@ -2,33 +2,49 @@ package build
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
func DefaultSectorSize() abi.SectorSize {
|
||||
szs := make([]abi.SectorSize, 0, len(miner.SupportedProofTypes))
|
||||
for spt := range miner.SupportedProofTypes {
|
||||
ss, err := spt.SectorSize()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
szs = append(szs, ss)
|
||||
}
|
||||
|
||||
sort.Slice(szs, func(i, j int) bool {
|
||||
return szs[i] < szs[i]
|
||||
})
|
||||
|
||||
return szs[0]
|
||||
}
|
||||
|
||||
// Core network constants
|
||||
|
||||
func BlocksTopic(netName dtypes.NetworkName) string { return "/fil/blocks/" + string(netName) }
|
||||
func MessagesTopic(netName dtypes.NetworkName) string { return "/fil/msgs/" + string(netName) }
|
||||
func DhtProtocolName(netName dtypes.NetworkName) protocol.ID {
|
||||
return protocol.ID("/fil/kad/" + string(netName))
|
||||
}
|
||||
|
||||
// /////
|
||||
// Storage
|
||||
|
||||
const UnixfsChunkSize uint64 = 1 << 20
|
||||
const UnixfsLinksPerLevel = 1024
|
||||
|
||||
func SupportedSectorSize(ssize uint64) bool {
|
||||
for _, ss := range SectorSizes {
|
||||
if ssize == ss {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const SectorChallengeRatioDiv = 25
|
||||
|
||||
// /////
|
||||
// Payments
|
||||
|
||||
// Epochs
|
||||
const PaymentChannelClosingDelay = 6 * 60 * 60 / BlockDelay // six hours
|
||||
|
||||
// /////
|
||||
// Consensus / Network
|
||||
|
||||
@ -39,10 +55,10 @@ const AllowableClockDrift = 1
|
||||
const ForkLengthThreshold = Finality
|
||||
|
||||
// Blocks (e)
|
||||
const BlocksPerEpoch = 5
|
||||
var BlocksPerEpoch = uint64(builtin.ExpectedLeadersPerEpoch)
|
||||
|
||||
// Epochs
|
||||
const Finality = 500
|
||||
const Finality = miner.ChainFinalityish
|
||||
|
||||
// constants for Weight calculation
|
||||
// The ratio of weight contributed by short-term vs long-term factors in a given round
|
||||
@ -56,20 +72,18 @@ const WRatioDen = 2
|
||||
const SealRandomnessLookback = Finality
|
||||
|
||||
// Epochs
|
||||
const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000
|
||||
const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000 // TODO: Get from spec specs-actors
|
||||
|
||||
// Maximum lookback that randomness can be sourced from for a seal proof submission
|
||||
const MaxSealLookback = SealRandomnessLookbackLimit + 2000
|
||||
const MaxSealLookback = SealRandomnessLookbackLimit + 2000 // TODO: Get from specs-actors
|
||||
|
||||
// /////
|
||||
// Mining
|
||||
|
||||
// Epochs
|
||||
const EcRandomnessLookback = 300
|
||||
const TicketRandomnessLookback = 1
|
||||
|
||||
const PowerCollateralProportion = 5
|
||||
const PerCapitaCollateralProportion = 1
|
||||
const CollateralPrecision = 1000
|
||||
const WinningPoStSectorSetLookback = 10
|
||||
|
||||
// /////
|
||||
// Devnet settings
|
||||
@ -77,23 +91,15 @@ const CollateralPrecision = 1000
|
||||
const TotalFilecoin = 2_000_000_000
|
||||
const MiningRewardTotal = 1_400_000_000
|
||||
|
||||
const InitialRewardStr = "153856861913558700202"
|
||||
|
||||
var InitialReward *big.Int
|
||||
|
||||
const FilecoinPrecision = 1_000_000_000_000_000_000
|
||||
|
||||
var InitialRewardBalance *big.Int
|
||||
|
||||
// TODO: Move other important consts here
|
||||
|
||||
func init() {
|
||||
InitialReward = new(big.Int)
|
||||
|
||||
var ok bool
|
||||
InitialReward, ok = InitialReward.
|
||||
SetString(InitialRewardStr, 10)
|
||||
if !ok {
|
||||
panic("could not parse InitialRewardStr")
|
||||
}
|
||||
InitialRewardBalance = big.NewInt(MiningRewardTotal)
|
||||
InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(FilecoinPrecision))
|
||||
}
|
||||
|
||||
// Sync
|
||||
@ -106,5 +112,12 @@ const BlsSignatureCacheSize = 40000
|
||||
// ///////
|
||||
// Limits
|
||||
|
||||
// TODO: If this is gonna stay, it should move to specs-actors
|
||||
const BlockMessageLimit = 512
|
||||
const MinerMaxSectors = 1 << 48
|
||||
|
||||
var DrandCoeffs = []string{
|
||||
"82c279cce744450e68de98ee08f9698a01dd38f8e3be3c53f2b840fb9d09ad62a0b6b87981e179e1b14bc9a2d284c985",
|
||||
"82d51308ad346c686f81b8094551597d7b963295cbf313401a93df9baf52d5ae98a87745bee70839a4d6e65c342bd15b",
|
||||
"94eebfd53f4ba6a3b8304236400a12e73885e5a781509a5c8d41d2e8b476923d8ea6052649b3c17282f596217f96c5de",
|
||||
"8dc4231e42b4edf39e86ef1579401692480647918275da767d3e558c520d6375ad953530610fd27daf110187877a65d0",
|
||||
}
|
||||
|
@ -1,33 +1,26 @@
|
||||
// +build !debug
|
||||
// +build !2k
|
||||
|
||||
package build
|
||||
|
||||
var SectorSizes = []uint64{
|
||||
32 << 30,
|
||||
import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(2 << 30)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG512MiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG32GiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG64GiBSeal: {},
|
||||
}
|
||||
}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 45
|
||||
const BlockDelay = builtin.EpochDurationSeconds
|
||||
|
||||
const PropagationDelay = 6
|
||||
|
||||
// FallbackPoStDelay is the number of epochs the miner needs to wait after
|
||||
// ElectionPeriodStart before starting fallback post computation
|
||||
//
|
||||
// Epochs
|
||||
const FallbackPoStDelay = 30
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 200
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepDelay = 8
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
||||
|
||||
// Bytes
|
||||
var MinimumMinerPower uint64 = 512 << 30 // 512GB
|
||||
|
@ -1,103 +1,152 @@
|
||||
{
|
||||
"v20-proof-of-spacetime-election-5f585aca354eb68e411c8582ed0efd800792430e4e76d73468c4fc03f1a8d6d2.params": {
|
||||
"cid": "QmX7tYeNPWae2fjZ3Am6GB9dmHvLqvoz8dKo3PR98VYxH9",
|
||||
"digest": "39a9edec3355516674f0d12b926be493",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": {
|
||||
"cid": "QmYkygifkXnrnsN4MJsjBFHTQJHx294CyikDgDK8nYxdGh",
|
||||
"digest": "df3f30442a6d6b4192f5071fb17e820c",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.vk": {
|
||||
"cid": "QmdXyqbmy2bkJA9Kyhh6z25GrTCq48LwX6c1mxPsm54wi7",
|
||||
"digest": "0bea3951abf9557a3569f68e52a30c6c",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.params": {
|
||||
"cid": "Qmf5XZZtP5VcYTf65MbKjLVabcS6cYMbr2rFShmfJzh5e5",
|
||||
"digest": "655e6277638edc8c658094f6f0b33d54",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.vk": {
|
||||
"cid": "QmPuhdWnAXBks43emnkqi9FQzyU1gASKyz23zrD27BPGs8",
|
||||
"digest": "57690e3a6a94c3f704802a674b34f36b",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.params": {
|
||||
"cid": "QmPNVgTN7N5vDtD5u7ERMTLcvUtrKRBfYVUDr6uW3pKhX7",
|
||||
"digest": "3d390654f58e603b896ac70c653f5676",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.vk": {
|
||||
"cid": "Qmbj61Zez7v5xA7nSCnmWbyLYznWJDWeusz7Yg8EcgVdoN",
|
||||
"digest": "8c170a164743c39576a7f47a1b51e6f3",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.params": {
|
||||
"cid": "QmRApb8RZoBK3cqicT7V3ydXg8yVvqPFMPrQNXP33aBihp",
|
||||
"digest": "b1b58ff9a297b82885e8a7dfb035f83c",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.vk": {
|
||||
"cid": "QmcytF1dTdqMFoyXi931j1RgmGtLfR9LLLaBznRt1tPQyD",
|
||||
"digest": "1a09e00c641f192f55af3433a028f050",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.params": {
|
||||
"cid": "QmPvr54tWaVeP4WnekivzUAJitTqsQfvikBvAHNEaDNQSw",
|
||||
"digest": "9380e41368ed4083dbc922b290d3b786",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.vk": {
|
||||
"cid": "QmXyVLVDRCcxA9SjT7PeK8HFtyxZ2ZH3SHa8KoGLw8VGJt",
|
||||
"digest": "f0731a7e20f90704bd38fc5d27882f6d",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.params": {
|
||||
"cid": "Qmf5f6ko3dqj7qauzXpZqxM9B2x2sL977K6gE2ppNwuJPv",
|
||||
"digest": "273ebb8c896326b7c292bee8b775fd38",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.vk": {
|
||||
"cid": "QmfP3MQe8koW63n5MkDENENVHxib78MJYYyZvbneCsuze8",
|
||||
"digest": "3dd94da9da64e51b3445bc528d84e76d",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.params": {
|
||||
"cid": "QmYEeeCE8uT2bsVkxcqqUYeMmMEbe6rfmo8wQCv7jFHqqm",
|
||||
"digest": "c947f2021304ed43b7216f7a8436e294",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-proof-of-spacetime-election-5f585aca354eb68e411c8582ed0efd800792430e4e76d73468c4fc03f1a8d6d2.vk": {
|
||||
"cid": "QmbNGx7pNbGiEr8ykoHxVXHW2LNSmGdsxKtj1onZCyguCX",
|
||||
"digest": "0227ae7df4f2affe529ebafbbc7540ee",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.vk": {
|
||||
"cid": "QmXB63ExriFjB4ywWnXTnFwCcLFfCeEP3h15qtL5i7F4aX",
|
||||
"digest": "ab20d7b253e7e9a0d2ccdf7599ec8ec3",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a4e18190d4b4657ba1b4d08a341871b2a6f398e327cb9951b28ab141fbdbf49d.params": {
|
||||
"cid": "QmRGZsNp4mp1cZshcXqt3VMuWscAEsiMa2iepF4CsWWoiv",
|
||||
"digest": "991041a354b12c280542741f58c7f2ca",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a4e18190d4b4657ba1b4d08a341871b2a6f398e327cb9951b28ab141fbdbf49d.vk": {
|
||||
"cid": "QmWpmrhCGVcfqLyqp5oGAnhPmCE5hGTPaauHi25mpQwRSU",
|
||||
"digest": "91fac550e1f9bccab213830bb0c85bd6",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a9eb6d90b896a282ec2d3a875c6143e3fcff778f0da1460709e051833651559b.params": {
|
||||
"cid": "QmenSZXh1EsSyHiSRvA6wb8yaPhYBTjrKehJw96Px5HnN4",
|
||||
"digest": "6322eacd2773163ddd51f9ca7d645fc4",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a9eb6d90b896a282ec2d3a875c6143e3fcff778f0da1460709e051833651559b.vk": {
|
||||
"cid": "QmPvZoMKofw6eDhDg5ESJA2QAZP8HvM6qMQk7fw4pq9bQf",
|
||||
"digest": "0df62745fceac922e3e70847cfc70b52",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v20-proof-of-spacetime-election-bf872523641b1de33553db2a177df13e412d7b3b0103e6696ae0a1cf5d525259.params": {
|
||||
"cid": "QmVibFqzkZoL8cwQmzj8njPokCQGCCx4pBcUH77bzgJgV9",
|
||||
"digest": "de9d71e672f286706a1673bd57abdaac",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v20-proof-of-spacetime-election-bf872523641b1de33553db2a177df13e412d7b3b0103e6696ae0a1cf5d525259.vk": {
|
||||
"cid": "QmZa5FX27XyiEXQQLQpHqtMJKLzrcY8wMuj3pxzmSimSyu",
|
||||
"digest": "7f796d3a0f13499181e44b5eee0cc744",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v20-proof-of-spacetime-election-ffc3fb192364238b60977839d14e3154d4a98313e30d46694a12af54b6874975.params": {
|
||||
"cid": "Qmbt2SWWAmMcYoY3DAiRDXA8fAuqdqRLWucJMSxYmzBCmN",
|
||||
"digest": "151ae0ae183fc141e8c2bebc28e5cc10",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-proof-of-spacetime-election-ffc3fb192364238b60977839d14e3154d4a98313e30d46694a12af54b6874975.vk": {
|
||||
"cid": "QmUxvPu4xdVmjMFihUKoYyEdXBqxsXkvmxRweU7KouWHji",
|
||||
"digest": "95eb89588e9d1832aca044c3a13178af",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-117839dacd1ef31e5968a6fd13bcd6fa86638d85c40c9241a1d07c2a954eb89b.params": {
|
||||
"cid": "QmQZe8eLo2xXbhSDxtyYZNqEjqjdcWGdADywECRvNEZQdX",
|
||||
"digest": "fcd50e2e08a8560a6bb3418e883567ed",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-117839dacd1ef31e5968a6fd13bcd6fa86638d85c40c9241a1d07c2a954eb89b.vk": {
|
||||
"cid": "Qme1hn6QT1covfoUFGDZkqoE1pMTax9FNW3nWWmTNqFe7y",
|
||||
"digest": "872e244d86499fd659082e3bcf3f13e7",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-b46f3a1051afbb67f70aae7082da95def62eee943662f3e1bf69837fb08aaae4.params": {
|
||||
"cid": "QmSfrPDC9jwY4MKrjzhCqDBBAG44wSDM8oE5NuDwWSh2xN",
|
||||
"digest": "0a338b941c5f17946340de5fc95cab30",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.params": {
|
||||
"cid": "QmW5Yxg3L1NSzuQVcRMHMbG3uvVoi4dTLzVaDpnEUPQpnA",
|
||||
"digest": "079ba19645828ae42b22b0e3f4866e8d",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-stacked-proof-of-replication-b46f3a1051afbb67f70aae7082da95def62eee943662f3e1bf69837fb08aaae4.vk": {
|
||||
"cid": "QmTDGynCmnbaZNBP3Bv3F3duC3ecKRubCKeMUiQQZYbGpF",
|
||||
"digest": "c752e070a6b7aa8b79aa661a6b600b55",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.vk": {
|
||||
"cid": "QmQzZ5dJ11tcSBees38WX41tZLXS9BqpEti253m5QcnTNs",
|
||||
"digest": "c76125a50a7de315165de359b5174ae4",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e71093863cadc71de61f38311ee45816633973bbf34849316b147f8d2e66f199.params": {
|
||||
"cid": "QmXjSSnMUnc7EjQBYtTHhvLU3kXJTbUyhVhJRSTRehh186",
|
||||
"digest": "efa407fd09202dffd15799a8518e73d3",
|
||||
"sector_size": 1024
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.params": {
|
||||
"cid": "QmNk3wga1tS53FUu1QnkK8ehWA2cqpCnSEAPv3KLxdJxNa",
|
||||
"digest": "421e4790c0b80e0107a7ff67acf14084",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e71093863cadc71de61f38311ee45816633973bbf34849316b147f8d2e66f199.vk": {
|
||||
"cid": "QmYHW3zhQouDP4okFbXSsRMcZ8bokKGvzxqbv7ZrunPMiG",
|
||||
"digest": "b2f09a0ccb62da28c890d5b881c8dcd2",
|
||||
"sector_size": 1024
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.vk": {
|
||||
"cid": "QmVQCHGsrUtbn9RjHs1e6GXfeXDW5m9w4ge48PSX3Z2as2",
|
||||
"digest": "8b60e9cc1470a6729c687d6cf0a1f79c",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e99a585174b6a45b254ba4780d72c89ad808c305c6d11711009ade4f39dba8e9.params": {
|
||||
"cid": "QmUhyfNeLb32LfSkjsUwTFYLXQGMj6JQ8daff4DdVMt79q",
|
||||
"digest": "b53c1916a63839ec345aa2224e9198b7",
|
||||
"sector_size": 1073741824
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.params": {
|
||||
"cid": "QmTL3VvydaMFWKvE5VzxjgKsJYgL9JMM4JVYNtQxdj9JK1",
|
||||
"digest": "2685f31124b22ea6b2857e5a5e87ffa3",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e99a585174b6a45b254ba4780d72c89ad808c305c6d11711009ade4f39dba8e9.vk": {
|
||||
"cid": "QmWReGfbuoozNErbskmFvqV4q36BY6F2WWb4cVFc3zoYkA",
|
||||
"digest": "20d58a3fae7343481f8298a2dd493dd7",
|
||||
"sector_size": 1073741824
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.vk": {
|
||||
"cid": "QmSVWbLqQYbUbbJyfsRMzEib2rfSqMtnPks1Nw22omcBQm",
|
||||
"digest": "efe703cd2839597c7ca5c2a906b74296",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-f571ee2386f4c65a68e802747f2d78691006fc81a67971c4d9641403fffece16.params": {
|
||||
"cid": "QmSAHu14Pe8iav6BYCt9XkpHJ73XM7tcpY4d9JK9BST9HU",
|
||||
"digest": "7698426202c7e07b26ef056d31485b3a",
|
||||
"sector_size": 16777216
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.params": {
|
||||
"cid": "QmU9dH31nZZUJnsogR4Ld4ySUcH6wm2RgmGiujwnqtbU6k",
|
||||
"digest": "fcef8e87ae2afd7a28aae44347b804cf",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v20-stacked-proof-of-replication-f571ee2386f4c65a68e802747f2d78691006fc81a67971c4d9641403fffece16.vk": {
|
||||
"cid": "QmaKtFLShnhMGVn7P9UsHjkgqtqRFSwCStqqykBN7u8dax",
|
||||
"digest": "834408e5c3fce6ec5d1bf64e64cee94e",
|
||||
"sector_size": 16777216
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.vk": {
|
||||
"cid": "QmdJ15DMGPooye5NaPcRfXUdHUDibcN7hKjbmTGuu1K4AQ",
|
||||
"digest": "2ee2b3518229680db15161d4f582af37",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.params": {
|
||||
"cid": "QmZgtxcY3tMXXQxZTA7ZTUDXLVUnfxNcerXgeW4gG2NnfP",
|
||||
"digest": "3273c7135cb75684248b475781b738ee",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.vk": {
|
||||
"cid": "QmSS6ZkAV2aGZcgKgdPpEEgihXF1ryZX8PSAZDWSoeL1d4",
|
||||
"digest": "1519b5f61d9044a59f2bdc57537c094b",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.params": {
|
||||
"cid": "QmQBGXeiNn6hVwbR6qFarQqiNGDdKk4h9ucfyvcXyfYz2N",
|
||||
"digest": "7d5f896f435c38e93bcda6dd168d860b",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.vk": {
|
||||
"cid": "QmPrZgBVGMckEAeu5eSJnLmiAwcPQjKjZe5ir6VaQ5AxKs",
|
||||
"digest": "fe6d2de44580a0db5a4934688899b92f",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.params": {
|
||||
"cid": "QmZL2cq45XJn5BFzagAZwgFmLrcM1W6CXoiEF9C5j5tjEF",
|
||||
"digest": "acdfed9f0512bc85a01a9fb871d475d5",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.vk": {
|
||||
"cid": "QmQ4zB7nNa1tDYNifBkExRnZtwtxZw775iaqvVsZyRi6Q2",
|
||||
"digest": "524a2f3e9d6826593caebc41bb545c40",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.params": {
|
||||
"cid": "QmY7DitNKXFeLQt9QoVQkfjM1EvRnprqUVxjmkTXkHDNka",
|
||||
"digest": "f27271c0537ba65ade2ec045f8fbd069",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.vk": {
|
||||
"cid": "QmUJsvoCuQ4LszPmeRVAkMYb5qY95ctz3UXKhu8xLzyFKo",
|
||||
"digest": "576b292938c6c9d0a0e721bd867a543b",
|
||||
"sector_size": 68719476736
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@ import "fmt"
|
||||
var CurrentCommit string
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.2.8"
|
||||
const BuildVersion = "0.3.0"
|
||||
|
||||
var UserVersion = BuildVersion + CurrentCommit
|
||||
|
||||
@ -31,7 +31,7 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
|
||||
}
|
||||
|
||||
// APIVersion is a semver version of the rpc api exposed
|
||||
var APIVersion Version = newVer(0, 1, 7)
|
||||
var APIVersion Version = newVer(0, 3, 0)
|
||||
|
||||
const (
|
||||
majorMask = 0xff0000
|
||||
|
@ -1,48 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type CronActor struct{}
|
||||
|
||||
type callTuple struct {
|
||||
addr address.Address
|
||||
method uint64
|
||||
}
|
||||
|
||||
var CronActors = []callTuple{
|
||||
{StoragePowerAddress, SPAMethods.CheckProofSubmissions},
|
||||
}
|
||||
|
||||
type CronActorState struct{}
|
||||
|
||||
type cAMethods struct {
|
||||
EpochTick uint64
|
||||
}
|
||||
|
||||
var CAMethods = cAMethods{2}
|
||||
|
||||
func (ca CronActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: nil,
|
||||
2: ca.EpochTick,
|
||||
}
|
||||
}
|
||||
|
||||
func (ca CronActor) EpochTick(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != CronAddress {
|
||||
return nil, aerrors.New(1, "EpochTick is only callable as a part of tipset state computation")
|
||||
}
|
||||
|
||||
for _, call := range CronActors {
|
||||
_, err := vmctx.Send(call.addr, call.method, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, err // todo: this very bad?
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -1,239 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var log = logging.Logger("actors")
|
||||
|
||||
var EmptyCBOR cid.Cid
|
||||
|
||||
const (
|
||||
GasCreateActor = 100
|
||||
)
|
||||
|
||||
var BuiltInActors map[cid.Cid]bool
|
||||
|
||||
func init() {
|
||||
|
||||
n, err := cbor.WrapObject(map[string]string{}, mh.SHA2_256, -1)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
EmptyCBOR = n.Cid()
|
||||
}
|
||||
|
||||
type InitActor struct{}
|
||||
|
||||
type InitActorState struct {
|
||||
AddressMap cid.Cid
|
||||
|
||||
NextID uint64
|
||||
}
|
||||
|
||||
type iAMethods struct {
|
||||
Exec uint64
|
||||
}
|
||||
|
||||
var IAMethods = iAMethods{2}
|
||||
|
||||
func (ia InitActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: nil,
|
||||
2: ia.Exec,
|
||||
}
|
||||
}
|
||||
|
||||
type ExecParams struct {
|
||||
Code cid.Cid
|
||||
Params []byte
|
||||
}
|
||||
|
||||
func CreateExecParams(act cid.Cid, obj cbg.CBORMarshaler) ([]byte, aerrors.ActorError) {
|
||||
encparams, err := SerializeParams(obj)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "creating ExecParams")
|
||||
}
|
||||
|
||||
return SerializeParams(&ExecParams{
|
||||
Code: act,
|
||||
Params: encparams,
|
||||
})
|
||||
}
|
||||
|
||||
func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams) ([]byte, aerrors.ActorError) {
|
||||
beginState := vmctx.Storage().GetHead()
|
||||
|
||||
var self InitActorState
|
||||
if err := vmctx.Storage().Get(beginState, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.ChargeGas(GasCreateActor); err != nil {
|
||||
return nil, aerrors.Wrap(err, "run out of gas")
|
||||
}
|
||||
|
||||
// Make sure that only the actors defined in the spec can be launched.
|
||||
if !IsBuiltinActor(p.Code) {
|
||||
return nil, aerrors.New(1,
|
||||
"cannot launch actor instance that is not a builtin actor")
|
||||
}
|
||||
|
||||
// Ensure that singletons can be only launched once.
|
||||
// TODO: do we want to enforce this? If so how should actors be marked as such?
|
||||
if IsSingletonActor(p.Code) {
|
||||
return nil, aerrors.New(1, "cannot launch another actor of this type")
|
||||
}
|
||||
|
||||
// This generates a unique address for this actor that is stable across message
|
||||
// reordering
|
||||
creator := vmctx.Message().From
|
||||
nonce := vmctx.Message().Nonce
|
||||
addr, err := ComputeActorAddress(creator, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set up the actor itself
|
||||
actor := types.Actor{
|
||||
Code: p.Code,
|
||||
Balance: types.NewInt(0),
|
||||
Head: EmptyCBOR,
|
||||
Nonce: 0,
|
||||
}
|
||||
|
||||
// The call to the actors constructor will set up the initial state
|
||||
// from the given parameters, setting `actor.Head` to a new value when successful.
|
||||
// TODO: can constructors fail?
|
||||
//actor.Constructor(p.Params)
|
||||
|
||||
// Store the mapping of address to actor ID.
|
||||
idAddr, nerr := self.AddActor(vmctx.Ipld(), addr)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Escalate(err, "adding new actor mapping")
|
||||
}
|
||||
|
||||
// NOTE: This is a privileged call that only the init actor is allowed to make
|
||||
// FIXME: Had to comment this because state is not in interface
|
||||
state, err := vmctx.StateTree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := state.SetActor(idAddr, &actor); err != nil {
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
return nil, aerrors.Absorb(err, 1, "SetActor, actor not found")
|
||||
}
|
||||
return nil, aerrors.Escalate(err, "inserting new actor into state tree")
|
||||
}
|
||||
|
||||
// '1' is reserved for constructor methods
|
||||
_, err = vmctx.Send(idAddr, 1, vmctx.Message().Value, p.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(beginState, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return idAddr.Bytes(), nil
|
||||
}
|
||||
|
||||
func IsBuiltinActor(code cid.Cid) bool {
|
||||
return BuiltInActors[code]
|
||||
}
|
||||
|
||||
func IsSingletonActor(code cid.Cid) bool {
|
||||
return code == StoragePowerCodeCid || code == StorageMarketCodeCid || code == InitCodeCid || code == CronCodeCid
|
||||
}
|
||||
|
||||
func (ias *InitActorState) AddActor(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
||||
nid := ias.NextID
|
||||
|
||||
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if err := amap.Set(context.TODO(), string(addr.Bytes()), nid); err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if err := amap.Flush(context.TODO()); err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
ncid, err := cst.Put(context.TODO(), amap)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
ias.AddressMap = ncid
|
||||
ias.NextID++
|
||||
|
||||
return NewIDAddress(nid)
|
||||
}
|
||||
|
||||
func (ias *InitActorState) Lookup(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
||||
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("ias lookup failed loading hamt node: %w", err)
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
err = amap.Find(context.TODO(), string(addr.Bytes()), &val)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("ias lookup failed to do find: %w", err)
|
||||
}
|
||||
|
||||
ival, ok := val.(uint64)
|
||||
if !ok {
|
||||
return address.Undef, fmt.Errorf("invalid value in init actor state, expected uint64, got %T", val)
|
||||
}
|
||||
|
||||
return address.NewIDAddress(ival)
|
||||
}
|
||||
|
||||
type AccountActorState struct {
|
||||
Address address.Address
|
||||
}
|
||||
|
||||
func ComputeActorAddress(creator address.Address, nonce uint64) (address.Address, ActorError) {
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.Write(creator.Bytes())
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not write address")
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.BigEndian, nonce)
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not write nonce")
|
||||
}
|
||||
|
||||
addr, err := address.NewActorAddress(buf.Bytes())
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not create address")
|
||||
}
|
||||
return addr, nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,903 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
amt2 "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type StorageMinerActor2 struct{}
|
||||
|
||||
func (sma StorageMinerActor2) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: sma.StorageMinerConstructor,
|
||||
2: sma.PreCommitSector,
|
||||
3: sma.ProveCommitSector,
|
||||
4: sma.SubmitFallbackPoSt,
|
||||
//5: sma.SlashStorageFault,
|
||||
//6: sma.GetCurrentProvingSet,
|
||||
//7: sma.ArbitrateDeal,
|
||||
//8: sma.DePledge,
|
||||
9: sma.GetOwner,
|
||||
10: sma.GetWorkerAddr,
|
||||
11: withUpdates(
|
||||
update{0, sma.GetPower},
|
||||
update{build.ForkMissingSnowballs, sma.GetPower2},
|
||||
), // FORK
|
||||
12: sma.GetPeerID,
|
||||
13: sma.GetSectorSize,
|
||||
14: sma.UpdatePeerID,
|
||||
//15: sma.ChangeWorker,
|
||||
16: sma.IsSlashed,
|
||||
17: sma.CheckMiner,
|
||||
18: sma.DeclareFaults,
|
||||
19: sma.SlashConsensusFault,
|
||||
20: sma.SubmitElectionPoSt,
|
||||
}
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) StorageMinerConstructor(act *types.Actor, vmctx types.VMContext, params *StorageMinerConstructorParams) ([]byte, ActorError) {
|
||||
minerInfo := &MinerInfo{
|
||||
Owner: params.Owner,
|
||||
Worker: params.Worker,
|
||||
PeerID: params.PeerID,
|
||||
SectorSize: params.SectorSize,
|
||||
}
|
||||
|
||||
minfocid, err := vmctx.Storage().Put(minerInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var self StorageMinerActorState
|
||||
sectors := amt2.NewAMT(types.WrapStorage(vmctx.Storage()))
|
||||
scid, serr := sectors.Flush()
|
||||
if serr != nil {
|
||||
return nil, aerrors.HandleExternalError(serr, "initializing AMT")
|
||||
}
|
||||
|
||||
self.Sectors = scid
|
||||
self.ProvingSet = scid
|
||||
self.Info = minfocid
|
||||
|
||||
storage := vmctx.Storage()
|
||||
c, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := storage.Commit(EmptyCBOR, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) PreCommitSector(act *types.Actor, vmctx types.VMContext, params *SectorPreCommitInfo) ([]byte, ActorError) {
|
||||
|
||||
ctx := vmctx.Context()
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.SealEpoch >= vmctx.BlockHeight()+build.SealRandomnessLookback {
|
||||
return nil, aerrors.Newf(1, "sector commitment must be based off past randomness (%d >= %d)", params.SealEpoch, vmctx.BlockHeight()+build.SealRandomnessLookback)
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight()-params.SealEpoch+build.SealRandomnessLookback > build.SealRandomnessLookbackLimit {
|
||||
return nil, aerrors.Newf(2, "sector commitment must be recent enough (was %d)", vmctx.BlockHeight()-params.SealEpoch+build.SealRandomnessLookback)
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != mi.Worker {
|
||||
return nil, aerrors.New(1, "not authorized to precommit sector for miner")
|
||||
}
|
||||
|
||||
// make sure the miner isnt trying to submit a pre-existing sector
|
||||
unique, err := SectorIsUnique(ctx, vmctx.Storage(), self.Sectors, params.SectorNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !unique {
|
||||
return nil, aerrors.New(3, "sector already committed!")
|
||||
}
|
||||
|
||||
// Power of the miner after adding this sector
|
||||
futurePower := types.BigAdd(self.Power, types.NewInt(mi.SectorSize))
|
||||
collateralRequired := CollateralForPower(futurePower)
|
||||
|
||||
// TODO: grab from market?
|
||||
if act.Balance.LessThan(collateralRequired) {
|
||||
return nil, aerrors.New(4, "not enough collateral")
|
||||
}
|
||||
|
||||
self.PreCommittedSectors[uintToStringKey(params.SectorNumber)] = &PreCommittedSector{
|
||||
Info: *params,
|
||||
ReceivedEpoch: vmctx.BlockHeight(),
|
||||
}
|
||||
|
||||
if len(self.PreCommittedSectors) > 4096 {
|
||||
return nil, aerrors.New(5, "too many precommitted sectors")
|
||||
}
|
||||
|
||||
nstate, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) ProveCommitSector(act *types.Actor, vmctx types.VMContext, params *SectorProveCommitInfo) ([]byte, ActorError) {
|
||||
ctx := vmctx.Context()
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != mi.Worker {
|
||||
return nil, aerrors.New(1, "not authorized to submit sector proof for miner")
|
||||
}
|
||||
|
||||
us, ok := self.PreCommittedSectors[uintToStringKey(params.SectorID)]
|
||||
if !ok {
|
||||
return nil, aerrors.New(1, "no pre-commitment found for sector")
|
||||
}
|
||||
|
||||
if us.ReceivedEpoch+build.InteractivePoRepDelay >= vmctx.BlockHeight() {
|
||||
return nil, aerrors.New(2, "too early for proof submission")
|
||||
}
|
||||
|
||||
delete(self.PreCommittedSectors, uintToStringKey(params.SectorID))
|
||||
|
||||
// TODO: ensure normalization to ID address
|
||||
maddr := vmctx.Message().To
|
||||
|
||||
if vmctx.BlockHeight()-us.Info.SealEpoch > build.MaxSealLookback {
|
||||
return nil, aerrors.Newf(5, "source randomness for sector SealEpoch too far in past (epoch %d)", us.Info.SealEpoch)
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight()-us.ReceivedEpoch > build.MaxSealLookback {
|
||||
return nil, aerrors.Newf(6, "source randomness for sector ReceivedEpoch too far in past (epoch %d)", us.ReceivedEpoch)
|
||||
}
|
||||
|
||||
ticket, err := vmctx.GetRandomness(us.Info.SealEpoch - build.SealRandomnessLookback)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to get ticket randomness")
|
||||
}
|
||||
|
||||
seed, err := vmctx.GetRandomness(us.ReceivedEpoch + build.InteractivePoRepDelay)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to get randomness for prove sector commitment")
|
||||
}
|
||||
|
||||
enc, err := SerializeParams(&ComputeDataCommitmentParams{
|
||||
DealIDs: params.DealIDs,
|
||||
SectorSize: mi.SectorSize,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to serialize ComputeDataCommitmentParams")
|
||||
}
|
||||
|
||||
commD, err := vmctx.Send(StorageMarketAddress, SMAMethods.ComputeDataCommitment, types.NewInt(0), enc)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrapf(err, "failed to compute data commitment (sector %d, deals: %v)", params.SectorID, params.DealIDs)
|
||||
}
|
||||
|
||||
if ok, err := vmctx.Sys().ValidatePoRep(ctx, maddr, mi.SectorSize, commD, us.Info.CommR, ticket, params.Proof, seed, params.SectorID); err != nil {
|
||||
return nil, err
|
||||
} else if !ok {
|
||||
return nil, aerrors.Newf(2, "porep proof was invalid (t:%x; s:%x(%d); p:%s)", ticket, seed, us.ReceivedEpoch+build.InteractivePoRepDelay, truncateHexPrint(params.Proof))
|
||||
}
|
||||
|
||||
// Note: There must exist a unique index in the miner's sector set for each
|
||||
// sector ID. The `faults`, `recovered`, and `done` parameters of the
|
||||
// SubmitPoSt method express indices into this sector set.
|
||||
nssroot, err := AddToSectorSet2(ctx, types.WrapStorage(vmctx.Storage()), self.Sectors, params.SectorID, us.Info.CommR, commD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.Sectors = nssroot
|
||||
|
||||
// if miner is not mining, start their proving period now
|
||||
// Note: As written here, every miners first PoSt will only be over one sector.
|
||||
// We could set up a 'grace period' for starting mining that would allow miners
|
||||
// to submit several sectors for their first proving period. Alternatively, we
|
||||
// could simply make the 'PreCommitSector' call take multiple sectors at a time.
|
||||
//
|
||||
// Note: Proving period is a function of sector size; small sectors take less
|
||||
// time to prove than large sectors do. Sector size is selected when pledging.
|
||||
pss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
|
||||
}
|
||||
|
||||
if pss.Count == 0 && !self.Active {
|
||||
self.ProvingSet = self.Sectors
|
||||
// TODO: probably want to wait until the miner is above a certain
|
||||
// threshold before starting this
|
||||
self.ElectionPeriodStart = vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
nstate, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
activateParams, err := SerializeParams(&ActivateStorageDealsParams{
|
||||
Deals: params.DealIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(StorageMarketAddress, SMAMethods.ActivateStorageDeals, types.NewInt(0), activateParams)
|
||||
return nil, aerrors.Wrapf(err, "calling ActivateStorageDeals failed")
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) SubmitFallbackPoSt(act *types.Actor, vmctx types.VMContext, params *SubmitFallbackPoStParams) ([]byte, ActorError) {
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != mi.Worker {
|
||||
return nil, aerrors.New(1, "not authorized to submit post for miner")
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: handle fees
|
||||
msgVal := vmctx.Message().Value
|
||||
if msgVal.LessThan(feesRequired) {
|
||||
return nil, aerrors.New(2, "not enough funds to pay post submission fees")
|
||||
}
|
||||
|
||||
if msgVal.GreaterThan(feesRequired) {
|
||||
_, err := vmctx.Send(vmctx.Message().From, 0,
|
||||
types.BigSub(msgVal, feesRequired), nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not refund excess fees")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
var seed [sectorbuilder.CommLen]byte
|
||||
{
|
||||
randHeight := self.ElectionPeriodStart + build.FallbackPoStDelay
|
||||
if vmctx.BlockHeight() <= randHeight {
|
||||
// TODO: spec, retcode
|
||||
return nil, aerrors.Newf(1, "submit fallback PoSt called too early (%d < %d)", vmctx.BlockHeight(), randHeight)
|
||||
}
|
||||
|
||||
rand, err := vmctx.GetRandomness(randHeight)
|
||||
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get randomness for PoST")
|
||||
}
|
||||
if len(rand) < len(seed) {
|
||||
return nil, aerrors.Escalate(fmt.Errorf("randomness too small (%d < %d)",
|
||||
len(rand), len(seed)), "improper randomness")
|
||||
}
|
||||
copy(seed[:], rand)
|
||||
}
|
||||
|
||||
pss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
|
||||
}
|
||||
|
||||
ss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
|
||||
}
|
||||
|
||||
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(err, 5, "RLE+ invalid")
|
||||
}
|
||||
|
||||
activeFaults := uint64(0)
|
||||
var sectorInfos []types.PublicSectorInfo
|
||||
if err := pss.ForEach(func(id uint64, v *cbg.Deferred) error {
|
||||
if faults[id] {
|
||||
activeFaults++
|
||||
return nil
|
||||
}
|
||||
|
||||
var comms [][]byte
|
||||
if err := cbor.DecodeInto(v.Raw, &comms); err != nil {
|
||||
return xerrors.New("could not decode comms")
|
||||
}
|
||||
si := types.PublicSectorInfo{
|
||||
SectorID: id,
|
||||
}
|
||||
commR := comms[0]
|
||||
if len(commR) != len(si.CommR) {
|
||||
return xerrors.Errorf("commR length is wrong: %d", len(commR))
|
||||
}
|
||||
copy(si.CommR[:], commR)
|
||||
|
||||
sectorInfos = append(sectorInfos, si)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, aerrors.Absorb(err, 3, "could not decode sectorset")
|
||||
}
|
||||
|
||||
proverID := vmctx.Message().To // TODO: normalize to ID address
|
||||
|
||||
var candidates []types.Candidate
|
||||
for _, t := range params.Candidates {
|
||||
var partial [32]byte
|
||||
copy(partial[:], t.Partial)
|
||||
candidates = append(candidates, types.Candidate{
|
||||
PartialTicket: partial,
|
||||
SectorID: t.SectorID,
|
||||
SectorChallengeIndex: t.ChallengeIndex,
|
||||
})
|
||||
}
|
||||
|
||||
if ok, lerr := vmctx.Sys().VerifyFallbackPost(vmctx.Context(), mi.SectorSize,
|
||||
sectorInfos, seed[:], params.Proof, candidates, proverID, activeFaults); !ok || lerr != nil {
|
||||
if lerr != nil {
|
||||
// TODO: study PoST errors
|
||||
return nil, aerrors.Absorb(lerr, 4, "PoST error")
|
||||
}
|
||||
if !ok {
|
||||
return nil, aerrors.New(4, "PoST invalid")
|
||||
}
|
||||
}
|
||||
|
||||
// Post submission is successful!
|
||||
if err := onSuccessfulPoSt2(self, vmctx, activeFaults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(oldstate, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetPower(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.Power.Bytes(), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetPower2(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.SlashedAt != 0 {
|
||||
return types.NewInt(0).Bytes(), nil
|
||||
}
|
||||
|
||||
return self.Power.Bytes(), nil
|
||||
}
|
||||
|
||||
func SectorIsUnique2(ctx context.Context, s types.Storage, sroot cid.Cid, sid uint64) (bool, ActorError) {
|
||||
found, _, _, err := GetFromSectorSet2(ctx, s, sroot, sid)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return !found, nil
|
||||
}
|
||||
|
||||
func AddToSectorSet2(ctx context.Context, blks amt2.Blocks, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) {
|
||||
if sectorID >= build.MinerMaxSectors {
|
||||
return cid.Undef, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
|
||||
}
|
||||
ssr, err := amt2.LoadAMT(blks, ss)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "could not load sector set node")
|
||||
}
|
||||
|
||||
// TODO: Spec says to use SealCommitment, and construct commD from deals each time,
|
||||
// but that would make SubmitPoSt way, way more expensive
|
||||
if err := ssr.Set(sectorID, [][]byte{commR, commD}); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to set commitment in sector set")
|
||||
}
|
||||
|
||||
ncid, err := ssr.Flush()
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush sector set")
|
||||
}
|
||||
|
||||
return ncid, nil
|
||||
}
|
||||
|
||||
func GetFromSectorSet2(ctx context.Context, s types.Storage, ss cid.Cid, sectorID uint64) (bool, []byte, []byte, ActorError) {
|
||||
if sectorID >= build.MinerMaxSectors {
|
||||
return false, nil, nil, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
|
||||
}
|
||||
|
||||
ssr, err := amt2.LoadAMT(types.WrapStorage(s), ss)
|
||||
if err != nil {
|
||||
return false, nil, nil, aerrors.HandleExternalError(err, "could not load sector set node")
|
||||
}
|
||||
|
||||
var comms [][]byte
|
||||
err = ssr.Get(sectorID, &comms)
|
||||
if err != nil {
|
||||
if _, ok := err.(*amt2.ErrNotFound); ok {
|
||||
return false, nil, nil, nil
|
||||
}
|
||||
return false, nil, nil, aerrors.HandleExternalError(err, "failed to find sector in sector set")
|
||||
}
|
||||
|
||||
if len(comms) != 2 {
|
||||
return false, nil, nil, aerrors.Newf(20, "sector set entry should only have 2 elements")
|
||||
}
|
||||
|
||||
return true, comms[0], comms[1], nil
|
||||
}
|
||||
|
||||
func RemoveFromSectorSet2(ctx context.Context, s types.Storage, ss cid.Cid, ids []uint64) (cid.Cid, aerrors.ActorError) {
|
||||
|
||||
ssr, err := amt2.LoadAMT(types.WrapStorage(s), ss)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "could not load sector set node")
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if err := ssr.Delete(id); err != nil {
|
||||
log.Warnf("failed to delete sector %d from set: %s", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
ncid, err := ssr.Flush()
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush sector set")
|
||||
}
|
||||
|
||||
return ncid, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetWorkerAddr(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mi.Worker.Bytes(), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetOwner(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mi.Owner.Bytes(), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetPeerID(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(mi.PeerID), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) UpdatePeerID(act *types.Actor, vmctx types.VMContext, params *UpdatePeerIDParams) ([]byte, ActorError) {
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != mi.Worker {
|
||||
return nil, aerrors.New(2, "only the mine worker may update the peer ID")
|
||||
}
|
||||
|
||||
mi.PeerID = params.PeerID
|
||||
|
||||
mic, err := vmctx.Storage().Put(mi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Info = mic
|
||||
|
||||
c, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(oldstate, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) GetSectorSize(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, err := loadMinerInfo(vmctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.NewInt(mi.SectorSize).Bytes(), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) IsSlashed(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
_, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cbg.EncodeBool(self.SlashedAt != 0), nil
|
||||
}
|
||||
|
||||
// TODO: better name
|
||||
func (sma StorageMinerActor2) CheckMiner(act *types.Actor, vmctx types.VMContext, params *CheckMinerParams) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != StoragePowerAddress {
|
||||
return nil, aerrors.New(2, "only the storage power actor can check miner")
|
||||
}
|
||||
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !isLate(vmctx.BlockHeight(), self) {
|
||||
// Everything's fine
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if self.SlashedAt != 0 {
|
||||
// Don't slash more than necessary
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if params.NetworkPower.Equals(self.Power) {
|
||||
// Don't break the network when there's only one miner left
|
||||
|
||||
log.Warnf("can't slash miner %s for missed PoSt, no power would be left in the network", vmctx.Message().To)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Slash for being late
|
||||
|
||||
self.SlashedAt = vmctx.BlockHeight()
|
||||
oldPower := self.Power
|
||||
|
||||
if vmctx.BlockHeight() > build.ForkMissingSnowballs {
|
||||
self.Power = types.NewInt(0)
|
||||
}
|
||||
|
||||
nstate, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
if err := oldPower.MarshalCBOR(&out); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "marshaling return value")
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) DeclareFaults(act *types.Actor, vmctx types.VMContext, params *DeclareFaultsParams) ([]byte, ActorError) {
|
||||
oldstate, self, aerr := loadState(vmctx)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
mi, aerr := loadMinerInfo(vmctx, self)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
if vmctx.Message().From != mi.Worker {
|
||||
return nil, aerrors.New(1, "not authorized to declare faults for miner")
|
||||
}
|
||||
|
||||
nfaults, err := types.MergeBitFields(params.Faults, self.FaultSet)
|
||||
if err != nil {
|
||||
return nil, aerrors.Absorb(err, 1, "failed to merge bitfields")
|
||||
}
|
||||
|
||||
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.HandleExternalError(nerr, "failed to load sector set")
|
||||
}
|
||||
|
||||
cf, nerr := nfaults.Count()
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(nerr, 2, "could not decode RLE+")
|
||||
}
|
||||
|
||||
if cf > 2*ss.Count {
|
||||
return nil, aerrors.Newf(3, "too many declared faults: %d > %d", cf, 2*ss.Count)
|
||||
}
|
||||
|
||||
self.FaultSet = nfaults
|
||||
|
||||
self.LastFaultSubmission = vmctx.BlockHeight()
|
||||
|
||||
nstate, aerr := vmctx.Storage().Put(self)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) SlashConsensusFault(act *types.Actor, vmctx types.VMContext, params *MinerSlashConsensusFault) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != StoragePowerAddress {
|
||||
return nil, aerrors.New(1, "SlashConsensusFault may only be called by the storage market actor")
|
||||
}
|
||||
|
||||
slashedCollateral := params.SlashedCollateral
|
||||
if slashedCollateral.LessThan(act.Balance) {
|
||||
slashedCollateral = act.Balance
|
||||
}
|
||||
|
||||
// Some of the slashed collateral should be paid to the slasher
|
||||
// GROWTH_RATE determines how fast the slasher share of slashed collateral will increase as block elapses
|
||||
// current GROWTH_RATE results in SLASHER_SHARE reaches 1 after 30 blocks
|
||||
// TODO: define arithmetic precision and rounding for this operation
|
||||
blockElapsed := vmctx.BlockHeight() - params.AtHeight
|
||||
|
||||
slasherShare := slasherShare(params.SlashedCollateral, blockElapsed)
|
||||
|
||||
burnPortion := types.BigSub(slashedCollateral, slasherShare)
|
||||
|
||||
_, err := vmctx.Send(vmctx.Message().From, 0, slasherShare, nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to pay slasher")
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(BurntFundsAddress, 0, burnPortion, nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to burn funds")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > build.ForkMissingSnowballs {
|
||||
oldstate, self, err := loadState(vmctx)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to load state for slashing")
|
||||
}
|
||||
|
||||
self.Power = types.NewInt(0)
|
||||
|
||||
ncid, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this still allows the miner to commit sectors and submit posts,
|
||||
// their users could potentially be unaffected, but the miner will never be
|
||||
// able to mine a block again
|
||||
// One potential issue: the miner will have to pay back the slashed
|
||||
// collateral to continue submitting PoSts, which includes pledge
|
||||
// collateral that they no longer really 'need'
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor2) SubmitElectionPoSt(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
if vmctx.Message().From != NetworkAddress {
|
||||
return nil, aerrors.Newf(1, "submit election post can only be called by the storage power actor")
|
||||
}
|
||||
|
||||
oldstate, self, aerr := loadState(vmctx)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
if self.SlashedAt != 0 {
|
||||
return nil, aerrors.New(1, "slashed miners can't perform election PoSt")
|
||||
}
|
||||
|
||||
pss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
|
||||
}
|
||||
|
||||
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
|
||||
}
|
||||
|
||||
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
|
||||
}
|
||||
|
||||
activeFaults := uint64(0)
|
||||
for f := range faults {
|
||||
if f > amt2.MaxIndex {
|
||||
continue
|
||||
}
|
||||
|
||||
var comms [][]byte
|
||||
err := pss.Get(f, &comms)
|
||||
if err != nil {
|
||||
var notfound *amt2.ErrNotFound
|
||||
if !xerrors.As(err, ¬found) {
|
||||
return nil, aerrors.HandleExternalError(err, "failed to find sector in sector set")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
activeFaults++
|
||||
}
|
||||
|
||||
if err := onSuccessfulPoSt2(self, vmctx, activeFaults); err != nil { // TODO
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ncid, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := vmctx.Storage().Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func onSuccessfulPoSt2(self *StorageMinerActorState, vmctx types.VMContext, activeFaults uint64) aerrors.ActorError {
|
||||
// FORK
|
||||
if vmctx.BlockHeight() < build.ForkBootyBayHeight {
|
||||
return onSuccessfulPoSt(self, vmctx, activeFaults)
|
||||
}
|
||||
|
||||
var mi MinerInfo
|
||||
if err := vmctx.Storage().Get(self.Info, &mi); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if nerr != nil {
|
||||
return aerrors.HandleExternalError(nerr, "failed to load proving set")
|
||||
}
|
||||
|
||||
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
|
||||
if nerr != nil {
|
||||
return aerrors.HandleExternalError(nerr, "failed to load sector set")
|
||||
}
|
||||
|
||||
faults, nerr := self.FaultSet.All(2 * ss.Count)
|
||||
if nerr != nil {
|
||||
return aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
|
||||
}
|
||||
|
||||
self.FaultSet = types.NewBitField()
|
||||
|
||||
oldPower := self.Power
|
||||
newPower := types.BigMul(types.NewInt(pss.Count-activeFaults), types.NewInt(mi.SectorSize))
|
||||
|
||||
// If below the minimum size requirement, miners have zero power
|
||||
if newPower.LessThan(types.NewInt(build.MinimumMinerPower)) {
|
||||
newPower = types.NewInt(0)
|
||||
}
|
||||
|
||||
self.Power = newPower
|
||||
|
||||
delta := types.BigSub(self.Power, oldPower)
|
||||
if self.SlashedAt != 0 {
|
||||
self.SlashedAt = 0
|
||||
delta = self.Power
|
||||
}
|
||||
|
||||
prevSlashingDeadline := self.ElectionPeriodStart + build.SlashablePowerDelay
|
||||
if !self.Active && newPower.GreaterThan(types.NewInt(0)) {
|
||||
self.Active = true
|
||||
prevSlashingDeadline = 0
|
||||
}
|
||||
|
||||
if !(oldPower.IsZero() && newPower.IsZero()) {
|
||||
enc, err := SerializeParams(&UpdateStorageParams{
|
||||
Delta: delta,
|
||||
NextSlashDeadline: vmctx.BlockHeight() + build.SlashablePowerDelay,
|
||||
PreviousSlashDeadline: prevSlashingDeadline,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(StoragePowerAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "updating storage failed")
|
||||
}
|
||||
|
||||
self.ElectionPeriodStart = vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
var ncid cid.Cid
|
||||
var err aerrors.ActorError
|
||||
|
||||
ncid, err = RemoveFromSectorSet2(vmctx.Context(), vmctx.Storage(), self.Sectors, faults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Sectors = ncid
|
||||
self.ProvingSet = ncid
|
||||
return nil
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/rlepluslazy"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
func TestMinerCommitSectors(t *testing.T) {
|
||||
var worker, client address.Address
|
||||
var minerAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&worker, 1000000),
|
||||
HarnessAddr(&client, 1000000),
|
||||
HarnessActor(&minerAddr, &worker, actors.StorageMinerCodeCid,
|
||||
func() cbg.CBORMarshaler {
|
||||
return &actors.StorageMinerConstructorParams{
|
||||
Owner: worker,
|
||||
Worker: worker,
|
||||
SectorSize: 1024,
|
||||
PeerID: "fakepeerid",
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
h.vm.Syscalls.ValidatePoRep = func(ctx context.Context, maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, aerrors.ActorError) {
|
||||
// all proofs are valid
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ret, _ := h.SendFunds(t, worker, minerAddr, types.NewInt(100000))
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.InvokeWithValue(t, client, actors.StorageMarketAddress, actors.SMAMethods.AddBalance, types.NewInt(2000), nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
addSectorToMiner(h, t, minerAddr, worker, client, 1)
|
||||
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
}
|
||||
|
||||
type badRuns struct {
|
||||
done bool
|
||||
}
|
||||
|
||||
func (br *badRuns) HasNext() bool {
|
||||
return !br.done
|
||||
}
|
||||
|
||||
func (br *badRuns) NextRun() (rlepluslazy.Run, error) {
|
||||
br.done = true
|
||||
return rlepluslazy.Run{true, math.MaxInt64}, nil
|
||||
}
|
||||
|
||||
var _ rlepluslazy.RunIterator = (*badRuns)(nil)
|
||||
|
||||
func TestMinerSubmitBadFault(t *testing.T) {
|
||||
oldSS, oldMin := build.SectorSizes, build.MinimumMinerPower
|
||||
build.SectorSizes, build.MinimumMinerPower = []uint64{1024}, 1024
|
||||
defer func() {
|
||||
build.SectorSizes, build.MinimumMinerPower = oldSS, oldMin
|
||||
}()
|
||||
|
||||
var worker, client address.Address
|
||||
var minerAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&worker, 1000000),
|
||||
HarnessAddr(&client, 1000000),
|
||||
HarnessAddMiner(&minerAddr, &worker),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
h.vm.Syscalls.ValidatePoRep = func(ctx context.Context, maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, aerrors.ActorError) {
|
||||
// all proofs are valid
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ret, _ := h.SendFunds(t, worker, minerAddr, types.NewInt(100000))
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.InvokeWithValue(t, client, actors.StorageMarketAddress, actors.SMAMethods.AddBalance, types.NewInt(2000), nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
addSectorToMiner(h, t, minerAddr, worker, client, 1)
|
||||
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
bf := types.NewBitField()
|
||||
bf.Set(6)
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.Invoke(t, actors.NetworkAddress, minerAddr, actors.MAMethods.SubmitElectionPoSt, nil)
|
||||
ApplyOK(t, ret)
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
st, err := getMinerState(context.TODO(), h.vm.StateTree(), h.bs, minerAddr)
|
||||
assert.NoError(t, err)
|
||||
expectedPower := st.Power
|
||||
if types.BigCmp(expectedPower, types.NewInt(1024)) != 0 {
|
||||
t.Errorf("Expected power of 1024, got %s", expectedPower)
|
||||
}
|
||||
|
||||
badnum := uint64(0)
|
||||
badnum--
|
||||
bf = types.NewBitField()
|
||||
bf.Set(badnum)
|
||||
bf.Set(badnum - 1)
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.Invoke(t, actors.NetworkAddress, minerAddr, actors.MAMethods.SubmitElectionPoSt, nil)
|
||||
|
||||
ApplyOK(t, ret)
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
st, err = getMinerState(context.TODO(), h.vm.StateTree(), h.bs, minerAddr)
|
||||
assert.NoError(t, err)
|
||||
currentPower := st.Power
|
||||
if types.BigCmp(expectedPower, currentPower) != 0 {
|
||||
t.Errorf("power changed and shouldn't have: %s != %s", expectedPower, currentPower)
|
||||
}
|
||||
|
||||
bf.Set(badnum - 2)
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
|
||||
if ret.ExitCode != 3 {
|
||||
t.Errorf("expected exit code 3, got %d: %+v", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
rle, err := rlepluslazy.EncodeRuns(&badRuns{}, []byte{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
bf, err = types.NewBitFieldFromBytes(rle)
|
||||
assert.NoError(t, err)
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
|
||||
if ret.ExitCode != 3 {
|
||||
t.Errorf("expected exit code 3, got %d: %+v", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
|
||||
bf = types.NewBitField()
|
||||
bf.Set(1)
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.Invoke(t, actors.NetworkAddress, minerAddr, actors.MAMethods.SubmitElectionPoSt, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{})
|
||||
|
||||
}
|
||||
|
||||
func addSectorToMiner(h *Harness, t *testing.T, minerAddr, worker, client address.Address, sid uint64) {
|
||||
t.Helper()
|
||||
s := sectorbuilder.UserBytesForSectorSize(1024)
|
||||
deal := h.makeFakeDeal(t, minerAddr, worker, client, s)
|
||||
ret, _ := h.Invoke(t, worker, actors.StorageMarketAddress, actors.SMAMethods.PublishStorageDeals,
|
||||
&actors.PublishStorageDealsParams{
|
||||
Deals: []actors.StorageDealProposal{*deal},
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var dealIds actors.PublishStorageDealResponse
|
||||
if err := dealIds.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dealid := dealIds.DealIDs[0]
|
||||
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.PreCommitSector,
|
||||
&actors.SectorPreCommitInfo{
|
||||
SectorNumber: sid,
|
||||
CommR: []byte("cats"),
|
||||
SealEpoch: 10,
|
||||
DealIDs: []uint64{dealid},
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
h.BlockHeight += 100
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.ProveCommitSector,
|
||||
&actors.SectorProveCommitInfo{
|
||||
Proof: []byte("prooofy"),
|
||||
SectorID: sid,
|
||||
DealIDs: []uint64{dealid}, // TODO: weird that i have to pass this again
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func assertSectorIDs(h *Harness, t *testing.T, maddr address.Address, ids []uint64) {
|
||||
t.Helper()
|
||||
sectors, err := getMinerSectorSet(context.TODO(), h.vm.StateTree(), h.bs, maddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(sectors) != len(ids) {
|
||||
t.Fatal("miner has wrong number of sectors in their sector set")
|
||||
}
|
||||
|
||||
all := make(map[uint64]bool)
|
||||
for _, s := range sectors {
|
||||
all[s.SectorID] = true
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if !all[id] {
|
||||
t.Fatal("expected to find sector ID: ", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMinerState(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) (*actors.StorageMinerActorState, error) {
|
||||
mact, err := st.GetActor(maddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
|
||||
var mstate actors.StorageMinerActorState
|
||||
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mstate, nil
|
||||
}
|
||||
|
||||
func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
mstate, err := getMinerState(ctx, st, bs, maddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmgr.LoadSectorsFromSet(ctx, bs, mstate.Sectors)
|
||||
}
|
||||
|
||||
func (h *Harness) makeFakeDeal(t *testing.T, miner, worker, client address.Address, size uint64) *actors.StorageDealProposal {
|
||||
data := make([]byte, size)
|
||||
rand.Read(data)
|
||||
commP, err := sectorbuilder.GeneratePieceCommitment(bytes.NewReader(data), size)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prop := actors.StorageDealProposal{
|
||||
PieceRef: commP[:],
|
||||
PieceSize: size,
|
||||
|
||||
Client: client,
|
||||
Provider: miner,
|
||||
|
||||
ProposalExpiration: 10000,
|
||||
Duration: 150,
|
||||
|
||||
StoragePricePerEpoch: types.NewInt(1),
|
||||
StorageCollateral: types.NewInt(0),
|
||||
}
|
||||
|
||||
if err := api.SignWith(context.TODO(), h.w.Sign, client, &prop); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &prop
|
||||
}
|
@ -1,431 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
type MultiSigActor struct{}
|
||||
type MultiSigActorState struct {
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
NextTxID uint64
|
||||
|
||||
InitialBalance types.BigInt
|
||||
StartingBlock uint64
|
||||
UnlockDuration uint64
|
||||
|
||||
//TODO: make this map/sharray/whatever
|
||||
Transactions []MTransaction
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) canSpend(act *types.Actor, amnt types.BigInt, height uint64) bool {
|
||||
if msas.UnlockDuration == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
offset := height - msas.StartingBlock
|
||||
if offset > msas.UnlockDuration {
|
||||
return true
|
||||
}
|
||||
|
||||
minBalance := types.BigDiv(msas.InitialBalance, types.NewInt(msas.UnlockDuration))
|
||||
minBalance = types.BigMul(minBalance, types.NewInt(offset))
|
||||
return !minBalance.LessThan(types.BigSub(act.Balance, amnt))
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) isSigner(addr address.Address) bool {
|
||||
for _, s := range msas.Signers {
|
||||
if s == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) getTransaction(txid uint64) (*MTransaction, ActorError) {
|
||||
if txid >= uint64(len(msas.Transactions)) {
|
||||
return nil, aerrors.Newf(1, "could not get transaction (numbers of tx %d,want to get txid %d)", len(msas.Transactions), txid)
|
||||
}
|
||||
return &msas.Transactions[txid], nil
|
||||
}
|
||||
|
||||
type MTransaction struct {
|
||||
Created uint64 // NOT USED ??
|
||||
TxID uint64
|
||||
|
||||
To address.Address
|
||||
Value types.BigInt
|
||||
Method uint64
|
||||
Params []byte
|
||||
|
||||
Approved []address.Address
|
||||
Complete bool
|
||||
Canceled bool
|
||||
RetCode uint64
|
||||
}
|
||||
|
||||
func (tx MTransaction) Active() ActorError {
|
||||
if tx.Complete {
|
||||
return aerrors.New(2, "transaction already completed")
|
||||
}
|
||||
if tx.Canceled {
|
||||
return aerrors.New(3, "transaction canceled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type musigMethods struct {
|
||||
MultiSigConstructor uint64
|
||||
Propose uint64
|
||||
Approve uint64
|
||||
Cancel uint64
|
||||
ClearCompleted uint64
|
||||
AddSigner uint64
|
||||
RemoveSigner uint64
|
||||
SwapSigner uint64
|
||||
ChangeRequirement uint64
|
||||
}
|
||||
|
||||
var MultiSigMethods = musigMethods{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
func (msa MultiSigActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: msa.MultiSigConstructor,
|
||||
2: msa.Propose,
|
||||
3: msa.Approve,
|
||||
4: msa.Cancel,
|
||||
//5: msa.ClearCompleted,
|
||||
6: msa.AddSigner,
|
||||
7: msa.RemoveSigner,
|
||||
8: msa.SwapSigner,
|
||||
9: msa.ChangeRequirement,
|
||||
}
|
||||
}
|
||||
|
||||
type MultiSigConstructorParams struct {
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
UnlockDuration uint64
|
||||
}
|
||||
|
||||
func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigConstructorParams) ([]byte, ActorError) {
|
||||
self := &MultiSigActorState{
|
||||
Signers: params.Signers,
|
||||
Required: params.Required,
|
||||
}
|
||||
|
||||
if params.UnlockDuration != 0 {
|
||||
self.InitialBalance = vmctx.Message().Value
|
||||
self.UnlockDuration = params.UnlockDuration
|
||||
self.StartingBlock = vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
head, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not put new head")
|
||||
}
|
||||
err = vmctx.Storage().Commit(EmptyCBOR, head)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not commit new head")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type MultiSigProposeParams struct {
|
||||
To address.Address
|
||||
Value types.BigInt
|
||||
Method uint64
|
||||
Params []byte
|
||||
}
|
||||
|
||||
func (MultiSigActor) load(vmctx types.VMContext) (cid.Cid, *MultiSigActorState, ActorError) {
|
||||
var self MultiSigActorState
|
||||
head := vmctx.Storage().GetHead()
|
||||
|
||||
err := vmctx.Storage().Get(head, &self)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, aerrors.Wrap(err, "could not get self")
|
||||
}
|
||||
return head, &self, nil
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) loadAndVerify(vmctx types.VMContext) (cid.Cid, *MultiSigActorState, ActorError) {
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
if !self.isSigner(vmctx.Message().From) {
|
||||
return cid.Undef, nil, aerrors.New(1, "not authorized")
|
||||
}
|
||||
return head, self, nil
|
||||
}
|
||||
|
||||
func (MultiSigActor) save(vmctx types.VMContext, oldHead cid.Cid, self *MultiSigActorState) ActorError {
|
||||
newHead, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "could not put new head")
|
||||
}
|
||||
err = vmctx.Storage().Commit(oldHead, newHead)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "could not commit new head")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Propose(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigProposeParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txid := self.NextTxID
|
||||
self.NextTxID++
|
||||
|
||||
{
|
||||
tx := MTransaction{
|
||||
TxID: txid,
|
||||
To: params.To,
|
||||
Value: params.Value,
|
||||
Method: params.Method,
|
||||
Params: params.Params,
|
||||
Approved: []address.Address{vmctx.Message().From},
|
||||
}
|
||||
self.Transactions = append(self.Transactions, tx)
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.Required == 1 {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
}
|
||||
tx.RetCode = uint64(aerrors.RetCode(err))
|
||||
tx.Complete = true
|
||||
}
|
||||
|
||||
err = msa.save(vmctx, head, self)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "saving state")
|
||||
}
|
||||
|
||||
// REVIEW: On one hand, I like being very explicit about how we're doing the serialization
|
||||
// on the other, maybe we shouldnt do direct calls to underlying serialization libs?
|
||||
return cbg.CborEncodeMajorType(cbg.MajUnsignedInt, tx.TxID), nil
|
||||
}
|
||||
|
||||
type MultiSigTxID struct {
|
||||
TxID uint64
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Approve(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigTxID) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not approve")
|
||||
}
|
||||
|
||||
for _, signer := range tx.Approved {
|
||||
if signer == vmctx.Message().From {
|
||||
return nil, aerrors.New(4, "already signed this message")
|
||||
}
|
||||
}
|
||||
tx.Approved = append(tx.Approved, vmctx.Message().From)
|
||||
if uint64(len(tx.Approved)) >= self.Required {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
}
|
||||
tx.RetCode = uint64(aerrors.RetCode(err))
|
||||
tx.Complete = true
|
||||
}
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Cancel(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigTxID) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not cancel")
|
||||
}
|
||||
|
||||
proposer := tx.Approved[0]
|
||||
if proposer != vmctx.Message().From && self.isSigner(proposer) {
|
||||
return nil, aerrors.New(4, "cannot cancel another signers transaction")
|
||||
}
|
||||
tx.Canceled = true
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigAddSignerParam struct {
|
||||
Signer address.Address
|
||||
Increase bool
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) AddSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigAddSignerParam) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "add signer must be called by wallet itself")
|
||||
}
|
||||
if self.isSigner(params.Signer) {
|
||||
return nil, aerrors.New(5, "new address is already a signer")
|
||||
}
|
||||
|
||||
self.Signers = append(self.Signers, params.Signer)
|
||||
if params.Increase {
|
||||
self.Required = self.Required + 1
|
||||
}
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigRemoveSignerParam struct {
|
||||
Signer address.Address
|
||||
Decrease bool
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) RemoveSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigRemoveSignerParam) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "remove signer must be called by wallet itself")
|
||||
}
|
||||
if !self.isSigner(params.Signer) {
|
||||
return nil, aerrors.New(5, "given address was not a signer")
|
||||
}
|
||||
|
||||
newSigners := make([]address.Address, 0, len(self.Signers)-1)
|
||||
for _, s := range self.Signers {
|
||||
if s != params.Signer {
|
||||
newSigners = append(newSigners, s)
|
||||
}
|
||||
}
|
||||
if params.Decrease || uint64(len(self.Signers)-1) < self.Required {
|
||||
self.Required = self.Required - 1
|
||||
}
|
||||
|
||||
self.Signers = newSigners
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigSwapSignerParams struct {
|
||||
From address.Address
|
||||
To address.Address
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) SwapSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigSwapSignerParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "swap signer must be called by wallet itself")
|
||||
}
|
||||
|
||||
if !self.isSigner(params.From) {
|
||||
return nil, aerrors.New(5, "given old address was not a signer")
|
||||
}
|
||||
if self.isSigner(params.To) {
|
||||
return nil, aerrors.New(6, "given new address was already a signer")
|
||||
}
|
||||
|
||||
newSigners := make([]address.Address, 0, len(self.Signers))
|
||||
for _, s := range self.Signers {
|
||||
if s != params.From {
|
||||
newSigners = append(newSigners, s)
|
||||
}
|
||||
}
|
||||
newSigners = append(newSigners, params.To)
|
||||
self.Signers = newSigners
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigChangeReqParams struct {
|
||||
Req uint64
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) ChangeRequirement(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigChangeReqParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "change requirement must be called by wallet itself")
|
||||
}
|
||||
|
||||
if params.Req < 1 {
|
||||
return nil, aerrors.New(5, "requirement must be at least 1")
|
||||
}
|
||||
|
||||
if params.Req > uint64(len(self.Signers)) {
|
||||
return nil, aerrors.New(6, "requirement must be at most the numbers of signers")
|
||||
}
|
||||
|
||||
self.Required = params.Req
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/stretchr/testify/assert"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
)
|
||||
|
||||
func TestMultiSigCreate(t *testing.T) {
|
||||
var creatorAddr, sig1Addr, sig2Addr, outsideAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&sig1Addr, 100000),
|
||||
HarnessAddr(&sig2Addr, 100000),
|
||||
HarnessAddr(&outsideAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.MultisigCodeCid,
|
||||
&actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
Required: 2,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func ApplyOK(t testing.TB, ret *vm.ApplyRet) {
|
||||
t.Helper()
|
||||
if ret.ExitCode != 0 {
|
||||
t.Fatalf("exit code should be 0, got %d, actorErr: %+v", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
if ret.ActorErr != nil {
|
||||
t.Fatalf("somehow got an error with exit == 0: %s", ret.ActorErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiSigOps(t *testing.T) {
|
||||
var creatorAddr, sig1Addr, sig2Addr, outsideAddr address.Address
|
||||
var multSigAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&sig1Addr, 100000),
|
||||
HarnessAddr(&sig2Addr, 100000),
|
||||
HarnessAddr(&outsideAddr, 100000),
|
||||
HarnessActor(&multSigAddr, &creatorAddr, actors.MultisigCodeCid,
|
||||
func() cbg.CBORMarshaler {
|
||||
return &actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
Required: 2,
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
{
|
||||
const chargeVal = 2000
|
||||
// Send funds into the multisig
|
||||
ret, _ := h.SendFunds(t, creatorAddr, multSigAddr, types.NewInt(chargeVal))
|
||||
ApplyOK(t, ret)
|
||||
h.AssertBalanceChange(t, creatorAddr, -chargeVal)
|
||||
h.AssertBalanceChange(t, multSigAddr, chargeVal)
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer funds outside of multsig
|
||||
const sendVal = 1000
|
||||
ret, _ := h.Invoke(t, creatorAddr, multSigAddr, actors.MultiSigMethods.Propose,
|
||||
&actors.MultiSigProposeParams{
|
||||
To: outsideAddr,
|
||||
Value: types.NewInt(sendVal),
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var txIDParam actors.MultiSigTxID
|
||||
err := cbor.DecodeInto(ret.Return, &txIDParam.TxID)
|
||||
assert.NoError(t, err, "decoding txid")
|
||||
|
||||
ret, _ = h.Invoke(t, outsideAddr, multSigAddr, actors.MultiSigMethods.Approve,
|
||||
&txIDParam)
|
||||
assert.Equal(t, uint8(1), ret.ExitCode, "outsideAddr should not approve")
|
||||
h.AssertBalanceChange(t, multSigAddr, 0)
|
||||
|
||||
ret2, _ := h.Invoke(t, sig1Addr, multSigAddr, actors.MultiSigMethods.Approve,
|
||||
&txIDParam)
|
||||
ApplyOK(t, ret2)
|
||||
|
||||
h.AssertBalanceChange(t, outsideAddr, sendVal)
|
||||
h.AssertBalanceChange(t, multSigAddr, -sendVal)
|
||||
}
|
||||
|
||||
}
|
@ -1,302 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/minio/blake2b-simd"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type PaymentChannelActor struct{}
|
||||
|
||||
type PaymentInfo struct {
|
||||
PayChActor address.Address
|
||||
Payer address.Address
|
||||
ChannelMessage *cid.Cid
|
||||
|
||||
Vouchers []*types.SignedVoucher
|
||||
}
|
||||
|
||||
type LaneState struct {
|
||||
Closed bool
|
||||
Redeemed types.BigInt
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
type PaymentChannelActorState struct {
|
||||
From address.Address
|
||||
To address.Address
|
||||
|
||||
ToSend types.BigInt
|
||||
|
||||
ClosingAt uint64
|
||||
MinCloseHeight uint64
|
||||
|
||||
// TODO: needs to be map[uint64]*laneState
|
||||
// waiting on refmt#35 to be fixed
|
||||
LaneStates map[string]*LaneState
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: pca.Constructor,
|
||||
2: pca.UpdateChannelState,
|
||||
3: pca.Close,
|
||||
4: pca.Collect,
|
||||
5: pca.GetOwner,
|
||||
6: pca.GetToSend,
|
||||
}
|
||||
}
|
||||
|
||||
type pcaMethods struct {
|
||||
Constructor uint64
|
||||
UpdateChannelState uint64
|
||||
Close uint64
|
||||
Collect uint64
|
||||
GetOwner uint64
|
||||
GetToSend uint64
|
||||
}
|
||||
|
||||
var PCAMethods = pcaMethods{1, 2, 3, 4, 5, 6}
|
||||
|
||||
type PCAConstructorParams struct {
|
||||
To address.Address
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Constructor(act *types.Actor, vmctx types.VMContext, params *PCAConstructorParams) ([]byte, ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
self.From = vmctx.Origin()
|
||||
self.To = params.To
|
||||
self.LaneStates = make(map[string]*LaneState)
|
||||
|
||||
storage := vmctx.Storage()
|
||||
c, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := storage.Commit(EmptyCBOR, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type PCAUpdateChannelStateParams struct {
|
||||
Sv types.SignedVoucher
|
||||
Secret []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func hash(b []byte) []byte {
|
||||
s := blake2b.Sum256(b)
|
||||
return s[:]
|
||||
}
|
||||
|
||||
type PaymentVerifyParams struct {
|
||||
Extra []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.VMContext, params *PCAUpdateChannelStateParams) ([]byte, ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
oldstate := vmctx.Storage().GetHead()
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sv := params.Sv
|
||||
|
||||
vb, nerr := sv.SigningBytes()
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(nerr, 1, "failed to serialize signedvoucher")
|
||||
}
|
||||
|
||||
if err := vmctx.VerifySignature(sv.Signature, self.From, vb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < sv.TimeLock {
|
||||
return nil, aerrors.New(2, "cannot use this voucher yet!")
|
||||
}
|
||||
|
||||
if len(sv.SecretPreimage) > 0 {
|
||||
if !bytes.Equal(hash(params.Secret), sv.SecretPreimage) {
|
||||
return nil, aerrors.New(3, "incorrect secret!")
|
||||
}
|
||||
}
|
||||
|
||||
if sv.Extra != nil {
|
||||
encoded, err := SerializeParams(&PaymentVerifyParams{sv.Extra.Data, params.Proof})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(sv.Extra.Actor, sv.Extra.Method, types.NewInt(0), encoded)
|
||||
if err != nil {
|
||||
return nil, aerrors.Newf(4, "spend voucher verification failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
ls, ok := self.LaneStates[fmt.Sprint(sv.Lane)]
|
||||
if !ok {
|
||||
ls = new(LaneState)
|
||||
ls.Redeemed = types.NewInt(0) // TODO: kinda annoying that this doesnt default to a usable value
|
||||
self.LaneStates[fmt.Sprint(sv.Lane)] = ls
|
||||
}
|
||||
if ls.Closed {
|
||||
return nil, aerrors.New(5, "cannot redeem a voucher on a closed lane")
|
||||
}
|
||||
|
||||
if ls.Nonce > sv.Nonce {
|
||||
return nil, aerrors.New(6, "voucher has an outdated nonce, cannot redeem")
|
||||
}
|
||||
|
||||
mergeValue := types.NewInt(0)
|
||||
for _, merge := range sv.Merges {
|
||||
if merge.Lane == sv.Lane {
|
||||
return nil, aerrors.New(7, "voucher cannot merge its own lane")
|
||||
}
|
||||
|
||||
ols := self.LaneStates[fmt.Sprint(merge.Lane)]
|
||||
|
||||
if ols.Nonce >= merge.Nonce {
|
||||
return nil, aerrors.New(8, "merge in voucher has outdated nonce, cannot redeem")
|
||||
}
|
||||
|
||||
mergeValue = types.BigAdd(mergeValue, ols.Redeemed)
|
||||
ols.Nonce = merge.Nonce
|
||||
}
|
||||
|
||||
ls.Nonce = sv.Nonce
|
||||
balanceDelta := types.BigSub(sv.Amount, types.BigAdd(mergeValue, ls.Redeemed))
|
||||
ls.Redeemed = sv.Amount
|
||||
|
||||
newSendBalance := types.BigAdd(self.ToSend, balanceDelta)
|
||||
if newSendBalance.LessThan(types.NewInt(0)) {
|
||||
// TODO: is this impossible?
|
||||
return nil, aerrors.New(9, "voucher would leave channel balance negative")
|
||||
}
|
||||
|
||||
if newSendBalance.GreaterThan(act.Balance) {
|
||||
return nil, aerrors.New(10, "not enough funds in channel to cover voucher")
|
||||
}
|
||||
|
||||
log.Info("vals: ", newSendBalance, sv.Amount, balanceDelta, mergeValue, ls.Redeemed)
|
||||
self.ToSend = newSendBalance
|
||||
|
||||
if sv.MinCloseHeight != 0 {
|
||||
if self.ClosingAt != 0 && self.ClosingAt < sv.MinCloseHeight {
|
||||
self.ClosingAt = sv.MinCloseHeight
|
||||
}
|
||||
if self.MinCloseHeight < sv.MinCloseHeight {
|
||||
self.MinCloseHeight = sv.MinCloseHeight
|
||||
}
|
||||
}
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Close(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
oldstate := storage.GetHead()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != self.From && vmctx.Message().From != self.To {
|
||||
return nil, aerrors.New(1, "not authorized to close channel")
|
||||
}
|
||||
|
||||
if self.ClosingAt != 0 {
|
||||
return nil, aerrors.New(2, "channel already closing")
|
||||
}
|
||||
|
||||
self.ClosingAt = vmctx.BlockHeight() + build.PaymentChannelClosingDelay
|
||||
if self.ClosingAt < self.MinCloseHeight {
|
||||
self.ClosingAt = self.MinCloseHeight
|
||||
}
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Collect(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
oldstate := storage.GetHead()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.ClosingAt == 0 {
|
||||
return nil, aerrors.New(1, "payment channel not closing or closed")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < self.ClosingAt {
|
||||
return nil, aerrors.New(2, "payment channel not closed yet")
|
||||
}
|
||||
_, err := vmctx.Send(self.From, 0, types.BigSub(act.Balance, self.ToSend), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = vmctx.Send(self.To, 0, self.ToSend, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.ToSend = types.NewInt(0)
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) GetOwner(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(storage.GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.From.Bytes(), nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) GetToSend(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(storage.GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.ToSend.Bytes(), nil
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
)
|
||||
|
||||
func TestPaychCreate(t *testing.T) {
|
||||
var creatorAddr, targetAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&targetAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func signVoucher(t *testing.T, w *wallet.Wallet, addr address.Address, sv *types.SignedVoucher) {
|
||||
vb, err := sv.SigningBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := w.Sign(context.TODO(), addr, vb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sv.Signature = sig
|
||||
}
|
||||
|
||||
func TestPaychUpdate(t *testing.T) {
|
||||
var creatorAddr, targetAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&targetAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
pch, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret, _ = h.SendFunds(t, creatorAddr, pch, types.NewInt(5000))
|
||||
ApplyOK(t, ret)
|
||||
|
||||
sv := &types.SignedVoucher{
|
||||
Amount: types.NewInt(100),
|
||||
Nonce: 1,
|
||||
}
|
||||
signVoucher(t, h.w, creatorAddr, sv)
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.UpdateChannelState, &actors.PCAUpdateChannelStateParams{
|
||||
Sv: *sv,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.GetToSend, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
bi := types.BigFromBytes(ret.Return)
|
||||
if bi.String() != "100" {
|
||||
t.Fatal("toSend amount was wrong: ", bi.String())
|
||||
}
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.Close, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
// now we have to 'wait' for the chain to advance.
|
||||
h.BlockHeight = 1000
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.Collect, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
h.AssertBalanceChange(t, targetAddr, 100)
|
||||
h.AssertBalanceChange(t, creatorAddr, -100)
|
||||
}
|
@ -1,677 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
)
|
||||
|
||||
type StorageMarketActor struct{}
|
||||
|
||||
type smaMethods struct {
|
||||
Constructor uint64
|
||||
WithdrawBalance uint64
|
||||
AddBalance uint64
|
||||
CheckLockedBalance uint64
|
||||
PublishStorageDeals uint64
|
||||
HandleCronAction uint64
|
||||
SettleExpiredDeals uint64
|
||||
ProcessStorageDealsPayment uint64
|
||||
SlashStorageDealCollateral uint64
|
||||
GetLastExpirationFromDealIDs uint64
|
||||
ActivateStorageDeals uint64
|
||||
ComputeDataCommitment uint64
|
||||
}
|
||||
|
||||
var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||||
|
||||
func (sma StorageMarketActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
2: sma.WithdrawBalance,
|
||||
3: sma.AddBalance,
|
||||
// 4: sma.CheckLockedBalance,
|
||||
5: sma.PublishStorageDeals,
|
||||
// 6: sma.HandleCronAction,
|
||||
// 7: sma.SettleExpiredDeals,
|
||||
// 8: sma.ProcessStorageDealsPayment,
|
||||
// 9: sma.SlashStorageDealCollateral,
|
||||
// 10: sma.GetLastExpirationFromDealIDs,
|
||||
11: sma.ActivateStorageDeals, // TODO: move under PublishStorageDeals after specs team approves
|
||||
12: sma.ComputeDataCommitment,
|
||||
}
|
||||
}
|
||||
|
||||
type StorageParticipantBalance struct {
|
||||
Locked types.BigInt
|
||||
Available types.BigInt
|
||||
}
|
||||
|
||||
type StorageMarketState struct {
|
||||
Balances cid.Cid // hamt<addr, StorageParticipantBalance>
|
||||
Deals cid.Cid // amt<StorageDeal>
|
||||
|
||||
NextDealID uint64 // TODO: spec
|
||||
}
|
||||
|
||||
// TODO: Drop in favour of car storage
|
||||
type SerializationMode = uint64
|
||||
|
||||
const (
|
||||
SerializationUnixFSv0 = iota
|
||||
// IPLD / car
|
||||
)
|
||||
|
||||
type StorageDealProposal struct {
|
||||
PieceRef []byte // cid bytes // TODO: spec says to use cid.Cid, probably not a good idea
|
||||
PieceSize uint64
|
||||
|
||||
Client address.Address
|
||||
Provider address.Address
|
||||
|
||||
ProposalExpiration uint64
|
||||
Duration uint64 // TODO: spec
|
||||
|
||||
StoragePricePerEpoch types.BigInt
|
||||
StorageCollateral types.BigInt
|
||||
|
||||
ProposerSignature *types.Signature
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) TotalStoragePrice() types.BigInt {
|
||||
return types.BigMul(sdp.StoragePricePerEpoch, types.NewInt(sdp.Duration))
|
||||
}
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
|
||||
func (sdp *StorageDealProposal) Sign(ctx context.Context, sign SignFunc) error {
|
||||
if sdp.ProposerSignature != nil {
|
||||
return xerrors.New("signature already present in StorageDealProposal")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := sdp.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
sig, err := sign(ctx, buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sdp.ProposerSignature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) Cid() (cid.Cid, error) {
|
||||
nd, err := cborutil.AsIpld(sdp)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return nd.Cid(), nil
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) Verify(worker address.Address) error {
|
||||
if sdp.Client != worker || worker == address.Undef {
|
||||
unsigned := *sdp
|
||||
unsigned.ProposerSignature = nil
|
||||
var buf bytes.Buffer
|
||||
if err := unsigned.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sigs.Verify(sdp.ProposerSignature, sdp.Client, buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type OnChainDeal struct {
|
||||
PieceRef []byte // cid bytes // TODO: spec says to use cid.Cid, probably not a good idea
|
||||
PieceSize uint64
|
||||
|
||||
Client address.Address
|
||||
Provider address.Address
|
||||
|
||||
ProposalExpiration uint64
|
||||
Duration uint64 // TODO: spec
|
||||
|
||||
StoragePricePerEpoch types.BigInt
|
||||
StorageCollateral types.BigInt
|
||||
ActivationEpoch uint64 // 0 = inactive
|
||||
}
|
||||
|
||||
type WithdrawBalanceParams struct {
|
||||
Balance types.BigInt
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) WithdrawBalance(act *types.Actor, vmctx types.VMContext, params *WithdrawBalanceParams) ([]byte, ActorError) {
|
||||
// TODO: (spec) this should be 2-stage
|
||||
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
if balance.Available.LessThan(params.Balance) {
|
||||
return nil, aerrors.Newf(1, "can not withdraw more funds than available: %s > %s", params.Balance, b[0].Available)
|
||||
}
|
||||
|
||||
balance.Available = types.BigSub(balance.Available, params.Balance)
|
||||
|
||||
_, err = vmctx.Send(vmctx.Message().From, 0, params.Balance, nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "sending funds failed")
|
||||
}
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
balance.Available = types.BigAdd(balance.Available, vmctx.Message().Value)
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func setMarketBalances(vmctx types.VMContext, nd *hamt.Node, set map[address.Address]StorageParticipantBalance) (cid.Cid, ActorError) {
|
||||
keys := make([]address.Address, 0, len(set))
|
||||
for k := range set {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0
|
||||
})
|
||||
for _, addr := range keys {
|
||||
balance := set[addr]
|
||||
if err := nd.Set(vmctx.Context(), string(addr.Bytes()), &balance); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "setting new balance")
|
||||
}
|
||||
}
|
||||
if err := nd.Flush(vmctx.Context()); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "flushing balance hamt")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(vmctx.Context(), nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to balances storage")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func GetMarketBalances(ctx context.Context, store *hamt.CborIpldStore, rcid cid.Cid, addrs ...address.Address) ([]StorageParticipantBalance, *hamt.Node, ActorError) {
|
||||
ctx, span := trace.StartSpan(ctx, "GetMarketBalances")
|
||||
defer span.End()
|
||||
|
||||
nd, err := hamt.LoadNode(ctx, store, rcid)
|
||||
if err != nil {
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
out := make([]StorageParticipantBalance, len(addrs))
|
||||
|
||||
for i, a := range addrs {
|
||||
var balance StorageParticipantBalance
|
||||
err = nd.Find(ctx, string(a.Bytes()), &balance)
|
||||
switch err {
|
||||
case hamt.ErrNotFound:
|
||||
out[i] = StorageParticipantBalance{
|
||||
Locked: types.NewInt(0),
|
||||
Available: types.NewInt(0),
|
||||
}
|
||||
case nil:
|
||||
out[i] = balance
|
||||
default:
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to do set lookup")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return out, nd, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) CheckLockedBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
type PublishStorageDealsParams struct {
|
||||
Deals []StorageDealProposal
|
||||
}
|
||||
|
||||
type PublishStorageDealResponse struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *PublishStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// todo: handle duplicate deals
|
||||
|
||||
if len(params.Deals) == 0 {
|
||||
return nil, aerrors.New(1, "no storage deals in params.Deals")
|
||||
}
|
||||
|
||||
out := PublishStorageDealResponse{
|
||||
DealIDs: make([]uint64, len(params.Deals)),
|
||||
}
|
||||
|
||||
workerBytes, aerr := vmctx.Send(params.Deals[0].Provider, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
// TODO: REVIEW: Do we want to check if provider exists in the power actor?
|
||||
|
||||
for i, deal := range params.Deals {
|
||||
if err := self.validateDeal(vmctx, deal, providerWorker); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := deals.Set(self.NextDealID, &OnChainDeal{
|
||||
PieceRef: deal.PieceRef,
|
||||
PieceSize: deal.PieceSize,
|
||||
|
||||
Client: deal.Client,
|
||||
Provider: deal.Provider,
|
||||
|
||||
ProposalExpiration: deal.ProposalExpiration,
|
||||
Duration: deal.Duration,
|
||||
|
||||
StoragePricePerEpoch: deal.StoragePricePerEpoch,
|
||||
StorageCollateral: deal.StorageCollateral,
|
||||
ActivationEpoch: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal in deal AMT")
|
||||
}
|
||||
out.DealIDs[i] = self.NextDealID
|
||||
|
||||
self.NextDealID++
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
if err := out.MarshalCBOR(&outBuf); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "serialising output")
|
||||
}
|
||||
|
||||
return outBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (st *StorageMarketState) validateDeal(vmctx types.VMContext, deal StorageDealProposal, providerWorker address.Address) aerrors.ActorError {
|
||||
ctx, span := trace.StartSpan(vmctx.Context(), "validateDeal")
|
||||
defer span.End()
|
||||
|
||||
if vmctx.BlockHeight() > deal.ProposalExpiration {
|
||||
return aerrors.New(1, "deal proposal already expired")
|
||||
}
|
||||
|
||||
if vmctx.Message().From != providerWorker {
|
||||
return aerrors.New(2, "Deals must be submitted by the miner worker")
|
||||
}
|
||||
|
||||
if err := deal.Verify(providerWorker); err != nil {
|
||||
return aerrors.Absorb(err, 3, "verifying proposer signature")
|
||||
}
|
||||
|
||||
// TODO: do some caching (changes gas so needs to be in spec too)
|
||||
b, bnd, aerr := GetMarketBalances(ctx, vmctx.Ipld(), st.Balances, deal.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return aerrors.Wrap(aerr, "getting client, and provider balances")
|
||||
}
|
||||
clientBalance := b[0]
|
||||
providerBalance := b[1]
|
||||
|
||||
totalPrice := deal.TotalStoragePrice()
|
||||
|
||||
if clientBalance.Available.LessThan(totalPrice) {
|
||||
return aerrors.Newf(5, "client doesn't have enough available funds to cover storage price; %d < %d", clientBalance.Available, totalPrice)
|
||||
}
|
||||
|
||||
clientBalance = lockFunds(clientBalance, totalPrice)
|
||||
|
||||
// TODO: REVIEW: Not clear who pays for this
|
||||
if providerBalance.Available.LessThan(deal.StorageCollateral) {
|
||||
return aerrors.Newf(6, "provider doesn't have enough available funds to cover StorageCollateral; %d < %d", providerBalance.Available, deal.StorageCollateral)
|
||||
}
|
||||
|
||||
providerBalance = lockFunds(providerBalance, deal.StorageCollateral)
|
||||
|
||||
// TODO: piece checks (e.g. size > sectorSize)?
|
||||
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
deal.Client: clientBalance,
|
||||
providerWorker: providerBalance,
|
||||
})
|
||||
if aerr != nil {
|
||||
return aerr
|
||||
}
|
||||
|
||||
st.Balances = bcid
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ActivateStorageDealsParams struct {
|
||||
Deals []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ActivateStorageDeals(act *types.Actor, vmctx types.VMContext, params *ActivateStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
for _, deal := range params.Deals {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(3, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if vmctx.Message().From != dealInfo.Provider {
|
||||
return nil, aerrors.New(1, "ActivateStorageDeals can only be called by the deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.ProposalExpiration {
|
||||
return nil, aerrors.New(2, "deal cannot be activated: proposal expired")
|
||||
}
|
||||
|
||||
if dealInfo.ActivationEpoch > 0 {
|
||||
// this probably can't happen in practice
|
||||
return nil, aerrors.New(3, "deal already active")
|
||||
}
|
||||
|
||||
dealInfo.ActivationEpoch = vmctx.BlockHeight()
|
||||
|
||||
if err := deals.Set(deal, &dealInfo); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal info in AMT failed")
|
||||
}
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr := vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ProcessStorageDealsPaymentParams struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ProcessStorageDealsPayment(act *types.Actor, vmctx types.VMContext, params *ProcessStorageDealsPaymentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// TODO: Would be nice if send could assert actor type
|
||||
workerBytes, aerr := vmctx.Send(vmctx.Message().From, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(2, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(3, "ProcessStorageDealsPayment can only be called by deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < dealInfo.ActivationEpoch {
|
||||
// TODO: This is probably fatal
|
||||
return nil, aerrors.New(4, "ActivationEpoch lower than block height")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.ActivationEpoch+dealInfo.Duration {
|
||||
// Deal expired, miner should drop it
|
||||
// TODO: process payment for the remainder of last proving period
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
toPay := types.BigMul(dealInfo.StoragePricePerEpoch, types.NewInt(build.SlashablePowerDelay))
|
||||
|
||||
b, bnd, aerr := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, dealInfo.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
clientBal := b[0]
|
||||
providerBal := b[1]
|
||||
|
||||
clientBal.Locked, providerBal.Available = transferFunds(clientBal.Locked, providerBal.Available, toPay)
|
||||
|
||||
// TODO: call set once
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
dealInfo.Client: clientBal,
|
||||
providerWorker: providerBal,
|
||||
})
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
}
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func lockFunds(p StorageParticipantBalance, amt types.BigInt) StorageParticipantBalance {
|
||||
p.Available, p.Locked = transferFunds(p.Available, p.Locked, amt)
|
||||
return p
|
||||
}
|
||||
|
||||
func transferFunds(from, to, amt types.BigInt) (types.BigInt, types.BigInt) {
|
||||
// TODO: some asserts
|
||||
return types.BigSub(from, amt), types.BigAdd(to, amt)
|
||||
}
|
||||
|
||||
type ComputeDataCommitmentParams struct {
|
||||
DealIDs []uint64
|
||||
SectorSize uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ComputeDataCommitment(act *types.Actor, vmctx types.VMContext, params *ComputeDataCommitmentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
if len(params.DealIDs) == 0 {
|
||||
return nil, aerrors.New(3, "no deal IDs")
|
||||
}
|
||||
|
||||
var pieces []sectorbuilder.PublicPieceInfo
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(4, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(5, "referenced deal was not from caller")
|
||||
}
|
||||
|
||||
var commP [32]byte
|
||||
copy(commP[:], dealInfo.PieceRef)
|
||||
|
||||
pieces = append(pieces, sectorbuilder.PublicPieceInfo{
|
||||
Size: dealInfo.PieceSize,
|
||||
CommP: commP,
|
||||
})
|
||||
}
|
||||
|
||||
commd, err := sectorbuilder.GenerateDataCommitment(params.SectorSize, pieces)
|
||||
if err != nil {
|
||||
return nil, aerrors.Absorb(err, 6, "failed to generate data commitment from pieces")
|
||||
}
|
||||
|
||||
return commd[:], nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) HandleCronAction(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SettleExpiredDeals(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SlashStorageDealCollateral(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) GetLastExpirationFromDealIDs(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
@ -1,858 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/trace"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
)
|
||||
|
||||
type StoragePowerActor struct{}
|
||||
|
||||
type spaMethods struct {
|
||||
Constructor uint64
|
||||
CreateStorageMiner uint64
|
||||
ArbitrateConsensusFault uint64
|
||||
UpdateStorage uint64
|
||||
GetTotalStorage uint64
|
||||
PowerLookup uint64
|
||||
IsValidMiner uint64
|
||||
PledgeCollateralForSize uint64
|
||||
CheckProofSubmissions uint64
|
||||
}
|
||||
|
||||
var SPAMethods = spaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
func (spa StoragePowerActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
//1: spa.StoragePowerConstructor,
|
||||
2: spa.CreateStorageMiner,
|
||||
3: spa.ArbitrateConsensusFault,
|
||||
4: spa.UpdateStorage,
|
||||
5: spa.GetTotalStorage,
|
||||
6: spa.PowerLookup,
|
||||
7: spa.IsValidMiner,
|
||||
8: spa.PledgeCollateralForSize,
|
||||
9: spa.CheckProofSubmissions,
|
||||
}
|
||||
}
|
||||
|
||||
type StoragePowerState struct {
|
||||
Miners cid.Cid
|
||||
ProvingBuckets cid.Cid // amt[ProvingPeriodBucket]hamt[minerAddress]struct{}
|
||||
MinerCount uint64
|
||||
LastMinerCheck uint64
|
||||
|
||||
TotalStorage types.BigInt
|
||||
}
|
||||
|
||||
type CreateStorageMinerParams struct {
|
||||
Owner address.Address
|
||||
Worker address.Address
|
||||
SectorSize uint64
|
||||
PeerID peer.ID
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VMContext, params *CreateStorageMinerParams) ([]byte, ActorError) {
|
||||
if !build.SupportedSectorSize(params.SectorSize) {
|
||||
return nil, aerrors.Newf(1, "Unsupported sector size: %d", params.SectorSize)
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqColl, err := pledgeCollateralForSize(vmctx, types.NewInt(0), self.TotalStorage, self.MinerCount+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().Value.LessThan(reqColl) {
|
||||
return nil, aerrors.Newf(1, "not enough funds passed to cover required miner collateral (needed %s, got %s)", reqColl, vmctx.Message().Value)
|
||||
}
|
||||
|
||||
// FORK
|
||||
minerCid := StorageMinerCodeCid
|
||||
if vmctx.BlockHeight() > build.ForkFrigidHeight {
|
||||
minerCid = StorageMiner2CodeCid
|
||||
}
|
||||
|
||||
encoded, err := CreateExecParams(minerCid, &StorageMinerConstructorParams{
|
||||
Owner: params.Owner,
|
||||
Worker: params.Worker,
|
||||
SectorSize: params.SectorSize,
|
||||
PeerID: params.PeerID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(InitAddress, IAMethods.Exec, vmctx.Message().Value, encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
naddr, nerr := address.NewFromBytes(ret)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(nerr, 2, "could not read address of new actor")
|
||||
}
|
||||
|
||||
ncid, err := MinerSetAdd(context.TODO(), vmctx, self.Miners, naddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.Miners = ncid
|
||||
self.MinerCount++
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return naddr.Bytes(), nil
|
||||
}
|
||||
|
||||
type ArbitrateConsensusFaultParams struct {
|
||||
Block1 *types.BlockHeader
|
||||
Block2 *types.BlockHeader
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) ArbitrateConsensusFault(act *types.Actor, vmctx types.VMContext, params *ArbitrateConsensusFaultParams) ([]byte, ActorError) {
|
||||
if params == nil || params.Block1 == nil || params.Block2 == nil {
|
||||
return nil, aerrors.New(1, "failed to parse params")
|
||||
}
|
||||
|
||||
if params.Block1.Miner != params.Block2.Miner {
|
||||
return nil, aerrors.New(2, "blocks must be from the same miner")
|
||||
}
|
||||
|
||||
// FORK
|
||||
if vmctx.BlockHeight() > build.ForkBlizzardHeight {
|
||||
if params.Block1.Height <= build.ForkBlizzardHeight {
|
||||
return nil, aerrors.New(10, "cannot slash miners with blocks from before blizzard")
|
||||
}
|
||||
|
||||
if params.Block2.Height <= build.ForkBlizzardHeight {
|
||||
return nil, aerrors.New(11, "cannot slash miners with blocks from before blizzard")
|
||||
}
|
||||
|
||||
if params.Block1.Cid() == params.Block2.Cid() {
|
||||
return nil, aerrors.New(3, "blocks must be different")
|
||||
}
|
||||
}
|
||||
|
||||
rval, err := vmctx.Send(params.Block1.Miner, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to get miner worker")
|
||||
}
|
||||
|
||||
worker, oerr := address.NewFromBytes(rval)
|
||||
if oerr != nil {
|
||||
// REVIEW: should this be fatal? i can't think of a real situation that would get us here
|
||||
return nil, aerrors.Absorb(oerr, 3, "response from 'GetWorkerAddr' was not a valid address")
|
||||
}
|
||||
|
||||
if err := sigs.CheckBlockSignature(params.Block1, vmctx.Context(), worker); err != nil {
|
||||
return nil, aerrors.Absorb(err, 4, "block1 did not have valid signature")
|
||||
}
|
||||
|
||||
if err := sigs.CheckBlockSignature(params.Block2, vmctx.Context(), worker); err != nil {
|
||||
return nil, aerrors.Absorb(err, 5, "block2 did not have valid signature")
|
||||
}
|
||||
|
||||
// see the "Consensus Faults" section of the faults spec (faults.md)
|
||||
// for details on these slashing conditions.
|
||||
if !shouldSlash(params.Block1, params.Block2) {
|
||||
return nil, aerrors.New(6, "blocks do not prove a slashable offense")
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if types.BigCmp(self.TotalStorage, types.NewInt(0)) == 0 {
|
||||
return nil, aerrors.Fatal("invalid state, storage power actor has zero total storage")
|
||||
}
|
||||
|
||||
miner := params.Block1.Miner
|
||||
if has, err := MinerSetHas(vmctx, self.Miners, miner); err != nil {
|
||||
return nil, aerrors.Wrapf(err, "failed to check miner in set")
|
||||
} else if !has {
|
||||
return nil, aerrors.New(7, "either already slashed or not a miner")
|
||||
}
|
||||
|
||||
minerPower, err := powerLookup(context.TODO(), vmctx, &self, miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slashedCollateral, err := pledgeCollateralForSize(vmctx, minerPower, self.TotalStorage, self.MinerCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enc, err := SerializeParams(&MinerSlashConsensusFault{
|
||||
Slasher: vmctx.Message().From,
|
||||
AtHeight: params.Block1.Height,
|
||||
SlashedCollateral: slashedCollateral,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(miner, MAMethods.SlashConsensusFault, types.NewInt(0), enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the miner from the list of network miners
|
||||
ncid, err := MinerSetRemove(context.TODO(), vmctx, self.Miners, miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.Miners = ncid
|
||||
self.MinerCount--
|
||||
|
||||
self.TotalStorage = types.BigSub(self.TotalStorage, minerPower)
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func cidArrContains(a []cid.Cid, b cid.Cid) bool {
|
||||
for _, c := range a {
|
||||
if b == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldSlash(block1, block2 *types.BlockHeader) bool {
|
||||
// First slashing condition, blocks have the same ticket round
|
||||
if block1.Height == block2.Height {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Second slashing condition requires having access to the parent tipset blocks
|
||||
// This might not always be available, needs some thought on the best way to deal with this
|
||||
|
||||
|
||||
// Second slashing condition, miner ignored own block when mining
|
||||
// Case A: block2 could have been in block1's parent set but is not
|
||||
b1ParentHeight := block1.Height - len(block1.Tickets)
|
||||
|
||||
block1ParentTipSet := block1.Parents
|
||||
if !cidArrContains(block1.Parents, block2.Cid()) &&
|
||||
b1ParentHeight == block2.Height &&
|
||||
block1ParentTipSet.ParentCids == block2.ParentCids {
|
||||
return true
|
||||
}
|
||||
|
||||
// Case B: block1 could have been in block2's parent set but is not
|
||||
block2ParentTipSet := parentOf(block2)
|
||||
if !block2Parent.contains(block1) &&
|
||||
block2ParentTipSet.Height == block1.Height &&
|
||||
block2ParentTipSet.ParentCids == block1.ParentCids {
|
||||
return true
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type UpdateStorageParams struct {
|
||||
Delta types.BigInt
|
||||
NextSlashDeadline uint64
|
||||
PreviousSlashDeadline uint64
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMContext, params *UpdateStorageParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
has, err := MinerSetHas(vmctx, self.Miners, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, aerrors.New(1, "update storage must only be called by a miner actor")
|
||||
}
|
||||
|
||||
self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta)
|
||||
|
||||
previousBucket := params.PreviousSlashDeadline % build.SlashablePowerDelay
|
||||
nextBucket := params.NextSlashDeadline % build.SlashablePowerDelay
|
||||
|
||||
if previousBucket == nextBucket && params.PreviousSlashDeadline != 0 {
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil // Nothing to do
|
||||
}
|
||||
|
||||
buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets)
|
||||
if eerr != nil {
|
||||
return nil, aerrors.HandleExternalError(eerr, "loading proving buckets amt")
|
||||
}
|
||||
|
||||
if params.PreviousSlashDeadline != 0 { // delete from previous bucket
|
||||
err := deleteMinerFromBucket(vmctx, buckets, previousBucket)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrapf(err, "delete from bucket %d, next %d", previousBucket, nextBucket)
|
||||
}
|
||||
}
|
||||
|
||||
err = addMinerToBucket(vmctx, buckets, nextBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.ProvingBuckets, eerr = buckets.Flush()
|
||||
if eerr != nil {
|
||||
return nil, aerrors.HandleExternalError(eerr, "flushing proving buckets")
|
||||
}
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func deleteMinerFromBucket(vmctx types.VMContext, buckets *amt.Root, previousBucket uint64) aerrors.ActorError {
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(previousBucket, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
return aerrors.HandleExternalError(err, "proving bucket missing")
|
||||
case nil: // noop
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
err = bhamt.Delete(vmctx.Context(), string(vmctx.Message().From.Bytes()))
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "deleting miner from proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Flush(vmctx.Context())
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "flushing previous proving bucket")
|
||||
}
|
||||
|
||||
bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "putting previous proving bucket hamt")
|
||||
}
|
||||
|
||||
err = buckets.Set(previousBucket, bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addMinerToBucket(vmctx types.VMContext, buckets *amt.Root, nextBucket uint64) aerrors.ActorError {
|
||||
var bhamt *hamt.Node
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(nextBucket, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
bhamt = hamt.NewNode(vmctx.Ipld())
|
||||
case nil:
|
||||
bhamt, err = hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Set(vmctx.Context(), string(vmctx.Message().From.Bytes()), CborNull)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting miner in proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Flush(vmctx.Context())
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "flushing previous proving bucket")
|
||||
}
|
||||
|
||||
bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "putting previous proving bucket hamt")
|
||||
}
|
||||
|
||||
err = buckets.Set(nextBucket, bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) GetTotalStorage(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.TotalStorage.Bytes(), nil
|
||||
}
|
||||
|
||||
type PowerLookupParams struct {
|
||||
Miner address.Address
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) PowerLookup(act *types.Actor, vmctx types.VMContext, params *PowerLookupParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, aerrors.Wrap(err, "getting head")
|
||||
}
|
||||
|
||||
pow, err := powerLookup(context.TODO(), vmctx, &self, params.Miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pow.Bytes(), nil
|
||||
}
|
||||
|
||||
func powerLookup(ctx context.Context, vmctx types.VMContext, self *StoragePowerState, miner address.Address) (types.BigInt, ActorError) {
|
||||
has, err := MinerSetHas(vmctx, self.Miners, miner)
|
||||
if err != nil {
|
||||
return types.EmptyInt, err
|
||||
}
|
||||
|
||||
if !has {
|
||||
// A miner could be registered with storage power actor, but removed for some reasons, e.g. consensus fault
|
||||
return types.EmptyInt, aerrors.New(1, "miner not registered with storage power actor, or removed already")
|
||||
}
|
||||
|
||||
// TODO: Use local amt
|
||||
ret, err := vmctx.Send(miner, MAMethods.GetPower, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return types.EmptyInt, aerrors.Wrap(err, "invoke Miner.GetPower")
|
||||
}
|
||||
|
||||
return types.BigFromBytes(ret), nil
|
||||
}
|
||||
|
||||
type IsValidMinerParam struct {
|
||||
Addr address.Address
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContext, param *IsValidMinerParam) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
has, err := MinerSetHas(vmctx, self.Miners, param.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !has {
|
||||
log.Warnf("Miner INVALID: not in set: %s", param.Addr)
|
||||
|
||||
return cbg.CborBoolFalse, nil
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(param.Addr, MAMethods.IsSlashed, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slashed := bytes.Equal(ret, cbg.CborBoolTrue)
|
||||
|
||||
if slashed {
|
||||
log.Warnf("Miner INVALID: /SLASHED/ : %s", param.Addr)
|
||||
}
|
||||
|
||||
return cbg.EncodeBool(!slashed), nil
|
||||
}
|
||||
|
||||
type PledgeCollateralParams struct {
|
||||
Size types.BigInt
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) PledgeCollateralForSize(act *types.Actor, vmctx types.VMContext, param *PledgeCollateralParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalCollateral, err := pledgeCollateralForSize(vmctx, param.Size, self.TotalStorage, self.MinerCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return totalCollateral.Bytes(), nil
|
||||
}
|
||||
|
||||
func pledgeCollateralForSize(vmctx types.VMContext, size, totalStorage types.BigInt, minerCount uint64) (types.BigInt, aerrors.ActorError) {
|
||||
netBalance, err := vmctx.GetBalance(NetworkAddress)
|
||||
if err != nil {
|
||||
return types.EmptyInt, err
|
||||
}
|
||||
|
||||
// TODO: the spec says to also grab 'total vested filecoin' and include it as available
|
||||
// If we don't factor that in, we effectively assume all of the locked up filecoin is 'available'
|
||||
// the blocker on that right now is that its hard to tell how much filecoin is unlocked
|
||||
|
||||
availableFilecoin := types.BigSub(
|
||||
types.BigMul(types.NewInt(build.TotalFilecoin), types.NewInt(build.FilecoinPrecision)),
|
||||
netBalance,
|
||||
)
|
||||
|
||||
totalPowerCollateral := types.BigDiv(
|
||||
types.BigMul(
|
||||
availableFilecoin,
|
||||
types.NewInt(build.PowerCollateralProportion),
|
||||
),
|
||||
types.NewInt(build.CollateralPrecision),
|
||||
)
|
||||
|
||||
totalPerCapitaCollateral := types.BigDiv(
|
||||
types.BigMul(
|
||||
availableFilecoin,
|
||||
types.NewInt(build.PerCapitaCollateralProportion),
|
||||
),
|
||||
types.NewInt(build.CollateralPrecision),
|
||||
)
|
||||
|
||||
// REVIEW: for bootstrapping purposes, we skip the power portion of the
|
||||
// collateral if there is no collateral in the network yet
|
||||
powerCollateral := types.NewInt(0)
|
||||
if types.BigCmp(totalStorage, types.NewInt(0)) != 0 {
|
||||
powerCollateral = types.BigDiv(
|
||||
types.BigMul(
|
||||
totalPowerCollateral,
|
||||
size,
|
||||
),
|
||||
totalStorage,
|
||||
)
|
||||
}
|
||||
|
||||
perCapCollateral := types.BigDiv(
|
||||
totalPerCapitaCollateral,
|
||||
types.NewInt(minerCount),
|
||||
)
|
||||
|
||||
return types.BigAdd(powerCollateral, perCapCollateral), nil
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) CheckProofSubmissions(act *types.Actor, vmctx types.VMContext, param *struct{}) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != CronAddress {
|
||||
return nil, aerrors.New(1, "CheckProofSubmissions is only callable from the cron actor")
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := self.LastMinerCheck; i < vmctx.BlockHeight(); i++ {
|
||||
height := i + 1
|
||||
|
||||
err := checkProofSubmissionsAtH(vmctx, &self, height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
self.LastMinerCheck = vmctx.BlockHeight()
|
||||
|
||||
nroot, aerr := vmctx.Storage().Put(&self)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkProofSubmissionsAtH(vmctx types.VMContext, self *StoragePowerState, height uint64) aerrors.ActorError {
|
||||
bucketID := height % build.SlashablePowerDelay
|
||||
|
||||
buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets)
|
||||
if eerr != nil {
|
||||
return aerrors.HandleExternalError(eerr, "loading proving buckets amt")
|
||||
}
|
||||
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(bucketID, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
return nil // nothing to do
|
||||
case nil:
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
|
||||
forRemoval := make([]address.Address, 0)
|
||||
|
||||
err = bhamt.ForEach(vmctx.Context(), func(k string, val interface{}) error {
|
||||
_, span := trace.StartSpan(vmctx.Context(), "StoragePowerActor.CheckProofSubmissions.loop")
|
||||
defer span.End()
|
||||
|
||||
maddr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return aerrors.Escalate(err, "parsing miner address")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > build.ForkMissingSnowballs {
|
||||
has, aerr := MinerSetHas(vmctx, self.Miners, maddr)
|
||||
if aerr != nil {
|
||||
return aerr
|
||||
}
|
||||
|
||||
if !has {
|
||||
forRemoval = append(forRemoval, maddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
span.AddAttributes(trace.StringAttribute("miner", maddr.String()))
|
||||
|
||||
params, err := SerializeParams(&CheckMinerParams{NetworkPower: self.TotalStorage})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(maddr, MAMethods.CheckMiner, types.NewInt(0), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil // miner is fine
|
||||
}
|
||||
|
||||
var power types.BigInt
|
||||
if err := power.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return xerrors.Errorf("unmarshaling CheckMiner response (%x): %w", ret, err)
|
||||
}
|
||||
|
||||
if power.GreaterThan(types.NewInt(0)) {
|
||||
log.Warnf("slashing miner %s for missed PoSt (%s B, H: %d, Bucket: %d)", maddr, power, height, bucketID)
|
||||
|
||||
self.TotalStorage = types.BigSub(self.TotalStorage, power)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "iterating miners in proving bucket")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > build.ForkMissingSnowballs && len(forRemoval) > 0 {
|
||||
nBucket, err := MinerSetRemove(vmctx.Context(), vmctx, bucket, forRemoval...)
|
||||
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "could not remove miners from set")
|
||||
}
|
||||
|
||||
eerr := buckets.Set(bucketID, nBucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(eerr, "could not set the bucket")
|
||||
}
|
||||
ncid, eerr := buckets.Flush()
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(eerr, "could not flush buckets")
|
||||
}
|
||||
self.ProvingBuckets = ncid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MinerSetHas(vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (bool, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return false, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
err = nd.Find(vmctx.Context(), string(maddr.Bytes()), nil)
|
||||
switch err {
|
||||
case hamt.ErrNotFound:
|
||||
return false, nil
|
||||
case nil:
|
||||
return true, nil
|
||||
default:
|
||||
return false, aerrors.HandleExternalError(err, "failed to do set lookup")
|
||||
}
|
||||
}
|
||||
|
||||
func MinerSetList(ctx context.Context, cst *hamt.CborIpldStore, rcid cid.Cid) ([]address.Address, error) {
|
||||
nd, err := hamt.LoadNode(ctx, cst, rcid)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner set: %w", err)
|
||||
}
|
||||
|
||||
var out []address.Address
|
||||
err = nd.ForEach(ctx, func(k string, val interface{}) error {
|
||||
addr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out = append(out, addr)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func MinerSetAdd(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (cid.Cid, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
mkey := string(maddr.Bytes())
|
||||
err = nd.Find(ctx, mkey, nil)
|
||||
if err == nil {
|
||||
return cid.Undef, aerrors.New(20, "miner already in set")
|
||||
}
|
||||
|
||||
if !xerrors.Is(err, hamt.ErrNotFound) {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to do miner set check")
|
||||
}
|
||||
|
||||
if err := nd.Set(ctx, mkey, uint64(1)); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "adding miner address to set failed")
|
||||
}
|
||||
|
||||
if err := nd.Flush(ctx); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush miner set")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(ctx, nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to persist miner set to storage")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func MinerSetRemove(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddrs ...address.Address) (cid.Cid, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
for _, maddr := range maddrs {
|
||||
mkey := string(maddr.Bytes())
|
||||
switch nd.Delete(ctx, mkey) {
|
||||
default:
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to delete miner from set")
|
||||
case hamt.ErrNotFound:
|
||||
return cid.Undef, aerrors.New(1, "miner not found in set on delete")
|
||||
case nil:
|
||||
}
|
||||
}
|
||||
|
||||
if err := nd.Flush(ctx); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush miner set")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(ctx, nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to persist miner set to storage")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type cbgNull struct{}
|
||||
|
||||
var CborNull = &cbgNull{}
|
||||
|
||||
func (cbgNull) MarshalCBOR(w io.Writer) error {
|
||||
n, err := w.Write(cbg.CborNull)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return xerrors.New("expected to write 1 byte")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cbgNull) UnmarshalCBOR(r io.Reader) error {
|
||||
b := [1]byte{}
|
||||
n, err := r.Read(b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return xerrors.New("expected 1 byte")
|
||||
}
|
||||
if !bytes.Equal(b[:], cbg.CborNull) {
|
||||
return xerrors.New("expected cbor null")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
. "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
var ownerAddr, workerAddr address.Address
|
||||
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&ownerAddr, 1000000),
|
||||
HarnessAddr(&workerAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
|
||||
var minerAddr address.Address
|
||||
{
|
||||
// cheating the bootstrapping problem
|
||||
cheatStorageMarketTotal(t, h.vm, h.cs.Blockstore())
|
||||
|
||||
ret, _ := h.InvokeWithValue(t, ownerAddr, StoragePowerAddress, SPAMethods.CreateStorageMiner,
|
||||
types.NewInt(500000),
|
||||
&CreateStorageMinerParams{
|
||||
Owner: ownerAddr,
|
||||
Worker: workerAddr,
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var err error
|
||||
minerAddr, err = address.NewFromBytes(ret.Return)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner,
|
||||
&IsValidMinerParam{Addr: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
var output bool
|
||||
err := cbor.DecodeInto(ret.Return, &output)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding: %+v", err)
|
||||
}
|
||||
|
||||
if !output {
|
||||
t.Fatalf("%s is miner but IsValidMiner call returned false", minerAddr)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
power := types.BigFromBytes(ret.Return)
|
||||
|
||||
if types.BigCmp(power, types.NewInt(0)) != 0 {
|
||||
t.Fatalf("power should be zero, is: %s", power)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, minerAddr, MAMethods.GetOwner, nil)
|
||||
ApplyOK(t, ret)
|
||||
oA, err := address.NewFromBytes(ret.Return)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ownerAddr, oA, "return from GetOwner should be equal to the owner")
|
||||
}
|
||||
|
||||
{
|
||||
b1 := fakeBlock(t, minerAddr, 100)
|
||||
b2 := fakeBlock(t, minerAddr, 101)
|
||||
|
||||
signBlock(t, h.w, workerAddr, b1)
|
||||
signBlock(t, h.w, workerAddr, b2)
|
||||
|
||||
h.BlockHeight = build.ForkBlizzardHeight + 1
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
|
||||
&ArbitrateConsensusFaultParams{
|
||||
Block1: b1,
|
||||
Block2: b1,
|
||||
})
|
||||
assert.Equal(t, uint8(3), ret.ExitCode, "should have failed with exit 3")
|
||||
|
||||
ret, _ = h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
|
||||
&ArbitrateConsensusFaultParams{
|
||||
Block1: b1,
|
||||
Block2: b2,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
assert.Equal(t, ret.ExitCode, byte(1))
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, &IsValidMinerParam{minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
assert.Equal(t, ret.Return, cbg.CborBoolFalse)
|
||||
}
|
||||
}
|
||||
|
||||
func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||
t.Helper()
|
||||
|
||||
sma, err := vm.StateTree().GetActor(StoragePowerAddress)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
|
||||
var smastate StoragePowerState
|
||||
if err := cst.Get(context.TODO(), sma.Head, &smastate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
smastate.TotalStorage = types.NewInt(10000)
|
||||
|
||||
c, err := cst.Put(context.TODO(), &smastate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sma.Head = c
|
||||
|
||||
if err := vm.StateTree().SetActor(StoragePowerAddress, sma); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader {
|
||||
c := fakeCid(t, 1)
|
||||
return &types.BlockHeader{Height: 8000, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
|
||||
}
|
||||
|
||||
func fakeCid(t *testing.T, s int) cid.Cid {
|
||||
t.Helper()
|
||||
c, err := cid.NewPrefixV1(cid.Raw, mh.IDENTITY).Sum([]byte(fmt.Sprintf("%d", s)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func signBlock(t *testing.T, w *wallet.Wallet, worker address.Address, blk *types.BlockHeader) {
|
||||
t.Helper()
|
||||
sb, err := blk.SigningBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := w.Sign(context.TODO(), worker, sb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blk.BlockSig = sig
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var AccountCodeCid cid.Cid
|
||||
var CronCodeCid cid.Cid
|
||||
var StoragePowerCodeCid cid.Cid
|
||||
var StorageMarketCodeCid cid.Cid
|
||||
var StorageMinerCodeCid cid.Cid
|
||||
var StorageMiner2CodeCid cid.Cid
|
||||
var MultisigCodeCid cid.Cid
|
||||
var InitCodeCid cid.Cid
|
||||
var PaymentChannelCodeCid cid.Cid
|
||||
|
||||
var InitAddress = mustIDAddress(0)
|
||||
var NetworkAddress = mustIDAddress(1)
|
||||
var StoragePowerAddress = mustIDAddress(2)
|
||||
var StorageMarketAddress = mustIDAddress(3) // TODO: missing from spec
|
||||
var CronAddress = mustIDAddress(4)
|
||||
var BurntFundsAddress = mustIDAddress(99)
|
||||
|
||||
func mustIDAddress(i uint64) address.Address {
|
||||
a, err := address.NewIDAddress(i)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func init() {
|
||||
pref := cid.NewPrefixV1(cid.Raw, mh.IDENTITY)
|
||||
mustSum := func(s string) cid.Cid {
|
||||
c, err := pref.Sum([]byte(s))
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
AccountCodeCid = mustSum("fil/1/account") // TODO: spec
|
||||
CronCodeCid = mustSum("fil/1/cron")
|
||||
StoragePowerCodeCid = mustSum("fil/1/power")
|
||||
StorageMarketCodeCid = mustSum("fil/1/market")
|
||||
StorageMinerCodeCid = mustSum("fil/1/miner")
|
||||
StorageMiner2CodeCid = mustSum("fil/1/miner/2")
|
||||
MultisigCodeCid = mustSum("fil/1/multisig")
|
||||
InitCodeCid = mustSum("fil/1/init")
|
||||
PaymentChannelCodeCid = mustSum("fil/1/paych")
|
||||
|
||||
BuiltInActors = map[cid.Cid]bool{
|
||||
StorageMarketCodeCid: true,
|
||||
StoragePowerCodeCid: true,
|
||||
StorageMinerCodeCid: true,
|
||||
StorageMiner2CodeCid: true,
|
||||
AccountCodeCid: true,
|
||||
InitCodeCid: true,
|
||||
MultisigCodeCid: true,
|
||||
PaymentChannelCodeCid: true,
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
. "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
)
|
||||
|
||||
func blsaddr(n uint64) address.Address {
|
||||
buf := make([]byte, 48)
|
||||
binary.PutUvarint(buf, n)
|
||||
|
||||
addr, err := address.NewBLSAddress(buf)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore) {
|
||||
bs := bstore.NewBlockstore(dstore.NewMapDatastore())
|
||||
|
||||
from := blsaddr(0)
|
||||
maddr := blsaddr(1)
|
||||
|
||||
actors := map[address.Address]types.BigInt{
|
||||
from: types.NewInt(1000000),
|
||||
maddr: types.NewInt(0),
|
||||
}
|
||||
st, err := gen.MakeInitialStateTree(bs, actors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cs := store.NewChainStore(bs, nil, nil)
|
||||
|
||||
// TODO: should probabaly mock out the randomness bit, nil works for now
|
||||
vm, err := vm.NewVM(stateroot, 1, nil, maddr, cs.Blockstore(), cs.VMSys())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return vm, []address.Address{from, maddr}, bs
|
||||
}
|
||||
|
||||
func TestVMInvokeMethod(t *testing.T) {
|
||||
vm, addrs, _ := setupVMTestEnv(t)
|
||||
from := addrs[0]
|
||||
|
||||
var err error
|
||||
cenc, err := SerializeParams(&StorageMinerConstructorParams{Owner: from, Worker: from})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
execparams := &ExecParams{
|
||||
Code: StorageMinerCodeCid,
|
||||
Params: cenc,
|
||||
}
|
||||
enc, err := SerializeParams(execparams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: InitAddress,
|
||||
From: from,
|
||||
Method: IAMethods.Exec,
|
||||
Params: enc,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(10000),
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
t.Fatal("invocation failed")
|
||||
}
|
||||
|
||||
outaddr, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if outaddr.String() != "t0102" {
|
||||
t.Fatal("hold up")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageMarketActorCreateMiner(t *testing.T) {
|
||||
vm, addrs, bs := setupVMTestEnv(t)
|
||||
from := addrs[0]
|
||||
maddr := addrs[1]
|
||||
|
||||
cheatStorageMarketTotal(t, vm, bs)
|
||||
|
||||
params := &StorageMinerConstructorParams{
|
||||
Owner: maddr,
|
||||
Worker: maddr,
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
}
|
||||
var err error
|
||||
enc, err := SerializeParams(params)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: StoragePowerAddress,
|
||||
From: from,
|
||||
Method: SPAMethods.CreateStorageMiner,
|
||||
Params: enc,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(10000),
|
||||
Value: types.NewInt(50000),
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
fmt.Println(ret.ActorErr)
|
||||
t.Fatal("invocation failed: ", ret.ExitCode)
|
||||
}
|
||||
|
||||
outaddr, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if outaddr.String() != "t0102" {
|
||||
t.Fatal("hold up")
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
)
|
||||
|
||||
func NewIDAddress(id uint64) (address.Address, ActorError) {
|
||||
a, err := address.NewIDAddress(id)
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not create ID Address")
|
||||
}
|
||||
return a, nil
|
||||
}
|
@ -3,13 +3,14 @@ package aerrors
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func IsFatal(err ActorError) bool {
|
||||
return err != nil && err.IsFatal()
|
||||
}
|
||||
func RetCode(err ActorError) uint8 {
|
||||
func RetCode(err ActorError) exitcode.ExitCode {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
@ -25,12 +26,12 @@ type internalActorError interface {
|
||||
type ActorError interface {
|
||||
error
|
||||
IsFatal() bool
|
||||
RetCode() uint8
|
||||
RetCode() exitcode.ExitCode
|
||||
}
|
||||
|
||||
type actorError struct {
|
||||
fatal bool
|
||||
retCode uint8
|
||||
retCode exitcode.ExitCode
|
||||
|
||||
msg string
|
||||
frame xerrors.Frame
|
||||
@ -41,7 +42,7 @@ func (e *actorError) IsFatal() bool {
|
||||
return e.fatal
|
||||
}
|
||||
|
||||
func (e *actorError) RetCode() uint8 {
|
||||
func (e *actorError) RetCode() exitcode.ExitCode {
|
||||
return e.retCode
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/xerrors"
|
||||
@ -31,5 +32,5 @@ func TestAbsorbeError(t *testing.T) {
|
||||
aw3 := Wrap(aw2, "creating miner in storage market")
|
||||
t.Logf("Verbose error: %+v", aw3)
|
||||
t.Logf("Normal error: %v", aw3)
|
||||
assert.Equal(t, uint8(35), RetCode(aw3))
|
||||
assert.Equal(t, exitcode.ExitCode(35), RetCode(aw3))
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// New creates a new non-fatal error
|
||||
func New(retCode uint8, message string) ActorError {
|
||||
func New(retCode exitcode.ExitCode, message string) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@ -29,7 +30,7 @@ func New(retCode uint8, message string) ActorError {
|
||||
}
|
||||
|
||||
// Newf creates a new non-fatal error
|
||||
func Newf(retCode uint8, format string, args ...interface{}) ActorError {
|
||||
func Newf(retCode exitcode.ExitCode, format string, args ...interface{}) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@ -48,6 +49,26 @@ func Newf(retCode uint8, format string, args ...interface{}) ActorError {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: bit hacky
|
||||
func NewfSkip(skip int, retCode exitcode.ExitCode, format string, args ...interface{}) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
retCode: 0,
|
||||
|
||||
msg: "tried creating an error and setting RetCode to 0",
|
||||
frame: xerrors.Caller(skip),
|
||||
err: fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
return &actorError{
|
||||
retCode: retCode,
|
||||
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
frame: xerrors.Caller(skip),
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(message string, args ...interface{}) ActorError {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@ -95,7 +116,7 @@ func Wrapf(err ActorError, format string, args ...interface{}) ActorError {
|
||||
}
|
||||
|
||||
// Absorb takes and error and makes in not fatal ActorError
|
||||
func Absorb(err error, retCode uint8, msg string) ActorError {
|
||||
func Absorb(err error, retCode exitcode.ExitCode, msg string) ActorError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@ -160,7 +181,7 @@ func HandleExternalError(err error, msg string) ActorError {
|
||||
}
|
||||
}
|
||||
|
||||
if xerrors.Is(err, &hamt.SerializationError{}) {
|
||||
if xerrors.Is(err, &cbor.SerializationError{}) {
|
||||
return &actorError{
|
||||
fatal: false,
|
||||
retCode: 253,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
||||
package actors
|
||||
|
||||
import "github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
|
||||
type ActorError = aerrors.ActorError
|
@ -1,46 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type update struct {
|
||||
start uint64
|
||||
method interface{}
|
||||
}
|
||||
|
||||
func withUpdates(updates ...update) interface{} {
|
||||
sort.Slice(updates, func(i, j int) bool { // so we iterate from newest below
|
||||
return updates[i].start > updates[j].start
|
||||
})
|
||||
|
||||
// <script type="application/javascript">
|
||||
|
||||
typ := reflect.TypeOf(updates[0].method)
|
||||
|
||||
out := reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) {
|
||||
vmctx := args[1].Interface().(types.VMContext)
|
||||
|
||||
for _, u := range updates {
|
||||
if vmctx.BlockHeight() > u.start {
|
||||
return reflect.ValueOf(u.method).Call(args)
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.ValueOf(notFound(vmctx)).Call([]reflect.Value{})
|
||||
})
|
||||
|
||||
return out.Interface()
|
||||
|
||||
// </script>
|
||||
}
|
||||
|
||||
func notFound(vmctx types.VMContext) func() ([]byte, ActorError) {
|
||||
return func() ([]byte, ActorError) {
|
||||
return nil, aerrors.Fatal("no code for method %d at height %d", vmctx.Message().Method, vmctx.BlockHeight())
|
||||
}
|
||||
}
|
@ -1,365 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
)
|
||||
|
||||
const testGasLimit = 10000
|
||||
|
||||
type HarnessInit struct {
|
||||
NAddrs uint64
|
||||
Addrs map[address.Address]types.BigInt
|
||||
Miner address.Address
|
||||
}
|
||||
|
||||
type HarnessStage int
|
||||
|
||||
const (
|
||||
HarnessPreInit HarnessStage = iota
|
||||
HarnessPostInit
|
||||
)
|
||||
|
||||
type HarnessOpt func(testing.TB, *Harness) error
|
||||
|
||||
type Harness struct {
|
||||
HI HarnessInit
|
||||
Stage HarnessStage
|
||||
Nonces map[address.Address]uint64
|
||||
GasCharges map[address.Address]types.BigInt
|
||||
Rand vm.Rand
|
||||
BlockHeight uint64
|
||||
|
||||
lastBalanceCheck map[address.Address]types.BigInt
|
||||
|
||||
ctx context.Context
|
||||
bs blockstore.Blockstore
|
||||
vm *vm.VM
|
||||
cs *store.ChainStore
|
||||
w *wallet.Wallet
|
||||
}
|
||||
|
||||
var HarnessMinerFunds = types.NewInt(1000000)
|
||||
|
||||
func HarnessAddr(addr *address.Address, value uint64) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPreInit {
|
||||
return nil
|
||||
}
|
||||
hi := &h.HI
|
||||
if addr.Empty() {
|
||||
k, err := h.w.GenerateKey(types.KTSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
*addr = k
|
||||
}
|
||||
hi.Addrs[*addr] = types.NewInt(value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func HarnessMiner(addr *address.Address) HarnessOpt {
|
||||
return func(_ testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPreInit {
|
||||
return nil
|
||||
}
|
||||
hi := &h.HI
|
||||
if addr.Empty() {
|
||||
*addr = hi.Miner
|
||||
return nil
|
||||
}
|
||||
delete(hi.Addrs, hi.Miner)
|
||||
hi.Miner = *addr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func HarnessActor(actor *address.Address, creator *address.Address, code cid.Cid, params func() cbg.CBORMarshaler) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPostInit {
|
||||
return nil
|
||||
}
|
||||
if !actor.Empty() {
|
||||
return xerrors.New("actor address should be empty")
|
||||
}
|
||||
|
||||
ret, _ := h.CreateActor(t, *creator, code, params())
|
||||
if ret.ExitCode != 0 {
|
||||
return xerrors.Errorf("creating actor: %w", ret.ActorErr)
|
||||
}
|
||||
var err error
|
||||
*actor, err = address.NewFromBytes(ret.Return)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func HarnessAddMiner(addr *address.Address, creator *address.Address) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPostInit {
|
||||
return nil
|
||||
}
|
||||
if !addr.Empty() {
|
||||
return xerrors.New("actor address should be empty")
|
||||
}
|
||||
ret, _ := h.InvokeWithValue(t, *creator, actors.StoragePowerAddress,
|
||||
actors.SPAMethods.CreateStorageMiner, types.NewInt(3000), &actors.StorageMinerConstructorParams{
|
||||
Owner: *creator,
|
||||
Worker: *creator,
|
||||
SectorSize: 1024,
|
||||
PeerID: "fakepeerid",
|
||||
})
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return xerrors.Errorf("creating actor: %w", ret.ActorErr)
|
||||
}
|
||||
var err error
|
||||
*addr, err = address.NewFromBytes(ret.Return)
|
||||
return err
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func HarnessCtx(ctx context.Context) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
h.ctx = ctx
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewHarness(t *testing.T, options ...HarnessOpt) *Harness {
|
||||
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := &Harness{
|
||||
Stage: HarnessPreInit,
|
||||
Nonces: make(map[address.Address]uint64),
|
||||
Rand: &fakeRand{},
|
||||
HI: HarnessInit{
|
||||
NAddrs: 1,
|
||||
Miner: blsaddr(0),
|
||||
Addrs: map[address.Address]types.BigInt{
|
||||
blsaddr(0): HarnessMinerFunds,
|
||||
},
|
||||
},
|
||||
GasCharges: make(map[address.Address]types.BigInt),
|
||||
|
||||
lastBalanceCheck: make(map[address.Address]types.BigInt),
|
||||
w: w,
|
||||
ctx: context.Background(),
|
||||
bs: bstore.NewBlockstore(dstore.NewMapDatastore()),
|
||||
BlockHeight: 0,
|
||||
}
|
||||
for _, opt := range options {
|
||||
err := opt(t, h)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying options: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
st, err := gen.MakeInitialStateTree(h.bs, h.HI.Addrs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err = gen.SetupStorageMarketActor(h.bs, stateroot, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h.cs = store.NewChainStore(h.bs, nil, vm.Syscalls(sectorbuilder.ProofVerifier))
|
||||
h.vm, err = vm.NewVM(stateroot, 1, h.Rand, h.HI.Miner, h.cs.Blockstore(), h.cs.VMSys())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.Stage = HarnessPostInit
|
||||
for _, opt := range options {
|
||||
err := opt(t, h)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying options: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Harness) Apply(t testing.TB, msg types.Message) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
if msg.Nonce == 0 {
|
||||
msg.Nonce, _ = h.Nonces[msg.From]
|
||||
h.Nonces[msg.From] = msg.Nonce + 1
|
||||
}
|
||||
|
||||
ret, err := h.vm.ApplyMessage(h.ctx, &msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying message: %+v", err)
|
||||
}
|
||||
|
||||
if ret != nil {
|
||||
if prev, ok := h.GasCharges[msg.From]; ok {
|
||||
h.GasCharges[msg.From] = types.BigAdd(prev, ret.GasUsed)
|
||||
} else {
|
||||
h.GasCharges[msg.From] = ret.GasUsed
|
||||
}
|
||||
}
|
||||
|
||||
stateroot, err := h.vm.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatalf("Flushing VM: %+v", err)
|
||||
}
|
||||
cst := hamt.CSTFromBstore(h.bs)
|
||||
state, err := state.LoadStateTree(cst, stateroot)
|
||||
if err != nil {
|
||||
t.Fatalf("Loading state tree: %+v", err)
|
||||
}
|
||||
return ret, state
|
||||
}
|
||||
|
||||
func (h *Harness) CreateActor(t testing.TB, from address.Address,
|
||||
code cid.Cid, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
|
||||
return h.Apply(t, types.Message{
|
||||
To: actors.InitAddress,
|
||||
From: from,
|
||||
Method: actors.IAMethods.Exec,
|
||||
Params: DumpObject(t,
|
||||
&actors.ExecParams{
|
||||
Code: code,
|
||||
Params: DumpObject(t, params),
|
||||
}),
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
Value: types.NewInt(0),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) SendFunds(t testing.TB, from address.Address, to address.Address,
|
||||
value types.BigInt) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
return h.Apply(t, types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: 0,
|
||||
Value: value,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) Invoke(t testing.TB, from address.Address, to address.Address,
|
||||
method uint64, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
return h.InvokeWithValue(t, from, to, method, types.NewInt(0), params)
|
||||
}
|
||||
|
||||
func (h *Harness) InvokeWithValue(t testing.TB, from address.Address, to address.Address,
|
||||
method uint64, value types.BigInt, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
h.vm.SetBlockHeight(h.BlockHeight)
|
||||
return h.Apply(t, types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: method,
|
||||
Value: value,
|
||||
Params: DumpObject(t, params),
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) AssertBalance(t testing.TB, addr address.Address, amt uint64) {
|
||||
t.Helper()
|
||||
|
||||
b, err := h.vm.ActorBalance(addr)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
if types.BigCmp(types.NewInt(amt), b) != 0 {
|
||||
t.Errorf("expected %s to have balanced of %d. Instead has %s", addr, amt, b)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Harness) AssertBalanceChange(t testing.TB, addr address.Address, amt int64) {
|
||||
t.Helper()
|
||||
lastBalance, ok := h.lastBalanceCheck[addr]
|
||||
if !ok {
|
||||
lastBalance, ok = h.HI.Addrs[addr]
|
||||
if !ok {
|
||||
lastBalance = types.NewInt(0)
|
||||
}
|
||||
}
|
||||
|
||||
var expected types.BigInt
|
||||
|
||||
if amt >= 0 {
|
||||
expected = types.BigAdd(lastBalance, types.NewInt(uint64(amt)))
|
||||
} else {
|
||||
expected = types.BigSub(lastBalance, types.NewInt(uint64(-amt)))
|
||||
}
|
||||
|
||||
h.lastBalanceCheck[addr] = expected
|
||||
|
||||
if gasUsed, ok := h.GasCharges[addr]; ok {
|
||||
expected = types.BigSub(expected, gasUsed)
|
||||
}
|
||||
|
||||
b, err := h.vm.ActorBalance(addr)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
if types.BigCmp(expected, b) != 0 {
|
||||
t.Errorf("expected %s to have balanced of %d. Instead has %s", addr, amt, b)
|
||||
}
|
||||
}
|
||||
|
||||
func DumpObject(t testing.TB, obj cbg.CBORMarshaler) []byte {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
t.Helper()
|
||||
b := new(bytes.Buffer)
|
||||
if err := obj.MarshalCBOR(b); err != nil {
|
||||
t.Fatalf("dumping params: %+v", err)
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
type fakeRand struct{}
|
||||
|
||||
func (fr *fakeRand) GetRandomness(ctx context.Context, h int64) ([]byte, error) {
|
||||
out := make([]byte, 32)
|
||||
rand.New(rand.NewSource(h)).Read(out)
|
||||
return out, nil
|
||||
}
|
@ -7,10 +7,6 @@ import (
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyStructCBOR = []byte{0xa0}
|
||||
)
|
||||
|
||||
func SerializeParams(i cbg.CBORMarshaler) ([]byte, aerrors.ActorError) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := i.MarshalCBOR(buf); err != nil {
|
||||
|
89
chain/beacon/beacon.go
Normal file
89
chain/beacon/beacon.go
Normal file
@ -0,0 +1,89 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var log = logging.Logger("beacon")
|
||||
|
||||
type Response struct {
|
||||
Entry types.BeaconEntry
|
||||
Err error
|
||||
}
|
||||
|
||||
type RandomBeacon interface {
|
||||
Entry(context.Context, uint64) <-chan Response
|
||||
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
|
||||
MaxBeaconRoundForEpoch(abi.ChainEpoch, types.BeaconEntry) uint64
|
||||
}
|
||||
|
||||
func ValidateBlockValues(b RandomBeacon, h *types.BlockHeader, prevEntry types.BeaconEntry) error {
|
||||
maxRound := b.MaxBeaconRoundForEpoch(h.Height, prevEntry)
|
||||
if maxRound == prevEntry.Round {
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
last := h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
if last.Round != maxRound {
|
||||
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
|
||||
}
|
||||
|
||||
for i, e := range h.BeaconEntries {
|
||||
if err := b.VerifyEntry(e, prevEntry); err != nil {
|
||||
return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err)
|
||||
}
|
||||
prevEntry = e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BeaconEntriesForBlock(ctx context.Context, beacon RandomBeacon, round abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
|
||||
start := time.Now()
|
||||
|
||||
maxRound := beacon.MaxBeaconRoundForEpoch(round, prev)
|
||||
if maxRound == prev.Round {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
|
||||
if prev.Round == 0 {
|
||||
prev.Round = maxRound - 1
|
||||
}
|
||||
|
||||
cur := maxRound
|
||||
var out []types.BeaconEntry
|
||||
for cur > prev.Round {
|
||||
rch := beacon.Entry(ctx, cur)
|
||||
select {
|
||||
case resp := <-rch:
|
||||
if resp.Err != nil {
|
||||
return nil, xerrors.Errorf("beacon entry request returned error: %w", resp.Err)
|
||||
}
|
||||
|
||||
out = append(out, resp.Entry)
|
||||
cur = resp.Entry.Round - 1
|
||||
case <-ctx.Done():
|
||||
return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for round %d: %w", round, ctx.Err())
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugw("fetching beacon entries", "took", time.Since(start), "numEntries", len(out))
|
||||
reverse(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func reverse(arr []types.BeaconEntry) {
|
||||
for i := 0; i < len(arr)/2; i++ {
|
||||
arr[i], arr[len(arr)-(1+i)] = arr[len(arr)-(1+i)], arr[i]
|
||||
}
|
||||
}
|
234
chain/beacon/drand/drand.go
Normal file
234
chain/beacon/drand/drand.go
Normal file
@ -0,0 +1,234 @@
|
||||
package drand
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
|
||||
dbeacon "github.com/drand/drand/beacon"
|
||||
"github.com/drand/drand/core"
|
||||
dkey "github.com/drand/drand/key"
|
||||
dnet "github.com/drand/drand/net"
|
||||
dproto "github.com/drand/drand/protobuf/drand"
|
||||
)
|
||||
|
||||
var log = logging.Logger("drand")
|
||||
|
||||
var drandServers = []string{
|
||||
"nicolas.drand.fil-test.net:443",
|
||||
"philipp.drand.fil-test.net:443",
|
||||
"mathilde.drand.fil-test.net:443",
|
||||
"ludovic.drand.fil-test.net:443",
|
||||
"gabbi.drand.fil-test.net:443",
|
||||
"linus.drand.fil-test.net:443",
|
||||
"jeff.drand.fil-test.net:443",
|
||||
}
|
||||
|
||||
var drandPubKey *dkey.DistPublic
|
||||
|
||||
func init() {
|
||||
drandPubKey = new(dkey.DistPublic)
|
||||
err := drandPubKey.FromTOML(&dkey.DistPublicTOML{Coefficients: build.DrandCoeffs})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type drandPeer struct {
|
||||
addr string
|
||||
tls bool
|
||||
}
|
||||
|
||||
func (dp *drandPeer) Address() string {
|
||||
return dp.addr
|
||||
}
|
||||
|
||||
func (dp *drandPeer) IsTLS() bool {
|
||||
return dp.tls
|
||||
}
|
||||
|
||||
type DrandBeacon struct {
|
||||
client dnet.Client
|
||||
|
||||
peers []dnet.Peer
|
||||
peersIndex int
|
||||
peersIndexMtx sync.Mutex
|
||||
|
||||
pubkey *dkey.DistPublic
|
||||
|
||||
// seconds
|
||||
interval time.Duration
|
||||
|
||||
drandGenTime uint64
|
||||
filGenTime uint64
|
||||
filRoundTime uint64
|
||||
|
||||
cacheLk sync.Mutex
|
||||
localCache map[uint64]types.BeaconEntry
|
||||
}
|
||||
|
||||
func NewDrandBeacon(genesisTs, interval uint64) (*DrandBeacon, error) {
|
||||
if genesisTs == 0 {
|
||||
panic("what are you doing this cant be zero")
|
||||
}
|
||||
db := &DrandBeacon{
|
||||
client: dnet.NewGrpcClient(),
|
||||
localCache: make(map[uint64]types.BeaconEntry),
|
||||
}
|
||||
for _, ds := range drandServers {
|
||||
db.peers = append(db.peers, &drandPeer{addr: ds, tls: true})
|
||||
}
|
||||
|
||||
db.peersIndex = rand.Intn(len(db.peers))
|
||||
|
||||
groupResp, err := db.client.Group(context.TODO(), db.peers[db.peersIndex], &dproto.GroupRequest{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get group response from beacon peer: %w", err)
|
||||
}
|
||||
|
||||
kgroup, err := core.ProtoToGroup(groupResp)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse group response: %w", err)
|
||||
}
|
||||
|
||||
// TODO: verify these values are what we expect them to be
|
||||
if !kgroup.PublicKey.Equal(drandPubKey) {
|
||||
return nil, xerrors.Errorf("public key does not match")
|
||||
}
|
||||
// fmt.Printf("Drand Pubkey:\n%#v\n", kgroup.PublicKey.TOML()) // use to print public key
|
||||
db.pubkey = drandPubKey
|
||||
db.interval = kgroup.Period
|
||||
db.drandGenTime = uint64(kgroup.GenesisTime)
|
||||
db.filRoundTime = interval
|
||||
db.filGenTime = genesisTs
|
||||
|
||||
// TODO: the stream currently gives you back *all* values since drand genesis.
|
||||
// Having the stream in the background is merely an optimization, so not a big deal to disable it for now
|
||||
// go db.handleStreamingUpdates()
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) rotatePeersIndex() {
|
||||
db.peersIndexMtx.Lock()
|
||||
nval := rand.Intn(len(db.peers))
|
||||
db.peersIndex = nval
|
||||
db.peersIndexMtx.Unlock()
|
||||
|
||||
log.Warnf("rotated to drand peer %d, %q", nval, db.peers[nval].Address())
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) getPeerIndex() int {
|
||||
db.peersIndexMtx.Lock()
|
||||
defer db.peersIndexMtx.Unlock()
|
||||
return db.peersIndex
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) handleStreamingUpdates() {
|
||||
for {
|
||||
p := db.peers[db.getPeerIndex()]
|
||||
ch, err := db.client.PublicRandStream(context.Background(), p, &dproto.PublicRandRequest{})
|
||||
if err != nil {
|
||||
log.Warnf("failed to get public rand stream to peer %q: %s", p.Address(), err)
|
||||
log.Warnf("trying again in 10 seconds")
|
||||
db.rotatePeersIndex()
|
||||
time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
|
||||
for e := range ch {
|
||||
db.cacheValue(types.BeaconEntry{
|
||||
Round: e.Round,
|
||||
Data: e.Signature,
|
||||
})
|
||||
}
|
||||
|
||||
log.Warnf("drand beacon stream to peer %q broke, reconnecting in 10 seconds", p.Address())
|
||||
db.rotatePeersIndex()
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.Response {
|
||||
// check cache, it it if there, otherwise query the endpoint
|
||||
cres := db.getCachedValue(round)
|
||||
if cres != nil {
|
||||
out := make(chan beacon.Response, 1)
|
||||
out <- beacon.Response{Entry: *cres}
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
|
||||
out := make(chan beacon.Response, 1)
|
||||
|
||||
go func() {
|
||||
p := db.peers[db.getPeerIndex()]
|
||||
resp, err := db.client.PublicRand(ctx, p, &dproto.PublicRandRequest{Round: round})
|
||||
|
||||
var br beacon.Response
|
||||
if err != nil {
|
||||
db.rotatePeersIndex()
|
||||
br.Err = xerrors.Errorf("drand peer %q failed publicRand request: %w", p.Address(), err)
|
||||
} else {
|
||||
br.Entry.Round = resp.GetRound()
|
||||
br.Entry.Data = resp.GetSignature()
|
||||
}
|
||||
|
||||
out <- br
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) cacheValue(e types.BeaconEntry) {
|
||||
db.cacheLk.Lock()
|
||||
defer db.cacheLk.Unlock()
|
||||
db.localCache[e.Round] = e
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry {
|
||||
db.cacheLk.Lock()
|
||||
defer db.cacheLk.Unlock()
|
||||
v, ok := db.localCache[round]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error {
|
||||
if prev.Round == 0 {
|
||||
// TODO handle genesis better
|
||||
return nil
|
||||
}
|
||||
b := &dbeacon.Beacon{
|
||||
PreviousSig: prev.Data,
|
||||
Round: curr.Round,
|
||||
Signature: curr.Data,
|
||||
}
|
||||
//log.Warnw("VerifyEntry", "beacon", b)
|
||||
err := dbeacon.VerifyBeacon(db.pubkey.Key(), b)
|
||||
if err == nil {
|
||||
db.cacheValue(curr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) MaxBeaconRoundForEpoch(filEpoch abi.ChainEpoch, prevEntry types.BeaconEntry) uint64 {
|
||||
// TODO: sometimes the genesis time for filecoin is zero and this goes negative
|
||||
latestTs := ((uint64(filEpoch) * db.filRoundTime) + db.filGenTime) - db.filRoundTime
|
||||
dround := (latestTs - db.drandGenTime) / uint64(db.interval.Seconds())
|
||||
return dround
|
||||
}
|
||||
|
||||
var _ beacon.RandomBeacon = (*DrandBeacon)(nil)
|
14
chain/beacon/drand/drand_test.go
Normal file
14
chain/beacon/drand/drand_test.go
Normal file
@ -0,0 +1,14 @@
|
||||
package drand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrintDrandPubkey(t *testing.T) {
|
||||
bc, err := NewDrandBeacon(1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Drand Pubkey:\n%#v\n", bc.pubkey.TOML())
|
||||
}
|
64
chain/beacon/mock.go
Normal file
64
chain/beacon/mock.go
Normal file
@ -0,0 +1,64 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Mock beacon assumes that filecoin rounds are 1:1 mapped with the beacon rounds
|
||||
type mockBeacon struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func NewMockBeacon(interval time.Duration) RandomBeacon {
|
||||
mb := &mockBeacon{interval: interval}
|
||||
|
||||
return mb
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) RoundTime() time.Duration {
|
||||
return mb.interval
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) entryForIndex(index uint64) types.BeaconEntry {
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, index)
|
||||
rval := blake2b.Sum256(buf)
|
||||
return types.BeaconEntry{
|
||||
Round: index,
|
||||
Data: rval[:],
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) Entry(ctx context.Context, index uint64) <-chan Response {
|
||||
e := mb.entryForIndex(index)
|
||||
out := make(chan Response, 1)
|
||||
out <- Response{Entry: e}
|
||||
return out
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, to types.BeaconEntry) error {
|
||||
// TODO: cache this, especially for bls
|
||||
oe := mb.entryForIndex(from.Round)
|
||||
if !bytes.Equal(from.Data, oe.Data) {
|
||||
return xerrors.Errorf("mock beacon entry was invalid!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) IsEntryForEpoch(e types.BeaconEntry, epoch abi.ChainEpoch, nulls int) (bool, error) {
|
||||
return int64(e.Round) <= int64(epoch) && int64(epoch)-int64(nulls) >= int64(e.Round), nil
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) MaxBeaconRoundForEpoch(epoch abi.ChainEpoch, prevEntry types.BeaconEntry) uint64 {
|
||||
return uint64(epoch)
|
||||
}
|
||||
|
||||
var _ RandomBeacon = (*mockBeacon)(nil)
|
@ -138,7 +138,7 @@ func (bss *BlockSyncService) processRequest(ctx context.Context, p peer.ID, req
|
||||
reqlen = BlockSyncMaxRequestLength
|
||||
}
|
||||
|
||||
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), reqlen, opts)
|
||||
chain, err := collectChainSegment(bss.cs, types.NewTipSetKey(req.Start...), reqlen, opts)
|
||||
if err != nil {
|
||||
log.Warn("encountered error while responding to block sync request: ", err)
|
||||
return &BlockSyncResponse{
|
||||
@ -158,18 +158,18 @@ func (bss *BlockSyncService) processRequest(ctx context.Context, p peer.ID, req
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) collectChainSegment(start types.TipSetKey, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
func collectChainSegment(cs *store.ChainStore, start types.TipSetKey, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
var bstips []*BSTipSet
|
||||
cur := start
|
||||
for {
|
||||
var bst BSTipSet
|
||||
ts, err := bss.cs.LoadTipSet(cur)
|
||||
ts, err := cs.LoadTipSet(cur)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed loading tipset %s: %w", cur, err)
|
||||
}
|
||||
|
||||
if opts.IncludeMessages {
|
||||
bmsgs, bmincl, smsgs, smincl, err := bss.gatherMessages(ts)
|
||||
bmsgs, bmincl, smsgs, smincl, err := gatherMessages(cs, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("gather messages failed: %w", err)
|
||||
}
|
||||
@ -194,7 +194,7 @@ func (bss *BlockSyncService) collectChainSegment(start types.TipSetKey, length u
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
func gatherMessages(cs *store.ChainStore, ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
blsmsgmap := make(map[cid.Cid]uint64)
|
||||
secpkmsgmap := make(map[cid.Cid]uint64)
|
||||
var secpkmsgs []*types.SignedMessage
|
||||
@ -202,7 +202,7 @@ func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message,
|
||||
var secpkincl, blsincl [][]uint64
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := bss.cs.MessagesForBlock(b)
|
||||
bmsgs, smsgs, err := cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-cid"
|
||||
graphsync "github.com/ipfs/go-graphsync"
|
||||
gsnet "github.com/ipfs/go-graphsync/network"
|
||||
host "github.com/libp2p/go-libp2p-core/host"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
@ -22,24 +24,26 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
incrt "github.com/filecoin-project/lotus/lib/increadtimeout"
|
||||
"github.com/filecoin-project/lotus/lib/peermgr"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
)
|
||||
|
||||
type BlockSync struct {
|
||||
bserv bserv.BlockService
|
||||
gsync graphsync.GraphExchange
|
||||
host host.Host
|
||||
|
||||
syncPeers *bsPeerTracker
|
||||
peerMgr *peermgr.PeerMgr
|
||||
}
|
||||
|
||||
func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermgr.MaybePeerMgr) *BlockSync {
|
||||
func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermgr.MaybePeerMgr, gs dtypes.Graphsync) *BlockSync {
|
||||
return &BlockSync{
|
||||
bserv: bserv,
|
||||
host: h,
|
||||
syncPeers: newPeerTracker(pmgr.Mgr),
|
||||
peerMgr: pmgr.Mgr,
|
||||
gsync: gs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +105,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count i
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Status == 0 {
|
||||
if res.Status == StatusOK || res.Status == StatusPartial {
|
||||
resp, err := bs.processBlocksResponse(req, res)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("success response from peer failed to process: %w", err)
|
||||
@ -110,6 +114,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count i
|
||||
bs.host.ConnManager().TagPeer(p, "bsync", 25)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
oerr = bs.processStatus(req, res)
|
||||
if oerr != nil {
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), oerr)
|
||||
@ -141,7 +146,7 @@ func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, tsk types.Tip
|
||||
|
||||
return bstsToFullTipSet(bts)
|
||||
case 101: // Partial Response
|
||||
return nil, xerrors.Errorf("partial responses are not handled")
|
||||
return nil, xerrors.Errorf("partial responses are not handled for single tipset fetching")
|
||||
case 201: // req.Start not found
|
||||
return nil, fmt.Errorf("not found")
|
||||
case 202: // Go Away
|
||||
@ -181,7 +186,7 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
req := &BlockSyncRequest{
|
||||
Start: h.Cids(),
|
||||
RequestLength: count,
|
||||
Options: BSOptMessages | BSOptBlocks,
|
||||
Options: BSOptMessages,
|
||||
}
|
||||
|
||||
var err error
|
||||
@ -201,8 +206,8 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
}
|
||||
|
||||
if res.Status == StatusPartial {
|
||||
log.Warn("dont yet handle partial responses")
|
||||
continue
|
||||
// TODO: track partial response sizes to ensure we don't overrequest too often
|
||||
return res.Chain, nil
|
||||
}
|
||||
|
||||
err = bs.processStatus(req, res)
|
||||
@ -234,14 +239,45 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
|
||||
}
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("peer", p.Pretty()),
|
||||
)
|
||||
}
|
||||
|
||||
gsproto := string(gsnet.ProtocolGraphsync)
|
||||
supp, err := bs.host.Peerstore().SupportsProtocols(p, BlockSyncProtocolID, gsproto)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get protocols for peer: %w", err)
|
||||
}
|
||||
|
||||
if len(supp) == 0 {
|
||||
return nil, xerrors.Errorf("peer %s supports no known sync protocols", p)
|
||||
}
|
||||
|
||||
switch supp[0] {
|
||||
case BlockSyncProtocolID:
|
||||
res, err := bs.fetchBlocksBlockSync(ctx, p, req)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("blocksync req failed: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
case gsproto:
|
||||
res, err := bs.fetchBlocksGraphSync(ctx, p, req)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("graphsync req failed: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("peerstore somehow returned unexpected protocols: %v", supp)
|
||||
}
|
||||
|
||||
}
|
||||
func (bs *BlockSync) fetchBlocksBlockSync(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockSyncFetch")
|
||||
defer span.End()
|
||||
|
||||
start := time.Now()
|
||||
s, err := bs.host.NewStream(inet.WithNoDial(ctx, "should already have connection"), p, BlockSyncProtocolID)
|
||||
if err != nil {
|
||||
bs.RemovePeer(p)
|
||||
@ -272,7 +308,6 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
|
||||
}
|
||||
|
||||
bs.syncPeers.logSuccess(p, time.Since(start))
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
@ -5,54 +7,60 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
var lengthBufBlockSyncRequest = []byte{131}
|
||||
|
||||
func (t *BlockSyncRequest) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
if _, err := w.Write(lengthBufBlockSyncRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Start ([]cid.Cid) (slice)
|
||||
if len(t.Start) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Start was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Start)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Start))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Start {
|
||||
if err := cbg.WriteCid(w, v); err != nil {
|
||||
if err := cbg.WriteCidBuf(scratch, w, v); err != nil {
|
||||
return xerrors.Errorf("failed writing cid field t.Start: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.RequestLength (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.RequestLength))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.RequestLength)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Options (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Options))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Options)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -66,7 +74,7 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Start ([]cid.Cid) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -78,9 +86,11 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Start = make([]cid.Cid, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
@ -92,42 +102,54 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.RequestLength (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.RequestLength = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.RequestLength = uint64(extra)
|
||||
// t.Options (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Options = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Options = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
var lengthBufBlockSyncResponse = []byte{131}
|
||||
|
||||
func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
if _, err := w.Write(lengthBufBlockSyncResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
if len(t.Chain) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Chain was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Chain)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Chain))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Chain {
|
||||
@ -137,7 +159,8 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
|
||||
// t.Status (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Status))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Status)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -146,10 +169,10 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Value in field t.Message was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Message)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Message))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Message)); err != nil {
|
||||
if _, err := io.WriteString(w, t.Message); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -157,8 +180,9 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -172,7 +196,7 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -184,9 +208,11 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Chain = make([]*BSTipSet, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v BSTipSet
|
||||
@ -199,18 +225,22 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Status (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Status = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Status = uint64(extra)
|
||||
// t.Message (string) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -220,21 +250,25 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var lengthBufBSTipSet = []byte{133}
|
||||
|
||||
func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{133}); err != nil {
|
||||
if _, err := w.Write(lengthBufBSTipSet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Blocks ([]*types.BlockHeader) (slice)
|
||||
if len(t.Blocks) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Blocks was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Blocks)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Blocks))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Blocks {
|
||||
@ -248,7 +282,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.BlsMessages was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMessages)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.BlsMessages))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.BlsMessages {
|
||||
@ -262,7 +296,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.BlsMsgIncludes was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMsgIncludes)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.BlsMsgIncludes))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.BlsMsgIncludes {
|
||||
@ -270,11 +304,11 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field v was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(v)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(v))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range v {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, uint64(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -285,7 +319,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.SecpkMessages was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMessages)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.SecpkMessages))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.SecpkMessages {
|
||||
@ -299,7 +333,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.SecpkMsgIncludes was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMsgIncludes)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.SecpkMsgIncludes))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.SecpkMsgIncludes {
|
||||
@ -307,11 +341,11 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field v was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(v)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(v))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range v {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, uint64(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -321,8 +355,9 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -336,7 +371,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Blocks ([]*types.BlockHeader) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -348,9 +383,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Blocks = make([]*types.BlockHeader, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.BlockHeader
|
||||
@ -363,7 +400,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.BlsMessages ([]*types.Message) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -375,9 +412,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMessages = make([]*types.Message, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.Message
|
||||
@ -390,7 +429,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.BlsMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -402,16 +441,18 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMsgIncludes = make([][]uint64, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
{
|
||||
var maj byte
|
||||
var extra uint64
|
||||
var err error
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -423,12 +464,14 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMsgIncludes[i] = make([]uint64, extra)
|
||||
}
|
||||
|
||||
for j := 0; j < int(extra); j++ {
|
||||
|
||||
maj, val, err := cbg.CborReadHeader(br)
|
||||
maj, val, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read uint64 for t.BlsMsgIncludes[i] slice: %w", err)
|
||||
}
|
||||
@ -437,7 +480,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
return xerrors.Errorf("value read for array t.BlsMsgIncludes[i] was not a uint, instead got %d", maj)
|
||||
}
|
||||
|
||||
t.BlsMsgIncludes[i][j] = val
|
||||
t.BlsMsgIncludes[i][j] = uint64(val)
|
||||
}
|
||||
|
||||
}
|
||||
@ -445,7 +488,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.SecpkMessages ([]*types.SignedMessage) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -457,9 +500,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMessages = make([]*types.SignedMessage, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedMessage
|
||||
@ -472,7 +517,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.SecpkMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -484,16 +529,18 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMsgIncludes = make([][]uint64, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
{
|
||||
var maj byte
|
||||
var extra uint64
|
||||
var err error
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -505,12 +552,14 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMsgIncludes[i] = make([]uint64, extra)
|
||||
}
|
||||
|
||||
for j := 0; j < int(extra); j++ {
|
||||
|
||||
maj, val, err := cbg.CborReadHeader(br)
|
||||
maj, val, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read uint64 for t.SecpkMsgIncludes[i] slice: %w", err)
|
||||
}
|
||||
@ -519,7 +568,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
return xerrors.Errorf("value read for array t.SecpkMsgIncludes[i] was not a uint, instead got %d", maj)
|
||||
}
|
||||
|
||||
t.SecpkMsgIncludes[i][j] = val
|
||||
t.SecpkMsgIncludes[i][j] = uint64(val)
|
||||
}
|
||||
|
||||
}
|
||||
|
151
chain/blocksync/graphsync_client.go
Normal file
151
chain/blocksync/graphsync_client.go
Normal file
@ -0,0 +1,151 @@
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-graphsync"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
store "github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||
basicnode "github.com/ipld/go-ipld-prime/node/basic"
|
||||
ipldselector "github.com/ipld/go-ipld-prime/traversal/selector"
|
||||
selectorbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// AMT selector recursion. An AMT has arity of 8 so this gives allows
|
||||
// us to retrieve trees with 8^10 (1,073,741,824) elements.
|
||||
amtRecursionDepth = uint32(10)
|
||||
|
||||
// some constants for looking up tuple encoded struct fields
|
||||
// field index of Parents field in a block header
|
||||
blockIndexParentsField = 5
|
||||
|
||||
// field index of Messages field in a block header
|
||||
blockIndexMessagesField = 10
|
||||
|
||||
// field index of AMT node in AMT head
|
||||
amtHeadNodeFieldIndex = 2
|
||||
|
||||
// field index of links array AMT node
|
||||
amtNodeLinksFieldIndex = 1
|
||||
|
||||
// field index of values array AMT node
|
||||
amtNodeValuesFieldIndex = 2
|
||||
|
||||
// maximum depth per traversal
|
||||
maxRequestLength = 50
|
||||
)
|
||||
|
||||
var amtSelector selectorbuilder.SelectorSpec
|
||||
|
||||
func init() {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
// amt selector -- needed to selector through a messages AMT
|
||||
amtSelector = ssb.ExploreIndex(amtHeadNodeFieldIndex,
|
||||
ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(amtRecursionDepth)),
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreIndex(amtNodeLinksFieldIndex,
|
||||
ssb.ExploreAll(ssb.ExploreRecursiveEdge())),
|
||||
ssb.ExploreIndex(amtNodeValuesFieldIndex,
|
||||
ssb.ExploreAll(ssb.Matcher())))))
|
||||
}
|
||||
|
||||
func selectorForRequest(req *BlockSyncRequest) ipld.Node {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
|
||||
bso := ParseBSOptions(req.Options)
|
||||
if bso.IncludeMessages {
|
||||
return ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(req.RequestLength)),
|
||||
ssb.ExploreIndex(blockIndexParentsField,
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreAll(
|
||||
ssb.ExploreIndex(blockIndexMessagesField,
|
||||
ssb.ExploreRange(0, 2, amtSelector),
|
||||
)),
|
||||
ssb.ExploreIndex(0, ssb.ExploreRecursiveEdge()),
|
||||
))).Node()
|
||||
}
|
||||
return ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(req.RequestLength)), ssb.ExploreIndex(blockIndexParentsField,
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreAll(
|
||||
ssb.Matcher(),
|
||||
),
|
||||
ssb.ExploreIndex(0, ssb.ExploreRecursiveEdge()),
|
||||
))).Node()
|
||||
}
|
||||
|
||||
func firstTipsetSelector(req *BlockSyncRequest) ipld.Node {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
|
||||
bso := ParseBSOptions(req.Options)
|
||||
if bso.IncludeMessages {
|
||||
return ssb.ExploreIndex(blockIndexMessagesField,
|
||||
ssb.ExploreRange(0, 2, amtSelector),
|
||||
).Node()
|
||||
}
|
||||
return ssb.Matcher().Node()
|
||||
|
||||
}
|
||||
|
||||
func (bs *BlockSync) executeGsyncSelector(ctx context.Context, p peer.ID, root cid.Cid, sel ipld.Node) error {
|
||||
extension := graphsync.ExtensionData{
|
||||
Name: "chainsync",
|
||||
Data: nil,
|
||||
}
|
||||
_, errs := bs.gsync.Request(ctx, p, cidlink.Link{Cid: root}, sel, extension)
|
||||
|
||||
for err := range errs {
|
||||
return xerrors.Errorf("failed to complete graphsync request: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback for interacting with other non-lotus nodes
|
||||
func (bs *BlockSync) fetchBlocksGraphSync(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
immediateTsSelector := firstTipsetSelector(req)
|
||||
|
||||
// Do this because we can only request one root at a time
|
||||
for _, r := range req.Start {
|
||||
if err := bs.executeGsyncSelector(ctx, p, r, immediateTsSelector); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if req.RequestLength > maxRequestLength {
|
||||
req.RequestLength = maxRequestLength
|
||||
}
|
||||
|
||||
sel := selectorForRequest(req)
|
||||
|
||||
// execute the selector forreal
|
||||
if err := bs.executeGsyncSelector(ctx, p, req.Start[0], sel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now pull the data we fetched out of the chainstore (where it should now be persisted)
|
||||
tempcs := store.NewChainStore(bs.bserv.Blockstore(), datastore.NewMapDatastore(), nil)
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
tsk := types.NewTipSetKey(req.Start...)
|
||||
chain, err := collectChainSegment(tempcs, tsk, req.RequestLength, opts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load chain data from chainstore after successful graphsync response (start = %v): %w", req.Start, err)
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{Chain: chain}, nil
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
@ -19,7 +20,7 @@ import (
|
||||
var log = logging.Logger("events")
|
||||
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type HeightHandler func(ctx context.Context, ts *types.TipSet, curH uint64) error
|
||||
type HeightHandler func(ctx context.Context, ts *types.TipSet, curH abi.ChainEpoch) error
|
||||
type RevertHandler func(ctx context.Context, ts *types.TipSet) error
|
||||
|
||||
type heightHandler struct {
|
||||
@ -31,9 +32,9 @@ type heightHandler struct {
|
||||
}
|
||||
|
||||
type eventApi interface {
|
||||
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
|
||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error)
|
||||
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // optional / for CalledMsg
|
||||
@ -65,11 +66,11 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
heightEvents: heightEvents{
|
||||
tsc: tsc,
|
||||
ctx: ctx,
|
||||
gcConfidence: uint64(gcConfidence),
|
||||
gcConfidence: abi.ChainEpoch(gcConfidence),
|
||||
|
||||
heightTriggers: map[uint64]*heightHandler{},
|
||||
htTriggerHeights: map[uint64][]uint64{},
|
||||
htHeights: map[uint64][]uint64{},
|
||||
htTriggerHeights: map[abi.ChainEpoch][]uint64{},
|
||||
htHeights: map[abi.ChainEpoch][]uint64{},
|
||||
},
|
||||
|
||||
calledEvents: calledEvents{
|
||||
@ -82,7 +83,7 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
revertQueue: map[msgH][]triggerH{},
|
||||
triggers: map[triggerId]*callHandler{},
|
||||
matchers: map[triggerId][]MatchFunc{},
|
||||
timeouts: map[uint64]map[triggerId]int{},
|
||||
timeouts: map[abi.ChainEpoch]map[triggerId]int{},
|
||||
},
|
||||
}
|
||||
|
||||
@ -141,6 +142,8 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
|
||||
}
|
||||
|
||||
e.readyOnce.Do(func() {
|
||||
e.at = cur[0].Val.Height()
|
||||
|
||||
e.ready.Done()
|
||||
})
|
||||
|
||||
|
@ -5,27 +5,27 @@ import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
const NoTimeout = math.MaxUint64
|
||||
const NoTimeout = math.MaxInt64
|
||||
|
||||
type triggerId = uint64
|
||||
|
||||
// msgH is the block height at which a message was present / event has happened
|
||||
type msgH = uint64
|
||||
type msgH = abi.ChainEpoch
|
||||
|
||||
// triggerH is the block height at which the listener will be notified about the
|
||||
// message (msgH+confidence)
|
||||
type triggerH = uint64
|
||||
type triggerH = abi.ChainEpoch
|
||||
|
||||
// `ts` is the tipset, in which the `msg` is included.
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type CalledHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (more bool, err error)
|
||||
type CalledHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
|
||||
|
||||
// CheckFunc is used for atomicity guarantees. If the condition the callbacks
|
||||
// wait for has already happened in tipset `ts`
|
||||
@ -39,7 +39,7 @@ type MatchFunc func(msg *types.Message) (bool, error)
|
||||
|
||||
type callHandler struct {
|
||||
confidence int
|
||||
timeout uint64
|
||||
timeout abi.ChainEpoch
|
||||
|
||||
disabled bool // TODO: GC after gcConfidence reached
|
||||
|
||||
@ -50,7 +50,7 @@ type callHandler struct {
|
||||
type queuedEvent struct {
|
||||
trigger triggerId
|
||||
|
||||
h uint64
|
||||
h abi.ChainEpoch
|
||||
msg *types.Message
|
||||
|
||||
called bool
|
||||
@ -62,6 +62,8 @@ type calledEvents struct {
|
||||
ctx context.Context
|
||||
gcConfidence uint64
|
||||
|
||||
at abi.ChainEpoch
|
||||
|
||||
lk sync.Mutex
|
||||
|
||||
ctr triggerId
|
||||
@ -77,20 +79,22 @@ type calledEvents struct {
|
||||
revertQueue map[msgH][]triggerH
|
||||
|
||||
// [timeoutH+confidence][triggerId]{calls}
|
||||
timeouts map[uint64]map[triggerId]int
|
||||
timeouts map[abi.ChainEpoch]map[triggerId]int
|
||||
}
|
||||
|
||||
func (e *calledEvents) headChangeCalled(rev, app []*types.TipSet) error {
|
||||
for _, ts := range rev {
|
||||
e.handleReverts(ts)
|
||||
e.at = ts.Height()
|
||||
}
|
||||
|
||||
for _, ts := range app {
|
||||
// called triggers
|
||||
|
||||
e.checkNewCalls(ts)
|
||||
e.applyWithConfidence(ts)
|
||||
e.applyTimeouts(ts)
|
||||
for ; e.at <= ts.Height(); e.at++ {
|
||||
e.applyWithConfidence(ts, e.at)
|
||||
e.applyTimeouts(ts)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -129,7 +133,7 @@ func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
for _, matchFn := range matchFns {
|
||||
ok, err := matchFn(msg)
|
||||
if err != nil {
|
||||
log.Warnf("event matcher failed: %s")
|
||||
log.Errorf("event matcher failed: %s", err)
|
||||
continue
|
||||
}
|
||||
matched = ok
|
||||
@ -153,11 +157,11 @@ func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message,
|
||||
// messages are not applied in the tipset they are included in
|
||||
appliedH := ts.Height() + 1
|
||||
|
||||
triggerH := appliedH + uint64(trigger.confidence)
|
||||
triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
|
||||
|
||||
byOrigH, ok := e.confQueue[triggerH]
|
||||
if !ok {
|
||||
byOrigH = map[uint64][]*queuedEvent{}
|
||||
byOrigH = map[abi.ChainEpoch][]*queuedEvent{}
|
||||
e.confQueue[triggerH] = byOrigH
|
||||
}
|
||||
|
||||
@ -170,8 +174,8 @@ func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message,
|
||||
e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
|
||||
}
|
||||
|
||||
func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
byOrigH, ok := e.confQueue[ts.Height()]
|
||||
func (e *calledEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
|
||||
byOrigH, ok := e.confQueue[height]
|
||||
if !ok {
|
||||
return // no triggers at thin height
|
||||
}
|
||||
@ -179,7 +183,7 @@ func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
for origH, events := range byOrigH {
|
||||
triggerTs, err := e.tsc.get(origH)
|
||||
if err != nil {
|
||||
log.Errorf("events: applyWithConfidence didn't find tipset for event; wanted %d; current %d", origH, ts.Height())
|
||||
log.Errorf("events: applyWithConfidence didn't find tipset for event; wanted %d; current %d", origH, height)
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
@ -198,9 +202,9 @@ func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
return
|
||||
}
|
||||
|
||||
more, err := trigger.handle(event.msg, rec, triggerTs, ts.Height())
|
||||
more, err := trigger.handle(event.msg, rec, triggerTs, height)
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, origH, ts.Height(), err)
|
||||
log.Errorf("chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, origH, height, err)
|
||||
continue // don't revert failed calls
|
||||
}
|
||||
|
||||
@ -231,9 +235,9 @@ func (e *calledEvents) applyTimeouts(ts *types.TipSet) {
|
||||
continue
|
||||
}
|
||||
|
||||
timeoutTs, err := e.tsc.get(ts.Height() - uint64(trigger.confidence))
|
||||
timeoutTs, err := e.tsc.get(ts.Height() - abi.ChainEpoch(trigger.confidence))
|
||||
if err != nil {
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-uint64(trigger.confidence), ts.Height())
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-abi.ChainEpoch(trigger.confidence), ts.Height())
|
||||
}
|
||||
|
||||
more, err := trigger.handle(nil, nil, timeoutTs, ts.Height())
|
||||
@ -304,7 +308,7 @@ func (e *calledEvents) messagesForTs(ts *types.TipSet, consume func(*types.Messa
|
||||
// containing the message. The tipset passed as the argument is the tipset
|
||||
// that is being dropped. Note that the message dropped may be re-applied
|
||||
// in a different tipset in small amount of time.
|
||||
func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, mf MatchFunc) error {
|
||||
func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MatchFunc) error {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
@ -322,7 +326,7 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand
|
||||
|
||||
e.triggers[id] = &callHandler{
|
||||
confidence: confidence,
|
||||
timeout: timeout + uint64(confidence),
|
||||
timeout: timeout + abi.ChainEpoch(confidence),
|
||||
|
||||
disabled: !more,
|
||||
|
||||
@ -333,15 +337,15 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand
|
||||
e.matchers[id] = append(e.matchers[id], mf)
|
||||
|
||||
if timeout != NoTimeout {
|
||||
if e.timeouts[timeout+uint64(confidence)] == nil {
|
||||
e.timeouts[timeout+uint64(confidence)] = map[uint64]int{}
|
||||
if e.timeouts[timeout+abi.ChainEpoch(confidence)] == nil {
|
||||
e.timeouts[timeout+abi.ChainEpoch(confidence)] = map[uint64]int{}
|
||||
}
|
||||
e.timeouts[timeout+uint64(confidence)][id] = 0
|
||||
e.timeouts[timeout+abi.ChainEpoch(confidence)][id] = 0
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, msg store.ChainMsg) error {
|
||||
func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error {
|
||||
return e.Called(e.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, e.MatchMsg(msg.VMMessage()))
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@ -12,7 +13,7 @@ import (
|
||||
type heightEvents struct {
|
||||
lk sync.Mutex
|
||||
tsc *tipSetCache
|
||||
gcConfidence uint64
|
||||
gcConfidence abi.ChainEpoch
|
||||
|
||||
ctr triggerId
|
||||
|
||||
@ -35,7 +36,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
// TODO: log error if h below gcconfidence
|
||||
// revert height-based triggers
|
||||
|
||||
revert := func(h uint64, ts *types.TipSet) {
|
||||
revert := func(h abi.ChainEpoch, ts *types.TipSet) {
|
||||
for _, tid := range e.htHeights[h] {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
|
||||
@ -80,7 +81,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
|
||||
// height triggers
|
||||
|
||||
apply := func(h uint64, ts *types.TipSet) error {
|
||||
apply := func(h abi.ChainEpoch, ts *types.TipSet) error {
|
||||
for _, tid := range e.htTriggerHeights[h] {
|
||||
hnd := e.heightTriggers[tid]
|
||||
if hnd.called {
|
||||
@ -88,7 +89,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
}
|
||||
hnd.called = true
|
||||
|
||||
triggerH := h - uint64(hnd.confidence)
|
||||
triggerH := h - abi.ChainEpoch(hnd.confidence)
|
||||
|
||||
incTs, err := e.tsc.getNonNull(triggerH)
|
||||
if err != nil {
|
||||
@ -139,13 +140,13 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
// specified height, `RevertHandler` will be called.
|
||||
//
|
||||
// ts passed to handlers is the tipset at the specified, or above, if lower tipsets were null
|
||||
func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence int, h uint64) error {
|
||||
func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence int, h abi.ChainEpoch) error {
|
||||
|
||||
e.lk.Lock() // Tricky locking, check your locks if you modify this function!
|
||||
|
||||
bestH := e.tsc.best().Height()
|
||||
|
||||
if bestH >= h+uint64(confidence) {
|
||||
if bestH >= h+abi.ChainEpoch(confidence) {
|
||||
ts, err := e.tsc.getNonNull(h)
|
||||
if err != nil {
|
||||
log.Warnf("events.ChainAt: calling HandleFunc with nil tipset, not found in cache: %s", err)
|
||||
@ -168,11 +169,11 @@ func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence
|
||||
|
||||
defer e.lk.Unlock()
|
||||
|
||||
if bestH >= h+uint64(confidence)+e.gcConfidence {
|
||||
if bestH >= h+abi.ChainEpoch(confidence)+e.gcConfidence {
|
||||
return nil
|
||||
}
|
||||
|
||||
triggerAt := h + uint64(confidence)
|
||||
triggerAt := h + abi.ChainEpoch(confidence)
|
||||
|
||||
id := e.ctr
|
||||
e.ctr++
|
||||
|
@ -6,6 +6,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
|
||||
@ -31,7 +34,7 @@ type fakeMsg struct {
|
||||
|
||||
type fakeCS struct {
|
||||
t *testing.T
|
||||
h uint64
|
||||
h abi.ChainEpoch
|
||||
tsc *tipSetCache
|
||||
|
||||
msgs map[cid.Cid]fakeMsg
|
||||
@ -48,11 +51,11 @@ func (fcs *fakeCS) StateGetActor(ctx context.Context, actor address.Address, tsk
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, uint64, types.TipSetKey) (*types.TipSet, error) {
|
||||
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
func makeTs(t *testing.T, h abi.ChainEpoch, msgcid cid.Cid) *types.TipSet {
|
||||
a, _ := address.NewFromString("t00")
|
||||
b, _ := address.NewFromString("t02")
|
||||
var ts, err = types.NewTipSet([]*types.BlockHeader{
|
||||
@ -66,8 +69,8 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
},
|
||||
{
|
||||
Height: h,
|
||||
@ -79,8 +82,8 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
},
|
||||
})
|
||||
|
||||
@ -89,21 +92,21 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
return ts
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainNotify(context.Context) (<-chan []*store.HeadChange, error) {
|
||||
out := make(chan []*store.HeadChange, 1)
|
||||
out <- []*store.HeadChange{{Type: store.HCCurrent, Val: fcs.tsc.best()}}
|
||||
func (fcs *fakeCS) ChainNotify(context.Context) (<-chan []*api.HeadChange, error) {
|
||||
out := make(chan []*api.HeadChange, 1)
|
||||
out <- []*api.HeadChange{{Type: store.HCCurrent, Val: fcs.tsc.best()}}
|
||||
|
||||
fcs.sub = func(rev, app []*types.TipSet) {
|
||||
notif := make([]*store.HeadChange, len(rev)+len(app))
|
||||
notif := make([]*api.HeadChange, len(rev)+len(app))
|
||||
|
||||
for i, r := range rev {
|
||||
notif[i] = &store.HeadChange{
|
||||
notif[i] = &api.HeadChange{
|
||||
Type: store.HCRevert,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
for i, r := range app {
|
||||
notif[i+len(rev)] = &store.HeadChange{
|
||||
notif[i+len(rev)] = &api.HeadChange{
|
||||
Type: store.HCApply,
|
||||
Val: r,
|
||||
}
|
||||
@ -206,7 +209,7 @@ func TestAt(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@ -271,7 +274,7 @@ func TestAtDoubleTrigger(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@ -313,8 +316,8 @@ func TestAtNullTrigger(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, uint64(6), ts.Height())
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, abi.ChainEpoch(6), ts.Height())
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
@ -347,7 +350,7 @@ func TestAtNullConf(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@ -388,7 +391,7 @@ func TestAtStart(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@ -422,7 +425,7 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 11, int(curH))
|
||||
applied = true
|
||||
@ -450,8 +453,8 @@ func TestAtChained(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
@ -486,8 +489,8 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
@ -520,7 +523,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
applied = true
|
||||
require.Equal(t, 6, int(ts.Height()))
|
||||
return nil
|
||||
@ -534,7 +537,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
|
||||
func matchAddrMethod(to address.Address, m uint64) func(msg *types.Message) (bool, error) {
|
||||
func matchAddrMethod(to address.Address, m abi.MethodNum) func(msg *types.Message) (bool, error) {
|
||||
return func(msg *types.Message) (bool, error) {
|
||||
return to == msg.To && m == msg.Method, nil
|
||||
}
|
||||
@ -560,11 +563,11 @@ func TestCalled(t *testing.T) {
|
||||
var applied, reverted bool
|
||||
var appliedMsg *types.Message
|
||||
var appliedTs *types.TipSet
|
||||
var appliedH uint64
|
||||
var appliedH abi.ChainEpoch
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
appliedMsg = msg
|
||||
@ -610,12 +613,12 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
require.Equal(t, uint64(7), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(7), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(10), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(1), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// revert some blocks, keep the message
|
||||
|
||||
@ -647,12 +650,12 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(9), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(9), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(12), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(12), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// revert and apply at different height
|
||||
|
||||
@ -668,12 +671,12 @@ func TestCalled(t *testing.T) {
|
||||
reverted = false
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(11), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(11), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(14), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(14), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// call method again
|
||||
|
||||
@ -765,11 +768,11 @@ func TestCalledTimeout(t *testing.T) {
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
called = true
|
||||
require.Nil(t, msg)
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
require.Equal(t, abi.ChainEpoch(20), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(23), curH)
|
||||
return false, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
@ -800,11 +803,11 @@ func TestCalledTimeout(t *testing.T) {
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return true, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
called = true
|
||||
require.Nil(t, msg)
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
require.Equal(t, abi.ChainEpoch(20), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(23), curH)
|
||||
return false, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
@ -839,14 +842,14 @@ func TestCalledOrder(t *testing.T) {
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
switch at {
|
||||
case 0:
|
||||
require.Equal(t, uint64(1), msg.Nonce)
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(4), ts.Height())
|
||||
case 1:
|
||||
require.Equal(t, uint64(2), msg.Nonce)
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(5), ts.Height())
|
||||
default:
|
||||
t.Fatal("apply should only get called twice, at: ", at)
|
||||
}
|
||||
@ -855,9 +858,9 @@ func TestCalledOrder(t *testing.T) {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
switch at {
|
||||
case 2:
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(5), ts.Height())
|
||||
case 3:
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(4), ts.Height())
|
||||
default:
|
||||
t.Fatal("revert should only get called twice, at: ", at)
|
||||
}
|
||||
@ -881,3 +884,68 @@ func TestCalledOrder(t *testing.T) {
|
||||
|
||||
fcs.advance(9, 1, nil)
|
||||
}
|
||||
|
||||
func TestCalledNull(t *testing.T) {
|
||||
fcs := &fakeCS{
|
||||
t: t,
|
||||
h: 1,
|
||||
|
||||
msgs: map[cid.Cid]fakeMsg{},
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
t0123, err := address.NewFromString("t0123")
|
||||
require.NoError(t, err)
|
||||
|
||||
more := true
|
||||
var applied, reverted bool
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
return more, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold, but with null tipset at the height
|
||||
// of application
|
||||
|
||||
fcs.advance(0, 3, nil, 10) // H=11 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(5, 1, nil, 10)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package events
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type tsByHFunc func(context.Context, uint64, types.TipSetKey) (*types.TipSet, error)
|
||||
type tsByHFunc func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
|
||||
// tipSetCache implements a simple ring-buffer cache to keep track of recent
|
||||
// tipsets
|
||||
@ -20,7 +21,7 @@ type tipSetCache struct {
|
||||
storage tsByHFunc
|
||||
}
|
||||
|
||||
func newTSCache(cap int, storage tsByHFunc) *tipSetCache {
|
||||
func newTSCache(cap abi.ChainEpoch, storage tsByHFunc) *tipSetCache {
|
||||
return &tipSetCache{
|
||||
cache: make([]*types.TipSet, cap),
|
||||
start: 0,
|
||||
@ -77,7 +78,7 @@ func (tsc *tipSetCache) revert(ts *types.TipSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) getNonNull(height uint64) (*types.TipSet, error) {
|
||||
func (tsc *tipSetCache) getNonNull(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
for {
|
||||
ts, err := tsc.get(height)
|
||||
if err != nil {
|
||||
@ -90,7 +91,7 @@ func (tsc *tipSetCache) getNonNull(height uint64) (*types.TipSet, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) get(height uint64) (*types.TipSet, error) {
|
||||
func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
if tsc.len == 0 {
|
||||
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
|
||||
return tsc.storage(context.TODO(), height, types.EmptyTSK)
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@ -11,12 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func TestTsCache(t *testing.T) {
|
||||
tsc := newTSCache(50, func(context.Context, uint64, types.TipSetKey) (*types.TipSet, error) {
|
||||
tsc := newTSCache(50, func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
})
|
||||
|
||||
h := uint64(75)
|
||||
h := abi.ChainEpoch(75)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
|
||||
@ -27,8 +29,8 @@ func TestTsCache(t *testing.T) {
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -54,12 +56,12 @@ func TestTsCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTsCacheNulls(t *testing.T) {
|
||||
tsc := newTSCache(50, func(context.Context, uint64, types.TipSetKey) (*types.TipSet, error) {
|
||||
tsc := newTSCache(50, func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
})
|
||||
|
||||
h := uint64(75)
|
||||
h := abi.ChainEpoch(75)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
add := func() {
|
||||
@ -69,8 +71,8 @@ func TestTsCacheNulls(t *testing.T) {
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -5,11 +5,10 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func (e *calledEvents) CheckMsg(ctx context.Context, smsg store.ChainMsg, hnd CalledHandler) CheckFunc {
|
||||
func (e *calledEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd CalledHandler) CheckFunc {
|
||||
msg := smsg.VMMessage()
|
||||
|
||||
return func(ts *types.TipSet) (done bool, more bool, err error) {
|
||||
|
481
chain/gen/gen.go
481
chain/gen/gen.go
@ -3,27 +3,32 @@ package gen
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
commcid "github.com/filecoin-project/go-fil-commcid"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-car"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
format "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/ipld/go-car"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/filecoin-project/go-address"
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@ -33,9 +38,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
)
|
||||
|
||||
var log = logging.Logger("gen")
|
||||
@ -49,18 +52,20 @@ type ChainGen struct {
|
||||
|
||||
cs *store.ChainStore
|
||||
|
||||
beacon beacon.RandomBeacon
|
||||
|
||||
sm *stmgr.StateManager
|
||||
|
||||
genesis *types.BlockHeader
|
||||
CurTipset *store.FullTipSet
|
||||
|
||||
Timestamper func(*types.TipSet, uint64) uint64
|
||||
Timestamper func(*types.TipSet, abi.ChainEpoch) uint64
|
||||
|
||||
GetMessages func(*ChainGen) ([]*types.SignedMessage, error)
|
||||
|
||||
w *wallet.Wallet
|
||||
|
||||
eppProvs map[address.Address]ElectionPoStProver
|
||||
eppProvs map[address.Address]WinningPoStProver
|
||||
Miners []address.Address
|
||||
receivers []address.Address
|
||||
banker address.Address
|
||||
@ -83,7 +88,11 @@ func (m mybs) Get(c cid.Cid) (block.Block, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func NewGenerator() (*ChainGen, error) {
|
||||
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
|
||||
saminer.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
|
||||
mr := repo.NewMemory(nil)
|
||||
lr, err := mr.Lock(repo.StorageMiner)
|
||||
if err != nil {
|
||||
@ -112,74 +121,81 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err)
|
||||
}
|
||||
|
||||
banker, err := w.GenerateKey(types.KTSecp256k1)
|
||||
banker, err := w.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate banker key: %w", err)
|
||||
}
|
||||
|
||||
receievers := make([]address.Address, msgsPerBlock)
|
||||
for r := range receievers {
|
||||
receievers[r], err = w.GenerateKey(types.KTBLS)
|
||||
receievers[r], err = w.GenerateKey(crypto.SigTypeBLS)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate receiver key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
maddr1, err := address.NewFromString("t0300")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maddr1 := genesis2.MinerAddress(0)
|
||||
|
||||
m1temp, err := ioutil.TempDir("", "preseal")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genm1, err := seed.PreSeal(maddr1, 1024, 0, 1, m1temp, []byte("some randomness"))
|
||||
genm1, k1, err := seed.PreSeal(maddr1, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m1temp, []byte("some randomness"), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maddr2, err := address.NewFromString("t0301")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maddr2 := genesis2.MinerAddress(1)
|
||||
|
||||
m2temp, err := ioutil.TempDir("", "preseal")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genm2, err := seed.PreSeal(maddr2, 1024, 0, 1, m2temp, []byte("some randomness"))
|
||||
genm2, k2, err := seed.PreSeal(maddr2, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m2temp, []byte("some randomness"), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mk1, err := w.Import(&genm1.Key)
|
||||
mk1, err := w.Import(k1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mk2, err := w.Import(&genm2.Key)
|
||||
mk2, err := w.Import(k2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minercfg := &GenMinerCfg{
|
||||
PeerIDs: []peer.ID{"peerID1", "peerID2"},
|
||||
PreSeals: map[string]genesis.GenesisMiner{
|
||||
maddr1.String(): *genm1,
|
||||
maddr2.String(): *genm2,
|
||||
sys := vm.Syscalls(&genFakeVerifier{})
|
||||
|
||||
tpl := genesis.Template{
|
||||
Accounts: []genesis.Actor{
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(40000),
|
||||
Meta: (&genesis.AccountMeta{Owner: mk1}).ActorMeta(),
|
||||
},
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(40000),
|
||||
Meta: (&genesis.AccountMeta{Owner: mk2}).ActorMeta(),
|
||||
},
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(50000),
|
||||
Meta: (&genesis.AccountMeta{Owner: banker}).ActorMeta(),
|
||||
},
|
||||
},
|
||||
MinerAddrs: []address.Address{maddr1, maddr2},
|
||||
Miners: []genesis.Miner{
|
||||
*genm1,
|
||||
*genm2,
|
||||
},
|
||||
NetworkName: "",
|
||||
Timestamp: uint64(time.Now().Add(-500 * build.BlockDelay * time.Second).Unix()),
|
||||
}
|
||||
|
||||
sys := vm.Syscalls(sectorbuilder.ProofVerifier)
|
||||
|
||||
genb, err := MakeGenesisBlock(bs, sys, map[address.Address]types.BigInt{
|
||||
mk1: types.FromFil(40000),
|
||||
mk2: types.FromFil(40000),
|
||||
banker: types.FromFil(50000),
|
||||
}, minercfg, 100000)
|
||||
genb, err := genesis2.MakeGenesisBlock(context.TODO(), bs, sys, tpl)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("make genesis block failed: %w", err)
|
||||
}
|
||||
@ -193,27 +209,28 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return nil, xerrors.Errorf("set genesis failed: %w", err)
|
||||
}
|
||||
|
||||
if len(minercfg.MinerAddrs) == 0 {
|
||||
return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address")
|
||||
}
|
||||
|
||||
mgen := make(map[address.Address]ElectionPoStProver)
|
||||
for _, m := range minercfg.MinerAddrs {
|
||||
mgen[m] = &eppProvider{}
|
||||
mgen := make(map[address.Address]WinningPoStProver)
|
||||
for i := range tpl.Miners {
|
||||
mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{}
|
||||
}
|
||||
|
||||
sm := stmgr.NewStateManager(cs)
|
||||
|
||||
miners := []address.Address{maddr1, maddr2}
|
||||
|
||||
beac := beacon.NewMockBeacon(time.Second)
|
||||
|
||||
gen := &ChainGen{
|
||||
bs: bs,
|
||||
cs: cs,
|
||||
sm: sm,
|
||||
msgsPerBlock: msgsPerBlock,
|
||||
genesis: genb.Genesis,
|
||||
beacon: beac,
|
||||
w: w,
|
||||
|
||||
GetMessages: getRandomMessages,
|
||||
Miners: minercfg.MinerAddrs,
|
||||
Miners: miners,
|
||||
eppProvs: mgen,
|
||||
banker: banker,
|
||||
receivers: receievers,
|
||||
@ -227,6 +244,10 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return gen, nil
|
||||
}
|
||||
|
||||
func NewGenerator() (*ChainGen, error) {
|
||||
return NewGeneratorWithSectors(1)
|
||||
}
|
||||
|
||||
func (cg *ChainGen) SetStateManager(sm *stmgr.StateManager) {
|
||||
cg.sm = sm
|
||||
}
|
||||
@ -246,46 +267,76 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
if err := car.WriteCar(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out); err != nil {
|
||||
if err := car.WriteCarWithWalker(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out, CarWalkFunc); err != nil {
|
||||
return nil, xerrors.Errorf("genesis car write car failed: %w", err)
|
||||
}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (*types.EPostProof, *types.Ticket, error) {
|
||||
func CarWalkFunc(nd format.Node) (out []*format.Link, err error) {
|
||||
for _, link := range nd.Links() {
|
||||
if link.Cid.Prefix().MhType == uint64(commcid.FC_SEALED_V1) || link.Cid.Prefix().MhType == uint64(commcid.FC_UNSEALED_V1) {
|
||||
continue
|
||||
}
|
||||
out = append(out, link)
|
||||
}
|
||||
|
||||
lastTicket := pts.MinTicket()
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round abi.ChainEpoch) ([]types.BeaconEntry, *types.ElectionProof, *types.Ticket, error) {
|
||||
mc := &mca{w: cg.w, sm: cg.sm, pv: ffiwrapper.ProofVerifier, bcn: cg.beacon}
|
||||
|
||||
mbi, err := mc.MinerGetBaseInfo(ctx, m, round, pts.Key())
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("get miner base info: %w", err)
|
||||
}
|
||||
|
||||
prev := mbi.PrevBeaconEntry
|
||||
|
||||
entries, err := beacon.BeaconEntriesForBlock(ctx, cg.beacon, round, prev)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("get beacon entries for block: %w", err)
|
||||
}
|
||||
|
||||
rbase := prev
|
||||
if len(entries) > 0 {
|
||||
rbase = entries[len(entries)-1]
|
||||
}
|
||||
|
||||
eproof, err := IsRoundWinner(ctx, pts, round, m, rbase, mbi, mc)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.MarshalCBOR(buf); err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("failed to cbor marshal address: %w", err)
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
buf.Write(pts.MinTicket().VRFProof)
|
||||
}
|
||||
|
||||
ticketRand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
st := pts.ParentState()
|
||||
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, cg.sm, st, m)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("get miner worker: %w", err)
|
||||
return nil, nil, nil, xerrors.Errorf("get miner worker: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, m, DSepTicket, lastTicket.VRFProof)
|
||||
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, ticketRand)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("compute VRF: %w", err)
|
||||
return nil, nil, nil, xerrors.Errorf("compute VRF: %w", err)
|
||||
}
|
||||
|
||||
tick := &types.Ticket{
|
||||
VRFProof: vrfout,
|
||||
}
|
||||
|
||||
eproofin, err := IsRoundWinner(ctx, pts, round, m, cg.eppProvs[m], &mca{w: cg.w, sm: cg.sm})
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
|
||||
}
|
||||
if eproofin == nil {
|
||||
return nil, tick, nil
|
||||
}
|
||||
eproof, err := ComputeProof(ctx, cg.eppProvs[m], eproofin)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("computing proof: %w", err)
|
||||
}
|
||||
|
||||
return eproof, tick, nil
|
||||
return entries, eproof, &types.Ticket{VRFProof: vrfout}, nil
|
||||
}
|
||||
|
||||
type MinedTipSet struct {
|
||||
@ -311,15 +362,21 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
|
||||
return nil, xerrors.Errorf("get random messages: %w", err)
|
||||
}
|
||||
|
||||
for round := int64(base.Height() + 1); len(blks) == 0; round++ {
|
||||
for round := base.Height() + 1; len(blks) == 0; round++ {
|
||||
for _, m := range miners {
|
||||
proof, t, err := cg.nextBlockProof(context.TODO(), base, m, round)
|
||||
bvals, et, ticket, err := cg.nextBlockProof(context.TODO(), base, m, round)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("next block proof: %w", err)
|
||||
}
|
||||
|
||||
if proof != nil {
|
||||
fblk, err := cg.makeBlock(base, m, proof, t, uint64(round), msgs)
|
||||
if et != nil {
|
||||
// TODO: maybe think about passing in more real parameters to this?
|
||||
wpost, err := cg.eppProvs[m].ComputeProof(context.TODO(), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fblk, err := cg.makeBlock(base, m, ticket, et, bvals, round, wpost, msgs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("making a block for next tipset failed: %w", err)
|
||||
}
|
||||
@ -341,16 +398,28 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, height uint64, msgs []*types.SignedMessage) (*types.FullBlock, error) {
|
||||
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, vrfticket *types.Ticket,
|
||||
eticket *types.ElectionProof, bvals []types.BeaconEntry, height abi.ChainEpoch,
|
||||
wpost []abi.PoStProof, msgs []*types.SignedMessage) (*types.FullBlock, error) {
|
||||
|
||||
var ts uint64
|
||||
if cg.Timestamper != nil {
|
||||
ts = cg.Timestamper(parents, height-parents.Height())
|
||||
} else {
|
||||
ts = parents.MinTimestamp() + ((height - parents.Height()) * build.BlockDelay)
|
||||
ts = parents.MinTimestamp() + uint64((height-parents.Height())*build.BlockDelay)
|
||||
}
|
||||
|
||||
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts)
|
||||
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, &api.BlockTemplate{
|
||||
Miner: m,
|
||||
Parents: parents.Key(),
|
||||
Ticket: vrfticket,
|
||||
Eproof: eticket,
|
||||
BeaconValues: bvals,
|
||||
Messages: msgs,
|
||||
Epoch: height,
|
||||
Timestamp: ts,
|
||||
WinningPoStProof: wpost,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -391,7 +460,7 @@ func getRandomMessages(cg *ChainGen) ([]*types.SignedMessage, error) {
|
||||
|
||||
Method: 0,
|
||||
|
||||
GasLimit: types.NewInt(10000),
|
||||
GasLimit: 10000,
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
|
||||
@ -417,232 +486,95 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
|
||||
}
|
||||
|
||||
type MiningCheckAPI interface {
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (api.MinerPower, error)
|
||||
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error)
|
||||
|
||||
StateMinerWorker(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
|
||||
StateMinerSectorSize(context.Context, address.Address, types.TipSetKey) (uint64, error)
|
||||
|
||||
StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error)
|
||||
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
}
|
||||
|
||||
type mca struct {
|
||||
w *wallet.Wallet
|
||||
sm *stmgr.StateManager
|
||||
w *wallet.Wallet
|
||||
sm *stmgr.StateManager
|
||||
pv ffiwrapper.Verifier
|
||||
bcn beacon.RandomBeacon
|
||||
}
|
||||
|
||||
func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, lb int64) ([]byte, error) {
|
||||
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), int64(lb))
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (api.MinerPower, error) {
|
||||
ts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
func (mca mca) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
|
||||
pts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return api.MinerPower{}, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
}
|
||||
mpow, tpow, err := stmgr.GetPower(ctx, mca.sm, ts, maddr)
|
||||
if err != nil {
|
||||
return api.MinerPower{}, err
|
||||
return nil, xerrors.Errorf("loading tipset key: %w", err)
|
||||
}
|
||||
|
||||
return api.MinerPower{
|
||||
MinerPower: mpow,
|
||||
TotalPower: tpow,
|
||||
}, err
|
||||
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (address.Address, error) {
|
||||
ts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
}
|
||||
return stmgr.GetMinerWorkerRaw(ctx, mca.sm, ts.ParentState(), maddr)
|
||||
func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
|
||||
return stmgr.MinerGetBaseInfo(ctx, mca.sm, mca.bcn, tsk, epoch, maddr, mca.pv)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerSectorSize(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (uint64, error) {
|
||||
ts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
}
|
||||
return stmgr.GetMinerSectorSize(ctx, mca.sm, ts, maddr)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerProvingSet(ctx context.Context, maddr address.Address, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
ts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
}
|
||||
return stmgr.GetMinerProvingSet(ctx, mca.sm, ts, maddr)
|
||||
}
|
||||
|
||||
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) {
|
||||
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*crypto.Signature, error) {
|
||||
return mca.w.Sign(ctx, a, v)
|
||||
}
|
||||
|
||||
type ElectionPoStProver interface {
|
||||
GenerateCandidates(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte) ([]sectorbuilder.EPostCandidate, error)
|
||||
ComputeProof(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte, []sectorbuilder.EPostCandidate) ([]byte, error)
|
||||
type WinningPoStProver interface {
|
||||
GenerateCandidates(context.Context, abi.PoStRandomness, uint64) ([]uint64, error)
|
||||
ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error)
|
||||
}
|
||||
|
||||
type eppProvider struct{}
|
||||
type wppProvider struct{}
|
||||
|
||||
func (epp *eppProvider) GenerateCandidates(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte) ([]sectorbuilder.EPostCandidate, error) {
|
||||
return []sectorbuilder.EPostCandidate{
|
||||
{
|
||||
SectorID: 1,
|
||||
PartialTicket: [32]byte{},
|
||||
Ticket: [32]byte{},
|
||||
SectorChallengeIndex: 1,
|
||||
},
|
||||
}, nil
|
||||
func (wpp *wppProvider) GenerateCandidates(ctx context.Context, _ abi.PoStRandomness, _ uint64) ([]uint64, error) {
|
||||
return []uint64{0}, nil
|
||||
}
|
||||
|
||||
func (epp *eppProvider) ComputeProof(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) {
|
||||
|
||||
return []byte("valid proof"), nil
|
||||
func (wpp *wppProvider) ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error) {
|
||||
return []abi.PoStProof{{
|
||||
ProofBytes: []byte("valid proof"),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
type ProofInput struct {
|
||||
sectors sectorbuilder.SortedPublicSectorInfo
|
||||
hvrf []byte
|
||||
winners []sectorbuilder.EPostCandidate
|
||||
vrfout []byte
|
||||
sectors []abi.SectorInfo
|
||||
hvrf []byte
|
||||
challengedSectors []uint64
|
||||
vrfout []byte
|
||||
}
|
||||
|
||||
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, epp ElectionPoStProver, a MiningCheckAPI) (*ProofInput, error) {
|
||||
r, err := a.ChainGetRandomness(ctx, ts.Key(), round-build.EcRandomnessLookback)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("chain get randomness: %w", err)
|
||||
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch,
|
||||
miner address.Address, brand types.BeaconEntry, mbi *api.MiningBaseInfo, a MiningCheckAPI) (*types.ElectionProof, error) {
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := miner.MarshalCBOR(buf); err != nil {
|
||||
return nil, xerrors.Errorf("failed to cbor marshal address: %w")
|
||||
}
|
||||
|
||||
mworker, err := a.StateMinerWorker(ctx, miner, ts.Key())
|
||||
electionRand, err := store.DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
|
||||
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, miner, DSepElectionPost, r)
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, mbi.WorkerKey, electionRand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
||||
}
|
||||
|
||||
pset, err := a.StateMinerProvingSet(ctx, miner, ts.Key())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load proving set for miner: %w", err)
|
||||
}
|
||||
if len(pset) == 0 {
|
||||
// TODO: wire in real power
|
||||
if !types.IsTicketWinner(vrfout, mbi.MinerPower, mbi.NetworkPower) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var sinfos []ffi.PublicSectorInfo
|
||||
for _, s := range pset {
|
||||
var commRa [32]byte
|
||||
copy(commRa[:], s.CommR)
|
||||
sinfos = append(sinfos, ffi.PublicSectorInfo{
|
||||
SectorID: s.SectorID,
|
||||
CommR: commRa,
|
||||
})
|
||||
}
|
||||
sectors := sectorbuilder.NewSortedPublicSectorInfo(sinfos)
|
||||
|
||||
hvrf := sha256.Sum256(vrfout)
|
||||
candidates, err := epp.GenerateCandidates(ctx, sectors, hvrf[:])
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate electionPoSt candidates: %w", err)
|
||||
}
|
||||
|
||||
pow, err := a.StateMinerPower(ctx, miner, ts.Key())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to check power: %w", err)
|
||||
}
|
||||
|
||||
ssize, err := a.StateMinerSectorSize(ctx, miner, ts.Key())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to look up miners sector size: %w", err)
|
||||
}
|
||||
|
||||
var winners []sectorbuilder.EPostCandidate
|
||||
for _, c := range candidates {
|
||||
if types.IsTicketWinner(c.PartialTicket[:], ssize, uint64(len(sinfos)), pow.TotalPower) {
|
||||
winners = append(winners, c)
|
||||
}
|
||||
}
|
||||
|
||||
// no winners, sad
|
||||
if len(winners) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &ProofInput{
|
||||
sectors: sectors,
|
||||
hvrf: hvrf[:],
|
||||
winners: winners,
|
||||
vrfout: vrfout,
|
||||
}, nil
|
||||
return &types.ElectionProof{VRFProof: vrfout}, nil
|
||||
}
|
||||
|
||||
func ComputeProof(ctx context.Context, epp ElectionPoStProver, pi *ProofInput) (*types.EPostProof, error) {
|
||||
proof, err := epp.ComputeProof(ctx, pi.sectors, pi.hvrf, pi.winners)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute snark for election proof: %w", err)
|
||||
}
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
|
||||
ept := types.EPostProof{
|
||||
Proof: proof,
|
||||
PostRand: pi.vrfout,
|
||||
}
|
||||
for _, win := range pi.winners {
|
||||
part := make([]byte, 32)
|
||||
copy(part, win.PartialTicket[:])
|
||||
ept.Candidates = append(ept.Candidates, types.EPostTicket{
|
||||
Partial: part,
|
||||
SectorID: win.SectorID,
|
||||
ChallengeIndex: win.SectorChallengeIndex,
|
||||
})
|
||||
}
|
||||
|
||||
return &ept, nil
|
||||
}
|
||||
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
|
||||
const (
|
||||
DSepTicket = 1
|
||||
DSepElectionPost = 2
|
||||
)
|
||||
|
||||
func hashVRFBase(personalization uint64, miner address.Address, input []byte) ([]byte, error) {
|
||||
if miner.Protocol() != address.ID {
|
||||
return nil, xerrors.Errorf("miner address for compute VRF must be an ID address")
|
||||
}
|
||||
|
||||
var persbuf [8]byte
|
||||
binary.LittleEndian.PutUint64(persbuf[:], personalization)
|
||||
|
||||
h := sha256.New()
|
||||
h.Write(persbuf[:])
|
||||
h.Write([]byte{0})
|
||||
h.Write(input)
|
||||
h.Write([]byte{0})
|
||||
h.Write(miner.Bytes())
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, input, vrfproof []byte) error {
|
||||
func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error {
|
||||
_, span := trace.StartSpan(ctx, "VerifyVRF")
|
||||
defer span.End()
|
||||
|
||||
vrfBase, err := hashVRFBase(p, miner, input)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("computing vrf base failed: %w", err)
|
||||
}
|
||||
|
||||
sig := &types.Signature{
|
||||
Type: types.KTBLS,
|
||||
sig := &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: vrfproof,
|
||||
}
|
||||
|
||||
@ -653,20 +585,35 @@ func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, inp
|
||||
return nil
|
||||
}
|
||||
|
||||
func ComputeVRF(ctx context.Context, sign SignFunc, worker, miner address.Address, p uint64, input []byte) ([]byte, error) {
|
||||
sigInput, err := hashVRFBase(p, miner, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func ComputeVRF(ctx context.Context, sign SignFunc, worker address.Address, sigInput []byte) ([]byte, error) {
|
||||
sig, err := sign(ctx, worker, sigInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sig.Type != types.KTBLS {
|
||||
if sig.Type != crypto.SigTypeBLS {
|
||||
return nil, fmt.Errorf("miner worker address was not a BLS key")
|
||||
}
|
||||
|
||||
return sig.Data, nil
|
||||
}
|
||||
|
||||
type genFakeVerifier struct{}
|
||||
|
||||
var _ ffiwrapper.Verifier = (*genFakeVerifier)(nil)
|
||||
|
||||
func (m genFakeVerifier) VerifySeal(svi abi.SealVerifyInfo) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) VerifyWinningPoSt(ctx context.Context, info abi.WinningPoStVerifyInfo) (bool, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) VerifyWindowPoSt(ctx context.Context, info abi.WindowPoStVerifyInfo) (bool, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, proof abi.RegisteredProof, id abi.ActorID, randomness abi.PoStRandomness, u uint64) ([]uint64, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
@ -3,20 +3,26 @@ package gen
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
build.SectorSizes = []uint64{1024}
|
||||
build.MinimumMinerPower = 1024
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
}
|
||||
|
||||
func testGeneration(t testing.TB, n int, msgs int) {
|
||||
g, err := NewGenerator()
|
||||
func testGeneration(t testing.TB, n int, msgs int, sectors int) {
|
||||
g, err := NewGeneratorWithSectors(sectors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
g.msgsPerBlock = msgs
|
||||
@ -24,30 +30,31 @@ func testGeneration(t testing.TB, n int, msgs int) {
|
||||
for i := 0; i < n; i++ {
|
||||
mts, err := g.NextTipSet()
|
||||
if err != nil {
|
||||
t.Fatalf("error at H:%d, %s", i, err)
|
||||
t.Fatalf("error at H:%d, %+v", i, err)
|
||||
}
|
||||
_ = mts
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainGeneration(t *testing.T) {
|
||||
testGeneration(t, 10, 20)
|
||||
testGeneration(t, 10, 20, 1)
|
||||
testGeneration(t, 10, 20, 25)
|
||||
}
|
||||
|
||||
func BenchmarkChainGeneration(b *testing.B) {
|
||||
b.Run("0-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 0)
|
||||
testGeneration(b, b.N, 0, 1)
|
||||
})
|
||||
|
||||
b.Run("10-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 10)
|
||||
testGeneration(b, b.N, 10, 1)
|
||||
})
|
||||
|
||||
b.Run("100-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 100)
|
||||
testGeneration(b, b.N, 100, 1)
|
||||
})
|
||||
|
||||
b.Run("1000-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 1000)
|
||||
testGeneration(b, b.N, 1000, 1)
|
||||
})
|
||||
}
|
||||
|
297
chain/gen/genesis/genesis.go
Normal file
297
chain/gen/genesis/genesis.go
Normal file
@ -0,0 +1,297 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
const AccountStart = 100
|
||||
const MinerStart = 1000
|
||||
const MaxAccounts = MinerStart - AccountStart
|
||||
|
||||
var log = logging.Logger("genesis")
|
||||
|
||||
type GenesisBootstrap struct {
|
||||
Genesis *types.BlockHeader
|
||||
}
|
||||
|
||||
/*
|
||||
From a list of parameters, create a genesis block / initial state
|
||||
|
||||
The process:
|
||||
- Bootstrap state (MakeInitialStateTree)
|
||||
- Create empty state
|
||||
- Create system actor
|
||||
- Make init actor
|
||||
- Create accounts mappings
|
||||
- Set NextID to MinerStart
|
||||
- Setup Reward (1.4B fil)
|
||||
- Setup Cron
|
||||
- Create empty power actor
|
||||
- Create empty market
|
||||
- Create verified registry
|
||||
- Setup burnt fund address
|
||||
- Initialize account / msig balances
|
||||
- Instantiate early vm with genesis syscalls
|
||||
- Create miners
|
||||
- Each:
|
||||
- power.CreateMiner, set msg value to PowerBalance
|
||||
- market.AddFunds with correct value
|
||||
- market.PublishDeals for related sectors
|
||||
- Set precommits
|
||||
- Commit presealed sectors
|
||||
|
||||
Data Types:
|
||||
|
||||
PreSeal :{
|
||||
CommR CID
|
||||
CommD CID
|
||||
SectorID SectorNumber
|
||||
Deal market.DealProposal # Start at 0, self-deal!
|
||||
}
|
||||
|
||||
Genesis: {
|
||||
Accounts: [ # non-miner, non-singleton actors, max len = MaxAccounts
|
||||
{
|
||||
Type: "account" / "multisig",
|
||||
Value: "attofil",
|
||||
[Meta: {msig settings, account key..}]
|
||||
},...
|
||||
],
|
||||
Miners: [
|
||||
{
|
||||
Owner, Worker Addr # ID
|
||||
MarketBalance, PowerBalance TokenAmount
|
||||
SectorSize uint64
|
||||
PreSeals []PreSeal
|
||||
},...
|
||||
],
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template genesis.Template) (*state.StateTree, error) {
|
||||
// Create empty state tree
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
_, err := cst.Put(context.TODO(), []struct{}{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("putting empty object: %w", err)
|
||||
}
|
||||
|
||||
state, err := state.NewStateTree(cst)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
emptyobject, err := cst.Put(context.TODO(), []struct{}{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed putting empty object: %w", err)
|
||||
}
|
||||
|
||||
// Create system actor
|
||||
|
||||
sysact, err := SetupSystemActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.SystemActorAddr, sysact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
// Create init actor
|
||||
|
||||
initact, err := SetupInitActor(bs, template.NetworkName, template.Accounts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.InitActorAddr, initact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup reward
|
||||
rewact, err := SetupRewardActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
|
||||
err = state.SetActor(builtin.RewardActorAddr, rewact)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set network account actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup cron
|
||||
cronact, err := SetupCronActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup cron actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.CronActorAddr, cronact); err != nil {
|
||||
return nil, xerrors.Errorf("set cron actor: %w", err)
|
||||
}
|
||||
|
||||
// Create empty power actor
|
||||
spact, err := SetupStoragePowerActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.StoragePowerActorAddr, spact); err != nil {
|
||||
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
// Create empty market actor
|
||||
marketact, err := SetupStorageMarketActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.StorageMarketActorAddr, marketact); err != nil {
|
||||
return nil, xerrors.Errorf("set market actor: %w", err)
|
||||
}
|
||||
|
||||
// Create verified registry
|
||||
verifact, err := SetupVerifiedRegistryActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.VerifiedRegistryActorAddr, verifact); err != nil {
|
||||
return nil, xerrors.Errorf("set market actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup burnt-funds
|
||||
err = state.SetActor(builtin.BurntFundsActorAddr, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: types.NewInt(0),
|
||||
Head: emptyobject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set burnt funds account actor: %w", err)
|
||||
}
|
||||
|
||||
// Create accounts
|
||||
for id, info := range template.Accounts {
|
||||
if info.Type != genesis.TAccount {
|
||||
return nil, xerrors.New("unsupported account type") // TODO: msigs
|
||||
}
|
||||
|
||||
ida, err := address.NewIDAddress(uint64(AccountStart + id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ainfo genesis.AccountMeta
|
||||
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
||||
return nil, xerrors.Errorf("unmarshaling account meta: %w", err)
|
||||
}
|
||||
|
||||
st, err := cst.Put(ctx, &account.State{Address: ainfo.Owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = state.SetActor(ida, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: info.Balance,
|
||||
Head: st,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setting account from actmap: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys runtime.Syscalls, template genesis.Template) (*GenesisBootstrap, error) {
|
||||
st, err := MakeInitialStateTree(ctx, bs, template)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("flush state tree failed: %w", err)
|
||||
}
|
||||
|
||||
// temp chainstore
|
||||
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys)
|
||||
stateroot, err = SetupStorageMiners(ctx, cs, stateroot, template.Miners)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage miners failed: %w", err)
|
||||
}
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
emptyroot, err := amt.FromArray(ctx, cst, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
mm := &types.MsgMeta{
|
||||
BlsMessages: emptyroot,
|
||||
SecpkMessages: emptyroot,
|
||||
}
|
||||
mmb, err := mm.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing msgmeta failed: %w", err)
|
||||
}
|
||||
if err := bs.Put(mmb); err != nil {
|
||||
return nil, xerrors.Errorf("putting msgmeta block to blockstore: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Empty Genesis root: %s", emptyroot)
|
||||
|
||||
genesisticket := &types.Ticket{
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
}
|
||||
|
||||
b := &types.BlockHeader{
|
||||
Miner: builtin.SystemActorAddr,
|
||||
Ticket: genesisticket,
|
||||
Parents: []cid.Cid{},
|
||||
Height: 0,
|
||||
ParentWeight: types.NewInt(0),
|
||||
ParentStateRoot: stateroot,
|
||||
Messages: mmb.Cid(),
|
||||
ParentMessageReceipts: emptyroot,
|
||||
BLSAggregate: nil,
|
||||
BlockSig: nil,
|
||||
Timestamp: template.Timestamp,
|
||||
ElectionProof: new(types.ElectionProof),
|
||||
BeaconEntries: []types.BeaconEntry{
|
||||
{
|
||||
Round: 0,
|
||||
Data: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sb, err := b.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing block header failed: %w", err)
|
||||
}
|
||||
|
||||
if err := bs.Put(sb); err != nil {
|
||||
return nil, xerrors.Errorf("putting header to blockstore: %w", err)
|
||||
}
|
||||
|
||||
return &GenesisBootstrap{
|
||||
Genesis: b,
|
||||
}, nil
|
||||
}
|
256
chain/gen/genesis/miners.go
Normal file
256
chain/gen/genesis/miners.go
Normal file
@ -0,0 +1,256 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
func MinerAddress(genesisIndex uint64) address.Address {
|
||||
maddr, err := address.NewIDAddress(MinerStart + genesisIndex)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return maddr
|
||||
}
|
||||
|
||||
type fakedSigSyscalls struct {
|
||||
runtime.Syscalls
|
||||
}
|
||||
|
||||
func (fss *fakedSigSyscalls) VerifySignature(signature crypto.Signature, signer address.Address, plaintext []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, miners []genesis.Miner) (cid.Cid, error) {
|
||||
vm, err := vm.NewVM(sroot, 0, &fakeRand{}, cs.Blockstore(), &fakedSigSyscalls{cs.VMSys()})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
|
||||
}
|
||||
|
||||
if len(miners) == 0 {
|
||||
return cid.Undef, xerrors.New("no genesis miners")
|
||||
}
|
||||
|
||||
for i, m := range miners {
|
||||
// Create miner through power actor
|
||||
|
||||
spt, err := ffiwrapper.SealProofTypeFromSectorSize(m.SectorSize)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
var maddr address.Address
|
||||
{
|
||||
constructorParams := &power.CreateMinerParams{
|
||||
Owner: m.Worker,
|
||||
Worker: m.Worker,
|
||||
Peer: m.PeerId,
|
||||
SealProofType: spt,
|
||||
}
|
||||
|
||||
params := mustEnc(constructorParams)
|
||||
rval, err := doExecValue(ctx, vm, builtin.StoragePowerActorAddr, m.Owner, m.PowerBalance, builtin.MethodsPower.CreateMiner, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
|
||||
var ma power.CreateMinerReturn
|
||||
if err := ma.UnmarshalCBOR(bytes.NewReader(rval)); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("unmarshaling CreateMinerReturn: %w", err)
|
||||
}
|
||||
|
||||
expma := MinerAddress(uint64(i))
|
||||
if ma.IDAddress != expma {
|
||||
return cid.Undef, xerrors.Errorf("miner assigned wrong address: %s != %s", ma.IDAddress, expma)
|
||||
}
|
||||
maddr = ma.IDAddress
|
||||
}
|
||||
|
||||
// Add market funds
|
||||
|
||||
{
|
||||
params := mustEnc(&maddr)
|
||||
_, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, m.MarketBalance, builtin.MethodsMarket.AddBalance, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
params := mustEnc(&m.Worker)
|
||||
_, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, big.Zero(), builtin.MethodsMarket.AddBalance, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Publish preseal deals
|
||||
|
||||
var dealIDs []abi.DealID
|
||||
{
|
||||
publish := func(params *market.PublishStorageDealsParams) error {
|
||||
fmt.Printf("publishing %d storage deals on miner %s with worker %s\n", len(params.Deals), params.Deals[0].Proposal.Provider, m.Worker)
|
||||
|
||||
ret, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, big.Zero(), builtin.MethodsMarket.PublishStorageDeals, mustEnc(params))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
var ids market.PublishStorageDealsReturn
|
||||
if err := ids.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return xerrors.Errorf("unmarsahling publishStorageDeals result: %w", err)
|
||||
}
|
||||
|
||||
dealIDs = append(dealIDs, ids.IDs...)
|
||||
return nil
|
||||
}
|
||||
|
||||
params := &market.PublishStorageDealsParams{}
|
||||
for _, preseal := range m.Sectors {
|
||||
params.Deals = append(params.Deals, market.ClientDealProposal{
|
||||
Proposal: preseal.Deal,
|
||||
ClientSignature: crypto.Signature{Type: crypto.SigTypeBLS}, // TODO: do we want to sign these? Or do we want to fake signatures for genesis setup?
|
||||
})
|
||||
|
||||
if len(params.Deals) == cbg.MaxLength {
|
||||
if err := publish(params); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
params = &market.PublishStorageDealsParams{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(params.Deals) > 0 {
|
||||
if err := publish(params); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit sectors
|
||||
for pi, preseal := range m.Sectors {
|
||||
// TODO: Maybe check seal (Can just be snark inputs, doesn't go into the genesis file)
|
||||
|
||||
// check deals, get dealWeight
|
||||
var dealWeight market.VerifyDealsOnSectorProveCommitReturn
|
||||
{
|
||||
params := &market.VerifyDealsOnSectorProveCommitParams{
|
||||
DealIDs: []abi.DealID{dealIDs[pi]},
|
||||
SectorExpiry: preseal.Deal.EndEpoch,
|
||||
}
|
||||
|
||||
ret, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, maddr, big.Zero(), builtin.MethodsMarket.VerifyDealsOnSectorProveCommit, mustEnc(params))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to verify preseal deals miner: %w", err)
|
||||
}
|
||||
if err := dealWeight.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("unmarshaling market onProveCommit result: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// update power claims
|
||||
{
|
||||
err = vm.MutateState(ctx, builtin.StoragePowerActorAddr, func(cst cbor.IpldStore, st *power.State) error {
|
||||
weight := &power.SectorStorageWeightDesc{
|
||||
SectorSize: m.SectorSize,
|
||||
Duration: preseal.Deal.Duration(),
|
||||
DealWeight: dealWeight.DealWeight,
|
||||
VerifiedDealWeight: dealWeight.VerifiedDealWeight,
|
||||
}
|
||||
|
||||
qapower := power.QAPowerForWeight(weight)
|
||||
|
||||
err := st.AddToClaim(&state.AdtStore{cst}, maddr, types.NewInt(uint64(weight.SectorSize)), qapower)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("add to claim: %w", err)
|
||||
}
|
||||
fmt.Println("Added weight to claim: ", st.TotalRawBytePower, st.TotalQualityAdjPower)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("register power claim in power actor: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Put sectors to miner sector sets
|
||||
{
|
||||
newSectorInfo := &miner.SectorOnChainInfo{
|
||||
Info: miner.SectorPreCommitInfo{
|
||||
RegisteredProof: preseal.ProofType,
|
||||
SectorNumber: preseal.SectorID,
|
||||
SealedCID: preseal.CommR,
|
||||
SealRandEpoch: 0,
|
||||
DealIDs: []abi.DealID{dealIDs[pi]},
|
||||
Expiration: preseal.Deal.EndEpoch,
|
||||
},
|
||||
ActivationEpoch: 0,
|
||||
DealWeight: dealWeight.DealWeight,
|
||||
VerifiedDealWeight: dealWeight.VerifiedDealWeight,
|
||||
}
|
||||
|
||||
err = vm.MutateState(ctx, maddr, func(cst cbor.IpldStore, st *miner.State) error {
|
||||
store := &state.AdtStore{cst}
|
||||
|
||||
if err = st.PutSector(store, newSectorInfo); err != nil {
|
||||
return xerrors.Errorf("failed to put sector: %v", err)
|
||||
}
|
||||
|
||||
if err := st.AddNewSectors(newSectorInfo.Info.SectorNumber); err != nil {
|
||||
return xerrors.Errorf("failed to add NewSector: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Cid{}, xerrors.Errorf("put to sset: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: to avoid division by zero, we set the initial power actor power to 1, this adjusts that back down so the accounting is accurate.
|
||||
err = vm.MutateState(ctx, builtin.StoragePowerActorAddr, func(cst cbor.IpldStore, st *power.State) error {
|
||||
st.TotalQualityAdjPower = big.Sub(st.TotalQualityAdjPower, big.NewInt(1))
|
||||
return nil
|
||||
})
|
||||
|
||||
c, err := vm.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("flushing vm: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// TODO: copied from actors test harness, deduplicate or remove from here
|
||||
type fakeRand struct{}
|
||||
|
||||
func (fr *fakeRand) GetRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
out := make([]byte, 32)
|
||||
_, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out)
|
||||
return out, nil
|
||||
}
|
29
chain/gen/genesis/t00_system.go
Normal file
29
chain/gen/genesis/t00_system.go
Normal file
@ -0,0 +1,29 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/system"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func SetupSystemActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
var st system.State
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
statecid, err := cst.Put(context.TODO(), &st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.SystemActorCodeID,
|
||||
Head: statecid,
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
69
chain/gen/genesis/t01_init.go
Normal file
69
chain/gen/genesis/t01_init.go
Normal file
@ -0,0 +1,69 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesis.Actor) (*types.Actor, error) {
|
||||
if len(initialActors) > MaxAccounts {
|
||||
return nil, xerrors.New("too many initial actors")
|
||||
}
|
||||
|
||||
var ias init_.State
|
||||
ias.NextID = MinerStart
|
||||
ias.NetworkName = netname
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
amap := hamt.NewNode(cst, hamt.UseTreeBitWidth(5)) // TODO: use spec adt map
|
||||
|
||||
for i, a := range initialActors {
|
||||
if a.Type != genesis.TAccount {
|
||||
return nil, xerrors.Errorf("unsupported account type: %s", a.Type) // TODO: Support msig (skip here)
|
||||
}
|
||||
|
||||
var ainfo genesis.AccountMeta
|
||||
if err := json.Unmarshal(a.Meta, &ainfo); err != nil {
|
||||
return nil, xerrors.Errorf("unmarshaling account meta: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("init set %s t0%d\n", ainfo.Owner, AccountStart+uint64(i))
|
||||
|
||||
if err := amap.Set(context.TODO(), string(ainfo.Owner.Bytes()), AccountStart+uint64(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := amap.Flush(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
amapcid, err := cst.Put(context.TODO(), amap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ias.AddressMap = amapcid
|
||||
|
||||
statecid, err := cst.Put(context.TODO(), &ias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.InitActorCodeID,
|
||||
Head: statecid,
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
30
chain/gen/genesis/t02_reward.go
Normal file
30
chain/gen/genesis/t02_reward.go
Normal file
@ -0,0 +1,30 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func SetupRewardActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
st := reward.ConstructState()
|
||||
st.LastPerEpochReward = types.FromFil(100)
|
||||
|
||||
hcid, err := cst.Put(context.TODO(), st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: builtin.RewardActorCodeID,
|
||||
Balance: types.BigInt{Int: build.InitialRewardBalance},
|
||||
Head: hcid,
|
||||
}, nil
|
||||
}
|
29
chain/gen/genesis/t03_cron.go
Normal file
29
chain/gen/genesis/t03_cron.go
Normal file
@ -0,0 +1,29 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/cron"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func SetupCronActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(bs)
|
||||
cas := cron.ConstructState(cron.BuiltInEntries())
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), cas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: builtin.CronActorCodeID,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
47
chain/gen/genesis/t04_power.go
Normal file
47
chain/gen/genesis/t04_power.go
Normal file
@ -0,0 +1,47 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func SetupStoragePowerActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
ctx := context.TODO()
|
||||
cst := cbor.NewCborStore(bs)
|
||||
nd := hamt.NewNode(cst, hamt.UseTreeBitWidth(5))
|
||||
emptyhamt, err := cst.Put(ctx, nd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sms := &power.State{
|
||||
TotalRawBytePower: big.NewInt(0),
|
||||
TotalQualityAdjPower: big.NewInt(1), // TODO: has to be 1 initially to avoid div by zero. Kinda annoying, should find a way to fix
|
||||
TotalPledgeCollateral: big.NewInt(0),
|
||||
MinerCount: 0,
|
||||
CronEventQueue: emptyhamt,
|
||||
LastEpochTick: 0,
|
||||
Claims: emptyhamt,
|
||||
NumMinersMeetingMinPower: 0,
|
||||
}
|
||||
|
||||
stcid, err := cst.Put(ctx, sms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: builtin.StoragePowerActorCodeID,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
42
chain/gen/genesis/t05_market.go
Normal file
42
chain/gen/genesis/t05_market.go
Normal file
@ -0,0 +1,42 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
a, err := amt.NewAMT(cst).Flush(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := cst.Put(context.TODO(), hamt.NewNode(cst, hamt.UseTreeBitWidth(5)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sms := market.ConstructState(a, h, h)
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.StorageMarketActorCodeID,
|
||||
Head: stcid,
|
||||
Balance: types.NewInt(0),
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
44
chain/gen/genesis/t06_vreg.go
Normal file
44
chain/gen/genesis/t06_vreg.go
Normal file
@ -0,0 +1,44 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func SetupVerifiedRegistryActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
h, err := cst.Put(context.TODO(), hamt.NewNode(cst, hamt.UseTreeBitWidth(5)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r [32]byte // TODO: grab from genesis template
|
||||
_, _ = rand.Read(r[:])
|
||||
k, _ := address.NewSecp256k1Address(r[:])
|
||||
|
||||
sms := verifreg.ConstructState(h, k)
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.VerifiedRegistryActorCodeID,
|
||||
Head: stcid,
|
||||
Balance: types.NewInt(0),
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
53
chain/gen/genesis/util.go
Normal file
53
chain/gen/genesis/util.go
Normal file
@ -0,0 +1,53 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
)
|
||||
|
||||
func mustEnc(i cbg.CBORMarshaler) []byte {
|
||||
enc, err := actors.SerializeParams(i)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method abi.MethodNum, params []byte) ([]byte, error) {
|
||||
return doExecValue(ctx, vm, to, from, types.NewInt(0), method, params)
|
||||
}
|
||||
|
||||
func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value types.BigInt, method abi.MethodNum, params []byte) ([]byte, error) {
|
||||
act, err := vm.StateTree().GetActor(from)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("doExec failed to get from actor: %w", err)
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyImplicitMessage(ctx, &types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: method,
|
||||
Params: params,
|
||||
GasLimit: 1_000_000_000_000_000,
|
||||
GasPrice: types.NewInt(0),
|
||||
Value: value,
|
||||
Nonce: act.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("doExec apply message failed: %w", err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return nil, xerrors.Errorf("failed to call method: %w", ret.ActorErr)
|
||||
}
|
||||
|
||||
return ret.Return, nil
|
||||
}
|
@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
|
||||
bls "github.com/filecoin-project/filecoin-ffi"
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@ -18,24 +19,33 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
)
|
||||
|
||||
func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height, timestamp uint64) (*types.FullBlock, error) {
|
||||
st, recpts, err := sm.TipSetState(ctx, parents)
|
||||
func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error) {
|
||||
|
||||
pts, err := sm.ChainStore().LoadTipSet(bt.Parents)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parent tipset: %w", err)
|
||||
}
|
||||
|
||||
st, recpts, err := sm.TipSetState(ctx, pts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load tipset state: %w", err)
|
||||
}
|
||||
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, miner)
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, bt.Miner)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
|
||||
}
|
||||
|
||||
next := &types.BlockHeader{
|
||||
Miner: miner,
|
||||
Parents: parents.Cids(),
|
||||
Ticket: ticket,
|
||||
Height: height,
|
||||
Timestamp: timestamp,
|
||||
EPostProof: *proof,
|
||||
Miner: bt.Miner,
|
||||
Parents: bt.Parents.Cids(),
|
||||
Ticket: bt.Ticket,
|
||||
ElectionProof: bt.Eproof,
|
||||
|
||||
BeaconEntries: bt.BeaconValues,
|
||||
Height: bt.Epoch,
|
||||
Timestamp: bt.Timestamp,
|
||||
WinPoStProof: bt.WinningPoStProof,
|
||||
ParentStateRoot: st,
|
||||
ParentMessageReceipts: recpts,
|
||||
}
|
||||
@ -44,9 +54,9 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
|
||||
var secpkMessages []*types.SignedMessage
|
||||
|
||||
var blsMsgCids, secpkMsgCids []cid.Cid
|
||||
var blsSigs []types.Signature
|
||||
for _, msg := range msgs {
|
||||
if msg.Signature.TypeCode() == types.IKTBLS {
|
||||
var blsSigs []crypto.Signature
|
||||
for _, msg := range bt.Messages {
|
||||
if msg.Signature.Type == crypto.SigTypeBLS {
|
||||
blsSigs = append(blsSigs, msg.Signature)
|
||||
blsMessages = append(blsMessages, &msg.Message)
|
||||
|
||||
@ -68,17 +78,17 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
|
||||
}
|
||||
}
|
||||
|
||||
bs := amt.WrapBlockstore(sm.ChainStore().Blockstore())
|
||||
blsmsgroot, err := amt.FromArray(bs, toIfArr(blsMsgCids))
|
||||
bs := cbor.NewCborStore(sm.ChainStore().Blockstore())
|
||||
blsmsgroot, err := amt.FromArray(ctx, bs, toIfArr(blsMsgCids))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("building bls amt: %w", err)
|
||||
}
|
||||
secpkmsgroot, err := amt.FromArray(bs, toIfArr(secpkMsgCids))
|
||||
secpkmsgroot, err := amt.FromArray(ctx, bs, toIfArr(secpkMsgCids))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("building secpk amt: %w", err)
|
||||
}
|
||||
|
||||
mmcid, err := bs.Put(&types.MsgMeta{
|
||||
mmcid, err := bs.Put(ctx, &types.MsgMeta{
|
||||
BlsMessages: blsmsgroot,
|
||||
SecpkMessages: secpkmsgroot,
|
||||
})
|
||||
@ -93,13 +103,13 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
|
||||
}
|
||||
|
||||
next.BLSAggregate = aggSig
|
||||
pweight, err := sm.ChainStore().Weight(ctx, parents)
|
||||
pweight, err := sm.ChainStore().Weight(ctx, pts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next.ParentWeight = pweight
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.ChainStore().Blockstore())
|
||||
cst := cbor.NewCborStore(sm.ChainStore().Blockstore())
|
||||
tree, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load state tree: %w", err)
|
||||
@ -131,7 +141,7 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
|
||||
return fullBlock, nil
|
||||
}
|
||||
|
||||
func aggregateSignatures(sigs []types.Signature) (types.Signature, error) {
|
||||
func aggregateSignatures(sigs []crypto.Signature) (*crypto.Signature, error) {
|
||||
var blsSigs []bls.Signature
|
||||
for _, s := range sigs {
|
||||
var bsig bls.Signature
|
||||
@ -140,8 +150,8 @@ func aggregateSignatures(sigs []types.Signature) (types.Signature, error) {
|
||||
}
|
||||
|
||||
aggSig := bls.Aggregate(blsSigs)
|
||||
return types.Signature{
|
||||
Type: types.KTBLS,
|
||||
return &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: aggSig[:],
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,677 +0,0 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
type GenesisBootstrap struct {
|
||||
Genesis *types.BlockHeader
|
||||
}
|
||||
|
||||
func SetupInitActor(bs bstore.Blockstore, addrs []address.Address) (*types.Actor, error) {
|
||||
var ias actors.InitActorState
|
||||
ias.NextID = 100
|
||||
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
amap := hamt.NewNode(cst)
|
||||
|
||||
for i, a := range addrs {
|
||||
if err := amap.Set(context.TODO(), string(a.Bytes()), 100+uint64(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ias.NextID += uint64(len(addrs))
|
||||
if err := amap.Flush(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
amapcid, err := cst.Put(context.TODO(), amap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ias.AddressMap = amapcid
|
||||
|
||||
statecid, err := cst.Put(context.TODO(), &ias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: actors.InitCodeCid,
|
||||
Head: statecid,
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
||||
|
||||
func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types.BigInt) (*state.StateTree, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
state, err := state.NewStateTree(cst)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
emptyobject, err := cst.Put(context.TODO(), map[string]string{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed putting empty object: %w", err)
|
||||
}
|
||||
|
||||
var addrs []address.Address
|
||||
for a := range actmap {
|
||||
addrs = append(addrs, a)
|
||||
}
|
||||
|
||||
initact, err := SetupInitActor(bs, addrs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.InitAddress, initact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
cronact, err := SetupCronActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup cron actor: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.CronAddress, cronact); err != nil {
|
||||
return nil, xerrors.Errorf("set cron actor: %w", err)
|
||||
}
|
||||
|
||||
spact, err := SetupStoragePowerActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.StoragePowerAddress, spact); err != nil {
|
||||
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
netAmt := types.FromFil(build.TotalFilecoin)
|
||||
for _, amt := range actmap {
|
||||
netAmt = types.BigSub(netAmt, amt)
|
||||
}
|
||||
|
||||
err = state.SetActor(actors.NetworkAddress, &types.Actor{
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: netAmt,
|
||||
Head: emptyobject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set network account actor: %w", err)
|
||||
}
|
||||
|
||||
err = state.SetActor(actors.BurntFundsAddress, &types.Actor{
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: types.NewInt(0),
|
||||
Head: emptyobject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set burnt funds account actor: %w", err)
|
||||
}
|
||||
|
||||
for a, v := range actmap {
|
||||
err = state.SetActor(a, &types.Actor{
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: v,
|
||||
Head: emptyobject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setting account from actmap: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func SetupCronActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
cas := &actors.CronActorState{}
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), cas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: actors.CronCodeCid,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetupStoragePowerActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
nd := hamt.NewNode(cst)
|
||||
emptyhamt, err := cst.Put(context.TODO(), nd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
emptyamt, err := amt.FromArray(blks, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
sms := &actors.StoragePowerState{
|
||||
Miners: emptyhamt,
|
||||
ProvingBuckets: emptyamt,
|
||||
TotalStorage: types.NewInt(0),
|
||||
}
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: actors.StoragePowerCodeCid,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetupStorageMarketActor(bs bstore.Blockstore, sroot cid.Cid, deals []actors.StorageDealProposal) (cid.Cid, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
nd := hamt.NewNode(cst)
|
||||
emptyHAMT, err := cst.Put(context.TODO(), nd)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
|
||||
cdeals := make([]cbg.CBORMarshaler, len(deals))
|
||||
for i, deal := range deals {
|
||||
cdeals[i] = &actors.OnChainDeal{
|
||||
PieceRef: deal.PieceRef,
|
||||
PieceSize: deal.PieceSize,
|
||||
Client: deal.Client,
|
||||
Provider: deal.Provider,
|
||||
ProposalExpiration: deal.ProposalExpiration,
|
||||
Duration: deal.Duration,
|
||||
StoragePricePerEpoch: deal.StoragePricePerEpoch,
|
||||
StorageCollateral: deal.StorageCollateral,
|
||||
ActivationEpoch: 1,
|
||||
}
|
||||
}
|
||||
|
||||
dealAmt, err := amt.FromArray(blks, cdeals)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
sms := &actors.StorageMarketState{
|
||||
Balances: emptyHAMT,
|
||||
Deals: dealAmt,
|
||||
NextDealID: uint64(len(deals)),
|
||||
}
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: actors.StorageMarketCodeCid,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}
|
||||
|
||||
state, err := state.LoadStateTree(cst, sroot)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.StorageMarketAddress, act); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
return state.Flush(context.TODO())
|
||||
}
|
||||
|
||||
type GenMinerCfg struct {
|
||||
PreSeals map[string]genesis.GenesisMiner
|
||||
|
||||
// The addresses of the created miner, this is set by the genesis setup
|
||||
MinerAddrs []address.Address
|
||||
|
||||
PeerIDs []peer.ID
|
||||
}
|
||||
|
||||
func mustEnc(i cbg.CBORMarshaler) []byte {
|
||||
enc, err := actors.SerializeParams(i)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, []actors.StorageDealProposal, error) {
|
||||
vm, err := vm.NewVM(sroot, 0, nil, actors.NetworkAddress, cs.Blockstore(), cs.VMSys())
|
||||
if err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("failed to create NewVM: %w", err)
|
||||
}
|
||||
|
||||
if len(gmcfg.MinerAddrs) == 0 {
|
||||
return cid.Undef, nil, xerrors.New("no genesis miners")
|
||||
}
|
||||
|
||||
if len(gmcfg.MinerAddrs) != len(gmcfg.PreSeals) {
|
||||
return cid.Undef, nil, xerrors.Errorf("miner address list, and preseal count doesn't match (%d != %d)", len(gmcfg.MinerAddrs), len(gmcfg.PreSeals))
|
||||
}
|
||||
|
||||
var deals []actors.StorageDealProposal
|
||||
|
||||
for i, maddr := range gmcfg.MinerAddrs {
|
||||
ps, psok := gmcfg.PreSeals[maddr.String()]
|
||||
if !psok {
|
||||
return cid.Undef, nil, xerrors.Errorf("no preseal for miner %s", maddr)
|
||||
}
|
||||
|
||||
minerParams := &actors.CreateStorageMinerParams{
|
||||
Owner: ps.Owner,
|
||||
Worker: ps.Worker,
|
||||
SectorSize: ps.SectorSize,
|
||||
PeerID: gmcfg.PeerIDs[i], // TODO: grab from preseal too
|
||||
}
|
||||
|
||||
params := mustEnc(minerParams)
|
||||
|
||||
// TODO: hardcoding 6500 here is a little fragile, it changes any
|
||||
// time anyone changes the initial account allocations
|
||||
rval, err := doExecValue(ctx, vm, actors.StoragePowerAddress, ps.Worker, types.FromFil(6500), actors.SPAMethods.CreateStorageMiner, params)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
|
||||
maddrret, err := address.NewFromBytes(rval)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
_, err = vm.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(cs.Blockstore())
|
||||
if err := reassignMinerActorAddress(vm, cst, maddrret, maddr); err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
power := types.BigMul(types.NewInt(minerParams.SectorSize), types.NewInt(uint64(len(ps.Sectors))))
|
||||
|
||||
params = mustEnc(&actors.UpdateStorageParams{Delta: power})
|
||||
|
||||
_, err = doExec(ctx, vm, actors.StoragePowerAddress, maddr, actors.SPAMethods.UpdateStorage, params)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("failed to update total storage: %w", err)
|
||||
}
|
||||
|
||||
// we have to flush the vm here because it buffers stuff internally for perf reasons
|
||||
if _, err := vm.Flush(ctx); err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("vm.Flush failed: %w", err)
|
||||
}
|
||||
|
||||
st := vm.StateTree()
|
||||
mact, err := st.GetActor(maddr)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("get miner actor failed: %w", err)
|
||||
}
|
||||
|
||||
var mstate actors.StorageMinerActorState
|
||||
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("getting miner actor state failed: %w", err)
|
||||
}
|
||||
mstate.Power = types.BigMul(types.NewInt(ps.SectorSize), types.NewInt(uint64(len(ps.Sectors))))
|
||||
|
||||
blks := amt.WrapBlockstore(cs.Blockstore())
|
||||
|
||||
for _, s := range ps.Sectors {
|
||||
nssroot, err := actors.AddToSectorSet(ctx, blks, mstate.Sectors, s.SectorID, s.CommR[:], s.CommD[:])
|
||||
if err != nil {
|
||||
return cid.Undef, nil, xerrors.Errorf("failed to add fake sector to sector set: %w", err)
|
||||
}
|
||||
mstate.Sectors = nssroot
|
||||
mstate.ProvingSet = nssroot
|
||||
|
||||
deals = append(deals, s.Deal)
|
||||
}
|
||||
|
||||
nstate, err := cst.Put(ctx, &mstate)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
mact.Head = nstate
|
||||
if err := st.SetActor(maddr, mact); err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c, err := vm.Flush(ctx)
|
||||
return c, deals, err
|
||||
}
|
||||
|
||||
func reassignMinerActorAddress(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error {
|
||||
if from == to {
|
||||
return nil
|
||||
}
|
||||
act, err := vm.StateTree().GetActor(from)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("reassign: failed to get 'from' actor: %w", err)
|
||||
}
|
||||
|
||||
_, err = vm.StateTree().GetActor(to)
|
||||
if err == nil {
|
||||
return xerrors.Errorf("cannot reassign actor, target address taken")
|
||||
}
|
||||
if err := vm.StateTree().SetActor(to, act); err != nil {
|
||||
return xerrors.Errorf("failed to reassign actor: %w", err)
|
||||
}
|
||||
|
||||
if err := adjustStorageMarketTracking(vm, cst, from, to); err != nil {
|
||||
return xerrors.Errorf("adjusting storage market tracking: %w", err)
|
||||
}
|
||||
|
||||
// Now, adjust the tracking in the init actor
|
||||
return initActorReassign(vm, cst, from, to)
|
||||
}
|
||||
|
||||
func adjustStorageMarketTracking(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error {
|
||||
ctx := context.TODO()
|
||||
act, err := vm.StateTree().GetActor(actors.StoragePowerAddress)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("loading storage power actor: %w", err)
|
||||
}
|
||||
|
||||
var spst actors.StoragePowerState
|
||||
if err := cst.Get(ctx, act.Head, &spst); err != nil {
|
||||
return xerrors.Errorf("loading storage power actor state: %w", err)
|
||||
}
|
||||
|
||||
miners, err := hamt.LoadNode(ctx, cst, spst.Miners)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("loading miner set: %w", err)
|
||||
}
|
||||
|
||||
if err := miners.Delete(ctx, string(from.Bytes())); err != nil {
|
||||
return xerrors.Errorf("deleting from spa set: %w", err)
|
||||
}
|
||||
|
||||
if err := miners.Set(ctx, string(to.Bytes()), uint64(1)); err != nil {
|
||||
return xerrors.Errorf("failed setting miner: %w", err)
|
||||
}
|
||||
|
||||
if err := miners.Flush(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nminerscid, err := cst.Put(ctx, miners)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spst.Miners = nminerscid
|
||||
|
||||
nhead, err := cst.Put(ctx, &spst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
act.Head = nhead
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initActorReassign(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error {
|
||||
ctx := context.TODO()
|
||||
initact, err := vm.StateTree().GetActor(actors.InitAddress)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("couldnt get init actor: %w", err)
|
||||
}
|
||||
|
||||
var st actors.InitActorState
|
||||
if err := cst.Get(ctx, initact.Head, &st); err != nil {
|
||||
return xerrors.Errorf("reassign loading init actor state: %w", err)
|
||||
}
|
||||
|
||||
amap, err := hamt.LoadNode(ctx, cst, st.AddressMap)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load init actor map: %w", err)
|
||||
}
|
||||
|
||||
target, err := address.IDFromAddress(from)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to extract ID: %w", err)
|
||||
}
|
||||
|
||||
var out string
|
||||
halt := xerrors.Errorf("halt")
|
||||
err = amap.ForEach(ctx, func(k string, v interface{}) error {
|
||||
_, val, err := cbg.CborReadHeader(bytes.NewReader(v.(*cbg.Deferred).Raw))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parsing int in map failed: %w", err)
|
||||
}
|
||||
|
||||
if val == target {
|
||||
out = k
|
||||
return halt
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
return xerrors.Errorf("could not find from address in init ID map")
|
||||
}
|
||||
if !xerrors.Is(err, halt) {
|
||||
return xerrors.Errorf("finding address in ID map failed: %w", err)
|
||||
}
|
||||
|
||||
if err := amap.Delete(ctx, out); err != nil {
|
||||
return xerrors.Errorf("deleting 'from' entry in amap: %w", err)
|
||||
}
|
||||
|
||||
if err := amap.Set(ctx, out, target); err != nil {
|
||||
return xerrors.Errorf("setting 'to' entry in amap: %w", err)
|
||||
}
|
||||
|
||||
if err := amap.Flush(ctx); err != nil {
|
||||
return xerrors.Errorf("failed to flush amap: %w", err)
|
||||
}
|
||||
|
||||
ncid, err := cst.Put(ctx, amap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st.AddressMap = ncid
|
||||
|
||||
nacthead, err := cst.Put(ctx, &st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
initact.Head = nacthead
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) {
|
||||
return doExecValue(ctx, vm, to, from, types.NewInt(0), method, params)
|
||||
}
|
||||
|
||||
func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value types.BigInt, method uint64, params []byte) ([]byte, error) {
|
||||
act, err := vm.StateTree().GetActor(from)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("doExec failed to get from actor: %w", err)
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyMessage(context.TODO(), &types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: method,
|
||||
Params: params,
|
||||
GasLimit: types.NewInt(1000000),
|
||||
GasPrice: types.NewInt(0),
|
||||
Value: value,
|
||||
Nonce: act.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("doExec apply message failed: %w", err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return nil, fmt.Errorf("failed to call method: %s", ret.ActorErr)
|
||||
}
|
||||
|
||||
return ret.Return, nil
|
||||
}
|
||||
|
||||
func MakeGenesisBlock(bs bstore.Blockstore, sys *types.VMSyscalls, balances map[address.Address]types.BigInt, gmcfg *GenMinerCfg, ts uint64) (*GenesisBootstrap, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
state, err := MakeInitialStateTree(bs, balances)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err := state.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("flush state tree failed: %w", err)
|
||||
}
|
||||
|
||||
// temp chainstore
|
||||
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys)
|
||||
stateroot, deals, err := SetupStorageMiners(ctx, cs, stateroot, gmcfg)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage miners failed: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err = SetupStorageMarketActor(bs, stateroot, deals)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err = AdjustInitActorStartID(ctx, bs, stateroot, 1000)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to adjust init actor start ID: %w", err)
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
|
||||
emptyroot, err := amt.FromArray(blks, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
mm := &types.MsgMeta{
|
||||
BlsMessages: emptyroot,
|
||||
SecpkMessages: emptyroot,
|
||||
}
|
||||
mmb, err := mm.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing msgmeta failed: %w", err)
|
||||
}
|
||||
if err := bs.Put(mmb); err != nil {
|
||||
return nil, xerrors.Errorf("putting msgmeta block to blockstore: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Empty Genesis root: %s", emptyroot)
|
||||
|
||||
genesisticket := &types.Ticket{
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
}
|
||||
|
||||
b := &types.BlockHeader{
|
||||
Miner: actors.InitAddress,
|
||||
Ticket: genesisticket,
|
||||
EPostProof: types.EPostProof{
|
||||
Proof: []byte("not a real proof"),
|
||||
PostRand: []byte("i guess this is kinda random"),
|
||||
},
|
||||
Parents: []cid.Cid{},
|
||||
Height: 0,
|
||||
ParentWeight: types.NewInt(0),
|
||||
ParentStateRoot: stateroot,
|
||||
Messages: mmb.Cid(),
|
||||
ParentMessageReceipts: emptyroot,
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS, Data: []byte("signatureeee")},
|
||||
BlockSig: &types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")},
|
||||
Timestamp: ts,
|
||||
}
|
||||
|
||||
sb, err := b.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing block header failed: %w", err)
|
||||
}
|
||||
|
||||
if err := bs.Put(sb); err != nil {
|
||||
return nil, xerrors.Errorf("putting header to blockstore: %w", err)
|
||||
}
|
||||
|
||||
return &GenesisBootstrap{
|
||||
Genesis: b,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AdjustInitActorStartID(ctx context.Context, bs blockstore.Blockstore, stateroot cid.Cid, val uint64) (cid.Cid, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
|
||||
tree, err := state.LoadStateTree(cst, stateroot)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
act, err := tree.GetActor(actors.InitAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
var st actors.InitActorState
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
st.NextID = val
|
||||
|
||||
nstate, err := cst.Put(ctx, &st)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
act.Head = nstate
|
||||
|
||||
if err := tree.SetActor(actors.InitAddress, act); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return tree.Flush(ctx)
|
||||
}
|
@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
@ -13,6 +15,8 @@ import (
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
)
|
||||
|
||||
var log = logging.Logger("market_adapter")
|
||||
|
||||
type FundMgr struct {
|
||||
sm *stmgr.StateManager
|
||||
mpool full.MpoolAPI
|
||||
@ -30,17 +34,17 @@ func NewFundMgr(sm *stmgr.StateManager, mpool full.MpoolAPI) *FundMgr {
|
||||
}
|
||||
}
|
||||
|
||||
func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr, wallet address.Address, amt types.BigInt) (cid.Cid, error) {
|
||||
fm.lk.Lock()
|
||||
avail, ok := fm.available[addr]
|
||||
if !ok {
|
||||
bal, err := fm.sm.MarketBalance(ctx, addr, nil)
|
||||
if err != nil {
|
||||
fm.lk.Unlock()
|
||||
return err
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
avail = bal.Available
|
||||
avail = types.BigSub(bal.Escrow, bal.Locked)
|
||||
}
|
||||
|
||||
toAdd := types.NewInt(0)
|
||||
@ -55,25 +59,24 @@ func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr address.Address, am
|
||||
|
||||
fm.lk.Unlock()
|
||||
|
||||
var err error
|
||||
params, err := actors.SerializeParams(&addr)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
smsg, err := fm.mpool.MpoolPushMessage(ctx, &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
From: addr,
|
||||
To: builtin.StorageMarketActorAddr,
|
||||
From: wallet,
|
||||
Value: toAdd,
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
Method: actors.SMAMethods.AddBalance,
|
||||
GasLimit: 1000000,
|
||||
Method: builtin.MethodsMarket.AddBalance,
|
||||
Params: params,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
_, r, err := fm.sm.WaitForMessage(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.ExitCode != 0 {
|
||||
return xerrors.Errorf("adding funds to storage miner market actor failed: exit %d", r.ExitCode)
|
||||
}
|
||||
return nil
|
||||
return smsg.Cid(), nil
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
@ -23,7 +25,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
@ -31,6 +32,15 @@ import (
|
||||
|
||||
var log = logging.Logger("messagepool")
|
||||
|
||||
const futureDebug = false
|
||||
|
||||
const ReplaceByFeeRatio = 1.25
|
||||
|
||||
var (
|
||||
rbfNum = types.NewInt(uint64((ReplaceByFeeRatio - 1) * 256))
|
||||
rbfDenom = types.NewInt(256)
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMessageTooBig = errors.New("message too big")
|
||||
|
||||
@ -41,11 +51,11 @@ var (
|
||||
ErrNotEnoughFunds = errors.New("not enough funds to execute transaction")
|
||||
|
||||
ErrInvalidToAddr = errors.New("message had invalid to address")
|
||||
|
||||
ErrBroadcastAnyway = errors.New("broadcasting message despite validation fail")
|
||||
)
|
||||
|
||||
const (
|
||||
msgTopic = "/fil/messages"
|
||||
|
||||
localMsgsDs = "/mpool/local"
|
||||
|
||||
localUpdates = "update"
|
||||
@ -75,6 +85,8 @@ type MessagePool struct {
|
||||
changes *lps.PubSub
|
||||
|
||||
localMsgs datastore.Datastore
|
||||
|
||||
netName dtypes.NetworkName
|
||||
}
|
||||
|
||||
type msgSet struct {
|
||||
@ -92,10 +104,20 @@ func (ms *msgSet) add(m *types.SignedMessage) error {
|
||||
if len(ms.msgs) == 0 || m.Message.Nonce >= ms.nextNonce {
|
||||
ms.nextNonce = m.Message.Nonce + 1
|
||||
}
|
||||
if _, has := ms.msgs[m.Message.Nonce]; has {
|
||||
if m.Cid() != ms.msgs[m.Message.Nonce].Cid() {
|
||||
log.Error("Add with duplicate nonce")
|
||||
return xerrors.Errorf("message to %s with nonce %d already in mpool", m.Message.To, m.Message.Nonce)
|
||||
exms, has := ms.msgs[m.Message.Nonce]
|
||||
if has {
|
||||
if m.Cid() != exms.Cid() {
|
||||
// check if RBF passes
|
||||
minPrice := exms.Message.GasPrice
|
||||
minPrice = types.BigAdd(minPrice, types.BigDiv(types.BigMul(minPrice, rbfNum), rbfDenom))
|
||||
minPrice = types.BigAdd(minPrice, types.NewInt(1))
|
||||
if types.BigCmp(m.Message.GasPrice, minPrice) > 0 {
|
||||
log.Infow("add with RBF", "oldprice", exms.Message.GasPrice,
|
||||
"newprice", m.Message.GasPrice, "addr", m.Message.From, "nonce", m.Message.Nonce)
|
||||
} else {
|
||||
log.Info("add with duplicate nonce")
|
||||
return xerrors.Errorf("message to %s with nonce %d already in mpool", m.Message.To, m.Message.Nonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
ms.msgs[m.Message.Nonce] = m
|
||||
@ -105,11 +127,12 @@ func (ms *msgSet) add(m *types.SignedMessage) error {
|
||||
|
||||
type Provider interface {
|
||||
SubscribeHeadChanges(func(rev, app []*types.TipSet) error) *types.TipSet
|
||||
PutMessage(m store.ChainMsg) (cid.Cid, error)
|
||||
PutMessage(m types.ChainMsg) (cid.Cid, error)
|
||||
PubSubPublish(string, []byte) error
|
||||
StateGetActor(address.Address, *types.TipSet) (*types.Actor, error)
|
||||
StateAccountKey(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
MessagesForBlock(*types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error)
|
||||
MessagesForTipset(*types.TipSet) ([]store.ChainMsg, error)
|
||||
MessagesForTipset(*types.TipSet) ([]types.ChainMsg, error)
|
||||
LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error)
|
||||
}
|
||||
|
||||
@ -127,7 +150,7 @@ func (mpp *mpoolProvider) SubscribeHeadChanges(cb func(rev, app []*types.TipSet)
|
||||
return mpp.sm.ChainStore().GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
func (mpp *mpoolProvider) PutMessage(m store.ChainMsg) (cid.Cid, error) {
|
||||
func (mpp *mpoolProvider) PutMessage(m types.ChainMsg) (cid.Cid, error) {
|
||||
return mpp.sm.ChainStore().PutMessage(m)
|
||||
}
|
||||
|
||||
@ -139,11 +162,15 @@ func (mpp *mpoolProvider) StateGetActor(addr address.Address, ts *types.TipSet)
|
||||
return mpp.sm.GetActor(addr, ts)
|
||||
}
|
||||
|
||||
func (mpp *mpoolProvider) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
return mpp.sm.ResolveToKeyAddress(ctx, addr, ts)
|
||||
}
|
||||
|
||||
func (mpp *mpoolProvider) MessagesForBlock(h *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
return mpp.sm.ChainStore().MessagesForBlock(h)
|
||||
}
|
||||
|
||||
func (mpp *mpoolProvider) MessagesForTipset(ts *types.TipSet) ([]store.ChainMsg, error) {
|
||||
func (mpp *mpoolProvider) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) {
|
||||
return mpp.sm.ChainStore().MessagesForTipset(ts)
|
||||
}
|
||||
|
||||
@ -151,7 +178,7 @@ func (mpp *mpoolProvider) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error)
|
||||
return mpp.sm.ChainStore().LoadTipSet(tsk)
|
||||
}
|
||||
|
||||
func New(api Provider, ds dtypes.MetadataDS) (*MessagePool, error) {
|
||||
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) {
|
||||
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
|
||||
mp := &MessagePool{
|
||||
closer: make(chan struct{}),
|
||||
@ -164,6 +191,7 @@ func New(api Provider, ds dtypes.MetadataDS) (*MessagePool, error) {
|
||||
changes: lps.New(50),
|
||||
localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)),
|
||||
api: api,
|
||||
netName: netName,
|
||||
}
|
||||
|
||||
if err := mp.loadLocal(); err != nil {
|
||||
@ -236,7 +264,7 @@ func (mp *MessagePool) repubLocal() {
|
||||
continue
|
||||
}
|
||||
|
||||
err = mp.api.PubSubPublish(msgTopic, msgb)
|
||||
err = mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
|
||||
if err != nil {
|
||||
errout = multierr.Append(errout, xerrors.Errorf("could not publish: %w", err))
|
||||
continue
|
||||
@ -281,7 +309,7 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
|
||||
}
|
||||
mp.lk.Unlock()
|
||||
|
||||
return m.Cid(), mp.api.PubSubPublish(msgTopic, msgb)
|
||||
return m.Cid(), mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
||||
@ -311,7 +339,7 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error
|
||||
|
||||
snonce, err := mp.getStateNonce(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %w", err)
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
|
||||
}
|
||||
|
||||
if snonce > m.Message.Nonce {
|
||||
@ -320,7 +348,7 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error
|
||||
|
||||
balance, err := mp.getStateBalance(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check sender balance: %w", err)
|
||||
return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrBroadcastAnyway)
|
||||
}
|
||||
|
||||
if balance.LessThan(m.Message.RequiredFunds()) {
|
||||
@ -341,8 +369,8 @@ func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error {
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
||||
log.Debugf("mpooladd: %s %s", m.Message.From, m.Message.Nonce)
|
||||
if m.Signature.Type == types.KTBLS {
|
||||
log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce)
|
||||
if m.Signature.Type == crypto.SigTypeBLS {
|
||||
mp.blsSigCache.Add(m.Cid(), m.Signature)
|
||||
}
|
||||
|
||||
@ -363,7 +391,7 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
||||
}
|
||||
|
||||
if err := mset.add(m); err != nil {
|
||||
log.Error(err)
|
||||
log.Info(err)
|
||||
}
|
||||
|
||||
mp.changes.Pub(api.MpoolUpdate{
|
||||
@ -447,22 +475,28 @@ func (mp *MessagePool) getStateBalance(addr address.Address, ts *types.TipSet) (
|
||||
return act.Balance, nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) PushWithNonce(addr address.Address, cb func(uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
|
||||
func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address, cb func(address.Address, uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
if addr.Protocol() == address.ID {
|
||||
log.Warnf("Called pushWithNonce with ID address (%s) this might not be handled properly yet", addr)
|
||||
|
||||
fromKey := addr
|
||||
if fromKey.Protocol() == address.ID {
|
||||
var err error
|
||||
fromKey, err = mp.api.StateAccountKey(ctx, fromKey, mp.curTs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("resolving sender key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
nonce, err := mp.getNonceLocked(addr, mp.curTs)
|
||||
nonce, err := mp.getNonceLocked(fromKey, mp.curTs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("get nonce locked failed: %w", err)
|
||||
}
|
||||
|
||||
msg, err := cb(nonce)
|
||||
msg, err := cb(fromKey, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -473,13 +507,13 @@ func (mp *MessagePool) PushWithNonce(addr address.Address, cb func(uint64) (*typ
|
||||
}
|
||||
|
||||
if err := mp.addLocked(msg); err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("add locked failed: %w", err)
|
||||
}
|
||||
if err := mp.addLocal(msg, msgb); err != nil {
|
||||
log.Errorf("addLocal failed: %+v", err)
|
||||
}
|
||||
|
||||
return msg, mp.api.PubSubPublish(msgTopic, msgb)
|
||||
return msg, mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) Remove(from address.Address, nonce uint64) {
|
||||
@ -625,9 +659,78 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
}
|
||||
}
|
||||
|
||||
if len(revert) > 0 && futureDebug {
|
||||
msgs, ts := mp.Pending()
|
||||
|
||||
buckets := map[address.Address]*statBucket{}
|
||||
|
||||
for _, v := range msgs {
|
||||
bkt, ok := buckets[v.Message.From]
|
||||
if !ok {
|
||||
bkt = &statBucket{
|
||||
msgs: map[uint64]*types.SignedMessage{},
|
||||
}
|
||||
buckets[v.Message.From] = bkt
|
||||
}
|
||||
|
||||
bkt.msgs[v.Message.Nonce] = v
|
||||
}
|
||||
|
||||
for a, bkt := range buckets {
|
||||
act, err := mp.api.StateGetActor(a, ts)
|
||||
if err != nil {
|
||||
log.Debugf("%s, err: %s\n", a, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var cmsg *types.SignedMessage
|
||||
var ok bool
|
||||
|
||||
cur := act.Nonce
|
||||
for {
|
||||
cmsg, ok = bkt.msgs[cur]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
cur++
|
||||
}
|
||||
|
||||
ff := uint64(math.MaxUint64)
|
||||
for k := range bkt.msgs {
|
||||
if k > cur && k < ff {
|
||||
ff = k
|
||||
}
|
||||
}
|
||||
|
||||
if ff != math.MaxUint64 {
|
||||
m := bkt.msgs[ff]
|
||||
|
||||
// cmsg can be nil if no messages from the current nonce are in the mpool
|
||||
ccid := "nil"
|
||||
if cmsg != nil {
|
||||
ccid = cmsg.Cid().String()
|
||||
}
|
||||
|
||||
log.Debugw("Nonce gap",
|
||||
"actor", a,
|
||||
"future_cid", m.Cid(),
|
||||
"future_nonce", ff,
|
||||
"current_cid", ccid,
|
||||
"current_nonce", cur,
|
||||
"revert_tipset", revert[0].Key(),
|
||||
"new_head", ts.Key(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type statBucket struct {
|
||||
msgs map[uint64]*types.SignedMessage
|
||||
}
|
||||
|
||||
func (mp *MessagePool) MessagesForBlocks(blks []*types.BlockHeader) ([]*types.SignedMessage, error) {
|
||||
out := make([]*types.SignedMessage, 0)
|
||||
|
||||
@ -656,7 +759,7 @@ func (mp *MessagePool) RecoverSig(msg *types.Message) *types.SignedMessage {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
sig, ok := val.(types.Signature)
|
||||
sig, ok := val.(crypto.Signature)
|
||||
if !ok {
|
||||
log.Errorf("value in signature cache was not a signature (got %T)", val)
|
||||
return nil
|
||||
@ -719,3 +822,17 @@ func (mp *MessagePool) loadLocal() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const MinGasPrice = 0
|
||||
|
||||
func (mp *MessagePool) EstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
// TODO: something smarter obviously
|
||||
switch nblocksincl {
|
||||
case 0:
|
||||
return types.NewInt(MinGasPrice + 2), nil
|
||||
case 1:
|
||||
return types.NewInt(MinGasPrice + 1), nil
|
||||
default:
|
||||
return types.NewInt(MinGasPrice), nil
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package messagepool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/mock"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
)
|
||||
@ -59,7 +60,7 @@ func (tma *testMpoolApi) SubscribeHeadChanges(cb func(rev, app []*types.TipSet)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tma *testMpoolApi) PutMessage(m store.ChainMsg) (cid.Cid, error) {
|
||||
func (tma *testMpoolApi) PutMessage(m types.ChainMsg) (cid.Cid, error) {
|
||||
return cid.Undef, nil
|
||||
}
|
||||
|
||||
@ -74,11 +75,18 @@ func (tma *testMpoolApi) StateGetActor(addr address.Address, ts *types.TipSet) (
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tma *testMpoolApi) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
if addr.Protocol() != address.BLS && addr.Protocol() != address.SECP256K1 {
|
||||
return address.Undef, fmt.Errorf("given address was not a key addr")
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (tma *testMpoolApi) MessagesForBlock(h *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
return nil, tma.bmsgs[h.Cid()], nil
|
||||
}
|
||||
|
||||
func (tma *testMpoolApi) MessagesForTipset(ts *types.TipSet) ([]store.ChainMsg, error) {
|
||||
func (tma *testMpoolApi) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) {
|
||||
if len(ts.Blocks()) != 1 {
|
||||
panic("cant deal with multiblock tipsets in this test")
|
||||
}
|
||||
@ -88,7 +96,7 @@ func (tma *testMpoolApi) MessagesForTipset(ts *types.TipSet) ([]store.ChainMsg,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []store.ChainMsg
|
||||
var out []types.ChainMsg
|
||||
for _, m := range bm {
|
||||
out = append(out, m)
|
||||
}
|
||||
@ -139,14 +147,14 @@ func TestMessagePool(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds)
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a := mock.MkBlock(nil, 1, 1)
|
||||
|
||||
sender, err := w.GenerateKey(types.KTBLS)
|
||||
sender, err := w.GenerateKey(crypto.SigTypeBLS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -180,7 +188,7 @@ func TestRevertMessages(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds)
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -188,7 +196,7 @@ func TestRevertMessages(t *testing.T) {
|
||||
a := mock.MkBlock(nil, 1, 1)
|
||||
b := mock.MkBlock(mock.TipSet(a), 1, 1)
|
||||
|
||||
sender, err := w.GenerateKey(types.KTBLS)
|
||||
sender, err := w.GenerateKey(crypto.SigTypeBLS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
@ -68,7 +69,7 @@ type message struct {
|
||||
// TipSet
|
||||
Cids []cid.Cid
|
||||
Blocks []*types.BlockHeader
|
||||
Height uint64
|
||||
Height abi.ChainEpoch
|
||||
Weight types.BigInt
|
||||
Time uint64
|
||||
Nonce uint64
|
||||
|
@ -4,46 +4,137 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
var log = logging.Logger("statetree")
|
||||
|
||||
// Stores actors state by their ID.
|
||||
type StateTree struct {
|
||||
root *hamt.Node
|
||||
Store *hamt.CborIpldStore
|
||||
Store cbor.IpldStore
|
||||
|
||||
actorcache map[address.Address]*types.Actor
|
||||
snapshot cid.Cid
|
||||
snaps *stateSnaps
|
||||
}
|
||||
|
||||
func NewStateTree(cst *hamt.CborIpldStore) (*StateTree, error) {
|
||||
type stateSnaps struct {
|
||||
layers []*stateSnapLayer
|
||||
}
|
||||
|
||||
type stateSnapLayer struct {
|
||||
actors map[address.Address]streeOp
|
||||
resolveCache map[address.Address]address.Address
|
||||
}
|
||||
|
||||
func newStateSnapLayer() *stateSnapLayer {
|
||||
return &stateSnapLayer{
|
||||
actors: make(map[address.Address]streeOp),
|
||||
resolveCache: make(map[address.Address]address.Address),
|
||||
}
|
||||
}
|
||||
|
||||
type streeOp struct {
|
||||
Act types.Actor
|
||||
Delete bool
|
||||
}
|
||||
|
||||
func newStateSnaps() *stateSnaps {
|
||||
ss := &stateSnaps{}
|
||||
ss.addLayer()
|
||||
return ss
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) addLayer() {
|
||||
ss.layers = append(ss.layers, newStateSnapLayer())
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) dropLayer() {
|
||||
ss.layers[len(ss.layers)-1] = nil // allow it to be GCed
|
||||
ss.layers = ss.layers[:len(ss.layers)-1]
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) mergeLastLayer() {
|
||||
last := ss.layers[len(ss.layers)-1]
|
||||
nextLast := ss.layers[len(ss.layers)-2]
|
||||
|
||||
for k, v := range last.actors {
|
||||
nextLast.actors[k] = v
|
||||
}
|
||||
|
||||
for k, v := range last.resolveCache {
|
||||
nextLast.resolveCache[k] = v
|
||||
}
|
||||
|
||||
ss.dropLayer()
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) resolveAddress(addr address.Address) (address.Address, bool) {
|
||||
for i := len(ss.layers) - 1; i >= 0; i-- {
|
||||
resa, ok := ss.layers[i].resolveCache[addr]
|
||||
if ok {
|
||||
return resa, true
|
||||
}
|
||||
}
|
||||
return address.Undef, false
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) cacheResolveAddress(addr, resa address.Address) {
|
||||
ss.layers[len(ss.layers)-1].resolveCache[addr] = resa
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) getActor(addr address.Address) (*types.Actor, error) {
|
||||
for i := len(ss.layers) - 1; i >= 0; i-- {
|
||||
act, ok := ss.layers[i].actors[addr]
|
||||
if ok {
|
||||
if act.Delete {
|
||||
return nil, types.ErrActorNotFound
|
||||
}
|
||||
|
||||
return &act.Act, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) setActor(addr address.Address, act *types.Actor) {
|
||||
ss.layers[len(ss.layers)-1].actors[addr] = streeOp{Act: *act}
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) deleteActor(addr address.Address) {
|
||||
ss.layers[len(ss.layers)-1].actors[addr] = streeOp{Delete: true}
|
||||
}
|
||||
|
||||
func NewStateTree(cst cbor.IpldStore) (*StateTree, error) {
|
||||
return &StateTree{
|
||||
root: hamt.NewNode(cst),
|
||||
Store: cst,
|
||||
actorcache: make(map[address.Address]*types.Actor),
|
||||
root: hamt.NewNode(cst, hamt.UseTreeBitWidth(5)),
|
||||
Store: cst,
|
||||
snaps: newStateSnaps(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func LoadStateTree(cst *hamt.CborIpldStore, c cid.Cid) (*StateTree, error) {
|
||||
nd, err := hamt.LoadNode(context.Background(), cst, c)
|
||||
func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) {
|
||||
nd, err := hamt.LoadNode(context.Background(), cst, c, hamt.UseTreeBitWidth(5))
|
||||
if err != nil {
|
||||
log.Errorf("loading hamt node %s failed: %s", c, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StateTree{
|
||||
root: nd,
|
||||
Store: cst,
|
||||
actorcache: make(map[address.Address]*types.Actor),
|
||||
root: nd,
|
||||
Store: cst,
|
||||
snaps: newStateSnaps(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -54,53 +145,64 @@ func (st *StateTree) SetActor(addr address.Address, act *types.Actor) error {
|
||||
}
|
||||
addr = iaddr
|
||||
|
||||
cact, ok := st.actorcache[addr]
|
||||
if ok {
|
||||
if act == cact {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
st.actorcache[addr] = act
|
||||
|
||||
return st.root.Set(context.TODO(), string(addr.Bytes()), act)
|
||||
st.snaps.setActor(addr, act)
|
||||
return nil
|
||||
}
|
||||
|
||||
// `LookupID` gets the ID address of this actor's `addr` stored in the `InitActor`.
|
||||
func (st *StateTree) LookupID(addr address.Address) (address.Address, error) {
|
||||
if addr.Protocol() == address.ID {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
act, err := st.GetActor(actors.InitAddress)
|
||||
resa, ok := st.snaps.resolveAddress(addr)
|
||||
if ok {
|
||||
return resa, nil
|
||||
}
|
||||
|
||||
act, err := st.GetActor(builtin.InitActorAddr)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("getting init actor: %w", err)
|
||||
}
|
||||
|
||||
var ias actors.InitActorState
|
||||
var ias init_.State
|
||||
if err := st.Store.Get(context.TODO(), act.Head, &ias); err != nil {
|
||||
return address.Undef, xerrors.Errorf("loading init actor state: %w", err)
|
||||
}
|
||||
|
||||
return ias.Lookup(st.Store, addr)
|
||||
a, err := ias.ResolveAddress(&AdtStore{st.Store}, addr)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("resolve address %s: %w", addr, err)
|
||||
}
|
||||
|
||||
st.snaps.cacheResolveAddress(addr, a)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// GetActor returns the actor from any type of `addr` provided.
|
||||
func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
|
||||
if addr == address.Undef {
|
||||
return nil, fmt.Errorf("GetActor called on undefined address")
|
||||
}
|
||||
|
||||
// Transform `addr` to its ID format.
|
||||
iaddr, err := st.LookupID(addr)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, hamt.ErrNotFound) {
|
||||
return nil, xerrors.Errorf("resolution lookup failed (%s): %w", addr, types.ErrActorNotFound)
|
||||
if xerrors.Is(err, init_.ErrAddressNotFound) {
|
||||
return nil, xerrors.Errorf("resolution lookup failed (%s): %w", addr, err)
|
||||
}
|
||||
return nil, xerrors.Errorf("address resolution: %w", err)
|
||||
}
|
||||
addr = iaddr
|
||||
|
||||
cact, ok := st.actorcache[addr]
|
||||
if ok {
|
||||
return cact, nil
|
||||
snapAct, err := st.snaps.getActor(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if snapAct != nil {
|
||||
return snapAct, nil
|
||||
}
|
||||
|
||||
var act types.Actor
|
||||
@ -112,21 +214,54 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
|
||||
return nil, xerrors.Errorf("hamt find failed: %w", err)
|
||||
}
|
||||
|
||||
st.actorcache[addr] = &act
|
||||
st.snaps.setActor(addr, &act)
|
||||
|
||||
return &act, nil
|
||||
}
|
||||
|
||||
func (st *StateTree) DeleteActor(addr address.Address) error {
|
||||
if addr == address.Undef {
|
||||
return xerrors.Errorf("DeleteActor called on undefined address")
|
||||
}
|
||||
|
||||
iaddr, err := st.LookupID(addr)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, init_.ErrAddressNotFound) {
|
||||
return xerrors.Errorf("resolution lookup failed (%s): %w", addr, err)
|
||||
}
|
||||
return xerrors.Errorf("address resolution: %w", err)
|
||||
}
|
||||
|
||||
addr = iaddr
|
||||
|
||||
_, err = st.GetActor(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st.snaps.deleteActor(addr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateTree.Flush")
|
||||
defer span.End()
|
||||
if len(st.snaps.layers) != 1 {
|
||||
return cid.Undef, xerrors.Errorf("tried to flush state tree with snapshots on the stack")
|
||||
}
|
||||
|
||||
for addr, act := range st.actorcache {
|
||||
if err := st.root.Set(ctx, string(addr.Bytes()), act); err != nil {
|
||||
return cid.Undef, err
|
||||
for addr, sto := range st.snaps.layers[0].actors {
|
||||
if sto.Delete {
|
||||
if err := st.root.Delete(ctx, string(addr.Bytes())); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
} else {
|
||||
if err := st.root.Set(ctx, string(addr.Bytes()), &sto.Act); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
}
|
||||
st.actorcache = make(map[address.Address]*types.Actor)
|
||||
|
||||
if err := st.root.Flush(ctx); err != nil {
|
||||
return cid.Undef, err
|
||||
@ -139,24 +274,24 @@ func (st *StateTree) Snapshot(ctx context.Context) error {
|
||||
ctx, span := trace.StartSpan(ctx, "stateTree.SnapShot")
|
||||
defer span.End()
|
||||
|
||||
ss, err := st.Flush(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st.snaps.addLayer()
|
||||
|
||||
st.snapshot = ss
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *StateTree) RegisterNewAddress(addr address.Address, act *types.Actor) (address.Address, error) {
|
||||
func (st *StateTree) ClearSnapshot() {
|
||||
st.snaps.mergeLastLayer()
|
||||
}
|
||||
|
||||
func (st *StateTree) RegisterNewAddress(addr address.Address) (address.Address, error) {
|
||||
var out address.Address
|
||||
err := st.MutateActor(actors.InitAddress, func(initact *types.Actor) error {
|
||||
var ias actors.InitActorState
|
||||
err := st.MutateActor(builtin.InitActorAddr, func(initact *types.Actor) error {
|
||||
var ias init_.State
|
||||
if err := st.Store.Get(context.TODO(), initact.Head, &ias); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oaddr, err := ias.AddActor(st.Store, addr)
|
||||
oaddr, err := ias.MapAddressToNewID(&AdtStore{st.Store}, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,20 +309,21 @@ func (st *StateTree) RegisterNewAddress(addr address.Address, act *types.Actor)
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if err := st.SetActor(out, act); err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (st *StateTree) Revert() error {
|
||||
nd, err := hamt.LoadNode(context.Background(), st.Store, st.snapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
type AdtStore struct{ cbor.IpldStore }
|
||||
|
||||
func (a *AdtStore) Context() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
var _ adt.Store = (*AdtStore)(nil)
|
||||
|
||||
func (st *StateTree) Revert() error {
|
||||
st.snaps.dropLayer()
|
||||
st.snaps.addLayer()
|
||||
|
||||
st.root = nd
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,19 @@ package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func BenchmarkStateTreeSet(b *testing.B) {
|
||||
cst := hamt.NewCborStore()
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@ -27,8 +30,8 @@ func BenchmarkStateTreeSet(b *testing.B) {
|
||||
}
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Code: builtin.StorageMinerActorCodeID,
|
||||
Head: builtin.AccountActorCodeID,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -38,7 +41,7 @@ func BenchmarkStateTreeSet(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
cst := hamt.NewCborStore()
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@ -54,8 +57,8 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
}
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Code: builtin.StorageMinerActorCodeID,
|
||||
Head: builtin.AccountActorCodeID,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -68,7 +71,7 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
cst := hamt.NewCborStore()
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@ -80,8 +83,8 @@ func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
}
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523 + uint64(i)),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Code: builtin.StorageMinerActorCodeID,
|
||||
Head: builtin.AccountActorCodeID,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -110,7 +113,7 @@ func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
}
|
||||
|
||||
func TestSetCache(t *testing.T) {
|
||||
cst := hamt.NewCborStore()
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -123,8 +126,8 @@ func TestSetCache(t *testing.T) {
|
||||
|
||||
act := &types.Actor{
|
||||
Balance: types.NewInt(0),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Code: builtin.StorageMinerActorCodeID,
|
||||
Head: builtin.AccountActorCodeID,
|
||||
Nonce: 0,
|
||||
}
|
||||
|
||||
@ -140,7 +143,133 @@ func TestSetCache(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if outact.Nonce != act.Nonce {
|
||||
t.Error("nonce didn't match")
|
||||
if outact.Nonce == 1 {
|
||||
t.Error("nonce should not have updated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var addrs []address.Address
|
||||
//for _, a := range []string{"t15ocrptbu4i5qucjvvwecihd7fqqgzb27pz5l5zy", "t1dpyvgavvl3f4ujlk6odedss54z6rt5gyuknsuva", "t1feiejbkcvozy7iltt2pxzuoq4d2kpbsusugan7a", "t3rgjfqybjx7bahuhfv7nwfg3tlm4i4zyvldfirjvzm5z5xwjoqbj3rfi2mpmlxpqwxxxafgpkjilqzpg7cefa"} {
|
||||
for _, a := range []string{"t0100", "t0101", "t0102", "t0103"} {
|
||||
addr, err := address.NewFromString(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := st.SetActor(addrs[0], &types.Actor{Code: builtin.AccountActorCodeID, Head: builtin.AccountActorCodeID, Balance: types.NewInt(55)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{ // sub call that will fail
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := st.SetActor(addrs[1], &types.Actor{Code: builtin.AccountActorCodeID, Head: builtin.AccountActorCodeID, Balance: types.NewInt(77)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := st.Revert(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st.ClearSnapshot()
|
||||
}
|
||||
|
||||
// more operations in top level call...
|
||||
if err := st.SetActor(addrs[2], &types.Actor{Code: builtin.AccountActorCodeID, Head: builtin.AccountActorCodeID, Balance: types.NewInt(123)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{ // sub call that succeeds
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := st.SetActor(addrs[3], &types.Actor{Code: builtin.AccountActorCodeID, Head: builtin.AccountActorCodeID, Balance: types.NewInt(5)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
st.ClearSnapshot()
|
||||
}
|
||||
|
||||
st.ClearSnapshot()
|
||||
|
||||
if _, err := st.Flush(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertHas(t, st, addrs[0])
|
||||
assertNotHas(t, st, addrs[1])
|
||||
assertHas(t, st, addrs[2])
|
||||
assertHas(t, st, addrs[3])
|
||||
}
|
||||
|
||||
func assertHas(t *testing.T, st *StateTree, addr address.Address) {
|
||||
_, err := st.GetActor(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNotHas(t *testing.T, st *StateTree, addr address.Address) {
|
||||
_, err := st.GetActor(addr)
|
||||
if err == nil {
|
||||
t.Fatal("shouldnt have found actor", addr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateTreeConsistency(t *testing.T) {
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var addrs []address.Address
|
||||
for i := 100; i < 150; i++ {
|
||||
a, err := address.NewIDAddress(uint64(i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addrs = append(addrs, a)
|
||||
}
|
||||
|
||||
randomCid, err := cid.Decode("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, a := range addrs {
|
||||
st.SetActor(a, &types.Actor{
|
||||
Code: randomCid,
|
||||
Head: randomCid,
|
||||
Balance: types.NewInt(uint64(10000 + i)),
|
||||
Nonce: uint64(1000 - i),
|
||||
})
|
||||
}
|
||||
|
||||
root, err := st.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("root is: ", root)
|
||||
if root.String() != "bafy2bzaceadyjnrv3sbjvowfl3jr4pdn5p2bf3exjjie2f3shg4oy5sub7h34" {
|
||||
t.Fatal("MISMATCH!")
|
||||
}
|
||||
}
|
||||
|
@ -4,28 +4,28 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
)
|
||||
|
||||
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*api.InvocResult, error) {
|
||||
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight abi.ChainEpoch) (*api.InvocResult, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw")
|
||||
defer span.End()
|
||||
|
||||
vmi, err := vm.NewVM(bstate, bheight, r, actors.NetworkAddress, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
vmi, err := vm.NewVM(bstate, bheight, r, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up vm: %w", err)
|
||||
}
|
||||
|
||||
if msg.GasLimit == types.EmptyInt {
|
||||
msg.GasLimit = types.NewInt(10000000000)
|
||||
if msg.GasLimit == 0 {
|
||||
msg.GasLimit = 10000000000
|
||||
}
|
||||
if msg.GasPrice == types.EmptyInt {
|
||||
msg.GasPrice = types.NewInt(0)
|
||||
@ -36,7 +36,7 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("gas_limit", int64(msg.GasLimit.Uint64())),
|
||||
trace.Int64Attribute("gas_limit", msg.GasLimit),
|
||||
trace.Int64Attribute("gas_price", int64(msg.GasPrice.Uint64())),
|
||||
trace.StringAttribute("value", msg.Value.String()),
|
||||
)
|
||||
@ -50,7 +50,7 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
|
||||
msg.Nonce = fromActor.Nonce
|
||||
|
||||
// TODO: maybe just use the invoker directly?
|
||||
ret, err := vmi.ApplyMessage(ctx, msg)
|
||||
ret, err := vmi.ApplyImplicitMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("apply message failed: %w", err)
|
||||
}
|
||||
@ -62,10 +62,11 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
|
||||
}
|
||||
|
||||
return &api.InvocResult{
|
||||
Msg: msg,
|
||||
MsgRct: &ret.MessageReceipt,
|
||||
Msg: msg,
|
||||
MsgRct: &ret.MessageReceipt,
|
||||
InternalExecutions: ret.InternalExecutions,
|
||||
Error: errs,
|
||||
Error: errs,
|
||||
Duration: ret.Duration,
|
||||
}, nil
|
||||
|
||||
}
|
||||
@ -100,5 +101,9 @@ func (sm *StateManager) Replay(ctx context.Context, ts *types.TipSet, mcid cid.C
|
||||
return nil, nil, xerrors.Errorf("unexpected error during execution: %w", err)
|
||||
}
|
||||
|
||||
if outr == nil {
|
||||
return nil, nil, xerrors.Errorf("given message not found in tipset")
|
||||
}
|
||||
|
||||
return outm, outr, nil
|
||||
}
|
||||
|
@ -3,54 +3,13 @@ package stmgr
|
||||
import (
|
||||
"context"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var ForksAtHeight = map[uint64]func(context.Context, *StateManager, cid.Cid) (cid.Cid, error){
|
||||
build.ForkBlizzardHeight: func(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
log.Warnw("Executing blizzard fork logic")
|
||||
nstate, err := fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("blizzard bug fix failed: %w", err)
|
||||
}
|
||||
return nstate, nil
|
||||
},
|
||||
build.ForkFrigidHeight: func(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
log.Warnw("Executing frigid fork logic")
|
||||
nstate, err := fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("frigid bug fix failed: %w", err)
|
||||
}
|
||||
return nstate, nil
|
||||
},
|
||||
build.ForkBootyBayHeight: func(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
log.Warnw("Executing booty bay fork logic")
|
||||
nstate, err := fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("booty bay bug fix failed: %w", err)
|
||||
}
|
||||
return nstate, nil
|
||||
},
|
||||
build.ForkMissingSnowballs: func(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
log.Warnw("Adding more snow to the world")
|
||||
nstate, err := fixTooFewSnowballs(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("missing snowballs bug fix failed: %w", err)
|
||||
}
|
||||
return nstate, nil
|
||||
},
|
||||
}
|
||||
var ForksAtHeight = map[abi.ChainEpoch]func(context.Context, *StateManager, cid.Cid) (cid.Cid, error){}
|
||||
|
||||
func (sm *StateManager) handleStateForks(ctx context.Context, pstate cid.Cid, height, parentH uint64) (_ cid.Cid, err error) {
|
||||
func (sm *StateManager) handleStateForks(ctx context.Context, pstate cid.Cid, height, parentH abi.ChainEpoch) (_ cid.Cid, err error) {
|
||||
for i := parentH; i < height; i++ {
|
||||
f, ok := ForksAtHeight[i]
|
||||
if ok {
|
||||
@ -64,163 +23,3 @@ func (sm *StateManager) handleStateForks(ctx context.Context, pstate cid.Cid, he
|
||||
|
||||
return pstate, nil
|
||||
}
|
||||
|
||||
func fixTooFewSnowballs(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
st, err := state.LoadStateTree(cst, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
spa, err := st.GetActor(actors.StoragePowerAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to get storage power actor: %w", err)
|
||||
}
|
||||
|
||||
var spast actors.StoragePowerState
|
||||
if err := cst.Get(ctx, spa.Head, &spast); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
miners, err := actors.MinerSetList(ctx, cst, spast.Miners)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
sum := types.NewInt(0)
|
||||
for _, m := range miners {
|
||||
mact, err := st.GetActor(m)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("getting miner actor to fix: %w", err)
|
||||
}
|
||||
|
||||
var mstate actors.StorageMinerActorState
|
||||
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
if mstate.SlashedAt != 0 {
|
||||
continue
|
||||
}
|
||||
sum = types.BigAdd(sum, mstate.Power)
|
||||
}
|
||||
|
||||
spast.TotalStorage = sum
|
||||
nspahead, err := cst.Put(ctx, &spast)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
spa.Head = nspahead
|
||||
|
||||
return st.Flush(ctx)
|
||||
}
|
||||
|
||||
/*
|
||||
1) Iterate through each miner in the chain:
|
||||
1.1) Fixup their sector set and proving set
|
||||
1.2) Change their code cid to point to the new miner actor code
|
||||
*/
|
||||
func fixBlizzardAMTBug(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
st, err := state.LoadStateTree(cst, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
spa, err := st.GetActor(actors.StoragePowerAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to get storage power actor: %w", err)
|
||||
}
|
||||
|
||||
var spast actors.StoragePowerState
|
||||
if err := cst.Get(ctx, spa.Head, &spast); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
miners, err := actors.MinerSetList(ctx, cst, spast.Miners)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
for _, m := range miners {
|
||||
mact, err := st.GetActor(m)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("getting miner actor to fix: %w", err)
|
||||
}
|
||||
|
||||
nhead, err := fixMiner(ctx, cst, sm.cs.Blockstore(), mact.Head)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("fixing miner: %w", err)
|
||||
}
|
||||
|
||||
if nhead != mact.Head {
|
||||
log.Warnf("Miner %s had changes", m)
|
||||
}
|
||||
|
||||
mact.Head = nhead
|
||||
mact.Code = actors.StorageMiner2CodeCid
|
||||
|
||||
if err := st.SetActor(m, mact); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
|
||||
return st.Flush(ctx)
|
||||
}
|
||||
|
||||
func fixMiner(ctx context.Context, cst *hamt.CborIpldStore, bs blockstore.Blockstore, mscid cid.Cid) (cid.Cid, error) {
|
||||
var mstate actors.StorageMinerActorState
|
||||
if err := cst.Get(ctx, mscid, &mstate); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
amts := amt.WrapBlockstore(bs)
|
||||
|
||||
nsectors, err := amtFsck(amts, mstate.Sectors)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("error fsck'ing sector set: %w", err)
|
||||
}
|
||||
mstate.Sectors = nsectors
|
||||
|
||||
nproving, err := amtFsck(amts, mstate.ProvingSet)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("error fsck'ing proving set: %w", err)
|
||||
}
|
||||
mstate.ProvingSet = nproving
|
||||
|
||||
nmcid, err := cst.Put(ctx, &mstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to put modified miner state: %w", err)
|
||||
}
|
||||
|
||||
return nmcid, nil
|
||||
}
|
||||
|
||||
func amtFsck(s amt.Blocks, ss cid.Cid) (cid.Cid, error) {
|
||||
a, err := amt.LoadAMT(s, ss)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("could not load AMT: %w", a)
|
||||
}
|
||||
|
||||
b := amt.NewAMT(s)
|
||||
|
||||
err = a.ForEach(func(id uint64, data *cbg.Deferred) error {
|
||||
err := b.Set(id, data)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not copy at idx (%d): %w", id, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("could not copy: %w", err)
|
||||
}
|
||||
|
||||
nss, err := b.Flush()
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("could not flush: %w", err)
|
||||
}
|
||||
|
||||
return nss, nil
|
||||
}
|
||||
|
@ -7,7 +7,16 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
@ -20,16 +29,17 @@ import (
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
func init() {
|
||||
build.SectorSizes = []uint64{1024}
|
||||
build.MinimumMinerPower = 1024
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
}
|
||||
|
||||
const testForkHeight = 40
|
||||
@ -51,7 +61,7 @@ func (tas *testActorState) UnmarshalCBOR(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
if t != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type in test actor state")
|
||||
return fmt.Errorf("wrong type in test actor state (got %d)", t)
|
||||
}
|
||||
tas.HasUpgraded = v
|
||||
return nil
|
||||
@ -64,34 +74,30 @@ func (ta *testActor) Exports() []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func (ta *testActor) Constructor(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
c, err := vmctx.Storage().Put(&testActorState{11})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (ta *testActor) Constructor(rt runtime.Runtime, params *adt.EmptyValue) *adt.EmptyValue {
|
||||
rt.ValidateImmediateCallerAcceptAny()
|
||||
rt.State().Create(&testActorState{11})
|
||||
fmt.Println("NEW ACTOR ADDRESS IS: ", rt.Message().Receiver())
|
||||
|
||||
fmt.Println("NEW ACTOR ADDRESS IS: ", vmctx.Message().To.String())
|
||||
|
||||
return nil, vmctx.Storage().Commit(actors.EmptyCBOR, c)
|
||||
return adt.Empty
|
||||
}
|
||||
|
||||
func (ta *testActor) TestMethod(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
func (ta *testActor) TestMethod(rt runtime.Runtime, params *adt.EmptyValue) *adt.EmptyValue {
|
||||
rt.ValidateImmediateCallerAcceptAny()
|
||||
var st testActorState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rt.State().Readonly(&st)
|
||||
|
||||
if vmctx.BlockHeight() > testForkHeight {
|
||||
if rt.CurrEpoch() > testForkHeight {
|
||||
if st.HasUpgraded != 55 {
|
||||
return nil, aerrors.Fatal("fork updating applied in wrong order")
|
||||
panic(aerrors.Fatal("fork updating applied in wrong order"))
|
||||
}
|
||||
} else {
|
||||
if st.HasUpgraded != 11 {
|
||||
return nil, aerrors.Fatal("fork updating happened too early")
|
||||
panic(aerrors.Fatal("fork updating happened too early"))
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return adt.Empty
|
||||
}
|
||||
|
||||
func TestForkHeightTriggers(t *testing.T) {
|
||||
@ -108,22 +114,14 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
|
||||
inv := vm.NewInvoker()
|
||||
|
||||
pref := cid.NewPrefixV1(cid.Raw, mh.IDENTITY)
|
||||
actcid, err := pref.Sum([]byte("testactor"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actors.BuiltInActors[actcid] = true
|
||||
|
||||
// predicting the address here... may break if other assumptions change
|
||||
taddr, err := address.NewIDAddress(1000)
|
||||
taddr, err := address.NewIDAddress(1002)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stmgr.ForksAtHeight[testForkHeight] = func(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
|
||||
cst := hamt.CSTFromBstore(sm.ChainStore().Blockstore())
|
||||
cst := cbor.NewCborStore(sm.ChainStore().Blockstore())
|
||||
st, err := state.LoadStateTree(cst, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
@ -136,7 +134,7 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
|
||||
var tas testActorState
|
||||
if err := cst.Get(ctx, act.Head, &tas); err != nil {
|
||||
return cid.Undef, err
|
||||
return cid.Undef, xerrors.Errorf("in fork handler, failed to run get: %w", err)
|
||||
}
|
||||
|
||||
tas.HasUpgraded = 55
|
||||
@ -155,9 +153,9 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
return st.Flush(ctx)
|
||||
}
|
||||
|
||||
inv.Register(actcid, &testActor{}, &testActorState{})
|
||||
sm.SetVMConstructor(func(c cid.Cid, h uint64, r vm.Rand, a address.Address, b blockstore.Blockstore, s *types.VMSyscalls) (*vm.VM, error) {
|
||||
nvm, err := vm.NewVM(c, h, r, a, b, s)
|
||||
inv.Register(builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{})
|
||||
sm.SetVMConstructor(func(c cid.Cid, h abi.ChainEpoch, r vm.Rand, b blockstore.Blockstore, s runtime.Syscalls) (*vm.VM, error) {
|
||||
nvm, err := vm.NewVM(c, h, r, b, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -169,17 +167,17 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
|
||||
var msgs []*types.SignedMessage
|
||||
|
||||
enc, err := actors.SerializeParams(&actors.ExecParams{Code: actcid})
|
||||
enc, err := actors.SerializeParams(&init_.ExecParams{CodeCID: builtin.PaymentChannelActorCodeID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
m := &types.Message{
|
||||
From: cg.Banker(),
|
||||
To: actors.InitAddress,
|
||||
Method: actors.IAMethods.Exec,
|
||||
To: builtin.InitActorAddr,
|
||||
Method: builtin.MethodsInit.Exec,
|
||||
Params: enc,
|
||||
GasLimit: types.NewInt(10000),
|
||||
GasLimit: 10000,
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
sig, err := cg.Wallet().Sign(ctx, cg.Banker(), m.Cid().Bytes())
|
||||
@ -206,7 +204,7 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
Method: 2,
|
||||
Params: nil,
|
||||
Nonce: nonce,
|
||||
GasLimit: types.NewInt(10000),
|
||||
GasLimit: 10000,
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
nonce++
|
||||
@ -217,7 +215,7 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
}
|
||||
|
||||
return []*types.SignedMessage{
|
||||
&types.SignedMessage{
|
||||
{
|
||||
Signature: *sig,
|
||||
Message: *m,
|
||||
},
|
||||
|
@ -6,12 +6,20 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@ -19,6 +27,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@ -31,7 +40,7 @@ type StateManager struct {
|
||||
stCache map[string][]cid.Cid
|
||||
compWait map[string]chan struct{}
|
||||
stlk sync.Mutex
|
||||
newVM func(cid.Cid, uint64, vm.Rand, address.Address, blockstore.Blockstore, *types.VMSyscalls) (*vm.VM, error)
|
||||
newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, runtime.Syscalls) (*vm.VM, error)
|
||||
}
|
||||
|
||||
func NewStateManager(cs *store.ChainStore) *StateManager {
|
||||
@ -108,7 +117,157 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c
|
||||
return st, rec, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb func(cid.Cid, *types.Message, *vm.ApplyRet) error) (cid.Cid, cid.Cid, error) {
|
||||
func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
|
||||
var trace []*api.InvocResult
|
||||
st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
|
||||
ir := &api.InvocResult{
|
||||
Msg: msg,
|
||||
MsgRct: &ret.MessageReceipt,
|
||||
InternalExecutions: ret.InternalExecutions,
|
||||
Duration: ret.Duration,
|
||||
}
|
||||
if ret.ActorErr != nil {
|
||||
ir.Error = ret.ActorErr.Error()
|
||||
}
|
||||
trace = append(trace, ir)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
return st, trace, nil
|
||||
}
|
||||
|
||||
type BlockMessages struct {
|
||||
Miner address.Address
|
||||
BlsMessages []types.ChainMsg
|
||||
SecpkMessages []types.ChainMsg
|
||||
TicketCount int64
|
||||
}
|
||||
|
||||
type ExecCallback func(cid.Cid, *types.Message, *vm.ApplyRet) error
|
||||
|
||||
func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback) (cid.Cid, cid.Cid, error) {
|
||||
vmi, err := sm.newVM(pstate, epoch, r, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
|
||||
}
|
||||
|
||||
var receipts []cbg.CBORMarshaler
|
||||
processedMsgs := map[cid.Cid]bool{}
|
||||
for _, b := range bms {
|
||||
penalty := types.NewInt(0)
|
||||
gasReward := big.Zero()
|
||||
|
||||
for _, cm := range append(b.BlsMessages, b.SecpkMessages...) {
|
||||
m := cm.VMMessage()
|
||||
if _, found := processedMsgs[m.Cid()]; found {
|
||||
continue
|
||||
}
|
||||
r, err := vmi.ApplyMessage(ctx, cm)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
receipts = append(receipts, &r.MessageReceipt)
|
||||
gasReward = big.Add(gasReward, big.Mul(m.GasPrice, big.NewInt(r.GasUsed)))
|
||||
penalty = big.Add(penalty, r.Penalty)
|
||||
|
||||
if cb != nil {
|
||||
if err := cb(cm.Cid(), m, r); err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
}
|
||||
processedMsgs[m.Cid()] = true
|
||||
}
|
||||
|
||||
var err error
|
||||
params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{
|
||||
Miner: b.Miner,
|
||||
Penalty: penalty,
|
||||
GasReward: gasReward,
|
||||
TicketCount: 1, // TODO: no longer need ticket count here.
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err)
|
||||
}
|
||||
|
||||
sysAct, err := vmi.StateTree().GetActor(builtin.SystemActorAddr)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get system actor: %w", err)
|
||||
}
|
||||
|
||||
rwMsg := &types.Message{
|
||||
From: builtin.SystemActorAddr,
|
||||
To: builtin.RewardActorAddr,
|
||||
Nonce: sysAct.Nonce,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: 1 << 30,
|
||||
Method: builtin.MethodsReward.AwardBlockReward,
|
||||
Params: params,
|
||||
}
|
||||
ret, err := vmi.ApplyImplicitMessage(ctx, rwMsg)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, err)
|
||||
}
|
||||
if cb != nil {
|
||||
if err := cb(rwMsg.Cid(), rwMsg, ret); err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: this nonce-getting is a tiny bit ugly
|
||||
ca, err := vmi.StateTree().GetActor(builtin.SystemActorAddr)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
cronMsg := &types.Message{
|
||||
To: builtin.CronActorAddr,
|
||||
From: builtin.SystemActorAddr,
|
||||
Nonce: ca.Nonce,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: 1 << 30, // Make super sure this is never too little
|
||||
Method: builtin.MethodsCron.EpochTick,
|
||||
Params: nil,
|
||||
}
|
||||
ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
if cb != nil {
|
||||
if err := cb(cronMsg.Cid(), cronMsg, ret); err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on cron message: %w", err)
|
||||
}
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode)
|
||||
}
|
||||
|
||||
bs := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
rectroot, err := amt.FromArray(ctx, bs, receipts)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
|
||||
}
|
||||
|
||||
st, err := vmi.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err)
|
||||
}
|
||||
|
||||
return st, rectroot, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb ExecCallback) (cid.Cid, cid.Cid, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "computeTipSetState")
|
||||
defer span.End()
|
||||
|
||||
@ -142,167 +301,55 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl
|
||||
|
||||
r := store.NewChainRand(sm.cs, cids, blks[0].Height)
|
||||
|
||||
vmi, err := sm.newVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
|
||||
}
|
||||
|
||||
netact, err := vmi.StateTree().GetActor(actors.NetworkAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err)
|
||||
}
|
||||
reward := vm.MiningReward(netact.Balance)
|
||||
for tsi, b := range blks {
|
||||
netact, err = vmi.StateTree().GetActor(actors.NetworkAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err)
|
||||
}
|
||||
vmi.SetBlockMiner(b.Miner)
|
||||
|
||||
owner, err := GetMinerOwner(ctx, sm, pstate, b.Miner)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get owner for miner %s: %w", b.Miner, err)
|
||||
}
|
||||
|
||||
act, err := vmi.StateTree().GetActor(owner)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get miner owner actor")
|
||||
}
|
||||
|
||||
if err := vm.Transfer(netact, act, reward); err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to deduct funds from network actor: %w", err)
|
||||
}
|
||||
|
||||
// all block miners created a valid post, go update the actor state
|
||||
postSubmitMsg := &types.Message{
|
||||
From: actors.NetworkAddress,
|
||||
Nonce: netact.Nonce,
|
||||
To: b.Miner,
|
||||
Method: actors.MAMethods.SubmitElectionPoSt,
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(10000000000),
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
ret, err := vmi.ApplyMessage(ctx, postSubmitMsg)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("submit election post message for block %s (miner %s) invocation failed: %w", b.Cid(), b.Miner, err)
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("submit election post invocation returned nonzero exit code: %d (err = %s, block = %s, miner = %s, tsi = %d)", ret.ExitCode, ret.ActorErr, b.Cid(), b.Miner, tsi)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can't use method from chainstore because it doesnt let us know who the block miners were
|
||||
applied := make(map[address.Address]uint64)
|
||||
balances := make(map[address.Address]types.BigInt)
|
||||
|
||||
preloadAddr := func(a address.Address) error {
|
||||
if _, ok := applied[a]; !ok {
|
||||
act, err := vmi.StateTree().GetActor(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
applied[a] = act.Nonce
|
||||
balances[a] = act.Balance
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var receipts []cbg.CBORMarshaler
|
||||
var blkmsgs []BlockMessages
|
||||
for _, b := range blks {
|
||||
vmi.SetBlockMiner(b.Miner)
|
||||
|
||||
bms, sms, err := sm.cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err)
|
||||
}
|
||||
|
||||
cmsgs := make([]store.ChainMsg, 0, len(bms)+len(sms))
|
||||
bm := BlockMessages{
|
||||
Miner: b.Miner,
|
||||
BlsMessages: make([]types.ChainMsg, 0, len(bms)),
|
||||
SecpkMessages: make([]types.ChainMsg, 0, len(sms)),
|
||||
TicketCount: 1, //int64(len(b.EPostProof.Proofs)), // TODO fix this
|
||||
}
|
||||
|
||||
for _, m := range bms {
|
||||
cmsgs = append(cmsgs, m)
|
||||
}
|
||||
for _, sm := range sms {
|
||||
cmsgs = append(cmsgs, sm)
|
||||
bm.BlsMessages = append(bm.BlsMessages, m)
|
||||
}
|
||||
|
||||
for _, cm := range cmsgs {
|
||||
m := cm.VMMessage()
|
||||
if err := preloadAddr(m.From); err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
if applied[m.From] != m.Nonce {
|
||||
continue
|
||||
}
|
||||
applied[m.From]++
|
||||
|
||||
if balances[m.From].LessThan(m.RequiredFunds()) {
|
||||
continue
|
||||
}
|
||||
balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds())
|
||||
|
||||
r, err := vmi.ApplyMessage(ctx, m)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
receipts = append(receipts, &r.MessageReceipt)
|
||||
|
||||
if cb != nil {
|
||||
if err := cb(cm.Cid(), m, r); err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
}
|
||||
for _, m := range sms {
|
||||
bm.SecpkMessages = append(bm.SecpkMessages, m)
|
||||
}
|
||||
|
||||
blkmsgs = append(blkmsgs, bm)
|
||||
}
|
||||
|
||||
// TODO: this nonce-getting is a tiny bit ugly
|
||||
ca, err := vmi.StateTree().GetActor(actors.CronAddress)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
ret, err := vmi.ApplyMessage(ctx, &types.Message{
|
||||
To: actors.CronAddress,
|
||||
From: actors.CronAddress,
|
||||
Nonce: ca.Nonce,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1 << 30), // Make super sure this is never too little
|
||||
Method: actors.CAMethods.EpochTick,
|
||||
Params: nil,
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode)
|
||||
}
|
||||
|
||||
bs := amt.WrapBlockstore(sm.cs.Blockstore())
|
||||
rectroot, err := amt.FromArray(bs, receipts)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
|
||||
}
|
||||
|
||||
st, err := vmi.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err)
|
||||
}
|
||||
|
||||
return st, rectroot, nil
|
||||
return sm.ApplyBlocks(ctx, pstate, blkmsgs, abi.ChainEpoch(blks[0].Height), r, cb)
|
||||
}
|
||||
|
||||
func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
|
||||
func (sm *StateManager) parentState(ts *types.TipSet) cid.Cid {
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
stcid := ts.ParentState()
|
||||
return ts.ParentState()
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, stcid)
|
||||
func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, sm.parentState(ts))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("load state tree: %w", err)
|
||||
}
|
||||
|
||||
return state.GetActor(addr)
|
||||
}
|
||||
|
||||
func (sm *StateManager) getActorRaw(addr address.Address, st cid.Cid) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("load state tree: %w", err)
|
||||
}
|
||||
@ -332,13 +379,34 @@ func (sm *StateManager) LoadActorState(ctx context.Context, a address.Address, o
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
if err := cst.Get(ctx, act.Head, out); err != nil {
|
||||
var r cbg.Deferred
|
||||
cst.Get(ctx, act.Head, &r)
|
||||
fmt.Printf("badhead %x\n", r.Raw)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) LoadActorStateRaw(ctx context.Context, a address.Address, out interface{}, st cid.Cid) (*types.Actor, error) {
|
||||
act, err := sm.getActorRaw(a, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
if err := cst.Get(ctx, act.Head, out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
||||
|
||||
// Similar to `vm.ResolveToKeyAddr` but does not allow `Actor` type of addresses. Uses the `TipSet` `ts`
|
||||
// to generate the VM state.
|
||||
func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
switch addr.Protocol() {
|
||||
case address.BLS, address.SECP256K1:
|
||||
@ -357,7 +425,7 @@ func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Ad
|
||||
return address.Undef, xerrors.Errorf("resolve address failed to get tipset state: %w", err)
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
tree, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("failed to load state tree")
|
||||
@ -380,6 +448,15 @@ func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Addres
|
||||
return pubk, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, sm.parentState(ts))
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("load state tree: %w", err)
|
||||
}
|
||||
return state.LookupID(addr)
|
||||
}
|
||||
|
||||
func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
m, err := sm.cs.GetCMessage(msg)
|
||||
if err != nil {
|
||||
@ -482,7 +559,38 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid) (*type
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m store.ChainMsg) (*types.TipSet, *types.MessageReceipt, error) {
|
||||
func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*types.TipSet, *types.MessageReceipt, error) {
|
||||
msg, err := sm.cs.GetCMessage(mcid)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load message: %w", err)
|
||||
}
|
||||
|
||||
head := sm.cs.GetHeaviestTipSet()
|
||||
|
||||
r, err := sm.tipsetExecutedMessage(head, mcid, msg.VMMessage())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
return head, r, nil
|
||||
}
|
||||
|
||||
fts, r, err := sm.searchBackForMsg(ctx, head, msg)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("failed to look back through chain for message %s", mcid)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if fts == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
return fts, r, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, error) {
|
||||
|
||||
cur := from
|
||||
for {
|
||||
@ -575,8 +683,8 @@ func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
r, err := hamt.LoadNode(ctx, cst, st)
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
r, err := hamt.LoadNode(ctx, cst, st, hamt.UseTreeBitWidth(5))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -597,18 +705,55 @@ func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (actors.StorageParticipantBalance, error) {
|
||||
var state actors.StorageMarketState
|
||||
if _, err := sm.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
||||
return actors.StorageParticipantBalance{}, err
|
||||
}
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
b, _, err := actors.GetMarketBalances(ctx, cst, state.Balances, addr)
|
||||
func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) {
|
||||
var state market.State
|
||||
_, err := sm.LoadActorState(ctx, builtin.StorageMarketActorAddr, &state, ts)
|
||||
if err != nil {
|
||||
return actors.StorageParticipantBalance{}, err
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
|
||||
return b[0], nil
|
||||
addr, err = sm.LookupID(ctx, addr, ts)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
|
||||
var out api.MarketBalance
|
||||
|
||||
et, err := adt.AsBalanceTable(sm.cs.Store(ctx), state.EscrowTable)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
ehas, err := et.Has(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
if ehas {
|
||||
out.Escrow, err = et.Get(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err)
|
||||
}
|
||||
} else {
|
||||
out.Escrow = big.Zero()
|
||||
}
|
||||
|
||||
lt, err := adt.AsBalanceTable(sm.cs.Store(ctx), state.LockedTable)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
lhas, err := lt.Has(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
if lhas {
|
||||
out.Locked, err = lt.Get(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err)
|
||||
}
|
||||
} else {
|
||||
out.Locked = big.Zero()
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error {
|
||||
@ -640,6 +785,6 @@ func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, uint64, vm.Rand, address.Address, blockstore.Blockstore, *types.VMSyscalls) (*vm.VM, error)) {
|
||||
func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, runtime.Syscalls) (*vm.VM, error)) {
|
||||
sm.newVM = nvm
|
||||
}
|
||||
|
@ -1,303 +1,333 @@
|
||||
package stmgr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
amt2 "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"os"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
func GetNetworkName(ctx context.Context, sm *StateManager, st cid.Cid) (dtypes.NetworkName, error) {
|
||||
var state init_.State
|
||||
_, err := sm.LoadActorStateRaw(ctx, builtin.InitActorAddr, &state, st)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return dtypes.NetworkName(state.NetworkName), nil
|
||||
}
|
||||
|
||||
func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
|
||||
recp, err := sm.CallRaw(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
Method: actors.MAMethods.GetWorkerAddr,
|
||||
}, st, nil, 0)
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorStateRaw(ctx, maddr, &mas, st)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
|
||||
return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
if recp.MsgRct.ExitCode != 0 {
|
||||
return address.Undef, xerrors.Errorf("getting miner worker addr failed (exit code %d)", recp.MsgRct.ExitCode)
|
||||
}
|
||||
|
||||
worker, err := address.NewFromBytes(recp.MsgRct.Return)
|
||||
cst := cbor.NewCborStore(sm.cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
return address.Undef, xerrors.Errorf("load state tree: %w", err)
|
||||
}
|
||||
|
||||
if worker.Protocol() == address.ID {
|
||||
return address.Undef, xerrors.Errorf("need to resolve worker address to a pubkeyaddr")
|
||||
}
|
||||
|
||||
return worker, nil
|
||||
return vm.ResolveToKeyAddr(state, cst, mas.Info.Worker)
|
||||
}
|
||||
|
||||
func GetMinerOwner(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
|
||||
recp, err := sm.CallRaw(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
Method: actors.MAMethods.GetOwner,
|
||||
}, st, nil, 0)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
|
||||
}
|
||||
|
||||
if recp.MsgRct.ExitCode != 0 {
|
||||
return address.Undef, xerrors.Errorf("getting miner owner addr failed (exit code %d)", recp.MsgRct.ExitCode)
|
||||
}
|
||||
|
||||
owner, err := address.NewFromBytes(recp.MsgRct.Return)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if owner.Protocol() == address.ID {
|
||||
return address.Undef, xerrors.Errorf("need to resolve owner address to a pubkeyaddr")
|
||||
}
|
||||
|
||||
return owner, nil
|
||||
func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (power.Claim, power.Claim, error) {
|
||||
return GetPowerRaw(ctx, sm, ts.ParentState(), maddr)
|
||||
}
|
||||
|
||||
func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (types.BigInt, types.BigInt, error) {
|
||||
var err error
|
||||
|
||||
var mpow types.BigInt
|
||||
func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, error) {
|
||||
var ps power.State
|
||||
_, err := sm.LoadActorStateRaw(ctx, builtin.StoragePowerActorAddr, &ps, st)
|
||||
if err != nil {
|
||||
return power.Claim{}, power.Claim{}, xerrors.Errorf("(get sset) failed to load power actor state: %w", err)
|
||||
}
|
||||
|
||||
var mpow power.Claim
|
||||
if maddr != address.Undef {
|
||||
enc, aerr := actors.SerializeParams(&actors.PowerLookupParams{maddr})
|
||||
if aerr != nil {
|
||||
return types.EmptyInt, types.EmptyInt, aerr
|
||||
}
|
||||
ret, err := sm.Call(ctx, &types.Message{
|
||||
From: maddr,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.PowerLookup,
|
||||
Params: enc,
|
||||
}, ts)
|
||||
cm, err := adt.AsMap(sm.cs.Store(ctx), ps.Claims)
|
||||
if err != nil {
|
||||
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain: %w", err)
|
||||
}
|
||||
if ret.MsgRct.ExitCode != 0 {
|
||||
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain (exit code %d)", ret.MsgRct.ExitCode)
|
||||
return power.Claim{}, power.Claim{}, err
|
||||
}
|
||||
|
||||
mpow = types.BigFromBytes(ret.MsgRct.Return)
|
||||
var claim power.Claim
|
||||
if _, err := cm.Get(adt.AddrKey(maddr), &claim); err != nil {
|
||||
return power.Claim{}, power.Claim{}, err
|
||||
}
|
||||
|
||||
mpow = claim
|
||||
}
|
||||
|
||||
ret, err := sm.Call(ctx, &types.Message{
|
||||
From: actors.StoragePowerAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.GetTotalStorage,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain: %w", err)
|
||||
}
|
||||
if ret.MsgRct.ExitCode != 0 {
|
||||
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain (exit code %d)", ret.MsgRct.ExitCode)
|
||||
}
|
||||
|
||||
tpow := types.BigFromBytes(ret.MsgRct.Return)
|
||||
|
||||
return mpow, tpow, nil
|
||||
}
|
||||
|
||||
func GetMinerPeerID(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (peer.ID, error) {
|
||||
recp, err := sm.Call(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
Method: actors.MAMethods.GetPeerID,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("call failed: %w", err)
|
||||
}
|
||||
|
||||
if recp.MsgRct.ExitCode != 0 {
|
||||
return "", xerrors.Errorf("getting miner peer ID failed (exit code %d)", recp.MsgRct.ExitCode)
|
||||
}
|
||||
|
||||
return peer.IDFromBytes(recp.MsgRct.Return)
|
||||
}
|
||||
|
||||
func GetMinerWorker(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (address.Address, error) {
|
||||
recp, err := sm.Call(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
Method: actors.MAMethods.GetWorkerAddr,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("call failed: %w", err)
|
||||
}
|
||||
|
||||
if recp.MsgRct.ExitCode != 0 {
|
||||
return address.Undef, xerrors.Errorf("getting miner peer ID failed (exit code %d)", recp.MsgRct.ExitCode)
|
||||
}
|
||||
|
||||
return address.NewFromBytes(recp.MsgRct.Return)
|
||||
}
|
||||
|
||||
func GetMinerElectionPeriodStart(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("(get eps) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return mas.ElectionPeriodStart, nil
|
||||
return mpow, power.Claim{
|
||||
RawBytePower: ps.TotalRawBytePower,
|
||||
QualityAdjPower: ps.TotalQualityAdjPower,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SectorSetSizes(ctx context.Context, sm *StateManager, maddr address.Address, ts *types.TipSet) (api.MinerSectors, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return api.MinerSectors{}, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(sm.ChainStore().Blockstore())
|
||||
ss, err := amt.LoadAMT(blks, mas.Sectors)
|
||||
notProving, err := abi.BitFieldUnion(mas.Faults, mas.Recoveries)
|
||||
if err != nil {
|
||||
return api.MinerSectors{}, err
|
||||
}
|
||||
|
||||
ps, err := amt.LoadAMT(blks, mas.ProvingSet)
|
||||
npc, err := notProving.Count()
|
||||
if err != nil {
|
||||
return api.MinerSectors{}, err
|
||||
}
|
||||
|
||||
blks := cbor.NewCborStore(sm.ChainStore().Blockstore())
|
||||
ss, err := amt.LoadAMT(ctx, blks, mas.Sectors)
|
||||
if err != nil {
|
||||
return api.MinerSectors{}, err
|
||||
}
|
||||
|
||||
return api.MinerSectors{
|
||||
Pset: ps.Count,
|
||||
Sset: ss.Count,
|
||||
Pset: ss.Count - npc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetMinerProvingSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
func PreCommitInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (miner.SectorPreCommitOnChainInfo, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get pset) failed to load miner actor state: %w", err)
|
||||
return miner.SectorPreCommitOnChainInfo{}, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.ProvingSet)
|
||||
i, ok, err := mas.GetPrecommittedSector(sm.cs.Store(ctx), sid)
|
||||
if err != nil {
|
||||
return miner.SectorPreCommitOnChainInfo{}, err
|
||||
}
|
||||
if !ok {
|
||||
return miner.SectorPreCommitOnChainInfo{}, xerrors.New("precommit not found")
|
||||
}
|
||||
|
||||
return *i, nil
|
||||
}
|
||||
|
||||
func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address, filter *abi.BitField, filterOut bool) ([]*api.ChainSectorInfo, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.Sectors)
|
||||
return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.Sectors, filter, filterOut)
|
||||
}
|
||||
|
||||
func GetSectorsForElectionPost(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*sectorbuilder.SortedPublicSectorInfo, error) {
|
||||
sectors, err := GetMinerProvingSet(ctx, sm, ts, maddr)
|
||||
func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]abi.SectorInfo, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorStateRaw(ctx, maddr, &mas, st)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get sector set for miner: %w", err)
|
||||
return nil, xerrors.Errorf("(get sectors) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
var uselessOtherArray []ffi.PublicSectorInfo
|
||||
for _, s := range sectors {
|
||||
var uselessBuffer [32]byte
|
||||
copy(uselessBuffer[:], s.CommR)
|
||||
uselessOtherArray = append(uselessOtherArray, ffi.PublicSectorInfo{
|
||||
SectorID: s.SectorID,
|
||||
CommR: uselessBuffer,
|
||||
})
|
||||
// TODO: Optimization: we could avoid loaditg the whole proving set here if we had AMT.GetNth with bitfield filtering
|
||||
sectorSet, err := GetProvingSetRaw(ctx, sm, mas)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting proving set: %w", err)
|
||||
}
|
||||
|
||||
ssi := sectorbuilder.NewSortedPublicSectorInfo(uselessOtherArray)
|
||||
return &ssi, nil
|
||||
if len(sectorSet) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
spt, err := ffiwrapper.SealProofTypeFromSectorSize(mas.Info.SectorSize)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting seal proof type: %w", err)
|
||||
}
|
||||
|
||||
wpt, err := spt.RegisteredWinningPoStProof()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting window proof type: %w", err)
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting miner ID: %w", err)
|
||||
}
|
||||
|
||||
ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, wpt, abi.ActorID(mid), rand, uint64(len(sectorSet)))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("generating winning post challenges: %w", err)
|
||||
}
|
||||
|
||||
out := make([]abi.SectorInfo, len(ids))
|
||||
for i, n := range ids {
|
||||
out[i] = abi.SectorInfo{
|
||||
RegisteredProof: wpt,
|
||||
SectorNumber: sectorSet[n].ID,
|
||||
SealedCID: sectorSet[n].Info.Info.SealedCID,
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GetMinerSectorSize(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
func StateMinerInfo(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (miner.MinerInfo, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorStateRaw(ctx, maddr, &mas, ts.ParentState())
|
||||
if err != nil {
|
||||
return miner.MinerInfo{}, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return mas.Info, nil
|
||||
}
|
||||
|
||||
func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (bool, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
|
||||
return false, xerrors.Errorf("(get miner slashed) failed to load miner actor state")
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
var minfo actors.MinerInfo
|
||||
if err := cst.Get(ctx, mas.Info, &minfo); err != nil {
|
||||
return 0, xerrors.Errorf("failed to read miner info: %w", err)
|
||||
}
|
||||
|
||||
return minfo.SectorSize, nil
|
||||
}
|
||||
|
||||
func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
var spas power.State
|
||||
_, err = sm.LoadActorState(ctx, builtin.StoragePowerActorAddr, &spas, ts)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("(get mslash) failed to load miner actor state: %w", err)
|
||||
return false, xerrors.Errorf("(get miner slashed) failed to load power actor state")
|
||||
}
|
||||
|
||||
return mas.SlashedAt, nil
|
||||
store := sm.cs.Store(ctx)
|
||||
|
||||
claims, err := adt.AsMap(store, spas.Claims)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ok, err := claims.Get(power.AddrKey(maddr), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
func GetMinerDeadlines(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*miner.Deadlines, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
ss, lerr := amt2.LoadAMT(amt.WrapBlockstore(sm.cs.Blockstore()), mas.Sectors)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
|
||||
}
|
||||
|
||||
return mas.FaultSet.All(2 * ss.Count)
|
||||
return mas.LoadDeadlines(sm.cs.Store(ctx))
|
||||
}
|
||||
|
||||
func GetStorageDeal(ctx context.Context, sm *StateManager, dealId uint64, ts *types.TipSet) (*actors.OnChainDeal, error) {
|
||||
var state actors.StorageMarketState
|
||||
if _, err := sm.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
||||
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]abi.SectorNumber, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
faults, err := mas.Faults.All(miner.SectorsMax)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("reading fault bit set: %w", err)
|
||||
}
|
||||
|
||||
out := make([]abi.SectorNumber, len(faults))
|
||||
for i, fault := range faults {
|
||||
out[i] = abi.SectorNumber(fault)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GetStorageDeal(ctx context.Context, sm *StateManager, dealId abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) {
|
||||
var state market.State
|
||||
if _, err := sm.LoadActorState(ctx, builtin.StorageMarketActorAddr, &state, ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(sm.ChainStore().Blockstore())
|
||||
da, err := amt.LoadAMT(blks, state.Deals)
|
||||
da, err := amt.LoadAMT(ctx, cbor.NewCborStore(sm.ChainStore().Blockstore()), state.Proposals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ocd actors.OnChainDeal
|
||||
if err := da.Get(dealId, &ocd); err != nil {
|
||||
var dp market.DealProposal
|
||||
if err := da.Get(ctx, uint64(dealId), &dp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ocd, nil
|
||||
sa, err := market.AsDealStateArray(sm.ChainStore().Store(ctx), state.States)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, found, err := sa.Get(dealId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
st = &market.DealState{
|
||||
SectorStartEpoch: -1,
|
||||
LastUpdatedEpoch: -1,
|
||||
SlashEpoch: -1,
|
||||
}
|
||||
}
|
||||
|
||||
return &api.MarketDeal{
|
||||
Proposal: dp,
|
||||
State: *st,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ListMinerActors(ctx context.Context, sm *StateManager, ts *types.TipSet) ([]address.Address, error) {
|
||||
var state actors.StoragePowerState
|
||||
if _, err := sm.LoadActorState(ctx, actors.StoragePowerAddress, &state, ts); err != nil {
|
||||
var state power.State
|
||||
if _, err := sm.LoadActorState(ctx, builtin.StoragePowerActorAddr, &state, ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.ChainStore().Blockstore())
|
||||
miners, err := actors.MinerSetList(ctx, cst, state.Miners)
|
||||
m, err := adt.AsMap(sm.cs.Store(ctx), state.Claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var miners []address.Address
|
||||
err = m.ForEach(nil, func(k string) error {
|
||||
a, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
miners = append(miners, a)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -305,23 +335,31 @@ func ListMinerActors(ctx context.Context, sm *StateManager, ts *types.TipSet) ([
|
||||
return miners, nil
|
||||
}
|
||||
|
||||
func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.Cid) ([]*api.ChainSectorInfo, error) {
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
a, err := amt.LoadAMT(blks, ssc)
|
||||
func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.Cid, filter *abi.BitField, filterOut bool) ([]*api.ChainSectorInfo, error) {
|
||||
a, err := amt.LoadAMT(ctx, cbor.NewCborStore(bs), ssc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sset []*api.ChainSectorInfo
|
||||
if err := a.ForEach(func(i uint64, v *cbg.Deferred) error {
|
||||
var comms [][]byte
|
||||
if err := cbor.DecodeInto(v.Raw, &comms); err != nil {
|
||||
if err := a.ForEach(ctx, func(i uint64, v *cbg.Deferred) error {
|
||||
if filter != nil {
|
||||
set, err := filter.IsSet(i)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("filter check error: %w", err)
|
||||
}
|
||||
if set == filterOut {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var oci miner.SectorOnChainInfo
|
||||
if err := cbor.DecodeInto(v.Raw, &oci); err != nil {
|
||||
return err
|
||||
}
|
||||
sset = append(sset, &api.ChainSectorInfo{
|
||||
SectorID: i,
|
||||
CommR: comms[0],
|
||||
CommD: comms[1],
|
||||
Info: oci,
|
||||
ID: abi.SectorNumber(i),
|
||||
})
|
||||
return nil
|
||||
}); err != nil {
|
||||
@ -331,36 +369,155 @@ func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.C
|
||||
return sset, nil
|
||||
}
|
||||
|
||||
func ComputeState(ctx context.Context, sm *StateManager, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
|
||||
func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
base, _, err := sm.TipSetState(ctx, ts)
|
||||
base, trace, err := sm.ExecutionTrace(ctx, ts)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
fstate, err := sm.handleStateForks(ctx, base, height, ts.Height())
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
r := store.NewChainRand(sm.cs, ts.Cids(), height)
|
||||
vmi, err := vm.NewVM(fstate, height, r, actors.NetworkAddress, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
vmi, err := vm.NewVM(fstate, height, r, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
for i, msg := range msgs {
|
||||
// TODO: Use the signed message length for secp messages
|
||||
ret, err := vmi.ApplyMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("applying message %s: %w", msg.Cid(), err)
|
||||
return cid.Undef, nil, xerrors.Errorf("applying message %s: %w", msg.Cid(), err)
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
}
|
||||
|
||||
return vmi.Flush(ctx)
|
||||
root, err := vmi.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
return root, trace, nil
|
||||
}
|
||||
|
||||
func GetProvingSetRaw(ctx context.Context, sm *StateManager, mas miner.State) ([]*api.ChainSectorInfo, error) {
|
||||
notProving, err := abi.BitFieldUnion(mas.Faults, mas.Recoveries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provset, err := LoadSectorsFromSet(ctx, sm.cs.Blockstore(), mas.Sectors, notProving, true)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get proving set: %w", err)
|
||||
}
|
||||
|
||||
return provset, nil
|
||||
}
|
||||
|
||||
func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, error) {
|
||||
var lbr abi.ChainEpoch
|
||||
if round > build.WinningPoStSectorSetLookback {
|
||||
lbr = round - build.WinningPoStSectorSetLookback
|
||||
}
|
||||
|
||||
// more null blocks than our lookback
|
||||
if lbr > ts.Height() {
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr, ts, true)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get lookback tipset: %w", err)
|
||||
}
|
||||
|
||||
return lbts, nil
|
||||
}
|
||||
|
||||
func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcn beacon.RandomBeacon, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) {
|
||||
ts, err := sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load tipset for mining base: %w", err)
|
||||
}
|
||||
|
||||
prev, err := sm.ChainStore().GetLatestBeaconEntry(ts)
|
||||
if err != nil {
|
||||
if os.Getenv("LOTUS_IGNORE_DRAND") != "_yes_" {
|
||||
return nil, xerrors.Errorf("failed to get latest beacon entry: %w", err)
|
||||
}
|
||||
|
||||
prev = &types.BeaconEntry{}
|
||||
}
|
||||
|
||||
entries, err := beacon.BeaconEntriesForBlock(ctx, bcn, round, *prev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbase := *prev
|
||||
if len(entries) > 0 {
|
||||
rbase = entries[len(entries)-1]
|
||||
}
|
||||
|
||||
lbts, err := GetLookbackTipSetForRound(ctx, sm, ts, round)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting lookback miner actor state: %w", err)
|
||||
}
|
||||
|
||||
lbst, _, err := sm.TipSetState(ctx, lbts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mas miner.State
|
||||
if _, err := sm.LoadActorStateRaw(ctx, maddr, &mas, lbst); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := maddr.MarshalCBOR(buf); err != nil {
|
||||
return nil, xerrors.Errorf("failed to marshal miner address: %w", err)
|
||||
}
|
||||
|
||||
prand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err)
|
||||
}
|
||||
|
||||
sectors, err := GetSectorsForWinningPoSt(ctx, pv, sm, lbst, maddr, prand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting wpost proving set: %w", err)
|
||||
}
|
||||
|
||||
if len(sectors) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mpow, tpow, err := GetPowerRaw(ctx, sm, lbst, maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get power: %w", err)
|
||||
}
|
||||
|
||||
worker, err := sm.ResolveToKeyAddress(ctx, mas.GetWorker(), ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("resolving worker address: %w", err)
|
||||
}
|
||||
|
||||
return &api.MiningBaseInfo{
|
||||
MinerPower: mpow.QualityAdjPower,
|
||||
NetworkPower: tpow.QualityAdjPower,
|
||||
Sectors: sectors,
|
||||
WorkerKey: worker,
|
||||
SectorSize: mas.Info.SectorSize,
|
||||
PrevBeaconEntry: *prev,
|
||||
BeaconEntries: entries,
|
||||
}, nil
|
||||
}
|
||||
|
@ -3,13 +3,21 @@ package store
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/minio/blake2b-simd"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
@ -18,21 +26,20 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
"go.uber.org/multierr"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
car "github.com/ipfs/go-car"
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
format "github.com/ipfs/go-ipld-format"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
car "github.com/ipld/go-car"
|
||||
carutil "github.com/ipld/go-car/util"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
pubsub "github.com/whyrusleeping/pubsub"
|
||||
"golang.org/x/xerrors"
|
||||
@ -53,7 +60,7 @@ type ChainStore struct {
|
||||
pubLk sync.Mutex
|
||||
|
||||
tstLk sync.Mutex
|
||||
tipsets map[uint64][]cid.Cid
|
||||
tipsets map[abi.ChainEpoch][]cid.Cid
|
||||
|
||||
reorgCh chan<- reorg
|
||||
headChangeNotifs []func(rev, app []*types.TipSet) error
|
||||
@ -61,17 +68,17 @@ type ChainStore struct {
|
||||
mmCache *lru.ARCCache
|
||||
tsCache *lru.ARCCache
|
||||
|
||||
vmcalls *types.VMSyscalls
|
||||
vmcalls runtime.Syscalls
|
||||
}
|
||||
|
||||
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls *types.VMSyscalls) *ChainStore {
|
||||
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Syscalls) *ChainStore {
|
||||
c, _ := lru.NewARC(2048)
|
||||
tsc, _ := lru.NewARC(4096)
|
||||
cs := &ChainStore{
|
||||
bs: bs,
|
||||
ds: ds,
|
||||
bestTips: pubsub.New(64),
|
||||
tipsets: make(map[uint64][]cid.Cid),
|
||||
tipsets: make(map[abi.ChainEpoch][]cid.Cid),
|
||||
mmCache: c,
|
||||
tsCache: tsc,
|
||||
vmcalls: vmcalls,
|
||||
@ -83,16 +90,16 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls *types.VMSy
|
||||
cs.pubLk.Lock()
|
||||
defer cs.pubLk.Unlock()
|
||||
|
||||
notif := make([]*HeadChange, len(rev)+len(app))
|
||||
notif := make([]*api.HeadChange, len(rev)+len(app))
|
||||
|
||||
for i, r := range rev {
|
||||
notif[i] = &HeadChange{
|
||||
notif[i] = &api.HeadChange{
|
||||
Type: HCRevert,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
for i, r := range app {
|
||||
notif[i+len(rev)] = &HeadChange{
|
||||
notif[i+len(rev)] = &api.HeadChange{
|
||||
Type: HCApply,
|
||||
Val: r,
|
||||
}
|
||||
@ -159,19 +166,14 @@ const (
|
||||
HCCurrent = "current"
|
||||
)
|
||||
|
||||
type HeadChange struct {
|
||||
Type string
|
||||
Val *types.TipSet
|
||||
}
|
||||
|
||||
func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*HeadChange {
|
||||
func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*api.HeadChange {
|
||||
cs.pubLk.Lock()
|
||||
subch := cs.bestTips.Sub("headchange")
|
||||
head := cs.GetHeaviestTipSet()
|
||||
cs.pubLk.Unlock()
|
||||
|
||||
out := make(chan []*HeadChange, 16)
|
||||
out <- []*HeadChange{{
|
||||
out := make(chan []*api.HeadChange, 16)
|
||||
out <- []*api.HeadChange{{
|
||||
Type: HCCurrent,
|
||||
Val: head,
|
||||
}}
|
||||
@ -191,7 +193,7 @@ func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*HeadChange {
|
||||
log.Warnf("head change sub is slow, has %d buffered entries", len(out))
|
||||
}
|
||||
select {
|
||||
case out <- val.([]*HeadChange):
|
||||
case out <- val.([]*api.HeadChange):
|
||||
case <-ctx.Done():
|
||||
}
|
||||
case <-ctx.Done():
|
||||
@ -592,7 +594,7 @@ func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) {
|
||||
return types.DecodeBlock(genb.RawData())
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetCMessage(c cid.Cid) (ChainMsg, error) {
|
||||
func (cs *ChainStore) GetCMessage(c cid.Cid) (types.ChainMsg, error) {
|
||||
m, err := cs.GetMessage(c)
|
||||
if err == nil {
|
||||
return m, nil
|
||||
@ -625,8 +627,9 @@ func (cs *ChainStore) GetSignedMessage(c cid.Cid) (*types.SignedMessage, error)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) {
|
||||
bs := amt.WrapBlockstore(cs.bs)
|
||||
a, err := amt.LoadAMT(bs, root)
|
||||
ctx := context.TODO()
|
||||
bs := cbor.NewCborStore(cs.bs)
|
||||
a, err := amt.LoadAMT(ctx, bs, root)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt load: %w", err)
|
||||
}
|
||||
@ -634,7 +637,7 @@ func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) {
|
||||
var cids []cid.Cid
|
||||
for i := uint64(0); i < a.Count; i++ {
|
||||
var c cbg.CborCid
|
||||
if err := a.Get(i, &c); err != nil {
|
||||
if err := a.Get(ctx, i, &c); err != nil {
|
||||
return nil, xerrors.Errorf("failed to load cid from amt: %w", err)
|
||||
}
|
||||
|
||||
@ -644,17 +647,11 @@ func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) {
|
||||
return cids, nil
|
||||
}
|
||||
|
||||
type ChainMsg interface {
|
||||
Cid() cid.Cid
|
||||
VMMessage() *types.Message
|
||||
ToStorageBlock() (block.Block, error)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]ChainMsg, error) {
|
||||
func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) {
|
||||
applied := make(map[address.Address]uint64)
|
||||
balances := make(map[address.Address]types.BigInt)
|
||||
|
||||
cst := hamt.CSTFromBstore(cs.bs)
|
||||
cst := cbor.NewCborStore(cs.bs)
|
||||
st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load state tree")
|
||||
@ -673,14 +670,14 @@ func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]ChainMsg, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out []ChainMsg
|
||||
var out []types.ChainMsg
|
||||
for _, b := range ts.Blocks() {
|
||||
bms, sms, err := cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get messages for block: %w", err)
|
||||
}
|
||||
|
||||
cmsgs := make([]ChainMsg, 0, len(bms)+len(sms))
|
||||
cmsgs := make([]types.ChainMsg, 0, len(bms)+len(sms))
|
||||
for _, m := range bms {
|
||||
cmsgs = append(cmsgs, m)
|
||||
}
|
||||
@ -723,10 +720,10 @@ func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error)
|
||||
return mmcids.bls, mmcids.secpk, nil
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(cs.bs)
|
||||
cst := cbor.NewCborStore(cs.bs)
|
||||
var msgmeta types.MsgMeta
|
||||
if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to load msgmeta: %w", err)
|
||||
return nil, nil, xerrors.Errorf("failed to load msgmeta (%s): %w", mmc, err)
|
||||
}
|
||||
|
||||
blscids, err := cs.readAMTCids(msgmeta.BlsMessages)
|
||||
@ -747,7 +744,7 @@ func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error)
|
||||
return blscids, secpkcids, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error) {
|
||||
func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*api.HeadChange, error) {
|
||||
fts, err := cs.LoadTipSet(from)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading from tipset %s: %w", from, err)
|
||||
@ -761,12 +758,12 @@ func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to type
|
||||
return nil, xerrors.Errorf("error getting tipset branches: %w", err)
|
||||
}
|
||||
|
||||
path := make([]*HeadChange, len(revert)+len(apply))
|
||||
path := make([]*api.HeadChange, len(revert)+len(apply))
|
||||
for i, r := range revert {
|
||||
path[i] = &HeadChange{Type: HCRevert, Val: r}
|
||||
path[i] = &api.HeadChange{Type: HCRevert, Val: r}
|
||||
}
|
||||
for j, i := 0, len(apply)-1; i >= 0; j, i = j+1, i-1 {
|
||||
path[j+len(revert)] = &HeadChange{Type: HCApply, Val: apply[i]}
|
||||
path[j+len(revert)] = &api.HeadChange{Type: HCApply, Val: apply[i]}
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
@ -791,14 +788,15 @@ func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message,
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) {
|
||||
bs := amt.WrapBlockstore(cs.bs)
|
||||
a, err := amt.LoadAMT(bs, b.ParentMessageReceipts)
|
||||
ctx := context.TODO()
|
||||
bs := cbor.NewCborStore(cs.bs)
|
||||
a, err := amt.LoadAMT(ctx, bs, b.ParentMessageReceipts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt load: %w", err)
|
||||
}
|
||||
|
||||
var r types.MessageReceipt
|
||||
if err := a.Get(uint64(i), &r); err != nil {
|
||||
if err := a.Get(ctx, uint64(i), &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -837,7 +835,35 @@ func (cs *ChainStore) Blockstore() bstore.Blockstore {
|
||||
return cs.bs
|
||||
}
|
||||
|
||||
func (cs *ChainStore) VMSys() *types.VMSyscalls {
|
||||
func ActorStore(ctx context.Context, bs blockstore.Blockstore) adt.Store {
|
||||
return &astore{
|
||||
cst: cbor.NewCborStore(bs),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
type astore struct {
|
||||
cst cbor.IpldStore
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (a *astore) Context() context.Context {
|
||||
return a.ctx
|
||||
}
|
||||
|
||||
func (a *astore) Get(ctx context.Context, c cid.Cid, out interface{}) error {
|
||||
return a.cst.Get(ctx, c, out)
|
||||
}
|
||||
|
||||
func (a *astore) Put(ctx context.Context, v interface{}) (cid.Cid, error) {
|
||||
return a.cst.Put(ctx, v)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) Store(ctx context.Context) adt.Store {
|
||||
return ActorStore(ctx, cs.bs)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) VMSys() runtime.Syscalls {
|
||||
return cs.vmcalls
|
||||
}
|
||||
|
||||
@ -863,22 +889,29 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
|
||||
return NewFullTipSet(out), nil
|
||||
}
|
||||
|
||||
func drawRandomness(t *types.Ticket, round int64) []byte {
|
||||
h := sha256.New()
|
||||
var buf [8]byte
|
||||
binary.LittleEndian.PutUint64(buf[:], uint64(round))
|
||||
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
h := blake2b.New256()
|
||||
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
||||
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
||||
}
|
||||
VRFDigest := blake2b.Sum256(rbase)
|
||||
h.Write(VRFDigest[:])
|
||||
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
||||
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
||||
}
|
||||
h.Write(entropy)
|
||||
|
||||
h.Write(t.VRFProof)
|
||||
h.Write(buf[:])
|
||||
|
||||
return h.Sum(nil)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, round int64) ([]byte, error) {
|
||||
func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) (out []byte, err error) {
|
||||
_, span := trace.StartSpan(ctx, "store.GetRandomness")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("round", round))
|
||||
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
||||
|
||||
//defer func() {
|
||||
//log.Infof("getRand %v %d %d %x -> %x", blks, pers, round, entropy, out)
|
||||
//}()
|
||||
for {
|
||||
nts, err := cs.LoadTipSet(types.NewTipSetKey(blks...))
|
||||
if err != nil {
|
||||
@ -887,27 +920,21 @@ func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, round i
|
||||
|
||||
mtb := nts.MinTicketBlock()
|
||||
|
||||
if int64(nts.Height()) <= round {
|
||||
return drawRandomness(nts.MinTicketBlock().Ticket, round), nil
|
||||
}
|
||||
|
||||
// special case for lookback behind genesis block
|
||||
// TODO(spec): this is not in the spec, need to sync that
|
||||
if mtb.Height == 0 {
|
||||
|
||||
// round is negative
|
||||
thash := drawRandomness(mtb.Ticket, round*-1)
|
||||
|
||||
// for negative lookbacks, just use the hash of the positive tickethash value
|
||||
h := sha256.Sum256(thash)
|
||||
return h[:], nil
|
||||
// if at (or just past -- for null epochs) appropriate epoch
|
||||
// or at genesis (works for negative epochs)
|
||||
if nts.Height() <= round || mtb.Height == 0 {
|
||||
return DrawRandomness(nts.MinTicketBlock().Ticket.VRFProof, pers, round, entropy)
|
||||
}
|
||||
|
||||
blks = mtb.Parents
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) {
|
||||
// GetTipsetByHeight returns the tipset on the chain behind 'ts' at the given
|
||||
// height. In the case that the given height is a null round, the 'prev' flag
|
||||
// selects the tipset before the null round if true, and the tipset following
|
||||
// the null round if false.
|
||||
func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, ts *types.TipSet, prev bool) (*types.TipSet, error) {
|
||||
if ts == nil {
|
||||
ts = cs.GetHeaviestTipSet()
|
||||
}
|
||||
@ -916,6 +943,10 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types
|
||||
return nil, xerrors.Errorf("looking for tipset with height less than start point")
|
||||
}
|
||||
|
||||
if h == ts.Height() {
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
if ts.Height()-h > build.ForkLengthThreshold {
|
||||
log.Warnf("expensive call to GetTipsetByHeight, seeking %d levels", ts.Height()-h)
|
||||
}
|
||||
@ -927,22 +958,32 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types
|
||||
}
|
||||
|
||||
if h > pts.Height() {
|
||||
if prev {
|
||||
return pts, nil
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
if h == pts.Height() {
|
||||
return pts, nil
|
||||
}
|
||||
|
||||
ts = pts
|
||||
}
|
||||
}
|
||||
|
||||
func recurseLinks(bs blockstore.Blockstore, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) {
|
||||
if root.Prefix().Codec != cid.DagCBOR {
|
||||
return in, nil
|
||||
}
|
||||
|
||||
data, err := bs.Get(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("recurse links get (%s) failed: %w", root, err)
|
||||
}
|
||||
|
||||
top, err := cbg.ScanForLinks(bytes.NewReader(data.RawData()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("scanning for links failed: %w", err)
|
||||
}
|
||||
|
||||
in = append(in, top...)
|
||||
@ -961,41 +1002,87 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, w io.Writer)
|
||||
if ts == nil {
|
||||
ts = cs.GetHeaviestTipSet()
|
||||
}
|
||||
bsrv := blockservice.New(cs.bs, nil)
|
||||
dserv := dag.NewDAGService(bsrv)
|
||||
return car.WriteCarWithWalker(ctx, dserv, ts.Cids(), w, func(nd format.Node) ([]*format.Link, error) {
|
||||
var b types.BlockHeader
|
||||
if err := b.UnmarshalCBOR(bytes.NewBuffer(nd.RawData())); err != nil {
|
||||
return nil, err
|
||||
|
||||
seen := cid.NewSet()
|
||||
|
||||
h := &car.CarHeader{
|
||||
Roots: ts.Cids(),
|
||||
Version: 1,
|
||||
}
|
||||
|
||||
if err := car.WriteHeader(h, w); err != nil {
|
||||
return xerrors.Errorf("failed to write car header: %s", err)
|
||||
}
|
||||
|
||||
blocksToWalk := ts.Cids()
|
||||
|
||||
walkChain := func(blk cid.Cid) error {
|
||||
if !seen.Visit(blk) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out []*format.Link
|
||||
for _, p := range b.Parents {
|
||||
out = append(out, &format.Link{Cid: p})
|
||||
}
|
||||
|
||||
cids, err := recurseLinks(cs.bs, b.Messages, nil)
|
||||
data, err := cs.bs.Get(blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return xerrors.Errorf("getting block: %w", err)
|
||||
}
|
||||
|
||||
for _, c := range cids {
|
||||
out = append(out, &format.Link{Cid: c})
|
||||
if err := carutil.LdWrite(w, blk.Bytes(), data.RawData()); err != nil {
|
||||
return xerrors.Errorf("failed to write block to car output: %w", err)
|
||||
}
|
||||
|
||||
var b types.BlockHeader
|
||||
if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil {
|
||||
return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err)
|
||||
}
|
||||
|
||||
for _, p := range b.Parents {
|
||||
blocksToWalk = append(blocksToWalk, p)
|
||||
}
|
||||
|
||||
cids, err := recurseLinks(cs.bs, b.Messages, []cid.Cid{b.Messages})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("recursing messages failed: %w", err)
|
||||
}
|
||||
|
||||
out := cids
|
||||
|
||||
if b.Height == 0 {
|
||||
cids, err := recurseLinks(cs.bs, b.ParentStateRoot, nil)
|
||||
cids, err := recurseLinks(cs.bs, b.ParentStateRoot, []cid.Cid{b.ParentStateRoot})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return xerrors.Errorf("recursing genesis state failed: %w", err)
|
||||
}
|
||||
|
||||
for _, c := range cids {
|
||||
out = append(out, &format.Link{Cid: c})
|
||||
out = append(out, cids...)
|
||||
}
|
||||
|
||||
for _, c := range out {
|
||||
if seen.Visit(c) {
|
||||
if c.Prefix().Codec != cid.DagCBOR {
|
||||
continue
|
||||
}
|
||||
data, err := cs.bs.Get(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("writing object to car (get %s): %w", c, err)
|
||||
}
|
||||
|
||||
if err := carutil.LdWrite(w, c.Bytes(), data.RawData()); err != nil {
|
||||
return xerrors.Errorf("failed to write out car object: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
for len(blocksToWalk) > 0 {
|
||||
next := blocksToWalk[0]
|
||||
blocksToWalk = blocksToWalk[1:]
|
||||
if err := walkChain(next); err != nil {
|
||||
return xerrors.Errorf("walk chain failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
|
||||
@ -1012,13 +1099,41 @@ func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry, error) {
|
||||
cur := ts
|
||||
for i := 0; i < 20; i++ {
|
||||
cbe := cur.Blocks()[0].BeaconEntries
|
||||
if len(cbe) > 0 {
|
||||
return &cbe[len(cbe)-1], nil
|
||||
}
|
||||
|
||||
if cur.Height() == 0 {
|
||||
return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry")
|
||||
}
|
||||
|
||||
next, err := cs.LoadTipSet(cur.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err)
|
||||
}
|
||||
cur = next
|
||||
}
|
||||
|
||||
if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" {
|
||||
return &types.BeaconEntry{
|
||||
Data: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset")
|
||||
}
|
||||
|
||||
type chainRand struct {
|
||||
cs *ChainStore
|
||||
blks []cid.Cid
|
||||
bh uint64
|
||||
bh abi.ChainEpoch
|
||||
}
|
||||
|
||||
func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64) vm.Rand {
|
||||
func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight abi.ChainEpoch) vm.Rand {
|
||||
return &chainRand{
|
||||
cs: cs,
|
||||
blks: blks,
|
||||
@ -1026,8 +1141,8 @@ func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64) vm.Rand {
|
||||
}
|
||||
}
|
||||
|
||||
func (cr *chainRand) GetRandomness(ctx context.Context, round int64) ([]byte, error) {
|
||||
return cr.cs.GetRandomness(ctx, cr.blks, round)
|
||||
func (cr *chainRand) GetRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
return cr.cs.GetRandomness(ctx, cr.blks, pers, round, entropy)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
|
@ -1,20 +1,30 @@
|
||||
package store_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
)
|
||||
|
||||
func init() {
|
||||
build.SectorSizes = []uint64{1024}
|
||||
build.MinimumMinerPower = 1024
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
}
|
||||
|
||||
func BenchmarkGetRandomness(b *testing.B) {
|
||||
@ -60,9 +70,43 @@ func BenchmarkGetRandomness(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := cs.GetRandomness(context.TODO(), last.Cids(), 500)
|
||||
_, err := cs.GetRandomness(context.TODO(), last.Cids(), crypto.DomainSeparationTag_SealRandomness, 500, nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainExportImport(t *testing.T) {
|
||||
cg, err := gen.NewGenerator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var last *types.TipSet
|
||||
for i := 0; i < 100; i++ {
|
||||
ts, err := cg.NextTipSet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
last = ts.TipSet.TipSet()
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := cg.ChainStore().Export(context.TODO(), last, buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nbs := blockstore.NewBlockstore(datastore.NewMapDatastore())
|
||||
cs := store.NewChainStore(nbs, datastore.NewMapDatastore(), nil)
|
||||
|
||||
root, err := cs.Import(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !root.Equals(last) {
|
||||
t.Fatal("imported chain differed from exported chain")
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
big2 "github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@ -23,19 +27,27 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
|
||||
|
||||
// >>> wFunction(totalPowerAtTipset(ts)) * 2^8 <<< + (wFunction(totalPowerAtTipset(ts)) * len(ts.blocks) * wRatio_num * 2^8) / (e * wRatio_den)
|
||||
|
||||
ret, err := cs.call(ctx, &types.Message{
|
||||
From: actors.StoragePowerAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.GetTotalStorage,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return types.EmptyInt, xerrors.Errorf("failed to get total power from chain: %w", err)
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return types.EmptyInt, xerrors.Errorf("failed to get total power from chain (exit code %d)", ret.ExitCode)
|
||||
tpow := big2.Zero()
|
||||
{
|
||||
cst := cbor.NewCborStore(cs.Blockstore())
|
||||
state, err := state.LoadStateTree(cst, ts.ParentState())
|
||||
if err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("load state tree: %w", err)
|
||||
}
|
||||
|
||||
act, err := state.GetActor(builtin.StoragePowerActorAddr)
|
||||
if err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("get power actor: %w", err)
|
||||
}
|
||||
|
||||
var st power.State
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("get power actor head: %w", err)
|
||||
}
|
||||
tpow = st.TotalQualityAdjPower // TODO: REVIEW: Is this correct?
|
||||
}
|
||||
|
||||
log2P := int64(0)
|
||||
tpow := types.BigFromBytes(ret.Return)
|
||||
if tpow.GreaterThan(zero) {
|
||||
log2P = int64(tpow.BitLen() - 1)
|
||||
} else {
|
||||
@ -60,13 +72,13 @@ func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.Ti
|
||||
|
||||
r := NewChainRand(cs, ts.Cids(), ts.Height())
|
||||
|
||||
vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs, cs.vmcalls)
|
||||
vmi, err := vm.NewVM(bstate, ts.Height(), r, cs.bs, cs.vmcalls)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up vm: %w", err)
|
||||
}
|
||||
|
||||
if msg.GasLimit == types.EmptyInt {
|
||||
msg.GasLimit = types.NewInt(10000000000)
|
||||
if msg.GasLimit == 0 {
|
||||
msg.GasLimit = 10000000000
|
||||
}
|
||||
if msg.GasPrice == types.EmptyInt {
|
||||
msg.GasPrice = types.NewInt(0)
|
||||
@ -83,6 +95,7 @@ func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.Ti
|
||||
msg.Nonce = fromActor.Nonce
|
||||
|
||||
// TODO: maybe just use the invoker directly?
|
||||
// TODO: use signed message length for secp messages
|
||||
ret, err := vmi.ApplyMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("apply message failed: %w", err)
|
||||
|
@ -4,11 +4,13 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
connmgr "github.com/libp2p/go-libp2p-core/connmgr"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
@ -22,7 +24,7 @@ import (
|
||||
|
||||
var log = logging.Logger("sub")
|
||||
|
||||
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager) {
|
||||
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager, bv *BlockValidator) {
|
||||
for {
|
||||
msg, err := bsub.Next(ctx)
|
||||
if err != nil {
|
||||
@ -37,8 +39,12 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
|
||||
blk, ok := msg.ValidatorData.(*types.BlockMsg)
|
||||
if !ok {
|
||||
log.Warnf("pubsub block validator passed on wrong type: %#v", msg.ValidatorData)
|
||||
return
|
||||
}
|
||||
|
||||
//nolint:golint
|
||||
src := peer.ID(msg.GetFrom())
|
||||
|
||||
go func() {
|
||||
log.Infof("New block over pubsub: %s", blk.Cid())
|
||||
|
||||
@ -46,13 +52,15 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
|
||||
log.Debug("about to fetch messages for block from pubsub")
|
||||
bmsgs, err := s.Bsync.FetchMessagesByCids(context.TODO(), blk.BlsMessages)
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s", err)
|
||||
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s; flagging source %s", err, src)
|
||||
bv.flagPeer(src)
|
||||
return
|
||||
}
|
||||
|
||||
smsgs, err := s.Bsync.FetchSignedMessagesByCids(context.TODO(), blk.SecpkMessages)
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s", err)
|
||||
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s; flagging source %s", err, src)
|
||||
bv.flagPeer(src)
|
||||
return
|
||||
}
|
||||
|
||||
@ -87,7 +95,7 @@ func NewBlockValidator(blacklist func(peer.ID)) *BlockValidator {
|
||||
p, _ := lru.New2Q(4096)
|
||||
return &BlockValidator{
|
||||
peers: p,
|
||||
killThresh: 5,
|
||||
killThresh: 10,
|
||||
blacklist: blacklist,
|
||||
recvBlocks: newBlockReceiptCache(),
|
||||
}
|
||||
@ -103,13 +111,15 @@ func (bv *BlockValidator) flagPeer(p peer.ID) {
|
||||
val := v.(int)
|
||||
|
||||
if val >= bv.killThresh {
|
||||
log.Warnf("blacklisting peer %s", p)
|
||||
bv.blacklist(p)
|
||||
return
|
||||
}
|
||||
|
||||
bv.peers.Add(p, v.(int)+1)
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool {
|
||||
func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
|
||||
stats.Record(ctx, metrics.BlockReceived.M(1))
|
||||
blk, err := types.DecodeBlockMsg(msg.GetData())
|
||||
if err != nil {
|
||||
@ -117,7 +127,7 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "invalid"))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
bv.flagPeer(pid)
|
||||
return false
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
if len(blk.BlsMessages)+len(blk.SecpkMessages) > build.BlockMessageLimit {
|
||||
@ -125,18 +135,18 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "too_many_messages"))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
bv.flagPeer(pid)
|
||||
return false
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
if bv.recvBlocks.add(blk.Header.Cid()) > 0 {
|
||||
// TODO: once these changes propagate to the network, we can consider
|
||||
// dropping peers who send us the same block multiple times
|
||||
return false
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
msg.ValidatorData = blk
|
||||
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
|
||||
return true
|
||||
return pubsub.ValidationAccept
|
||||
}
|
||||
|
||||
type blockReceiptCache struct {
|
||||
@ -170,27 +180,30 @@ func NewMessageValidator(mp *messagepool.MessagePool) *MessageValidator {
|
||||
return &MessageValidator{mp}
|
||||
}
|
||||
|
||||
func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool {
|
||||
func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
|
||||
stats.Record(ctx, metrics.MessageReceived.M(1))
|
||||
m, err := types.DecodeSignedMessage(msg.Message.GetData())
|
||||
if err != nil {
|
||||
log.Warnf("failed to decode incoming message: %s", err)
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "decode"))
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
return false
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
if err := mv.mpool.Add(m); err != nil {
|
||||
log.Warnf("failed to add message from network to message pool (From: %s, To: %s, Nonce: %d, Value: %s): %s", m.Message.From, m.Message.To, m.Message.Nonce, types.FIL(m.Message.Value), err)
|
||||
log.Debugf("failed to add message from network to message pool (From: %s, To: %s, Nonce: %d, Value: %s): %s", m.Message.From, m.Message.To, m.Message.Nonce, types.FIL(m.Message.Value), err)
|
||||
ctx, _ = tag.New(
|
||||
ctx,
|
||||
tag.Insert(metrics.FailureType, "add"),
|
||||
)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
return false
|
||||
if xerrors.Is(err, messagepool.ErrBroadcastAnyway) {
|
||||
return pubsub.ValidationAccept
|
||||
}
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
stats.Record(ctx, metrics.MessageValidationSuccess.M(1))
|
||||
return true
|
||||
return pubsub.ValidationAccept
|
||||
}
|
||||
|
||||
func HandleIncomingMessages(ctx context.Context, mpool *messagepool.MessagePool, msub *pubsub.Subscription) {
|
||||
|
506
chain/sync.go
506
chain/sync.go
@ -3,21 +3,20 @@ package chain
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Gurpartap/async"
|
||||
bls "github.com/filecoin-project/filecoin-ffi"
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p-core/connmgr"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
@ -27,17 +26,27 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
bls "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/filecoin-project/go-address"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/blocksync"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
)
|
||||
@ -50,6 +59,9 @@ type Syncer struct {
|
||||
// The interface for accessing and putting tipsets into local storage
|
||||
store *store.ChainStore
|
||||
|
||||
// handle to the random beacon for verification
|
||||
beacon beacon.RandomBeacon
|
||||
|
||||
// the state manager handles making state queries
|
||||
sm *stmgr.StateManager
|
||||
|
||||
@ -71,12 +83,14 @@ type Syncer struct {
|
||||
incoming *pubsub.PubSub
|
||||
|
||||
receiptTracker *blockReceiptTracker
|
||||
|
||||
verifier ffiwrapper.Verifier
|
||||
}
|
||||
|
||||
func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID) (*Syncer, error) {
|
||||
func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID, beacon beacon.RandomBeacon, verifier ffiwrapper.Verifier) (*Syncer, error) {
|
||||
gen, err := sm.ChainStore().GetGenesis()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("getting genesis block: %w", err)
|
||||
}
|
||||
|
||||
gent, err := types.NewTipSet([]*types.BlockHeader{gen})
|
||||
@ -85,6 +99,7 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm
|
||||
}
|
||||
|
||||
s := &Syncer{
|
||||
beacon: beacon,
|
||||
bad: NewBadBlockCache(),
|
||||
Genesis: gent,
|
||||
Bsync: bsync,
|
||||
@ -93,6 +108,7 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm
|
||||
self: self,
|
||||
receiptTracker: newBlockReceiptTracker(),
|
||||
connmgr: connmgr,
|
||||
verifier: verifier,
|
||||
|
||||
incoming: pubsub.New(50),
|
||||
}
|
||||
@ -216,7 +232,7 @@ func (syncer *Syncer) ValidateMsgMeta(fblk *types.FullBlock) error {
|
||||
// we implement that
|
||||
blockstore := syncer.store.Blockstore()
|
||||
|
||||
bs := amt.WrapBlockstore(blockstore)
|
||||
bs := cbor.NewCborStore(blockstore)
|
||||
smroot, err := computeMsgMeta(bs, bcids, scids)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("validating msgmeta, compute failed: %w", err)
|
||||
@ -283,7 +299,7 @@ func copyBlockstore(from, to bstore.Blockstore) error {
|
||||
// either validate it here, or ensure that its validated elsewhere (maybe make
|
||||
// sure the blocksync code checks it?)
|
||||
// maybe this code should actually live in blocksync??
|
||||
func zipTipSetAndMessages(bs amt.Blocks, ts *types.TipSet, allbmsgs []*types.Message, allsmsgs []*types.SignedMessage, bmi, smi [][]uint64) (*store.FullTipSet, error) {
|
||||
func zipTipSetAndMessages(bs cbor.IpldStore, ts *types.TipSet, allbmsgs []*types.Message, allsmsgs []*types.SignedMessage, bmi, smi [][]uint64) (*store.FullTipSet, error) {
|
||||
if len(ts.Blocks()) != len(smi) || len(ts.Blocks()) != len(bmi) {
|
||||
return nil, fmt.Errorf("msgincl length didnt match tipset size")
|
||||
}
|
||||
@ -316,7 +332,7 @@ func zipTipSetAndMessages(bs amt.Blocks, ts *types.TipSet, allbmsgs []*types.Mes
|
||||
}
|
||||
|
||||
if b.Messages != mrcid {
|
||||
return nil, fmt.Errorf("messages didnt match message root in header")
|
||||
return nil, fmt.Errorf("messages didnt match message root in header for ts %s", ts.Key())
|
||||
}
|
||||
|
||||
fb := &types.FullBlock{
|
||||
@ -331,18 +347,19 @@ func zipTipSetAndMessages(bs amt.Blocks, ts *types.TipSet, allbmsgs []*types.Mes
|
||||
return fts, nil
|
||||
}
|
||||
|
||||
func computeMsgMeta(bs amt.Blocks, bmsgCids, smsgCids []cbg.CBORMarshaler) (cid.Cid, error) {
|
||||
bmroot, err := amt.FromArray(bs, bmsgCids)
|
||||
func computeMsgMeta(bs cbor.IpldStore, bmsgCids, smsgCids []cbg.CBORMarshaler) (cid.Cid, error) {
|
||||
ctx := context.TODO()
|
||||
bmroot, err := amt.FromArray(ctx, bs, bmsgCids)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
smroot, err := amt.FromArray(bs, smsgCids)
|
||||
smroot, err := amt.FromArray(ctx, bs, smsgCids)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
mrcid, err := bs.Put(&types.MsgMeta{
|
||||
mrcid, err := bs.Put(ctx, &types.MsgMeta{
|
||||
BlsMessages: bmroot,
|
||||
SecpkMessages: smroot,
|
||||
})
|
||||
@ -465,35 +482,51 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
|
||||
}
|
||||
|
||||
func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error {
|
||||
var err error
|
||||
enc, err := actors.SerializeParams(&actors.IsValidMinerParam{Addr: maddr})
|
||||
var spast power.State
|
||||
|
||||
_, err := syncer.sm.LoadActorState(ctx, builtin.StoragePowerActorAddr, &spast, baseTs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := syncer.sm.Call(ctx, &types.Message{
|
||||
To: actors.StoragePowerAddress,
|
||||
From: maddr,
|
||||
Method: actors.SPAMethods.IsValidMiner,
|
||||
Params: enc,
|
||||
}, baseTs)
|
||||
cm, err := adt.AsMap(syncer.store.Store(ctx), spast.Claims)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("checking if block miner is valid failed: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if ret.MsgRct.ExitCode != 0 {
|
||||
return xerrors.Errorf("StorageMarket.IsValidMiner check failed (exit code %d)", ret.MsgRct.ExitCode)
|
||||
var claim power.Claim
|
||||
exist, err := cm.Get(adt.AddrKey(maddr), &claim)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(ret.MsgRct.Return, cbg.CborBoolTrue) {
|
||||
if !exist {
|
||||
return xerrors.New("miner isn't valid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrTemporal = errors.New("temporal error")
|
||||
|
||||
func blockSanityChecks(h *types.BlockHeader) error {
|
||||
if h.ElectionProof == nil {
|
||||
return xerrors.Errorf("block cannot have nil election proof")
|
||||
}
|
||||
|
||||
if h.Ticket == nil {
|
||||
return xerrors.Errorf("block cannot have nil ticket")
|
||||
}
|
||||
|
||||
if h.BlockSig == nil {
|
||||
return xerrors.Errorf("block had nil signature")
|
||||
}
|
||||
|
||||
if h.BLSAggregate == nil {
|
||||
return xerrors.Errorf("block had nil bls aggregate signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Should match up with 'Semantical Validation' in validation.md in the spec
|
||||
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validateBlock")
|
||||
@ -502,6 +535,10 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
log.Warn("insecure test validation is enabled, if you see this outside of a test, it is a severe bug!")
|
||||
}
|
||||
|
||||
if err := blockSanityChecks(b.Header); err != nil {
|
||||
return xerrors.Errorf("incoming header failed basic sanity checks: %w", err)
|
||||
}
|
||||
|
||||
h := b.Header
|
||||
|
||||
baseTs, err := syncer.store.LoadTipSet(types.NewTipSetKey(h.Parents...))
|
||||
@ -509,63 +546,40 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err)
|
||||
}
|
||||
|
||||
// fast checks first
|
||||
if h.BlockSig == nil {
|
||||
return xerrors.Errorf("block had nil signature")
|
||||
lbts, err := stmgr.GetLookbackTipSetForRound(ctx, syncer.sm, baseTs, h.Height)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get lookback tipset for block: %w", err)
|
||||
}
|
||||
|
||||
if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) {
|
||||
return xerrors.Errorf("block was from the future: %w", ErrTemporal)
|
||||
lbst, _, err := syncer.sm.TipSetState(ctx, lbts)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to compute lookback tipset state: %w", err)
|
||||
}
|
||||
if h.Timestamp > uint64(time.Now().Unix()) {
|
||||
|
||||
prevBeacon, err := syncer.store.GetLatestBeaconEntry(baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get latest beacon entry: %w", err)
|
||||
}
|
||||
|
||||
//nulls := h.Height - (baseTs.Height() + 1)
|
||||
|
||||
// fast checks first
|
||||
|
||||
now := uint64(time.Now().Unix())
|
||||
if h.Timestamp > now+build.AllowableClockDrift {
|
||||
return xerrors.Errorf("block was from the future (now=%d, blk=%d): %w", now, h.Timestamp, ErrTemporal)
|
||||
}
|
||||
if h.Timestamp > now {
|
||||
log.Warn("Got block from the future, but within threshold", h.Timestamp, time.Now().Unix())
|
||||
}
|
||||
|
||||
if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelay*(h.Height-baseTs.Height())) {
|
||||
if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelay*uint64(h.Height-baseTs.Height())) {
|
||||
log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), h.Height, baseTs.Height())
|
||||
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, h.Height-baseTs.Height())
|
||||
diff := (baseTs.MinTimestamp() + (build.BlockDelay * uint64(h.Height-baseTs.Height()))) - h.Timestamp
|
||||
|
||||
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d; diff %d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, h.Height-baseTs.Height(), diff)
|
||||
}
|
||||
|
||||
winnerCheck := async.Err(func() error {
|
||||
slashedAt, err := stmgr.GetMinerSlashed(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check if block miner was slashed: %w", err)
|
||||
}
|
||||
|
||||
if slashedAt != 0 {
|
||||
return xerrors.Errorf("received block was from miner slashed at height %d", slashedAt)
|
||||
}
|
||||
|
||||
mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed getting power: %w", err)
|
||||
}
|
||||
|
||||
ssize, err := stmgr.GetMinerSectorSize(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get sector size for block miner: %w", err)
|
||||
}
|
||||
|
||||
snum := types.BigDiv(mpow, types.NewInt(ssize))
|
||||
|
||||
if len(h.EPostProof.Candidates) == 0 {
|
||||
return xerrors.Errorf("no candidates")
|
||||
}
|
||||
|
||||
wins := make(map[uint64]bool)
|
||||
for _, t := range h.EPostProof.Candidates {
|
||||
if wins[t.ChallengeIndex] {
|
||||
return xerrors.Errorf("block had duplicate epost candidates")
|
||||
}
|
||||
wins[t.ChallengeIndex] = true
|
||||
|
||||
if !types.IsTicketWinner(t.Partial, ssize, snum.Uint64(), tpow) {
|
||||
return xerrors.Errorf("miner created a block but was not a winner")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
msgsCheck := async.Err(func() error {
|
||||
if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil {
|
||||
return xerrors.Errorf("block had invalid messages: %w", err)
|
||||
@ -605,11 +619,52 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
|
||||
}
|
||||
|
||||
waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, stateroot, h.Miner)
|
||||
waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, lbst, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err)
|
||||
}
|
||||
|
||||
winnerCheck := async.Err(func() error {
|
||||
rBeacon := *prevBeacon
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
rBeacon = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address to cbor: %w", err)
|
||||
}
|
||||
|
||||
//TODO: DST from spec actors when it is there
|
||||
vrfBase, err := store.DrawRandomness(rBeacon.Data, crypto.DomainSeparationTag_ElectionProofProduction, h.Height, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not draw randomness: %w", err)
|
||||
}
|
||||
|
||||
if err := gen.VerifyVRF(ctx, waddr, vrfBase, h.ElectionProof.VRFProof); err != nil {
|
||||
return xerrors.Errorf("validating block election proof failed: %w", err)
|
||||
}
|
||||
|
||||
slashed, err := stmgr.GetMinerSlashed(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check if block miner was slashed: %w", err)
|
||||
}
|
||||
|
||||
if slashed {
|
||||
return xerrors.Errorf("received block was from slashed or invalid miner")
|
||||
}
|
||||
|
||||
mpow, tpow, err := stmgr.GetPowerRaw(ctx, syncer.sm, lbst, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed getting power: %w", err)
|
||||
}
|
||||
|
||||
if !types.IsTicketWinner(h.ElectionProof.VRFProof, mpow.QualityAdjPower, tpow.QualityAdjPower) {
|
||||
return xerrors.Errorf("miner created a block but was not a winner")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
blockSigCheck := async.Err(func() error {
|
||||
if err := sigs.CheckBlockSignature(h, ctx, waddr); err != nil {
|
||||
return xerrors.Errorf("check block signature failed: %w", err)
|
||||
@ -617,19 +672,44 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
return nil
|
||||
})
|
||||
|
||||
beaconValuesCheck := async.Err(func() error {
|
||||
if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := beacon.ValidateBlockValues(syncer.beacon, h, *prevBeacon); err != nil {
|
||||
return xerrors.Errorf("failed to validate blocks random beacon values: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
tktsCheck := async.Err(func() error {
|
||||
vrfBase := baseTs.MinTicket().VRFProof
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address to cbor: %w", err)
|
||||
}
|
||||
|
||||
err := gen.VerifyVRF(ctx, waddr, h.Miner, gen.DSepTicket, vrfBase, h.Ticket.VRFProof)
|
||||
beaconBase := *prevBeacon
|
||||
if len(h.BeaconEntries) == 0 {
|
||||
buf.Write(baseTs.MinTicket().VRFProof)
|
||||
} else {
|
||||
beaconBase = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
|
||||
vrfBase, err := store.DrawRandomness(beaconBase.Data, crypto.DomainSeparationTag_TicketProduction, h.Height-build.TicketRandomnessLookback, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to compute vrf base for ticket: %w", err)
|
||||
}
|
||||
|
||||
err = gen.VerifyVRF(ctx, waddr, vrfBase, h.Ticket.VRFProof)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("validating block tickets failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
eproofCheck := async.Err(func() error {
|
||||
if err := syncer.VerifyElectionPoStProof(ctx, h, baseTs, waddr); err != nil {
|
||||
wproofCheck := async.Err(func() error {
|
||||
if err := syncer.VerifyWinningPoStProof(ctx, h, *prevBeacon, lbst, waddr); err != nil {
|
||||
return xerrors.Errorf("invalid election post: %w", err)
|
||||
}
|
||||
return nil
|
||||
@ -639,7 +719,8 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
minerCheck,
|
||||
tktsCheck,
|
||||
blockSigCheck,
|
||||
eproofCheck,
|
||||
beaconValuesCheck,
|
||||
wproofCheck,
|
||||
winnerCheck,
|
||||
msgsCheck,
|
||||
}
|
||||
@ -650,65 +731,83 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
}
|
||||
if merr != nil {
|
||||
mulErr := merr.(*multierror.Error)
|
||||
mulErr.ErrorFormat = func(es []error) string {
|
||||
if len(es) == 1 {
|
||||
return fmt.Sprintf("1 error occurred:\n\t* %+v\n\n", es[0])
|
||||
}
|
||||
|
||||
points := make([]string, len(es))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %+v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d errors occurred:\n\t%s\n\n",
|
||||
len(es), strings.Join(points, "\n\t"))
|
||||
}
|
||||
}
|
||||
|
||||
return merr
|
||||
}
|
||||
|
||||
func (syncer *Syncer) VerifyElectionPoStProof(ctx context.Context, h *types.BlockHeader, baseTs *types.TipSet, waddr address.Address) error {
|
||||
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), int64(h.Height-build.EcRandomnessLookback))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
|
||||
}
|
||||
|
||||
if err := VerifyElectionPoStVRF(ctx, h.EPostProof.PostRand, rand, waddr, h.Miner); err != nil {
|
||||
return xerrors.Errorf("checking eproof failed: %w", err)
|
||||
}
|
||||
|
||||
ssize, err := stmgr.GetMinerSectorSize(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get sector size for miner: %w", err)
|
||||
}
|
||||
|
||||
var winners []sectorbuilder.EPostCandidate
|
||||
for _, t := range h.EPostProof.Candidates {
|
||||
var partial [32]byte
|
||||
copy(partial[:], t.Partial)
|
||||
winners = append(winners, sectorbuilder.EPostCandidate{
|
||||
PartialTicket: partial,
|
||||
SectorID: t.SectorID,
|
||||
SectorChallengeIndex: t.ChallengeIndex,
|
||||
})
|
||||
}
|
||||
|
||||
if len(winners) == 0 {
|
||||
return xerrors.Errorf("no candidates")
|
||||
}
|
||||
|
||||
sectorInfo, err := stmgr.GetSectorsForElectionPost(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting election post sector set: %w", err)
|
||||
}
|
||||
|
||||
func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.BlockHeader, prevBeacon types.BeaconEntry, lbst cid.Cid, waddr address.Address) error {
|
||||
if build.InsecurePoStValidation {
|
||||
if string(h.EPostProof.Proof) == "valid proof" {
|
||||
if len(h.WinPoStProof) == 0 {
|
||||
return xerrors.Errorf("[TESTING] No winning post proof given")
|
||||
}
|
||||
|
||||
if string(h.WinPoStProof[0].ProofBytes) == "valid proof" {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("[TESTING] election post was invalid")
|
||||
return xerrors.Errorf("[TESTING] winning post was invalid")
|
||||
}
|
||||
hvrf := sha256.Sum256(h.EPostProof.PostRand)
|
||||
|
||||
ok, err := sectorbuilder.ProofVerifier.VerifyElectionPost(ctx, ssize, *sectorInfo, hvrf[:], h.EPostProof.Proof, winners, h.Miner)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address: %w", err)
|
||||
}
|
||||
|
||||
rbase := prevBeacon
|
||||
if len(h.BeaconEntries) > 0 {
|
||||
rbase = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
|
||||
rand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, h.Height, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get randomness for verifying winningPost proof: %w", err)
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get ID from miner address %s: %w", h.Miner, err)
|
||||
}
|
||||
|
||||
sectors, err := stmgr.GetSectorsForWinningPoSt(ctx, syncer.verifier, syncer.sm, lbst, h.Miner, rand)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting winning post sector set: %w", err)
|
||||
}
|
||||
|
||||
ok, err := ffiwrapper.ProofVerifier.VerifyWinningPoSt(ctx, abi.WinningPoStVerifyInfo{
|
||||
Randomness: rand,
|
||||
Proofs: h.WinPoStProof,
|
||||
ChallengedSectors: sectors,
|
||||
Prover: abi.ActorID(mid),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to verify election post: %w", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return xerrors.Errorf("election post was invalid")
|
||||
log.Errorf("invalid winning post (%x; %v)", rand, sectors)
|
||||
return xerrors.Errorf("winning post was invalid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: We should extract this somewhere else and make the message pool and miner use the same logic
|
||||
func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error {
|
||||
{
|
||||
var sigCids []cid.Cid // this is what we get for people not wanting the marshalcbor method on the cid type
|
||||
@ -731,31 +830,73 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
}
|
||||
|
||||
nonces := make(map[address.Address]uint64)
|
||||
balances := make(map[address.Address]types.BigInt)
|
||||
|
||||
stateroot, _, err := syncer.sm.TipSetState(ctx, baseTs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(syncer.store.Blockstore())
|
||||
cst := cbor.NewCborStore(syncer.store.Blockstore())
|
||||
st, err := state.LoadStateTree(cst, stateroot)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load base state tree: %w", err)
|
||||
}
|
||||
|
||||
checkMsg := func(m *types.Message) error {
|
||||
// Phase 1: syntactic validation, as defined in the spec
|
||||
|
||||
if m.Version != 0 {
|
||||
return xerrors.New("'Version' unsupported")
|
||||
}
|
||||
|
||||
if m.To == address.Undef {
|
||||
return xerrors.New("'To' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.From == address.Undef {
|
||||
return xerrors.New("'From' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.Value.LessThan(big.Zero()) {
|
||||
return xerrors.New("'Value' field cannot be negative")
|
||||
}
|
||||
|
||||
if m.Value.GreaterThan(types.TotalFilecoinInt) {
|
||||
return xerrors.New("'Value' field cannot be greater than total filecoin supply")
|
||||
}
|
||||
|
||||
if len(m.Params) != 0 && m.Method == 0 {
|
||||
return xerrors.New("'Params' field should be empty if no 'Method' is being called")
|
||||
}
|
||||
|
||||
if m.GasPrice.LessThan(big.Zero()) {
|
||||
return xerrors.New("'GasPrice' field cannot be negative")
|
||||
}
|
||||
|
||||
// TODO: This should be a thing
|
||||
//if m.GasLimit > BLOCK_GAS_LIMIT {
|
||||
// return xerrors.New("'GasLimit' field cannot be greater than a block's gas limit")
|
||||
//}
|
||||
|
||||
// since prices might vary with time, this is technically semantic validation
|
||||
if m.GasLimit < vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(m.ChainLength()) {
|
||||
return xerrors.New("'GasLimit' field cannot be less than the cost of storing a message on chain")
|
||||
}
|
||||
|
||||
// Phase 2: (Partial) semantic validation:
|
||||
// the sender exists and is an account actor, and the nonces make sense
|
||||
if _, ok := nonces[m.From]; !ok {
|
||||
// `GetActor` does not validate that this is an account actor.
|
||||
act, err := st.GetActor(m.From)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get actor: %w", err)
|
||||
}
|
||||
|
||||
// redundant check
|
||||
if !act.IsAccountActor() {
|
||||
return xerrors.New("Sender must be an account actor")
|
||||
}
|
||||
nonces[m.From] = act.Nonce
|
||||
balances[m.From] = act.Balance
|
||||
}
|
||||
|
||||
if nonces[m.From] != m.Nonce {
|
||||
@ -763,15 +904,9 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
}
|
||||
nonces[m.From]++
|
||||
|
||||
if balances[m.From].LessThan(m.RequiredFunds()) {
|
||||
return xerrors.Errorf("not enough funds for message execution")
|
||||
}
|
||||
|
||||
balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds())
|
||||
return nil
|
||||
}
|
||||
|
||||
bs := amt.WrapBlockstore(syncer.store.Blockstore())
|
||||
var blsCids []cbg.CBORMarshaler
|
||||
|
||||
for i, m := range b.BlsMessages {
|
||||
@ -789,6 +924,8 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
return xerrors.Errorf("block had invalid secpk message at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
// `From` being an account actor is only validated inside the `vm.ResolveToKeyAddr` call
|
||||
// in `StateManager.ResolveToKeyAddress` here (and not in `checkMsg`).
|
||||
kaddr, err := syncer.sm.ResolveToKeyAddress(ctx, m.Message.From, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to resolve key addr: %w", err)
|
||||
@ -802,17 +939,17 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
secpkCids = append(secpkCids, &c)
|
||||
}
|
||||
|
||||
bmroot, err := amt.FromArray(bs, blsCids)
|
||||
bmroot, err := amt.FromArray(ctx, cst, blsCids)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to build amt from bls msg cids: %w", err)
|
||||
}
|
||||
|
||||
smroot, err := amt.FromArray(bs, secpkCids)
|
||||
smroot, err := amt.FromArray(ctx, cst, secpkCids)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to build amt from bls msg cids: %w", err)
|
||||
}
|
||||
|
||||
mrcid, err := bs.Put(&types.MsgMeta{
|
||||
mrcid, err := cst.Put(ctx, &types.MsgMeta{
|
||||
BlsMessages: bmroot,
|
||||
SecpkMessages: smroot,
|
||||
})
|
||||
@ -827,7 +964,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
return nil
|
||||
}
|
||||
|
||||
func (syncer *Syncer) verifyBlsAggregate(ctx context.Context, sig types.Signature, msgs []cid.Cid, pubks []bls.PublicKey) error {
|
||||
func (syncer *Syncer) verifyBlsAggregate(ctx context.Context, sig *crypto.Signature, msgs []cid.Cid, pubks []bls.PublicKey) error {
|
||||
_, span := trace.StartSpan(ctx, "syncer.verifyBlsAggregate")
|
||||
defer span.End()
|
||||
span.AddAttributes(
|
||||
@ -877,15 +1014,45 @@ func (syncer *Syncer) collectHeaders(ctx context.Context, from *types.TipSet, to
|
||||
trace.Int64Attribute("toHeight", int64(to.Height())),
|
||||
)
|
||||
|
||||
markBad := func(fmts string, args ...interface{}) {
|
||||
for _, b := range from.Cids() {
|
||||
syncer.bad.Add(b, fmt.Sprintf(fmts, args...))
|
||||
}
|
||||
}
|
||||
|
||||
for _, pcid := range from.Parents().Cids() {
|
||||
if reason, ok := syncer.bad.Has(pcid); ok {
|
||||
for _, b := range from.Cids() {
|
||||
syncer.bad.Add(b, fmt.Sprintf("linked to %s", pcid))
|
||||
}
|
||||
markBad("linked to %s", pcid)
|
||||
return nil, xerrors.Errorf("chain linked to block marked previously as bad (%s, %s) (reason: %s)", from.Cids(), pcid, reason)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// ensure consistency of beacon entires
|
||||
targetBE := from.Blocks()[0].BeaconEntries
|
||||
sorted := sort.SliceIsSorted(targetBE, func(i, j int) bool {
|
||||
return targetBE[i].Round < targetBE[j].Round
|
||||
})
|
||||
if !sorted {
|
||||
syncer.bad.Add(from.Cids()[0], "wrong order of beacon entires")
|
||||
return nil, xerrors.Errorf("wrong order of beacon entires")
|
||||
}
|
||||
|
||||
for _, bh := range from.Blocks()[1:] {
|
||||
if len(targetBE) != len(bh.BeaconEntries) {
|
||||
// cannot mark bad, I think @Kubuxu
|
||||
return nil, xerrors.Errorf("tipset contained different number for beacon entires")
|
||||
}
|
||||
for i, be := range bh.BeaconEntries {
|
||||
if targetBE[i].Round != be.Round || !bytes.Equal(targetBE[i].Data, be.Data) {
|
||||
// cannot mark bad, I think @Kubuxu
|
||||
return nil, xerrors.Errorf("tipset contained different beacon entires")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
blockSet := []*types.TipSet{from}
|
||||
|
||||
at := from.Parents()
|
||||
@ -995,7 +1162,7 @@ loop:
|
||||
var ErrForkTooLong = fmt.Errorf("fork longer than threshold")
|
||||
|
||||
func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) {
|
||||
tips, err := syncer.Bsync.GetBlocks(ctx, from.Parents(), build.ForkLengthThreshold)
|
||||
tips, err := syncer.Bsync.GetBlocks(ctx, from.Parents(), int(build.ForkLengthThreshold))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1073,25 +1240,35 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
batchSize = i
|
||||
}
|
||||
|
||||
next := headers[i-batchSize]
|
||||
bstips, err := syncer.Bsync.GetChainMessages(ctx, next, uint64(batchSize+1))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
nextI := (i + 1) - batchSize // want to fetch batchSize values, 'i' points to last one we want to fetch, so its 'inclusive' of our request, thus we need to add one to our request start index
|
||||
|
||||
var bstout []*blocksync.BSTipSet
|
||||
for len(bstout) < batchSize {
|
||||
next := headers[nextI]
|
||||
|
||||
nreq := batchSize - len(bstout)
|
||||
bstips, err := syncer.Bsync.GetChainMessages(ctx, next, uint64(nreq))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
}
|
||||
|
||||
bstout = append(bstout, bstips...)
|
||||
nextI += len(bstips)
|
||||
}
|
||||
|
||||
for bsi := 0; bsi < len(bstips); bsi++ {
|
||||
for bsi := 0; bsi < len(bstout); bsi++ {
|
||||
// temp storage so we don't persist data we dont want to
|
||||
ds := dstore.NewMapDatastore()
|
||||
bs := bstore.NewBlockstore(ds)
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
blks := cbor.NewCborStore(bs)
|
||||
|
||||
this := headers[i-bsi]
|
||||
bstip := bstips[len(bstips)-(bsi+1)]
|
||||
bstip := bstout[len(bstout)-(bsi+1)]
|
||||
fts, err := zipTipSetAndMessages(blks, this, bstip.BlsMessages, bstip.SecpkMessages, bstip.BlsMsgIncludes, bstip.SecpkMsgIncludes)
|
||||
if err != nil {
|
||||
log.Warnw("zipping failed", "error", err, "bsi", bsi, "i", i,
|
||||
"height", this.Height(), "bstip-height", bstip.Blocks[0].Height,
|
||||
"bstips", bstips, "next-height", i+batchSize)
|
||||
"next-height", i+batchSize)
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
}
|
||||
|
||||
@ -1107,7 +1284,7 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
}
|
||||
}
|
||||
i -= windowSize
|
||||
i -= batchSize
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -1122,8 +1299,8 @@ func persistMessages(bs bstore.Blockstore, bst *blocksync.BSTipSet) error {
|
||||
}
|
||||
}
|
||||
for _, m := range bst.SecpkMessages {
|
||||
if m.Signature.Type != types.KTSecp256k1 {
|
||||
return xerrors.Errorf("unknown signature type on message %s: %q", m.Cid(), m.Signature.TypeCode)
|
||||
if m.Signature.Type != crypto.SigTypeSecp256k1 {
|
||||
return xerrors.Errorf("unknown signature type on message %s: %q", m.Cid(), m.Signature.Type)
|
||||
}
|
||||
//log.Infof("putting secp256k1 message: %s", m.Cid())
|
||||
if _, err := store.PutMessage(bs, m); err != nil {
|
||||
@ -1156,7 +1333,7 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error
|
||||
|
||||
ss.SetStage(api.StagePersistHeaders)
|
||||
|
||||
toPersist := make([]*types.BlockHeader, 0, len(headers)*build.BlocksPerEpoch)
|
||||
toPersist := make([]*types.BlockHeader, 0, len(headers)*int(build.BlocksPerEpoch))
|
||||
for _, ts := range headers {
|
||||
toPersist = append(toPersist, ts.Blocks()...)
|
||||
}
|
||||
@ -1181,8 +1358,8 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyElectionPoStVRF(ctx context.Context, evrf []byte, rand []byte, worker, miner address.Address) error {
|
||||
if err := gen.VerifyVRF(ctx, worker, miner, gen.DSepElectionPost, rand, evrf); err != nil {
|
||||
func VerifyElectionPoStVRF(ctx context.Context, evrf []byte, rand []byte, worker address.Address) error {
|
||||
if err := gen.VerifyVRF(ctx, worker, rand, evrf); err != nil {
|
||||
return xerrors.Errorf("failed to verify post_randomness vrf: %w", err)
|
||||
}
|
||||
|
||||
@ -1204,3 +1381,24 @@ func (syncer *Syncer) MarkBad(blk cid.Cid) {
|
||||
func (syncer *Syncer) CheckBadBlockCache(blk cid.Cid) (string, bool) {
|
||||
return syncer.bad.Has(blk)
|
||||
}
|
||||
func (syncer *Syncer) getLatestBeaconEntry(ctx context.Context, ts *types.TipSet) (*types.BeaconEntry, error) {
|
||||
cur := ts
|
||||
for i := 0; i < 20; i++ {
|
||||
cbe := cur.Blocks()[0].BeaconEntries
|
||||
if len(cbe) > 0 {
|
||||
return &cbe[len(cbe)-1], nil
|
||||
}
|
||||
|
||||
if cur.Height() == 0 {
|
||||
return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry")
|
||||
}
|
||||
|
||||
next, err := syncer.store.LoadTipSet(cur.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err)
|
||||
}
|
||||
cur = next
|
||||
}
|
||||
|
||||
return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset")
|
||||
}
|
||||
|
@ -13,11 +13,17 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
mocktypes "github.com/filecoin-project/lotus/chain/types/mock"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
@ -27,8 +33,10 @@ import (
|
||||
func init() {
|
||||
build.InsecurePoStValidation = true
|
||||
os.Setenv("TRUST_PARAMS", "1")
|
||||
build.SectorSizes = []uint64{1024}
|
||||
build.MinimumMinerPower = 1024
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
}
|
||||
|
||||
const source = 0
|
||||
@ -158,7 +166,6 @@ func (tu *syncTestUtil) pushTsExpectErr(to int, fts *store.FullTipSet, experr bo
|
||||
require.NoError(tu.t, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, src int, miners []int, wait, fail bool) *store.FullTipSet {
|
||||
@ -398,7 +405,7 @@ func TestSyncBadTimestamp(t *testing.T) {
|
||||
tu.waitUntilSync(0, client)
|
||||
|
||||
base := tu.g.CurTipset
|
||||
tu.g.Timestamper = func(pts *types.TipSet, tl uint64) uint64 {
|
||||
tu.g.Timestamper = func(pts *types.TipSet, tl abi.ChainEpoch) uint64 {
|
||||
return pts.MinTimestamp() + (build.BlockDelay / 2)
|
||||
}
|
||||
|
||||
@ -509,3 +516,30 @@ func runSyncBenchLength(b *testing.B, l int) {
|
||||
|
||||
tu.waitUntilSync(0, client)
|
||||
}
|
||||
|
||||
func TestSyncInputs(t *testing.T) {
|
||||
H := 10
|
||||
tu := prepSyncTest(t, H)
|
||||
|
||||
p1 := tu.addClientNode()
|
||||
|
||||
fn := tu.nds[p1].(*impl.FullNodeAPI)
|
||||
|
||||
s := fn.SyncAPI.Syncer
|
||||
|
||||
err := s.ValidateBlock(context.TODO(), &types.FullBlock{
|
||||
Header: &types.BlockHeader{},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("should error on empty block")
|
||||
}
|
||||
|
||||
h := mocktypes.MkBlock(nil, 123, 432)
|
||||
|
||||
h.ElectionProof = nil
|
||||
|
||||
err = s.ValidateBlock(context.TODO(), &types.FullBlock{Header: h})
|
||||
if err == nil {
|
||||
t.Fatal("should error on block with nil election proof")
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
@ -31,7 +33,7 @@ type SyncerState struct {
|
||||
Target *types.TipSet
|
||||
Base *types.TipSet
|
||||
Stage api.SyncStateStage
|
||||
Height uint64
|
||||
Height abi.ChainEpoch
|
||||
Message string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
@ -66,7 +68,7 @@ func (ss *SyncerState) Init(base, target *types.TipSet) {
|
||||
ss.End = time.Time{}
|
||||
}
|
||||
|
||||
func (ss *SyncerState) SetHeight(h uint64) {
|
||||
func (ss *SyncerState) SetHeight(h abi.ChainEpoch) {
|
||||
if ss == nil {
|
||||
return
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
)
|
||||
|
||||
var ErrActorNotFound = fmt.Errorf("actor not found")
|
||||
var ErrActorNotFound = init_.ErrAddressNotFound
|
||||
|
||||
type Actor struct {
|
||||
// Identifies the type of actor (string coded as a CID), see `chain/actors/actors.go`.
|
||||
Code cid.Cid
|
||||
Head cid.Cid
|
||||
Nonce uint64
|
||||
Balance BigInt
|
||||
}
|
||||
|
||||
func (a *Actor) IsAccountActor() bool {
|
||||
return a.Code == builtin.AccountActorCodeID
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user