Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b258eddc4 | ||
|
|
3bf0bd6ce9 | ||
|
|
0ed981be19 | ||
|
|
77113844a2 | ||
|
|
9da4296e21 | ||
|
|
9fc5f0cd9d | ||
|
|
2f2e7be12a | ||
|
|
9892bf4379 | ||
|
|
351a683812 | ||
|
|
445b7f3388 | ||
|
|
9e95372e5d | ||
|
|
b5660b58b3 | ||
|
|
b472ea371a | ||
|
|
6703bd2c71 | ||
|
|
36388de4e1 | ||
|
|
10159ba1bb | ||
|
|
792a2cad82 | ||
|
|
7f1b12d29e | ||
|
|
23b46a05f7 | ||
|
|
cd14a5eb40 | ||
|
|
2256e62648 | ||
|
|
6b42a78c3f | ||
|
|
6b2b22782b | ||
|
|
de52d3cadd | ||
|
|
7e43c40529 | ||
|
|
df9dfa9d0c | ||
|
|
08a2e0f82c | ||
|
|
01dc9c767e | ||
|
|
67b441838b | ||
|
|
94f2948020 | ||
|
|
f5540195de | ||
|
|
558e4c7669 | ||
|
|
c3a5da8586 | ||
|
|
02c8ab7839 | ||
|
|
8414569428 | ||
|
|
bd97eb98c5 | ||
|
|
e67fbda305 | ||
|
|
26e6bc32ee | ||
|
|
11a6bff416 | ||
|
|
e67ee227a3 | ||
|
|
f8ec1d9d22 | ||
|
|
7fb3360d6f | ||
|
|
cb35625a53 | ||
|
|
26870501a7 | ||
|
|
f5d061f34f | ||
|
|
362fbbf913 | ||
|
|
f8d5f610b6 | ||
|
|
e43174f2af | ||
|
|
16523c2ef9 | ||
|
|
d3a9006272 | ||
|
|
a404d535ee | ||
|
|
dcd01df2d3 | ||
|
|
ba5a5d1248 | ||
|
|
052b090bbf | ||
|
|
4654d9c192 | ||
|
|
1e877eae86 | ||
|
|
3c934ea21f | ||
|
|
52718e514c | ||
|
|
4310cc23ce | ||
|
|
bfb1cc6379 | ||
|
|
cbfc675f4f | ||
|
|
e7b5d678a9 | ||
|
|
effacec817 | ||
|
|
3a89388039 | ||
|
|
3366c02a36 | ||
|
|
a26cc96a49 | ||
|
|
6d9ac84359 | ||
|
|
882507e915 | ||
|
|
08e1b52281 | ||
|
|
cf153f89df | ||
|
|
d2401bc339 | ||
|
|
fd0bfc8777 | ||
|
|
7b295d7bb0 | ||
|
|
b6563ffc29 | ||
|
|
2594ff6233 | ||
|
|
807d803a81 | ||
|
|
2076cb106d | ||
|
|
5b304fa20a | ||
|
|
fbc0330fa8 | ||
|
|
f5bfaff003 | ||
|
|
eac17a8328 | ||
|
|
f9c9316990 | ||
|
|
d9f5f6e2c6 | ||
|
|
955b755055 | ||
|
|
0c6e4c6c40 | ||
|
|
57812fc61d | ||
|
|
62f05d4c14 | ||
|
|
e4de5c55ac | ||
|
|
efaf2c720b | ||
|
|
d1f419c9d1 | ||
|
|
c5ece2a892 | ||
|
|
7712d97270 | ||
|
|
8e90d5fb17 | ||
|
|
b2fcca87ff | ||
|
|
a46a9bb42e | ||
|
|
62d661f898 | ||
|
|
4436148fa1 | ||
|
|
3886e050b0 | ||
|
|
ff4b0c8f5d | ||
|
|
f82ee0a2a7 | ||
|
|
b5d603dd46 | ||
|
|
0b8ae006d3 | ||
|
|
55fd0a4e60 | ||
|
|
3c4acbc6cb | ||
|
|
8224aab851 | ||
|
|
28604f5a4c | ||
|
|
533081454a | ||
|
|
e94e64652b | ||
|
|
489ecef47b | ||
|
|
8b42ca76f8 | ||
|
|
be2d138aac | ||
|
|
1e782b3de2 | ||
|
|
8bd62165f5 | ||
|
|
ae8a9a93bf | ||
|
|
f7ab2103ce | ||
|
|
c3d5a9848b | ||
|
|
6d6d82a304 | ||
|
|
bc83c0221b | ||
|
|
c6bf252a21 | ||
|
|
f9ea6969c3 | ||
|
|
9e956ff9f4 | ||
|
|
0a45e97699 | ||
|
|
3962d4204b | ||
|
|
fd76216652 | ||
|
|
2a7dcb6e17 | ||
|
|
b02eaee0d4 | ||
|
|
6ccf24ce6c | ||
|
|
c6186a03f1 | ||
|
|
ee4f503e10 | ||
|
|
0c227e89b2 | ||
|
|
128933a655 | ||
|
|
055b215bc7 | ||
|
|
611dd77442 | ||
|
|
fb173c058e | ||
|
|
2edb32979f | ||
|
|
e3bed8d12a | ||
|
|
84de47377c | ||
|
|
06c99d0e18 | ||
|
|
0aebec6697 | ||
|
|
d4c50b8ea0 | ||
|
|
cc424d64fe | ||
|
|
48ea36d37f | ||
|
|
ffdd436b52 | ||
|
|
ec9286eb4b | ||
|
|
68f8d60323 | ||
|
|
5cabb068c0 | ||
|
|
01079b0815 | ||
|
|
7e15aa243e | ||
|
|
e9ca6c0871 | ||
|
|
604bf64ef6 | ||
|
|
925e057acb | ||
|
|
c47e96ca45 | ||
|
|
6354a8ce03 | ||
|
|
85ec972abb | ||
|
|
a8d38ad82f | ||
|
|
be6badc6a5 | ||
|
|
cf9275a2a2 | ||
|
|
66f707c243 | ||
|
|
1163e46622 | ||
|
|
08b99ff413 | ||
|
|
23472aa4f2 | ||
|
|
05edf50931 | ||
|
|
a8e39962d0 | ||
|
|
5a42bb166e | ||
|
|
3953227702 | ||
|
|
d8d8ce7526 | ||
|
|
95e754601e | ||
|
|
5e11da7316 | ||
|
|
24e36852af | ||
|
|
458acbf33d | ||
|
|
8a9707788c | ||
|
|
2fecf3a8f1 | ||
|
|
2ef8c1ae0c | ||
|
|
2fea9433d8 | ||
|
|
e06c19b51c | ||
|
|
34abc3d023 | ||
|
|
f6d41ee77d | ||
|
|
da2a11ed76 | ||
|
|
86f078dbea | ||
|
|
f739b7e377 |
Symlink
+1
@@ -0,0 +1 @@
|
||||
.gitignore
|
||||
@@ -2,6 +2,7 @@
|
||||
/lotus-storage-miner
|
||||
/lotus-seal-worker
|
||||
/lotus-seed
|
||||
/lotus-health
|
||||
/pond
|
||||
/townhall
|
||||
/fountain
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
FROM golang:1.13.4-buster
|
||||
MAINTAINER ldoublewood <ldoublewood@gmail.com>
|
||||
|
||||
ENV SRC_DIR /lotus
|
||||
|
||||
RUN apt-get update && apt-get install -y && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev
|
||||
|
||||
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
|
||||
# Get su-exec, a very minimal tool for dropping privileges,
|
||||
# and tini, a very minimal init daemon for containers
|
||||
ENV SUEXEC_VERSION v0.2
|
||||
ENV TINI_VERSION v0.18.0
|
||||
RUN set -x \
|
||||
&& cd /tmp \
|
||||
&& git clone https://github.com/ncopa/su-exec.git \
|
||||
&& cd su-exec \
|
||||
&& git checkout -q $SUEXEC_VERSION \
|
||||
&& make \
|
||||
&& cd /tmp \
|
||||
&& wget -q -O tini https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini \
|
||||
&& chmod +x tini
|
||||
|
||||
# Download packages first so they can be cached.
|
||||
COPY go.mod go.sum $SRC_DIR/
|
||||
COPY extern/ $SRC_DIR/extern/
|
||||
RUN cd $SRC_DIR \
|
||||
&& go mod download
|
||||
|
||||
COPY Makefile $SRC_DIR
|
||||
|
||||
# Because extern/filecoin-ffi building script need to get version number from git
|
||||
COPY .git/ $SRC_DIR/.git/
|
||||
COPY .gitmodules $SRC_DIR/
|
||||
|
||||
# Download dependence first
|
||||
RUN cd $SRC_DIR \
|
||||
&& mkdir $SRC_DIR/build \
|
||||
&& . $HOME/.cargo/env \
|
||||
&& make clean \
|
||||
&& make deps
|
||||
|
||||
|
||||
COPY . $SRC_DIR
|
||||
|
||||
# Build the thing.
|
||||
RUN cd $SRC_DIR \
|
||||
&& . $HOME/.cargo/env \
|
||||
&& make
|
||||
|
||||
# Now comes the actual target image, which aims to be as small as possible.
|
||||
FROM busybox:1-glibc
|
||||
MAINTAINER ldoublewood <ldoublewood@gmail.com>
|
||||
|
||||
# Get the executable binary and TLS CAs from the build container.
|
||||
ENV SRC_DIR /lotus
|
||||
COPY --from=0 $SRC_DIR/lotus /usr/local/bin/lotus
|
||||
COPY --from=0 $SRC_DIR/lotus-storage-miner /usr/local/bin/lotus-storage-miner
|
||||
COPY --from=0 /tmp/su-exec/su-exec /sbin/su-exec
|
||||
COPY --from=0 /tmp/tini /sbin/tini
|
||||
COPY --from=0 /etc/ssl/certs /etc/ssl/certs
|
||||
|
||||
|
||||
# This shared lib (part of glibc) doesn't seem to be included with busybox.
|
||||
COPY --from=0 /lib/x86_64-linux-gnu/libdl-2.28.so /lib/libdl.so.2
|
||||
COPY --from=0 /lib/x86_64-linux-gnu/libutil-2.28.so /lib/libutil.so.1
|
||||
COPY --from=0 /usr/lib/x86_64-linux-gnu/libOpenCL.so.1.0.0 /lib/libOpenCL.so.1
|
||||
COPY --from=0 /lib/x86_64-linux-gnu/librt-2.28.so /lib/librt.so.1
|
||||
COPY --from=0 /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/libgcc_s.so.1
|
||||
|
||||
# WS port
|
||||
EXPOSE 1234
|
||||
# P2P port
|
||||
EXPOSE 5678
|
||||
|
||||
|
||||
# Create the home directory and switch to a non-privileged user.
|
||||
ENV HOME_PATH /data
|
||||
ENV PARAMCACHE_PATH /var/tmp/filecoin-proof-parameters
|
||||
|
||||
RUN mkdir -p $HOME_PATH \
|
||||
&& adduser -D -h $HOME_PATH -u 1000 -G users lotus \
|
||||
&& chown lotus:users $HOME_PATH
|
||||
|
||||
|
||||
VOLUME $HOME_PATH
|
||||
VOLUME $PARAMCACHE_PATH
|
||||
|
||||
# Execute the daemon subcommand by default
|
||||
CMD ["/sbin/tini", "--", "lotus", "daemon"]
|
||||
@@ -14,7 +14,7 @@ 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))"
|
||||
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))"
|
||||
|
||||
## FFI
|
||||
|
||||
@@ -124,7 +124,7 @@ BINS+=townhall
|
||||
fountain:
|
||||
rm -f fountain
|
||||
go build -o fountain ./cmd/lotus-fountain
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain -i ./build
|
||||
.PHONY: fountain
|
||||
BINS+=fountain
|
||||
|
||||
@@ -149,6 +149,14 @@ stats:
|
||||
.PHONY: stats
|
||||
BINS+=stats
|
||||
|
||||
health:
|
||||
rm -f lotus-health
|
||||
go build -o lotus-health ./cmd/lotus-health
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec lotus-health -i ./build
|
||||
|
||||
.PHONY: health
|
||||
BINS+=health
|
||||
|
||||
# MISC
|
||||
|
||||
buildall: $(BINS)
|
||||
|
||||
+9
-1
@@ -37,6 +37,8 @@ type FullNode interface {
|
||||
ChainTipSetWeight(context.Context, *types.TipSet) (types.BigInt, error)
|
||||
ChainGetNode(ctx context.Context, p string) (interface{}, error)
|
||||
ChainGetMessage(context.Context, cid.Cid) (*types.Message, error)
|
||||
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error)
|
||||
ChainExport(context.Context, *types.TipSet) (<-chan []byte, error)
|
||||
|
||||
// syncer
|
||||
SyncState(context.Context) (*SyncState, error)
|
||||
@@ -93,7 +95,7 @@ type FullNode interface {
|
||||
//ClientListAsks() []Ask
|
||||
|
||||
// if tipset is nil, we'll use heaviest
|
||||
StateCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateCall(context.Context, *types.Message, *types.TipSet) (*MethodCall, error)
|
||||
StateReplay(context.Context, *types.TipSet, cid.Cid) (*ReplayResults, error)
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
|
||||
StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error)
|
||||
@@ -118,6 +120,7 @@ type FullNode interface {
|
||||
StateChangedActors(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateMinerSectorCount(context.Context, address.Address, *types.TipSet) (MinerSectors, error)
|
||||
StateCompute(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error)
|
||||
|
||||
MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error
|
||||
// MarketFreeBalance
|
||||
@@ -268,6 +271,11 @@ type ReplayResults struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
type MethodCall struct {
|
||||
types.MessageReceipt
|
||||
Error string
|
||||
}
|
||||
|
||||
type ActiveSync struct {
|
||||
Base *types.TipSet
|
||||
Target *types.TipSet
|
||||
|
||||
+15
-3
@@ -19,7 +19,7 @@ const (
|
||||
|
||||
Unsealed // sealing / queued
|
||||
PreCommitting // on chain pre-commit
|
||||
PreCommitted // waiting for seed
|
||||
WaitSeed // waiting for seed
|
||||
Committing
|
||||
CommitWait // waiting for message to land on chain
|
||||
Proving
|
||||
@@ -45,7 +45,7 @@ const (
|
||||
PreCommitFailed
|
||||
SealCommitFailed
|
||||
CommitFailed
|
||||
_
|
||||
PackingFailed
|
||||
_
|
||||
_
|
||||
_
|
||||
@@ -61,7 +61,7 @@ var SectorStates = []string{
|
||||
Packing: "Packing",
|
||||
Unsealed: "Unsealed",
|
||||
PreCommitting: "PreCommitting",
|
||||
PreCommitted: "PreCommitted",
|
||||
WaitSeed: "WaitSeed",
|
||||
Committing: "Committing",
|
||||
CommitWait: "CommitWait",
|
||||
Proving: "Proving",
|
||||
@@ -70,6 +70,7 @@ var SectorStates = []string{
|
||||
PreCommitFailed: "PreCommitFailed",
|
||||
SealCommitFailed: "SealCommitFailed",
|
||||
CommitFailed: "CommitFailed",
|
||||
PackingFailed: "PackingFailed",
|
||||
|
||||
FailedUnrecoverable: "FailedUnrecoverable",
|
||||
|
||||
@@ -107,6 +108,15 @@ type StorageMiner interface {
|
||||
WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error
|
||||
}
|
||||
|
||||
type SectorLog struct {
|
||||
Kind string
|
||||
Timestamp uint64
|
||||
|
||||
Trace string
|
||||
|
||||
Message string
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID uint64
|
||||
State SectorState
|
||||
@@ -119,6 +129,8 @@ type SectorInfo struct {
|
||||
Retries uint64
|
||||
|
||||
LastErr string
|
||||
|
||||
Log []SectorLog
|
||||
}
|
||||
|
||||
type SealedRef struct {
|
||||
|
||||
+32
-17
@@ -40,21 +40,23 @@ 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.TipSet) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, *types.TipSet) (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"`
|
||||
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.TipSet) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, *types.TipSet) (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.TipSet) (<-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"`
|
||||
@@ -97,7 +99,7 @@ type FullNodeStruct struct {
|
||||
StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"`
|
||||
StateMinerElectionPeriodStart func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"`
|
||||
StateMinerSectorSize func(context.Context, address.Address, *types.TipSet) (uint64, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error) `perm:"read"`
|
||||
StateReplay func(context.Context, *types.TipSet, cid.Cid) (*api.ReplayResults, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, *types.Actor, *types.TipSet) (*api.ActorState, error) `perm:"read"`
|
||||
@@ -114,6 +116,7 @@ type FullNodeStruct struct {
|
||||
StateGetReceipt func(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, *types.TipSet) (api.MinerSectors, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *types.Message, ts *types.TipSet, toht uint64) ([]cid.Cid, error) `perm:"read"`
|
||||
StateCompute func(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error) `perm:"read"`
|
||||
|
||||
MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"`
|
||||
|
||||
@@ -355,6 +358,14 @@ 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) {
|
||||
return c.Internal.ChainGetPath(ctx, from, to)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainExport(ctx context.Context, ts *types.TipSet) (<-chan []byte, error) {
|
||||
return c.Internal.ChainExport(ctx, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) SyncState(ctx context.Context) (*api.SyncState, error) {
|
||||
return c.Internal.SyncState(ctx)
|
||||
}
|
||||
@@ -399,7 +410,7 @@ func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address
|
||||
return c.Internal.StateMinerSectorSize(ctx, actor, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
|
||||
return c.Internal.StateCall(ctx, msg, ts)
|
||||
}
|
||||
|
||||
@@ -462,6 +473,10 @@ func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Mes
|
||||
return c.Internal.StateListMessages(ctx, match, ts, toht)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
|
||||
return c.Internal.StateCompute(ctx, height, msgs, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, amt)
|
||||
}
|
||||
|
||||
+155
-186
@@ -98,114 +98,95 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("PaymentInfo: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.Channel (address.Address) (struct)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Channel" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Channel", name)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.ChannelMessage (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "ChannelMessage" {
|
||||
return fmt.Errorf("expected struct map entry %s to be ChannelMessage", name)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Channel (address.Address) (struct)
|
||||
case "Channel":
|
||||
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.ChannelMessage (cid.Cid) (struct)
|
||||
case "ChannelMessage":
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
|
||||
}
|
||||
|
||||
t.ChannelMessage = &c
|
||||
}
|
||||
|
||||
}
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
case "Vouchers":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
t.ChannelMessage = &c
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
if extra > 0 {
|
||||
t.Vouchers = make([]*types.SignedVoucher, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedVoucher
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Vouchers[i] = &v
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
|
||||
}
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Vouchers" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Vouchers", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
if extra > 0 {
|
||||
t.Vouchers = make([]*types.SignedVoucher, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedVoucher
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Vouchers[i] = &v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRef) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
@@ -276,84 +257,66 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealedRef: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.SectorID (uint64) (uint64)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
switch name {
|
||||
// t.SectorID (uint64) (uint64)
|
||||
case "SectorID":
|
||||
|
||||
if name != "SectorID" {
|
||||
return fmt.Errorf("expected struct map entry %s to be SectorID", name)
|
||||
}
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = uint64(extra)
|
||||
// t.Offset (uint64) (uint64)
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
// t.Size (uint64) (uint64)
|
||||
case "Size":
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Offset" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Offset", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
// t.Size (uint64) (uint64)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Size" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Size", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Size = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
@@ -401,50 +364,56 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealedRefs: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
switch name {
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
case "Refs":
|
||||
|
||||
if name != "Refs" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Refs", name)
|
||||
}
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Refs: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Refs: array too large (%d)", extra)
|
||||
}
|
||||
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++ {
|
||||
|
||||
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
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var v SealedRef
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
t.Refs[i] = v
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
|
||||
t.Refs[i] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -65,7 +65,6 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
fmt.Println("mining a block now")
|
||||
if err := sn[0].MineOne(ctx); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
package build
|
||||
|
||||
// No forks yet \o/
|
||||
const ForkBlizzardHeight = 6288
|
||||
|
||||
const ForkFrigidHeight = 7950
|
||||
|
||||
const ForkBootyBayHeight = 11000
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
package build
|
||||
|
||||
import "os"
|
||||
|
||||
var SectorSizes = []uint64{1024}
|
||||
|
||||
// Seconds
|
||||
@@ -31,7 +29,3 @@ const InteractivePoRepConfidence = 6
|
||||
|
||||
// Bytes
|
||||
var MinimumMinerPower uint64 = 2 << 10 // 2KiB
|
||||
|
||||
func init() {
|
||||
os.Setenv("TRUST_PARAMS", "1")
|
||||
}
|
||||
|
||||
@@ -105,3 +105,4 @@ const BlsSignatureCacheSize = 40000
|
||||
// Limits
|
||||
|
||||
const BlockMessageLimit = 512
|
||||
const MinerMaxSectors = 1 << 48
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import "fmt"
|
||||
var CurrentCommit string
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.2.1"
|
||||
const BuildVersion = "0.2.6"
|
||||
|
||||
var UserVersion = BuildVersion + CurrentCommit
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams)
|
||||
|
||||
func IsBuiltinActor(code cid.Cid) bool {
|
||||
switch code {
|
||||
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
|
||||
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, StorageMiner2CodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const MaxSectors = 1 << 48
|
||||
|
||||
type StorageMinerActor struct{}
|
||||
|
||||
type StorageMinerActorState struct {
|
||||
@@ -571,7 +569,7 @@ func SectorIsUnique(ctx context.Context, s types.Storage, sroot cid.Cid, sid uin
|
||||
}
|
||||
|
||||
func AddToSectorSet(ctx context.Context, blks amt.Blocks, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) {
|
||||
if sectorID >= MaxSectors {
|
||||
if sectorID >= build.MinerMaxSectors {
|
||||
return cid.Undef, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
|
||||
}
|
||||
ssr, err := amt.LoadAMT(blks, ss)
|
||||
@@ -594,7 +592,7 @@ func AddToSectorSet(ctx context.Context, blks amt.Blocks, ss cid.Cid, sectorID u
|
||||
}
|
||||
|
||||
func GetFromSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID uint64) (bool, []byte, []byte, ActorError) {
|
||||
if sectorID >= MaxSectors {
|
||||
if sectorID >= build.MinerMaxSectors {
|
||||
return false, nil, nil, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,865 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
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: sma.GetPower, // TODO: Remove
|
||||
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 []ffi.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 := ffi.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 []sectorbuilder.EPostCandidate
|
||||
for _, t := range params.Candidates {
|
||||
var partial [32]byte
|
||||
copy(partial[:], t.Partial)
|
||||
candidates = append(candidates, sectorbuilder.EPostCandidate{
|
||||
PartialTicket: partial,
|
||||
SectorID: t.SectorID,
|
||||
SectorChallengeIndex: t.ChallengeIndex,
|
||||
})
|
||||
}
|
||||
|
||||
if ok, lerr := vmctx.Sys().VerifyFallbackPost(vmctx.Context(), mi.SectorSize,
|
||||
sectorbuilder.NewSortedPublicSectorInfo(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 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()
|
||||
|
||||
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 := self.Power.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")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-cbor-util"
|
||||
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"
|
||||
|
||||
@@ -85,7 +85,13 @@ func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VM
|
||||
return nil, aerrors.Newf(1, "not enough funds passed to cover required miner collateral (needed %s, got %s)", reqColl, vmctx.Message().Value)
|
||||
}
|
||||
|
||||
encoded, err := CreateExecParams(StorageMinerCodeCid, &StorageMinerConstructorParams{
|
||||
// FORK
|
||||
minerCid := StorageMinerCodeCid
|
||||
if vmctx.BlockHeight() > build.ForkFrigidHeight {
|
||||
minerCid = StorageMiner2CodeCid
|
||||
}
|
||||
|
||||
encoded, err := CreateExecParams(minerCid, &StorageMinerConstructorParams{
|
||||
Owner: params.Owner,
|
||||
Worker: params.Worker,
|
||||
SectorSize: params.SectorSize,
|
||||
@@ -138,6 +144,21 @@ func (spa StoragePowerActor) ArbitrateConsensusFault(act *types.Actor, vmctx typ
|
||||
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")
|
||||
|
||||
@@ -93,7 +93,15 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
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,
|
||||
@@ -145,7 +153,7 @@ func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||
|
||||
func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader {
|
||||
c := fakeCid(t, 1)
|
||||
return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
|
||||
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 {
|
||||
|
||||
@@ -12,6 +12,7 @@ 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
|
||||
@@ -46,6 +47,7 @@ func init() {
|
||||
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")
|
||||
|
||||
@@ -45,7 +45,7 @@ func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush()
|
||||
stateroot, err := st.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package actors_test
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"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"
|
||||
@@ -185,7 +186,7 @@ func NewHarness(t *testing.T, options ...HarnessOpt) *Harness {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush()
|
||||
stateroot, err := st.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@ package blocksync
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-cbor-util"
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
@@ -25,11 +25,7 @@ type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream,
|
||||
|
||||
const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(BlockSyncRequest{})
|
||||
cbor.RegisterCborType(BlockSyncResponse{})
|
||||
cbor.RegisterCborType(BSTipSet{})
|
||||
}
|
||||
const BlockSyncMaxRequestLength = 800
|
||||
|
||||
type BlockSyncService struct {
|
||||
cs *store.ChainStore
|
||||
@@ -55,8 +51,17 @@ func ParseBSOptions(optfield uint64) *BSOptions {
|
||||
}
|
||||
|
||||
const (
|
||||
BSOptBlocks = 1 << 0
|
||||
BSOptMessages = 1 << 1
|
||||
BSOptBlocks = 1 << iota
|
||||
BSOptMessages
|
||||
)
|
||||
|
||||
const (
|
||||
StatusOK = uint64(0)
|
||||
StatusPartial = uint64(101)
|
||||
StatusNotFound = uint64(201)
|
||||
StatusGoAway = uint64(202)
|
||||
StatusInternalError = uint64(203)
|
||||
StatusBadRequest = uint64(204)
|
||||
)
|
||||
|
||||
type BlockSyncResponse struct {
|
||||
@@ -93,28 +98,30 @@ func (bss *BlockSyncService) HandleStream(s inet.Stream) {
|
||||
log.Warnf("failed to read block sync request: %s", err)
|
||||
return
|
||||
}
|
||||
log.Infof("block sync request for: %s %d", req.Start, req.RequestLength)
|
||||
log.Infow("block sync request", "start", req.Start, "len", req.RequestLength)
|
||||
|
||||
resp, err := bss.processRequest(ctx, &req)
|
||||
resp, err := bss.processRequest(ctx, s.Conn().RemotePeer(), &req)
|
||||
if err != nil {
|
||||
log.Warn("failed to process block sync request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
writeDeadline := 60 * time.Second
|
||||
s.SetDeadline(time.Now().Add(writeDeadline))
|
||||
if err := cborutil.WriteCborRPC(s, resp); err != nil {
|
||||
log.Warn("failed to write back response for handle stream: ", err)
|
||||
log.Warnw("failed to write back response for handle stream", "err", err, "peer", s.Conn().RemotePeer())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
_, span := trace.StartSpan(ctx, "blocksync.ProcessRequest")
|
||||
defer span.End()
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
if len(req.Start) == 0 {
|
||||
return &BlockSyncResponse{
|
||||
Status: 204,
|
||||
Status: StatusBadRequest,
|
||||
Message: "no cids given in blocksync request",
|
||||
}, nil
|
||||
}
|
||||
@@ -122,20 +129,32 @@ func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncR
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("blocks", opts.IncludeBlocks),
|
||||
trace.BoolAttribute("messages", opts.IncludeMessages),
|
||||
trace.Int64Attribute("reqlen", int64(req.RequestLength)),
|
||||
)
|
||||
|
||||
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), req.RequestLength, opts)
|
||||
reqlen := req.RequestLength
|
||||
if reqlen > BlockSyncMaxRequestLength {
|
||||
log.Warnw("limiting blocksync request length", "orig", req.RequestLength, "peer", p)
|
||||
reqlen = BlockSyncMaxRequestLength
|
||||
}
|
||||
|
||||
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), reqlen, opts)
|
||||
if err != nil {
|
||||
log.Warn("encountered error while responding to block sync request: ", err)
|
||||
return &BlockSyncResponse{
|
||||
Status: 203,
|
||||
Status: StatusInternalError,
|
||||
Message: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
status := StatusOK
|
||||
if reqlen < req.RequestLength {
|
||||
status = StatusPartial
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{
|
||||
Chain: chain,
|
||||
Status: 0,
|
||||
Status: status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-cbor-util"
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"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/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
)
|
||||
@@ -44,15 +45,15 @@ func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermg
|
||||
|
||||
func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse) error {
|
||||
switch res.Status {
|
||||
case 101: // Partial Response
|
||||
case StatusPartial: // Partial Response
|
||||
return xerrors.Errorf("not handling partial blocksync responses yet")
|
||||
case 201: // req.Start not found
|
||||
case StatusNotFound: // req.Start not found
|
||||
return xerrors.Errorf("not found")
|
||||
case 202: // Go Away
|
||||
case StatusGoAway: // Go Away
|
||||
return xerrors.Errorf("not handling 'go away' blocksync responses yet")
|
||||
case 203: // Internal Error
|
||||
case StatusInternalError: // Internal Error
|
||||
return xerrors.Errorf("block sync peer errored: %s", res.Message)
|
||||
case 204:
|
||||
case StatusBadRequest:
|
||||
return xerrors.Errorf("block sync request invalid: %s", res.Message)
|
||||
default:
|
||||
return xerrors.Errorf("unrecognized response code: %d", res.Status)
|
||||
@@ -194,11 +195,16 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Status == 0 {
|
||||
if res.Status == StatusOK {
|
||||
bs.syncPeers.logGlobalSuccess(time.Since(start))
|
||||
return res.Chain, nil
|
||||
}
|
||||
|
||||
if res.Status == StatusPartial {
|
||||
log.Warn("dont yet handle partial responses")
|
||||
continue
|
||||
}
|
||||
|
||||
err = bs.processStatus(req, res)
|
||||
if err != nil {
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), err)
|
||||
@@ -241,16 +247,18 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
|
||||
bs.RemovePeer(p)
|
||||
return nil, xerrors.Errorf("failed to open stream to peer: %w", err)
|
||||
}
|
||||
s.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
defer s.SetDeadline(time.Time{})
|
||||
s.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, req); err != nil {
|
||||
s.SetWriteDeadline(time.Time{})
|
||||
bs.syncPeers.logFailure(p, time.Since(start))
|
||||
return nil, err
|
||||
}
|
||||
s.SetWriteDeadline(time.Time{})
|
||||
|
||||
var res BlockSyncResponse
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &res); err != nil {
|
||||
r := incrt.New(s, 50<<10, 5*time.Second)
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(r), &res); err != nil {
|
||||
bs.syncPeers.logFailure(p, time.Since(start))
|
||||
return nil, err
|
||||
}
|
||||
@@ -455,7 +463,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
|
||||
getPeerInitLat := func(p peer.ID) float64 {
|
||||
var res float64
|
||||
if bpt.pmgr != nil {
|
||||
if lat, ok := bpt.pmgr.GetPeerLatency(out[i]); ok {
|
||||
if lat, ok := bpt.pmgr.GetPeerLatency(p); ok {
|
||||
res = float64(lat)
|
||||
}
|
||||
}
|
||||
@@ -476,7 +484,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
|
||||
failRateJ := float64(pj.failures) / float64(pj.failures+pj.successes)
|
||||
costJ = float64(pj.averageTime) + failRateJ*float64(bpt.avgGlobalTime)
|
||||
} else {
|
||||
costI = getPeerInitLat(out[i])
|
||||
costJ = getPeerInitLat(out[j])
|
||||
}
|
||||
|
||||
return costI < costJ
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/ipfs/go-cid"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
+3
-3
@@ -254,7 +254,7 @@ func SetupStorageMarketActor(bs bstore.Blockstore, sroot cid.Cid, deals []actors
|
||||
return cid.Undef, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
return state.Flush()
|
||||
return state.Flush(context.TODO())
|
||||
}
|
||||
|
||||
type GenMinerCfg struct {
|
||||
@@ -563,7 +563,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, sys *types.VMSyscalls, balances map[
|
||||
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err := state.Flush()
|
||||
stateroot, err := state.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("flush state tree failed: %w", err)
|
||||
}
|
||||
@@ -673,5 +673,5 @@ func AdjustInitActorStartID(ctx context.Context, bs blockstore.Blockstore, state
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return tree.Flush()
|
||||
return tree.Flush(ctx)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func (ms *msgSet) add(m *types.SignedMessage) error {
|
||||
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")
|
||||
return xerrors.Errorf("message to %s with nonce %d already in mpool", m.Message.To, m.Message.Nonce)
|
||||
}
|
||||
}
|
||||
ms.msgs[m.Message.Nonce] = m
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -116,23 +117,29 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
|
||||
return &act, nil
|
||||
}
|
||||
|
||||
func (st *StateTree) Flush() (cid.Cid, error) {
|
||||
func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateTree.Flush")
|
||||
defer span.End()
|
||||
|
||||
for addr, act := range st.actorcache {
|
||||
if err := st.root.Set(context.TODO(), string(addr.Bytes()), act); err != nil {
|
||||
if err := st.root.Set(ctx, string(addr.Bytes()), act); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
st.actorcache = make(map[address.Address]*types.Actor)
|
||||
|
||||
if err := st.root.Flush(context.TODO()); err != nil {
|
||||
if err := st.root.Flush(ctx); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return st.Store.Put(context.TODO(), st.root)
|
||||
return st.Store.Put(ctx, st.root)
|
||||
}
|
||||
|
||||
func (st *StateTree) Snapshot() error {
|
||||
ss, err := st.Flush()
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
@@ -60,7 +61,7 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if _, err := st.Flush(); err != nil {
|
||||
if _, err := st.Flush(context.TODO()); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -88,7 +89,7 @@ func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := st.Flush(); err != nil {
|
||||
if _, err := st.Flush(context.TODO()); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -8,13 +8,14 @@ import (
|
||||
"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) (*types.MessageReceipt, error) {
|
||||
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*api.MethodCall, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw")
|
||||
defer span.End()
|
||||
|
||||
@@ -54,14 +55,19 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
|
||||
return nil, xerrors.Errorf("apply message failed: %w", err)
|
||||
}
|
||||
|
||||
var errs string
|
||||
if ret.ActorErr != nil {
|
||||
errs = ret.ActorErr.Error()
|
||||
log.Warnf("chain call failed: %s", ret.ActorErr)
|
||||
}
|
||||
return &ret.MessageReceipt, nil
|
||||
return &api.MethodCall{
|
||||
MessageReceipt: ret.MessageReceipt,
|
||||
Error: errs,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
@@ -3,14 +3,149 @@ 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/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"
|
||||
)
|
||||
|
||||
func (sm *StateManager) handleStateForks(ctx context.Context, pstate cid.Cid, height, parentH uint64) (_ cid.Cid, err error) {
|
||||
for i := parentH; i < height; i++ {
|
||||
switch i {
|
||||
case build.ForkBlizzardHeight:
|
||||
log.Warnw("Executing blizzard fork logic", "height", i)
|
||||
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("blizzard bug fix failed: %w", err)
|
||||
}
|
||||
case build.ForkFrigidHeight:
|
||||
log.Warnw("Executing frigid fork logic", "height", i)
|
||||
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("frigid bug fix failed: %w", err)
|
||||
}
|
||||
case build.ForkBootyBayHeight:
|
||||
log.Warnw("Executing booty bay fork logic", "height", i)
|
||||
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("booty bay bug fix failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pstate, nil
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
}
|
||||
|
||||
@@ -607,3 +607,32 @@ func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address,
|
||||
|
||||
return b[0], nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error {
|
||||
tschain := []*types.TipSet{ts}
|
||||
for ts.Height() != 0 {
|
||||
next, err := sm.cs.LoadTipSet(ts.Parents())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tschain = append(tschain, next)
|
||||
ts = next
|
||||
}
|
||||
|
||||
lastState := tschain[len(tschain)-1].ParentState()
|
||||
for i := len(tschain) - 1; i >= 0; i-- {
|
||||
cur := tschain[i]
|
||||
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
|
||||
if cur.ParentState() != lastState {
|
||||
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
|
||||
}
|
||||
st, _, err := sm.TipSetState(ctx, cur)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastState = st
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
|
||||
"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"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
@@ -311,3 +313,37 @@ 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) {
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
base, _, err := sm.TipSetState(ctx, ts)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
fstate, err := sm.handleStateForks(ctx, base, height, ts.Height())
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
r := store.NewChainRand(sm.cs, ts.Cids(), height)
|
||||
vmi, err := vm.NewVM(fstate, height, r, actors.NetworkAddress, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
for i, msg := range msgs {
|
||||
ret, err := vmi.ApplyMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return cid.Undef, 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)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -19,11 +21,16 @@ import (
|
||||
|
||||
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"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
pubsub "github.com/whyrusleeping/pubsub"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -730,6 +737,30 @@ 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) {
|
||||
fts, err := cs.LoadTipSet(from)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading from tipset %s: %w", from, err)
|
||||
}
|
||||
tts, err := cs.LoadTipSet(to)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading to tipset %s: %w", to, err)
|
||||
}
|
||||
revert, apply, err := cs.ReorgOps(fts, tts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error getting tipset branches: %w", err)
|
||||
}
|
||||
|
||||
path := make([]*HeadChange, len(revert)+len(apply))
|
||||
for i, r := range revert {
|
||||
path[i] = &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]}
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
blscids, secpkcids, err := cs.readMsgMetaCids(b.Messages)
|
||||
if err != nil {
|
||||
@@ -893,6 +924,84 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types
|
||||
}
|
||||
}
|
||||
|
||||
func recurseLinks(bs blockstore.Blockstore, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) {
|
||||
data, err := bs.Get(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
top, err := cbg.ScanForLinks(bytes.NewReader(data.RawData()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
in = append(in, top...)
|
||||
for _, c := range top {
|
||||
var err error
|
||||
in, err = recurseLinks(bs, c, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return in, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, w io.Writer) error {
|
||||
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
|
||||
}
|
||||
|
||||
var out []*format.Link
|
||||
for _, p := range b.Parents {
|
||||
out = append(out, &format.Link{Cid: p})
|
||||
}
|
||||
|
||||
cids, err := recurseLinks(cs.bs, b.Messages, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, c := range cids {
|
||||
out = append(out, &format.Link{Cid: c})
|
||||
}
|
||||
|
||||
if b.Height == 0 {
|
||||
cids, err := recurseLinks(cs.bs, b.ParentStateRoot, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, c := range cids {
|
||||
out = append(out, &format.Link{Cid: c})
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
|
||||
header, err := car.LoadCar(cs.Blockstore(), r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loadcar failed: %w", err)
|
||||
}
|
||||
|
||||
root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err)
|
||||
}
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
type chainRand struct {
|
||||
cs *ChainStore
|
||||
blks []cid.Cid
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil {
|
||||
|
||||
g, err := gen.NewGenerator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -2,7 +2,6 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/minio/sha256-simd"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"go.opencensus.io/trace"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -116,18 +114,6 @@ func (blk *BlockHeader) SigningBytes() ([]byte, error) {
|
||||
return blkcopy.Serialize()
|
||||
}
|
||||
|
||||
func (blk *BlockHeader) CheckBlockSignature(ctx context.Context, worker address.Address) error {
|
||||
_, span := trace.StartSpan(ctx, "checkBlockSignature")
|
||||
defer span.End()
|
||||
|
||||
sigb, err := blk.SigningBytes()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get block signing bytes: %w", err)
|
||||
}
|
||||
|
||||
return blk.BlockSig.Verify(worker, sigb)
|
||||
}
|
||||
|
||||
type MsgMeta struct {
|
||||
BlsMessages cid.Cid
|
||||
SecpkMessages cid.Cid
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//+build cgo
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (blk *BlockHeader) CheckBlockSignature(ctx context.Context, worker address.Address) error {
|
||||
_, span := trace.StartSpan(ctx, "checkBlockSignature")
|
||||
defer span.End()
|
||||
|
||||
if blk.BlockSig == nil {
|
||||
return xerrors.New("block signature not present")
|
||||
}
|
||||
|
||||
sigb, err := blk.SigningBytes()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get block signing bytes: %w", err)
|
||||
}
|
||||
|
||||
return blk.BlockSig.Verify(worker, sigb)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -116,5 +116,8 @@ func (s *Signature) UnmarshalCBOR(br io.Reader) error {
|
||||
}
|
||||
|
||||
func (s *Signature) Equals(o *Signature) bool {
|
||||
if s == nil || o == nil {
|
||||
return s == o
|
||||
}
|
||||
return s.Type == o.Type && bytes.Equal(s.Data, o.Data)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,14 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-crypto"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (s *Signature) Verify(addr address.Address, msg []byte) error {
|
||||
if s == nil {
|
||||
return xerrors.Errorf("signature is nil")
|
||||
}
|
||||
|
||||
if addr.Protocol() == address.ID {
|
||||
return fmt.Errorf("must resolve ID addresses before using them to verify a signature")
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func NewState() *StateWrapper {
|
||||
if err != nil {
|
||||
panic(err) // Never returns error, the error return should be removed.
|
||||
}
|
||||
root, err := treeImpl.Flush()
|
||||
root, err := treeImpl.Flush(context.TODO())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -208,7 +208,7 @@ func (s *StateWrapper) Signer() *keyStore {
|
||||
|
||||
// Flushes a state tree to storage and sets this state's root to that tree's root CID.
|
||||
func (s *StateWrapper) flush(tree *state.StateTree) (err error) {
|
||||
s.stateRoot, err = tree.Flush()
|
||||
s.stateRoot, err = tree.Flush(context.TODO())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ func newInvoker() *invoker {
|
||||
inv.register(actors.StoragePowerCodeCid, actors.StoragePowerActor{}, actors.StoragePowerState{})
|
||||
inv.register(actors.StorageMarketCodeCid, actors.StorageMarketActor{}, actors.StorageMarketState{})
|
||||
inv.register(actors.StorageMinerCodeCid, actors.StorageMinerActor{}, actors.StorageMinerActorState{})
|
||||
inv.register(actors.StorageMiner2CodeCid, actors.StorageMinerActor2{}, actors.StorageMinerActorState{})
|
||||
inv.register(actors.MultisigCodeCid, actors.MultiSigActor{}, actors.MultiSigActorState{})
|
||||
inv.register(actors.PaymentChannelCodeCid, actors.PaymentChannelActor{}, actors.PaymentChannelActorState{})
|
||||
|
||||
@@ -160,6 +161,10 @@ func DecodeParams(b []byte, out interface{}) error {
|
||||
}
|
||||
|
||||
func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {
|
||||
if code == actors.AccountCodeCid { // Account code special case
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
i := newInvoker() // TODO: register builtins in init block
|
||||
|
||||
typ, ok := i.builtInState[code]
|
||||
|
||||
+3
-3
@@ -430,7 +430,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
}
|
||||
|
||||
st := vm.cstate
|
||||
if err := st.Snapshot(); err != nil {
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
return nil, xerrors.Errorf("snapshot failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
|
||||
}
|
||||
if actorErr != nil {
|
||||
log.Warnf("[from=%s,to=%s,n=%d,m=%d,h=%d] Send actor error: %+v", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
|
||||
log.Warnw("Send actor error", "from", msg.From, "to", msg.To, "nonce", msg.Nonce, "method", msg.Method, "height", vm.blockHeight, "error", fmt.Sprintf("%+v", actorErr))
|
||||
}
|
||||
|
||||
var errcode uint8
|
||||
@@ -534,7 +534,7 @@ func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
|
||||
from := vm.buf
|
||||
to := vm.buf.Read()
|
||||
|
||||
root, err := vm.cstate.Flush()
|
||||
root, err := vm.cstate.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("flushing vm: %w", err)
|
||||
}
|
||||
|
||||
+117
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -26,6 +28,8 @@ var chainCmd = &cli.Command{
|
||||
chainSetHeadCmd,
|
||||
chainListCmd,
|
||||
chainGetCmd,
|
||||
chainExportCmd,
|
||||
slashConsensusFault,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -172,6 +176,10 @@ var chainGetMsgCmd = &cli.Command{
|
||||
Name: "getmessage",
|
||||
Usage: "Get and print a message by its cid",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must pass a cid of a message to get")
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -385,3 +393,112 @@ func printTipSet(format string, ts *types.TipSet) {
|
||||
|
||||
fmt.Println(format)
|
||||
}
|
||||
|
||||
var chainExportCmd = &cli.Command{
|
||||
Name: "export",
|
||||
Usage: "export chain to a car file",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "tipset",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify filename to export chain to")
|
||||
}
|
||||
|
||||
fi, err := os.Create(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
ts, err := loadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream, err := api.ChainExport(ctx, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for b := range stream {
|
||||
_, err := fi.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var slashConsensusFault = &cli.Command{
|
||||
Name: "slash-consensus",
|
||||
Usage: "Report consensus fault",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
c1, err := cid.Parse(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parsing cid 1: %w", err)
|
||||
}
|
||||
|
||||
b1, err := api.ChainGetBlock(ctx, c1)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting block 1: %w", err)
|
||||
}
|
||||
|
||||
c2, err := cid.Parse(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parsing cid 2: %w", err)
|
||||
}
|
||||
|
||||
b2, err := api.ChainGetBlock(ctx, c2)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting block 2: %w", err)
|
||||
}
|
||||
|
||||
def, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&actors.ArbitrateConsensusFaultParams{
|
||||
Block1: b1,
|
||||
Block2: b2,
|
||||
})
|
||||
|
||||
msg := &types.Message{
|
||||
To: actors.StoragePowerAddress,
|
||||
From: def,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(10000000),
|
||||
Method: actors.SPAMethods.ArbitrateConsensusFault,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(smsg.Cid())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -202,6 +202,7 @@ var Commands = []*cli.Command{
|
||||
clientCmd,
|
||||
fetchParamCmd,
|
||||
mpoolCmd,
|
||||
multisigCmd,
|
||||
netCmd,
|
||||
paychCmd,
|
||||
sendCmd,
|
||||
|
||||
+2
-1
@@ -124,7 +124,8 @@ var mpoolStat = &cli.Command{
|
||||
for a, bkt := range buckets {
|
||||
act, err := api.StateGetActor(ctx, a, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
fmt.Printf("%s, err: %s\n", a, err)
|
||||
continue
|
||||
}
|
||||
|
||||
cur := act.Nonce
|
||||
|
||||
+400
@@ -0,0 +1,400 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
var multisigCmd = &cli.Command{
|
||||
Name: "msig",
|
||||
Usage: "Interact with a multisig wallet",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "source",
|
||||
Usage: "specify the account to send propose from",
|
||||
},
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
msigCreateCmd,
|
||||
msigInspectCmd,
|
||||
msigProposeCmd,
|
||||
msigApproveCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var msigCreateCmd = &cli.Command{
|
||||
Name: "create",
|
||||
Usage: "Create a new multisig wallet",
|
||||
Flags: []cli.Flag{
|
||||
&cli.Uint64Flag{
|
||||
Name: "required",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "initial funds to give to multisig",
|
||||
Value: "0",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
var addrs []address.Address
|
||||
for _, a := range cctx.Args().Slice() {
|
||||
addr, err := address.NewFromString(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
|
||||
// get the address we're going to use to create the multisig (can be one of the above, as long as they have funds)
|
||||
sendAddr, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := cctx.String("value")
|
||||
filval, err := types.ParseFIL(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
required := cctx.Uint64("required")
|
||||
if required == 0 {
|
||||
required = uint64(len(addrs))
|
||||
}
|
||||
|
||||
// Set up constructor parameters for multisig
|
||||
msigParams := &actors.MultiSigConstructorParams{
|
||||
Signers: addrs,
|
||||
Required: required,
|
||||
}
|
||||
|
||||
enc, err := actors.SerializeParams(msigParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// new actors are created by invoking 'exec' on the init actor with the constructor params
|
||||
execParams := &actors.ExecParams{
|
||||
Code: actors.MultisigCodeCid,
|
||||
Params: enc,
|
||||
}
|
||||
|
||||
enc, err = actors.SerializeParams(execParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now we create the message to send this with
|
||||
msg := types.Message{
|
||||
To: actors.InitAddress,
|
||||
From: sendAddr,
|
||||
Method: actors.IAMethods.Exec,
|
||||
Params: enc,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
Value: types.BigInt(filval),
|
||||
}
|
||||
|
||||
// send the message out to the network
|
||||
smsg, err := api.MpoolPushMessage(ctx, &msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// wait for it to get mined into a block
|
||||
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check it executed successfully
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
fmt.Println("actor creation failed!")
|
||||
return err
|
||||
}
|
||||
|
||||
// get address of newly created miner
|
||||
msigaddr, err := address.NewFromBytes(wait.Receipt.Return)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Created new multisig: ", msigaddr.String())
|
||||
// TODO: maybe register this somewhere
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var msigInspectCmd = &cli.Command{
|
||||
Name: "inspect",
|
||||
Usage: "Inspect a multisig wallet",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify address of multisig to inspect")
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
act, err := api.StateGetActor(ctx, maddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj, err := api.ChainReadObj(ctx, act.Head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mstate actors.MultiSigActorState
|
||||
if err := mstate.UnmarshalCBOR(bytes.NewReader(obj)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Balance: %sfil\n", types.FIL(act.Balance))
|
||||
fmt.Printf("Threshold: %d / %d\n", mstate.Required, len(mstate.Signers))
|
||||
fmt.Println("Signers:")
|
||||
for _, s := range mstate.Signers {
|
||||
fmt.Printf("\t%s\n", s)
|
||||
}
|
||||
fmt.Println("Transactions: ", len(mstate.Transactions))
|
||||
if len(mstate.Transactions) > 0 {
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 4, 0, ' ', 0)
|
||||
fmt.Fprintf(w, "ID\tState\tTo\tValue\tMethod\tParams\n")
|
||||
for _, tx := range mstate.Transactions {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%d\t%x\n", tx.TxID, state(tx), tx.To, types.FIL(tx.Value), tx.Method, tx.Params)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func state(tx actors.MTransaction) string {
|
||||
if tx.Complete {
|
||||
return "done"
|
||||
}
|
||||
if tx.Canceled {
|
||||
return "canceled"
|
||||
}
|
||||
return "pending"
|
||||
}
|
||||
|
||||
var msigProposeCmd = &cli.Command{
|
||||
Name: "propose",
|
||||
Usage: "Propose a multisig transaction",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if cctx.Args().Len() < 3 {
|
||||
return fmt.Errorf("must pass multisig address, destination, and value")
|
||||
}
|
||||
|
||||
if cctx.Args().Len() > 3 && cctx.Args().Len() != 5 {
|
||||
return fmt.Errorf("usage: msig propose <msig addr> <desination> <value> [ <method> <params> ]")
|
||||
}
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := address.NewFromString(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := types.ParseFIL(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var method uint64
|
||||
var params []byte
|
||||
if cctx.Args().Len() == 5 {
|
||||
m, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
method = m
|
||||
|
||||
p, err := hex.DecodeString(cctx.Args().Get(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params = p
|
||||
}
|
||||
|
||||
enc, err := actors.SerializeParams(&actors.MultiSigProposeParams{
|
||||
To: dest,
|
||||
Value: types.BigInt(value),
|
||||
Method: method,
|
||||
Params: params,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var from address.Address
|
||||
if cctx.IsSet("source") {
|
||||
f, err := address.NewFromString(cctx.String("source"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = f
|
||||
} else {
|
||||
defaddr, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: msig,
|
||||
From: from,
|
||||
Value: types.NewInt(0),
|
||||
Method: actors.MultiSigMethods.Propose,
|
||||
Params: enc,
|
||||
GasLimit: types.NewInt(100000),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("send proposal in message: ", smsg.Cid())
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("proposal returned exit %d", wait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
_, v, err := cbg.CborReadHeader(bytes.NewReader(wait.Receipt.Return))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Transaction ID: %d\n", v)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var msigApproveCmd = &cli.Command{
|
||||
Name: "approve",
|
||||
Usage: "Approve a multisig transaction",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if cctx.Args().Len() < 2 {
|
||||
return fmt.Errorf("must pass multisig address and transaction ID")
|
||||
}
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc, err := actors.SerializeParams(&actors.MultiSigTxID{
|
||||
TxID: txid,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var from address.Address
|
||||
if cctx.IsSet("source") {
|
||||
f, err := address.NewFromString(cctx.String("source"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = f
|
||||
} else {
|
||||
defaddr, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: msig,
|
||||
From: from,
|
||||
Value: types.NewInt(0),
|
||||
Method: actors.MultiSigMethods.Approve,
|
||||
Params: enc,
|
||||
GasLimit: types.NewInt(100000),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("sent approval in message: ", smsg.Cid())
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("approve returned exit %d", wait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ var sendCmd = &cli.Command{
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "source",
|
||||
Usage: "optinally specifiy the account to send funds from",
|
||||
Usage: "optionally specify the account to send funds from",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
|
||||
+290
@@ -1,17 +1,24 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
@@ -37,6 +44,8 @@ var stateCmd = &cli.Command{
|
||||
stateSectorSizeCmd,
|
||||
stateReadStateCmd,
|
||||
stateListMessagesCmd,
|
||||
stateComputeStateCmd,
|
||||
stateCallCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -526,6 +535,10 @@ var stateListMessagesCmd = &cli.Command{
|
||||
Name: "toheight",
|
||||
Usage: "don't look before given block height",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "cids",
|
||||
Usage: "print message CIDs instead of messages",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
@@ -566,6 +579,11 @@ var stateListMessagesCmd = &cli.Command{
|
||||
}
|
||||
|
||||
for _, c := range msgs {
|
||||
if cctx.Bool("cids") {
|
||||
fmt.Println(c.String())
|
||||
continue
|
||||
}
|
||||
|
||||
m, err := api.ChainGetMessage(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -580,3 +598,275 @@ var stateListMessagesCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stateComputeStateCmd = &cli.Command{
|
||||
Name: "compute-state",
|
||||
Usage: "Perform state computations",
|
||||
Flags: []cli.Flag{
|
||||
&cli.Uint64Flag{
|
||||
Name: "height",
|
||||
Usage: "set the height to compute state at",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "apply-mpool-messages",
|
||||
Usage: "apply messages from the mempool to the computed state",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
ts, err := loadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := cctx.Uint64("height")
|
||||
if h == 0 {
|
||||
if ts == nil {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = head
|
||||
}
|
||||
h = ts.Height()
|
||||
}
|
||||
|
||||
var msgs []*types.Message
|
||||
if cctx.Bool("apply-mpool-messages") {
|
||||
pmsgs, err := api.MpoolPending(ctx, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pmsgs, err = miner.SelectMessages(ctx, api.StateGetActor, ts, pmsgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sm := range pmsgs {
|
||||
msgs = append(msgs, &sm.Message)
|
||||
}
|
||||
}
|
||||
|
||||
nstate, err := api.StateCompute(ctx, h, msgs, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("computed state cid: ", nstate)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stateCallCmd = &cli.Command{
|
||||
Name: "call",
|
||||
Usage: "Invoke a method on an actor locally",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "",
|
||||
Value: actors.NetworkAddress.String(),
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "specify value field for invocation",
|
||||
Value: "0",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ret",
|
||||
Usage: "specify how to parse output (auto, raw, addr, big)",
|
||||
Value: "auto",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() < 2 {
|
||||
return fmt.Errorf("must specify at least actor and method to invoke")
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
toa, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("given 'to' address %q was invalid: %w", cctx.Args().First(), err)
|
||||
}
|
||||
|
||||
froma, err := address.NewFromString(cctx.String("from"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("given 'from' address %q was invalid: %w", cctx.String("from"), err)
|
||||
}
|
||||
|
||||
ts, err := loadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
method, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("must pass method as a number")
|
||||
}
|
||||
|
||||
value, err := types.ParseFIL(cctx.String("value"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse 'value': %s", err)
|
||||
}
|
||||
|
||||
act, err := api.StateGetActor(ctx, toa, ts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup target actor: %s", err)
|
||||
}
|
||||
|
||||
params, err := parseParamsForMethod(act.Code, method, cctx.Args().Slice()[2:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse params: %s", err)
|
||||
}
|
||||
|
||||
ret, err := api.StateCall(ctx, &types.Message{
|
||||
From: froma,
|
||||
To: toa,
|
||||
Value: types.BigInt(value),
|
||||
GasLimit: types.NewInt(10000000000),
|
||||
GasPrice: types.NewInt(0),
|
||||
Method: method,
|
||||
Params: params,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("state call failed: %s", err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return fmt.Errorf("invocation failed (exit: %d): %s", ret.ExitCode, ret.Error)
|
||||
}
|
||||
|
||||
s, err := formatOutput(cctx.String("ret"), ret.Return)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to format output: %s", err)
|
||||
}
|
||||
|
||||
fmt.Printf("return: %s\n", s)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func formatOutput(t string, val []byte) (string, error) {
|
||||
switch t {
|
||||
case "raw", "hex":
|
||||
return fmt.Sprintf("%x", val), nil
|
||||
case "address", "addr", "a":
|
||||
a, err := address.NewFromBytes(val)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return a.String(), nil
|
||||
case "big", "int", "bigint":
|
||||
bi := types.BigFromBytes(val)
|
||||
return bi.String(), nil
|
||||
case "fil":
|
||||
bi := types.FIL(types.BigFromBytes(val))
|
||||
return bi.String(), nil
|
||||
case "pid", "peerid", "peer":
|
||||
pid, err := peer.IDFromBytes(val)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return pid.Pretty(), nil
|
||||
case "auto":
|
||||
if len(val) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
a, err := address.NewFromBytes(val)
|
||||
if err == nil {
|
||||
return "address: " + a.String(), nil
|
||||
}
|
||||
|
||||
pid, err := peer.IDFromBytes(val)
|
||||
if err == nil {
|
||||
return "peerID: " + pid.Pretty(), nil
|
||||
}
|
||||
|
||||
bi := types.BigFromBytes(val)
|
||||
return "bigint: " + bi.String(), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unrecognized output type: %q", t)
|
||||
}
|
||||
}
|
||||
|
||||
func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var f interface{}
|
||||
switch act {
|
||||
case actors.StorageMarketCodeCid:
|
||||
f = actors.StorageMarketActor{}.Exports()[method]
|
||||
case actors.StorageMinerCodeCid:
|
||||
f = actors.StorageMinerActor{}.Exports()[method]
|
||||
case actors.StoragePowerCodeCid:
|
||||
f = actors.StoragePowerActor{}.Exports()[method]
|
||||
case actors.MultisigCodeCid:
|
||||
f = actors.MultiSigActor{}.Exports()[method]
|
||||
case actors.PaymentChannelCodeCid:
|
||||
f = actors.PaymentChannelActor{}.Exports()[method]
|
||||
default:
|
||||
return nil, fmt.Errorf("the lazy devs didnt add support for that actor to this call yet")
|
||||
}
|
||||
|
||||
rf := reflect.TypeOf(f)
|
||||
if rf.NumIn() != 3 {
|
||||
return nil, fmt.Errorf("expected referenced method to have three arguments")
|
||||
}
|
||||
|
||||
paramObj := rf.In(2).Elem()
|
||||
if paramObj.NumField() != len(args) {
|
||||
return nil, fmt.Errorf("not enough arguments given to call that method (expecting %d)", paramObj.NumField())
|
||||
}
|
||||
|
||||
p := reflect.New(paramObj)
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch paramObj.Field(i).Type {
|
||||
case reflect.TypeOf(address.Address{}):
|
||||
a, err := address.NewFromString(args[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse address: %s", err)
|
||||
}
|
||||
p.Elem().Field(i).Set(reflect.ValueOf(a))
|
||||
case reflect.TypeOf(uint64(0)):
|
||||
val, err := strconv.ParseUint(args[i], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Elem().Field(i).Set(reflect.ValueOf(val))
|
||||
case reflect.TypeOf(peer.ID("")):
|
||||
pid, err := peer.IDB58Decode(args[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse peer ID: %s", err)
|
||||
}
|
||||
p.Elem().Field(i).Set(reflect.ValueOf(pid))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type for call (TODO): %s", paramObj.Field(i).Type)
|
||||
}
|
||||
}
|
||||
|
||||
m := p.Interface().(cbg.CBORMarshaler)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.MarshalCBOR(buf); err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal param object: %s", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -303,8 +303,8 @@ func main() {
|
||||
|
||||
epost2 := time.Now()
|
||||
|
||||
if !bytes.Equal(proof1, proof2) {
|
||||
log.Warn("separate epost calls returned different proof values (this might be bad)")
|
||||
if bytes.Equal(proof1, proof2) {
|
||||
log.Warn("separate epost calls returned the same proof values (this might be bad)")
|
||||
}
|
||||
|
||||
ok, err := sectorbuilder.ProofVerifier.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof1, candidates[:1], maddr)
|
||||
@@ -351,7 +351,9 @@ func main() {
|
||||
fmt.Printf("seal: preCommit: %s (%s)\n", bo.SealingResults[0].PreCommit, bps(bo.SectorSize, bo.SealingResults[0].PreCommit))
|
||||
fmt.Printf("seal: commit: %s (%s)\n", bo.SealingResults[0].Commit, bps(bo.SectorSize, bo.SealingResults[0].Commit))
|
||||
fmt.Printf("seal: verify: %s\n", bo.SealingResults[0].Verify)
|
||||
fmt.Printf("unseal: %s (%s)\n", bo.SealingResults[0].Unseal, bps(bo.SectorSize, bo.SealingResults[0].Unseal))
|
||||
if !c.Bool("skip-unseal") {
|
||||
fmt.Printf("unseal: %s (%s)\n", bo.SealingResults[0].Unseal, bps(bo.SectorSize, bo.SealingResults[0].Unseal))
|
||||
}
|
||||
}
|
||||
fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize*uint64(len(bo.SealingResults)), bo.PostGenerateCandidates))
|
||||
fmt.Printf("compute epost proof (cold): %s\n", bo.PostEProofCold)
|
||||
|
||||
+274
-23
@@ -2,16 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"golang.org/x/xerrors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/ipfs/go-cid"
|
||||
_ "github.com/lib/pq"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -75,13 +77,22 @@ create table if not exists blocks
|
||||
eprof bytea,
|
||||
prand bytea,
|
||||
ep0partial bytea,
|
||||
ep0sector bigint not null,
|
||||
ep0challangei bigint not null
|
||||
ep0sector numeric not null,
|
||||
ep0challangei numeric not null
|
||||
);
|
||||
|
||||
create unique index if not exists block_cid_uindex
|
||||
on blocks (cid);
|
||||
|
||||
create materialized view if not exists state_heights
|
||||
as select distinct height, parentstateroot from blocks;
|
||||
|
||||
create unique index if not exists state_heights_uindex
|
||||
on state_heights (height);
|
||||
|
||||
create index if not exists state_heights_height_index
|
||||
on state_heights (parentstateroot);
|
||||
|
||||
create table if not exists id_address_map
|
||||
(
|
||||
id text not null,
|
||||
@@ -117,6 +128,38 @@ create index if not exists id_address_map_address_index
|
||||
create index if not exists id_address_map_id_index
|
||||
on id_address_map (id);
|
||||
|
||||
create or replace function actor_tips(epoch bigint)
|
||||
returns table (id text,
|
||||
code text,
|
||||
head text,
|
||||
nonce int,
|
||||
balance text,
|
||||
stateroot text,
|
||||
height bigint,
|
||||
parentstateroot text) as
|
||||
$body$
|
||||
select distinct on (id) * from actors
|
||||
inner join state_heights sh on sh.parentstateroot = stateroot
|
||||
where height < $1
|
||||
order by id, height desc;
|
||||
$body$ language sql;
|
||||
|
||||
create table if not exists actor_states
|
||||
(
|
||||
head text not null,
|
||||
code text not null,
|
||||
state json not null
|
||||
);
|
||||
|
||||
create unique index if not exists actor_states_head_code_uindex
|
||||
on actor_states (head, code);
|
||||
|
||||
create index if not exists actor_states_head_index
|
||||
on actor_states (head);
|
||||
|
||||
create index if not exists actor_states_code_head_index
|
||||
on actor_states (head, code);
|
||||
|
||||
create table if not exists messages
|
||||
(
|
||||
cid text not null
|
||||
@@ -124,11 +167,11 @@ create table if not exists messages
|
||||
primary key,
|
||||
"from" text not null,
|
||||
"to" text not null,
|
||||
nonce int not null,
|
||||
nonce bigint not null,
|
||||
value text not null,
|
||||
gasprice int not null,
|
||||
gaslimit int not null,
|
||||
method int,
|
||||
gasprice bigint not null,
|
||||
gaslimit bigint not null,
|
||||
method bigint,
|
||||
params bytea
|
||||
);
|
||||
|
||||
@@ -184,7 +227,7 @@ create table if not exists miner_heads
|
||||
worker text not null,
|
||||
peerid text not null,
|
||||
sectorsize bigint not null,
|
||||
power bigint not null,
|
||||
power decimal not null,
|
||||
active bool,
|
||||
ppe bigint not null,
|
||||
slashed_at bigint not null,
|
||||
@@ -192,6 +235,73 @@ create table if not exists miner_heads
|
||||
primary key (head, addr)
|
||||
);
|
||||
|
||||
create or replace function miner_tips(epoch bigint)
|
||||
returns table (head text,
|
||||
addr text,
|
||||
stateroot text,
|
||||
sectorset text,
|
||||
setsize decimal,
|
||||
provingset text,
|
||||
provingsize decimal,
|
||||
owner text,
|
||||
worker text,
|
||||
peerid text,
|
||||
sectorsize bigint,
|
||||
power decimal,
|
||||
active bool,
|
||||
ppe bigint,
|
||||
slashed_at bigint,
|
||||
height bigint,
|
||||
parentstateroot text) as
|
||||
$body$
|
||||
select distinct on (addr) * from miner_heads
|
||||
inner join state_heights sh on sh.parentstateroot = stateroot
|
||||
where height < $1
|
||||
order by addr, height desc;
|
||||
$body$ language sql;
|
||||
|
||||
create table if not exists deals
|
||||
(
|
||||
id int not null,
|
||||
pieceRef text not null,
|
||||
pieceSize bigint not null,
|
||||
client text not null,
|
||||
provider text not null,
|
||||
expiration decimal not null,
|
||||
duration decimal not null,
|
||||
epochPrice decimal not null,
|
||||
collateral decimal not null,
|
||||
constraint deals_pk
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index if not exists deals_client_index
|
||||
on deals (client);
|
||||
|
||||
create unique index if not exists deals_id_uindex
|
||||
on deals (id);
|
||||
|
||||
create index if not exists deals_pieceRef_index
|
||||
on deals (pieceRef);
|
||||
|
||||
create index if not exists deals_provider_index
|
||||
on deals (provider);
|
||||
|
||||
create table if not exists deal_activations
|
||||
(
|
||||
deal bigint not null
|
||||
constraint deal_activations_deals_id_fk
|
||||
references deals,
|
||||
activation_epoch bigint not null,
|
||||
constraint deal_activations_pk
|
||||
primary key (deal)
|
||||
);
|
||||
|
||||
create index if not exists deal_activations_activation_epoch_index
|
||||
on deal_activations (activation_epoch);
|
||||
|
||||
create unique index if not exists deal_activations_deal_uindex
|
||||
on deal_activations (deal);
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -226,17 +336,15 @@ func (st *storage) hasList() map[cid.Cid]struct{} {
|
||||
return out
|
||||
}
|
||||
|
||||
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Cid) error {
|
||||
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]actorInfo) error {
|
||||
// Basic
|
||||
tx, err := st.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(`
|
||||
|
||||
create temp table a (like actors excluding constraints) on commit drop;
|
||||
|
||||
|
||||
`); err != nil {
|
||||
create temp table a (like actors excluding constraints) on commit drop;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("prep temp: %w", err)
|
||||
}
|
||||
|
||||
@@ -247,7 +355,7 @@ create temp table a (like actors excluding constraints) on commit drop;
|
||||
|
||||
for addr, acts := range actors {
|
||||
for act, st := range acts {
|
||||
if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.String()); err != nil {
|
||||
if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.stateroot.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -261,7 +369,47 @@ create temp table a (like actors excluding constraints) on commit drop;
|
||||
return xerrors.Errorf("actor put: %w", err)
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// States
|
||||
tx, err = st.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(`
|
||||
create temp table a (like actor_states excluding constraints) on commit drop;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("prep temp: %w", err)
|
||||
}
|
||||
|
||||
stmt, err = tx.Prepare(`copy a (head, code, state) from stdin `)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, acts := range actors {
|
||||
for act, st := range acts {
|
||||
if _, err := stmt.Exec(act.Head.String(), act.Code.String(), st.state); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into actor_states select * from a on conflict do nothing `); err != nil {
|
||||
return xerrors.Errorf("actor put: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error {
|
||||
@@ -602,11 +750,8 @@ func (st *storage) storeMpoolInclusions(msgs []api.MpoolUpdate) error {
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
|
||||
create temp table mi (like mpool_messages excluding constraints) on commit drop;
|
||||
|
||||
|
||||
`); err != nil {
|
||||
create temp table mi (like mpool_messages excluding constraints) on commit drop;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("prep temp: %w", err)
|
||||
}
|
||||
|
||||
@@ -639,6 +784,112 @@ create temp table mi (like mpool_messages excluding constraints) on commit drop;
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (st *storage) storeDeals(deals map[string]actors.OnChainDeal) error {
|
||||
tx, err := st.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
create temp table d (like deals excluding constraints) on commit drop;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("prep temp: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy d (id, pieceref, piecesize, client, "provider", expiration, duration, epochprice, collateral) from stdin `)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bloat uint64
|
||||
|
||||
for id, deal := range deals {
|
||||
if len(deal.PieceRef) > 40 {
|
||||
bloat += uint64(len(deal.PieceRef))
|
||||
continue
|
||||
}
|
||||
if _, err := stmt.Exec(
|
||||
id,
|
||||
hex.EncodeToString(deal.PieceRef),
|
||||
deal.PieceSize,
|
||||
deal.Client.String(),
|
||||
deal.Provider.String(),
|
||||
fmt.Sprint(deal.ProposalExpiration),
|
||||
fmt.Sprint(deal.Duration),
|
||||
deal.StoragePricePerEpoch.String(),
|
||||
deal.StorageCollateral.String(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bloat > 0 {
|
||||
log.Warnf("deal PieceRefs had %d bytes of garbage", bloat)
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into deals select * from d on conflict do nothing `); err != nil {
|
||||
return xerrors.Errorf("actor put: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Activations
|
||||
|
||||
tx, err = st.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
create temp table d (like deal_activations excluding constraints) on commit drop;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("prep temp: %w", err)
|
||||
}
|
||||
|
||||
stmt, err = tx.Prepare(`copy d (deal, activation_epoch) from stdin `)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for id, deal := range deals {
|
||||
if deal.ActivationEpoch == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := stmt.Exec(
|
||||
id,
|
||||
deal.ActivationEpoch,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := stmt.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into deal_activations select * from d on conflict do nothing `); err != nil {
|
||||
return xerrors.Errorf("actor put: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *storage) refreshViews() error {
|
||||
if _, err := st.db.Exec(`refresh materialized view state_heights`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *storage) close() error {
|
||||
return st.db.Close()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
@@ -60,9 +61,14 @@ type minerInfo struct {
|
||||
psize uint64
|
||||
}
|
||||
|
||||
type actorInfo struct {
|
||||
stateroot cid.Cid
|
||||
state string
|
||||
}
|
||||
|
||||
func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipSet) {
|
||||
addresses := map[address.Address]address.Address{}
|
||||
actors := map[address.Address]map[types.Actor]cid.Cid{}
|
||||
actors := map[address.Address]map[types.Actor]actorInfo{}
|
||||
var alk sync.Mutex
|
||||
|
||||
log.Infof("Getting synced block list")
|
||||
@@ -150,12 +156,26 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
ast, err := api.StateReadState(ctx, act, ts)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
state, err := json.Marshal(ast.State)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
alk.Lock()
|
||||
_, ok := actors[addr]
|
||||
if !ok {
|
||||
actors[addr] = map[types.Actor]cid.Cid{}
|
||||
actors[addr] = map[types.Actor]actorInfo{}
|
||||
}
|
||||
actors[addr][*act] = actorInfo{
|
||||
stateroot: bh.ParentStateRoot,
|
||||
state: string(state),
|
||||
}
|
||||
actors[addr][*act] = bh.ParentStateRoot
|
||||
addresses[addr] = address.Undef
|
||||
alk.Unlock()
|
||||
})
|
||||
@@ -181,13 +201,26 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
ast, err := api.StateReadState(ctx, &act, ts)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
state, err := json.Marshal(ast.State)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
alk.Lock()
|
||||
_, ok := actors[addr]
|
||||
if !ok {
|
||||
actors[addr] = map[types.Actor]cid.Cid{}
|
||||
actors[addr] = map[types.Actor]actorInfo{}
|
||||
}
|
||||
actors[addr][act] = actorInfo{
|
||||
stateroot: bh.ParentStateRoot,
|
||||
state: string(state),
|
||||
}
|
||||
actors[addr][act] = bh.ParentStateRoot
|
||||
addresses[addr] = address.Undef
|
||||
alk.Unlock()
|
||||
}
|
||||
@@ -221,14 +254,14 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
||||
|
||||
for addr, m := range actors {
|
||||
for actor, c := range m {
|
||||
if actor.Code != actors2.StorageMinerCodeCid {
|
||||
if !(actor.Code == actors2.StorageMinerCodeCid || actor.Code == actors2.StorageMiner2CodeCid) {
|
||||
continue
|
||||
}
|
||||
|
||||
miners[minerKey{
|
||||
addr: addr,
|
||||
act: actor,
|
||||
stateroot: c,
|
||||
stateroot: c.stateroot,
|
||||
}] = &minerInfo{}
|
||||
}
|
||||
}
|
||||
@@ -322,6 +355,29 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
||||
log.Infof("Sync stage done")
|
||||
}
|
||||
|
||||
log.Infof("Get deals")
|
||||
|
||||
// TODO: incremental, gather expired
|
||||
deals, err := api.StateMarketDeals(ctx, ts)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Store deals")
|
||||
|
||||
if err := st.storeDeals(deals); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Refresh views")
|
||||
|
||||
if err := st.refreshViews(); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Sync done")
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) {
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: actors.StoragePowerAddress,
|
||||
From: h.from,
|
||||
Value: collateral,
|
||||
Value: types.BigAdd(collateral, types.BigDiv(collateral, types.NewInt(100))),
|
||||
|
||||
Method: actors.SPAMethods.CreateStorageMiner,
|
||||
Params: params,
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
type CidWindow [][]cid.Cid
|
||||
|
||||
var log = logging.Logger("lotus-health")
|
||||
|
||||
func main() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
|
||||
log.Info("Starting health agent")
|
||||
|
||||
local := []*cli.Command{
|
||||
watchHeadCmd,
|
||||
}
|
||||
|
||||
app := &cli.App{
|
||||
Name: "lotus-health",
|
||||
Usage: "Tools for monitoring lotus daemon health",
|
||||
Version: build.UserVersion,
|
||||
Commands: local,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
EnvVars: []string{"LOTUS_PATH"},
|
||||
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var watchHeadCmd = &cli.Command{
|
||||
Name: "watch-head",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "threshold",
|
||||
Value: 3,
|
||||
Usage: "number of times head remains unchanged before failing health check",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "interval",
|
||||
Value: build.BlockDelay,
|
||||
Usage: "interval in seconds between chain head checks",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "systemd-unit",
|
||||
Value: "lotus-daemon.service",
|
||||
Usage: "systemd unit name to restart on health check failure",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "api-timeout",
|
||||
Value: build.BlockDelay,
|
||||
Usage: "timeout between API retries",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "api-retries",
|
||||
Value: 8,
|
||||
Usage: "number of API retry attemps",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
var headCheckWindow CidWindow
|
||||
threshold := c.Int("threshold")
|
||||
interval := time.Duration(c.Int("interval")) * time.Second
|
||||
name := c.String("systemd-unit")
|
||||
apiRetries := c.Int("api-retries")
|
||||
apiTimeout := time.Duration(c.Int("api-timeout")) * time.Second
|
||||
|
||||
nCh := make(chan interface{}, 1)
|
||||
sCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sCh, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
api, closer, err := getFullNodeAPI(c, apiRetries, apiTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(c)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
log.Info("Waiting for sync to complete")
|
||||
if err := waitForSyncComplete(ctx, api, apiRetries, apiTimeout); err != nil {
|
||||
nCh <- err
|
||||
return
|
||||
}
|
||||
headCheckWindow, err = updateWindow(ctx, api, headCheckWindow, threshold, apiRetries, apiTimeout)
|
||||
if err != nil {
|
||||
log.Warn("Failed to connect to API. Restarting systemd service")
|
||||
nCh <- nil
|
||||
return
|
||||
}
|
||||
ok := checkWindow(headCheckWindow, threshold)
|
||||
if !ok {
|
||||
log.Warn("Chain head has not updated. Restarting systemd service")
|
||||
nCh <- nil
|
||||
break
|
||||
}
|
||||
log.Info("Chain head is healthy")
|
||||
time.Sleep(interval)
|
||||
}
|
||||
return
|
||||
}()
|
||||
|
||||
restart, err := notifyHandler(name, nCh, sCh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if restart != "done" {
|
||||
return errors.New("Systemd unit failed to restart:" + restart)
|
||||
}
|
||||
log.Info("Restarting health agent")
|
||||
// Exit health agent and let supervisor restart health agent
|
||||
// Restarting lotus systemd unit kills api connection
|
||||
os.Exit(130)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* reads channel of slices of Cids
|
||||
* compares slices of Cids when len is greater or equal to `t` - threshold
|
||||
* if all slices are equal, head has not updated and returns false
|
||||
*/
|
||||
func checkWindow(window CidWindow, t int) bool {
|
||||
var dup int
|
||||
windowLen := len(window)
|
||||
if windowLen >= t {
|
||||
cidWindow:
|
||||
for i := range window {
|
||||
next := windowLen - 1 - i
|
||||
// if array length is different, head is changing
|
||||
if next >= 1 && len(window[next]) != len(window[next-1]) {
|
||||
break cidWindow
|
||||
}
|
||||
// if cids are different, head is changing
|
||||
for j := range window[next] {
|
||||
if next >= 1 && window[next][j] != window[next-1][j] {
|
||||
break cidWindow
|
||||
}
|
||||
}
|
||||
if i < (t - 1) {
|
||||
dup++
|
||||
}
|
||||
}
|
||||
|
||||
if dup == (t - 1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* returns a slice of slices of Cids
|
||||
* len of slice <= `t` - threshold
|
||||
*/
|
||||
func updateWindow(ctx context.Context, a api.FullNode, w CidWindow, t int, r int, to time.Duration) (CidWindow, error) {
|
||||
head, err := getHead(ctx, a, r, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
window := appendCIDsToWindow(w, head.Cids(), t)
|
||||
return window, err
|
||||
}
|
||||
|
||||
/*
|
||||
* get chain head from API
|
||||
* retries if API no available
|
||||
* returns tipset
|
||||
*/
|
||||
func getHead(ctx context.Context, a api.FullNode, r int, t time.Duration) (*types.TipSet, error) {
|
||||
for i := 0; i < r; i++ {
|
||||
head, err := a.ChainHead(ctx)
|
||||
if err != nil && i == (r-1) {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("Call to API failed. Retrying in %.0fs", t.Seconds())
|
||||
time.Sleep(t)
|
||||
continue
|
||||
}
|
||||
return head, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* appends slice of Cids to window slice
|
||||
* keeps a fixed window slice size, dropping older slices
|
||||
* returns new window
|
||||
*/
|
||||
func appendCIDsToWindow(w CidWindow, c []cid.Cid, t int) CidWindow {
|
||||
offset := len(w) - t + 1
|
||||
if offset >= 0 {
|
||||
return append(w[offset:], c)
|
||||
}
|
||||
return append(w, c)
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for node to sync
|
||||
*/
|
||||
func waitForSyncComplete(ctx context.Context, a api.FullNode, r int, t time.Duration) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(3 * time.Second):
|
||||
head, err := getHead(ctx, a, r, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if time.Now().Unix()-int64(head.MinTimestamp()) < build.BlockDelay {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A thin wrapper around lotus cli GetFullNodeAPI
|
||||
* Adds retry logic
|
||||
*/
|
||||
func getFullNodeAPI(ctx *cli.Context, r int, t time.Duration) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
for i := 0; i < r; i++ {
|
||||
api, closer, err := lcli.GetFullNodeAPI(ctx)
|
||||
if err != nil && i == (r-1) {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("API connection failed. Retrying in %.0fs", t.Seconds())
|
||||
time.Sleep(t)
|
||||
continue
|
||||
}
|
||||
return api, closer, err
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAppendCIDsToWindow(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var window CidWindow
|
||||
threshold := 3
|
||||
cid0 := makeCID("0")
|
||||
cid1 := makeCID("1")
|
||||
cid2 := makeCID("2")
|
||||
cid3 := makeCID("3")
|
||||
window = appendCIDsToWindow(window, []cid.Cid{cid0}, threshold)
|
||||
window = appendCIDsToWindow(window, []cid.Cid{cid1}, threshold)
|
||||
window = appendCIDsToWindow(window, []cid.Cid{cid2}, threshold)
|
||||
window = appendCIDsToWindow(window, []cid.Cid{cid3}, threshold)
|
||||
assert.Len(window, 3)
|
||||
assert.Equal(window[0][0], cid1)
|
||||
assert.Equal(window[1][0], cid2)
|
||||
assert.Equal(window[2][0], cid3)
|
||||
}
|
||||
|
||||
func TestCheckWindow(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
threshold := 3
|
||||
|
||||
var healthyHeadCheckWindow CidWindow
|
||||
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
ok := checkWindow(healthyHeadCheckWindow, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var healthyHeadCheckWindow1 CidWindow
|
||||
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
ok = checkWindow(healthyHeadCheckWindow1, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var healthyHeadCheckWindow2 CidWindow
|
||||
healthyHeadCheckWindow2 = appendCIDsToWindow(healthyHeadCheckWindow2, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow2 = appendCIDsToWindow(healthyHeadCheckWindow2, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
ok = checkWindow(healthyHeadCheckWindow2, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var healthyHeadCheckWindow3 CidWindow
|
||||
healthyHeadCheckWindow3 = appendCIDsToWindow(healthyHeadCheckWindow3, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
healthyHeadCheckWindow3 = appendCIDsToWindow(healthyHeadCheckWindow3, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
ok = checkWindow(healthyHeadCheckWindow3, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var healthyHeadCheckWindow4 CidWindow
|
||||
healthyHeadCheckWindow4 = appendCIDsToWindow(healthyHeadCheckWindow4, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, threshold)
|
||||
ok = checkWindow(healthyHeadCheckWindow4, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var healthyHeadCheckWindow5 CidWindow
|
||||
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
makeCID("bbff"),
|
||||
}, 5)
|
||||
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
|
||||
makeCID("bbcd"),
|
||||
makeCID("bbfe"),
|
||||
}, 5)
|
||||
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, 5)
|
||||
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
|
||||
makeCID("cbcd"),
|
||||
makeCID("cbfe"),
|
||||
}, 5)
|
||||
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
|
||||
makeCID("cbcd"),
|
||||
makeCID("cbfe"),
|
||||
}, 5)
|
||||
ok = checkWindow(healthyHeadCheckWindow5, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var unhealthyHeadCheckWindow CidWindow
|
||||
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
makeCID("fbcd"),
|
||||
}, threshold)
|
||||
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
makeCID("fbcd"),
|
||||
}, threshold)
|
||||
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
makeCID("fbcd"),
|
||||
}, threshold)
|
||||
ok = checkWindow(unhealthyHeadCheckWindow, threshold)
|
||||
assert.False(ok)
|
||||
|
||||
var unhealthyHeadCheckWindow1 CidWindow
|
||||
unhealthyHeadCheckWindow1 = appendCIDsToWindow(unhealthyHeadCheckWindow1, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
makeCID("fbcd"),
|
||||
}, threshold)
|
||||
unhealthyHeadCheckWindow1 = appendCIDsToWindow(unhealthyHeadCheckWindow1, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
makeCID("fbcd"),
|
||||
}, threshold)
|
||||
ok = checkWindow(unhealthyHeadCheckWindow1, threshold)
|
||||
assert.True(ok)
|
||||
|
||||
var unhealthyHeadCheckWindow2 CidWindow
|
||||
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
|
||||
makeCID("abcd"),
|
||||
}, threshold)
|
||||
ok = checkWindow(unhealthyHeadCheckWindow2, threshold)
|
||||
assert.False(ok)
|
||||
}
|
||||
|
||||
func makeCID(s string) cid.Cid {
|
||||
h1, err := mh.Sum([]byte(s), mh.SHA2_256, -1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return cid.NewCidV1(0x55, h1)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/coreos/go-systemd/dbus"
|
||||
)
|
||||
|
||||
func notifyHandler(n string, ch chan interface{}, sCh chan os.Signal) (string, error) {
|
||||
select {
|
||||
// alerts to restart systemd unit
|
||||
case <-ch:
|
||||
statusCh := make(chan string, 1)
|
||||
c, err := dbus.New()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = c.TryRestartUnit(n, "fail", statusCh)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
select {
|
||||
case result := <-statusCh:
|
||||
return result, nil
|
||||
}
|
||||
// SIGTERM
|
||||
case <-sCh:
|
||||
os.Exit(1)
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,10 @@ func (w *worker) processTask(ctx context.Context, task sectorbuilder.WorkerTask)
|
||||
if err := w.push("cache", task.SectorID); err != nil {
|
||||
return errRes(xerrors.Errorf("pushing precommited data: %w", err))
|
||||
}
|
||||
|
||||
if err := w.remove("staging", task.SectorID); err != nil {
|
||||
return errRes(xerrors.Errorf("cleaning up staged sector: %w", err))
|
||||
}
|
||||
case sectorbuilder.WorkerCommit:
|
||||
proof, err := w.sb.SealCommit(ctx, task.SectorID, task.SealTicket, task.SealSeed, task.Pieces, task.Rspco)
|
||||
if err != nil {
|
||||
@@ -127,6 +131,10 @@ func (w *worker) processTask(ctx context.Context, task sectorbuilder.WorkerTask)
|
||||
if err := w.push("cache", task.SectorID); err != nil {
|
||||
return errRes(xerrors.Errorf("pushing precommited data: %w", err))
|
||||
}
|
||||
|
||||
if err := w.remove("sealed", task.SectorID); err != nil {
|
||||
return errRes(xerrors.Errorf("cleaning up sealed sector: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
@@ -15,6 +15,14 @@ import (
|
||||
"github.com/filecoin-project/lotus/lib/tarutil"
|
||||
)
|
||||
|
||||
func (w *worker) sizeForType(typ string) int64 {
|
||||
size := int64(w.sb.SectorSize())
|
||||
if typ == "cache" {
|
||||
size *= 10
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (w *worker) fetch(typ string, sectorID uint64) error {
|
||||
outname := filepath.Join(w.repo, typ, w.sb.SectorName(sectorID))
|
||||
|
||||
@@ -37,7 +45,7 @@ func (w *worker) fetch(typ string, sectorID uint64) error {
|
||||
return xerrors.Errorf("non-200 code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
bar := pb.New64(resp.ContentLength)
|
||||
bar := pb.New64(w.sizeForType(typ))
|
||||
bar.ShowPercent = true
|
||||
bar.ShowSpeed = true
|
||||
bar.Units = pb.U_BYTES
|
||||
@@ -88,7 +96,7 @@ func (w *worker) push(typ string, sectorID uint64) error {
|
||||
return xerrors.Errorf("opening push reader: %w", err)
|
||||
}
|
||||
|
||||
bar := pb.New64(0)
|
||||
bar := pb.New64(w.sizeForType(typ))
|
||||
bar.ShowPercent = true
|
||||
bar.ShowSpeed = true
|
||||
bar.ShowCounters = true
|
||||
@@ -125,6 +133,11 @@ func (w *worker) push(typ string, sectorID uint64) error {
|
||||
}
|
||||
|
||||
// TODO: keep files around for later stages of sealing
|
||||
return w.remove(typ, sectorID)
|
||||
}
|
||||
|
||||
func (w *worker) remove(typ string, sectorID uint64) error {
|
||||
filename := filepath.Join(w.repo, typ, w.sb.SectorName(sectorID))
|
||||
return os.RemoveAll(filename)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,12 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
paramfetch "github.com/filecoin-project/go-paramfetch"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
badger "github.com/ipfs/go-ds-badger2"
|
||||
@@ -21,11 +26,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sealing"
|
||||
)
|
||||
|
||||
var initCmd = &cli.Command{
|
||||
@@ -242,17 +243,17 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, presealDir strin
|
||||
}
|
||||
|
||||
for _, sector := range meta.Sectors {
|
||||
sectorKey := datastore.NewKey(storage.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID))
|
||||
sectorKey := datastore.NewKey(sealing.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID))
|
||||
|
||||
dealID, err := findMarketDealID(ctx, api, sector.Deal)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("finding storage deal for pre-sealed sector %d: %w", sector.SectorID, err)
|
||||
}
|
||||
|
||||
info := &storage.SectorInfo{
|
||||
info := &sealing.SectorInfo{
|
||||
State: lapi.Proving,
|
||||
SectorID: sector.SectorID,
|
||||
Pieces: []storage.Piece{
|
||||
Pieces: []sealing.Piece{
|
||||
{
|
||||
DealID: dealID,
|
||||
Size: meta.SectorSize,
|
||||
@@ -262,9 +263,9 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, presealDir strin
|
||||
CommD: sector.CommD[:],
|
||||
CommR: sector.CommR[:],
|
||||
Proof: nil,
|
||||
Ticket: storage.SealTicket{},
|
||||
Ticket: sealing.SealTicket{},
|
||||
PreCommitMessage: nil,
|
||||
Seed: storage.SealSeed{},
|
||||
Seed: sealing.SealSeed{},
|
||||
CommitMessage: nil,
|
||||
}
|
||||
|
||||
@@ -556,7 +557,7 @@ func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID,
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: actors.StoragePowerAddress,
|
||||
From: owner,
|
||||
Value: collateral,
|
||||
Value: types.BigAdd(collateral, types.BigDiv(collateral, types.NewInt(100))),
|
||||
|
||||
Method: actors.SPAMethods.CreateStorageMiner,
|
||||
Params: params,
|
||||
|
||||
@@ -2,8 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
@@ -41,6 +44,12 @@ var sectorsCmd = &cli.Command{
|
||||
var sectorsStatusCmd = &cli.Command{
|
||||
Name: "status",
|
||||
Usage: "Get the seal status of a sector by its ID",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "log",
|
||||
Usage: "display event log",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
@@ -77,6 +86,17 @@ var sectorsStatusCmd = &cli.Command{
|
||||
if status.LastErr != "" {
|
||||
fmt.Printf("Last Error:\t\t%s\n", status.LastErr)
|
||||
}
|
||||
|
||||
if cctx.Bool("log") {
|
||||
fmt.Printf("--------\nEvent Log:\n")
|
||||
|
||||
for i, l := range status.Log {
|
||||
fmt.Printf("%d.\t%s:\t[%s]\t%s\n", i, time.Unix(int64(l.Timestamp), 0), l.Kind, l.Message)
|
||||
if l.Trace != "" {
|
||||
fmt.Printf("\t%s\n", l.Trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -131,17 +151,19 @@ var sectorsListCmd = &cli.Command{
|
||||
return list[i] < list[j]
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 4, 1, ' ', 0)
|
||||
|
||||
for _, s := range list {
|
||||
st, err := nodeApi.SectorsStatus(ctx, s)
|
||||
if err != nil {
|
||||
fmt.Printf("%d:\tError: %s\n", s, err)
|
||||
fmt.Fprintf(w, "%d:\tError: %s\n", s, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, inSSet := commitedIDs[s]
|
||||
_, inPSet := provingIDs[s]
|
||||
|
||||
fmt.Printf("%d: %s\tsSet: %s\tpSet: %s\ttktH: %d\tseedH: %d\tdeals: %v\n",
|
||||
fmt.Fprintf(w, "%d: %s\tsSet: %s\tpSet: %s\ttktH: %d\tseedH: %d\tdeals: %v\n",
|
||||
s,
|
||||
api.SectorStates[st.State],
|
||||
yesno(inSSet),
|
||||
@@ -151,7 +173,8 @@ var sectorsListCmd = &cli.Command{
|
||||
st.Deals,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
|
||||
return w.Flush()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,21 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
paramfetch "github.com/filecoin-project/go-paramfetch"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"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/vm"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
"github.com/filecoin-project/lotus/node/modules/testing"
|
||||
@@ -56,8 +63,32 @@ var DaemonCmd = &cli.Command{
|
||||
Name: "bootstrap",
|
||||
Value: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "import-chain",
|
||||
Usage: "on first run, load chain from given file",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "halt-after-import",
|
||||
Usage: "halt the process after importing chain from file",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pprof",
|
||||
Usage: "specify name of file for writing cpu profile to",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if prof := cctx.String("pprof"); prof != "" {
|
||||
profile, err := os.Create(prof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pprof.StartCPUProfile(profile); err != nil {
|
||||
return err
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
r, err := repo.NewFS(cctx.String("repo"))
|
||||
if err != nil {
|
||||
@@ -81,6 +112,16 @@ var DaemonCmd = &cli.Command{
|
||||
}
|
||||
}
|
||||
|
||||
chainfile := cctx.String("import-chain")
|
||||
if chainfile != "" {
|
||||
if err := ImportChain(r, chainfile); err != nil {
|
||||
return err
|
||||
}
|
||||
if cctx.Bool("halt-after-import") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
genesis := node.Options()
|
||||
if len(genBytes) > 0 {
|
||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genBytes))
|
||||
@@ -129,3 +170,50 @@ var DaemonCmd = &cli.Command{
|
||||
return serveRPC(api, stop, endpoint)
|
||||
},
|
||||
}
|
||||
|
||||
func ImportChain(r repo.Repo, fname string) error {
|
||||
fi, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lr, err := r.Lock(repo.FullNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lr.Close()
|
||||
|
||||
ds, err := lr.Datastore("/blocks")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mds, err := lr.Datastore("/metadata")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bs := blockstore.NewBlockstore(ds)
|
||||
|
||||
cst := store.NewChainStore(bs, mds, vm.Syscalls(sectorbuilder.ProofVerifier))
|
||||
|
||||
log.Info("importing chain from file...")
|
||||
ts, err := cst.Import(fi)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("importing chain failed: %w", err)
|
||||
}
|
||||
|
||||
stm := stmgr.NewStateManager(cst)
|
||||
|
||||
log.Infof("validating imported chain...")
|
||||
if err := stm.ValidateChain(context.TODO(), ts); err != nil {
|
||||
return xerrors.Errorf("chain validation failed: %w", err)
|
||||
}
|
||||
|
||||
log.Info("accepting %s as new head", ts.Cids())
|
||||
if err := cst.SetHead(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Vendored
+1
-1
Submodule extern/filecoin-ffi updated: f261762ff8...58761d3dca
+17
-6
@@ -10,8 +10,9 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/blocksync"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
"github.com/filecoin-project/lotus/paych"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sealing"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -121,14 +122,24 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteMapEncodersToFile("./storage/cbor_gen.go", "storage",
|
||||
storage.SealTicket{},
|
||||
storage.SealSeed{},
|
||||
storage.Piece{},
|
||||
storage.SectorInfo{},
|
||||
err = gen.WriteMapEncodersToFile("./storage/sealing/cbor_gen.go", "sealing",
|
||||
sealing.SealTicket{},
|
||||
sealing.SealSeed{},
|
||||
sealing.Piece{},
|
||||
sealing.SectorInfo{},
|
||||
sealing.Log{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteMapEncodersToFile("./lib/statemachine/cbor_gen.go", "statemachine",
|
||||
statemachine.TestState{},
|
||||
statemachine.TestEvent{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,19 @@ require (
|
||||
github.com/GeertJohan/go.rice v1.0.0
|
||||
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-00010101000000-000000000000
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/filecoin-project/chain-validation v0.0.3
|
||||
github.com/filecoin-project/filecoin-ffi v0.0.0-20191219131535-bb699517a590
|
||||
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f
|
||||
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc
|
||||
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0
|
||||
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
|
||||
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce
|
||||
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8
|
||||
github.com/filecoin-project/go-paramfetch v0.0.1
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55
|
||||
github.com/filecoin-project/go-statestore v0.1.0
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
@@ -30,10 +32,10 @@ require (
|
||||
github.com/ipfs/go-bitswap v0.1.8
|
||||
github.com/ipfs/go-block-format v0.0.2
|
||||
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c
|
||||
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1
|
||||
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291
|
||||
github.com/ipfs/go-cid v0.0.4
|
||||
github.com/ipfs/go-datastore v0.3.1
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d
|
||||
github.com/ipfs/go-filestore v0.0.2
|
||||
github.com/ipfs/go-fs-lock v0.0.1
|
||||
github.com/ipfs/go-graphsync v0.0.4
|
||||
@@ -84,7 +86,7 @@ require (
|
||||
github.com/prometheus/common v0.2.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8
|
||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
|
||||
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
|
||||
go.opencensus.io v0.22.2
|
||||
@@ -109,3 +111,5 @@ require (
|
||||
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
|
||||
|
||||
replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi
|
||||
|
||||
replace github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||
|
||||
@@ -3,14 +3,12 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.1.0 h1:WNc9HbA38xEQmsI40Tjd/MNU/g8byN2Of7lwIjv0Jdc=
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.1.0/go.mod h1:VYianECmuFPwU37O699Vc1GOcy+y8kOsfaxHRImmjbA=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
@@ -61,6 +59,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
@@ -77,10 +77,10 @@ github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzA
|
||||
github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
||||
github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo=
|
||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
||||
github.com/dgraph-io/badger/v2 v2.0.1 h1:+D6dhIqC6jIeCclnxMHqk4HPuXgrRN5UfBsLR4dNQ3A=
|
||||
github.com/dgraph-io/badger/v2 v2.0.1/go.mod h1:YoRSIp1LmAJ7zH7tZwRvjNMUYLxB4wl3ebYkaIruZ04=
|
||||
github.com/dgraph-io/ristretto v0.0.0-20191025175511-c1f00be0418e h1:aeUNgwup7PnDOBAD1BOKAqzb/W/NksOj6r3dwKKuqfg=
|
||||
github.com/dgraph-io/ristretto v0.0.0-20191025175511-c1f00be0418e/go.mod h1:edzKIzGvqUCMzhTVWbiTSe75zD9Xxq0GtSBtFmaUTZs=
|
||||
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200120142413-c3333a5a830e h1:Jz7uYxTCDVrtL5tzPxPu6o7Ybhom8Az7sWmjUO1OkQc=
|
||||
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200120142413-c3333a5a830e/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM=
|
||||
github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU=
|
||||
github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
@@ -91,7 +91,6 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.8.0 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY=
|
||||
github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8=
|
||||
@@ -100,35 +99,26 @@ github.com/filecoin-project/chain-validation v0.0.3 h1:luT/8kJ0WdMIqQ9Bm31W4JkuY
|
||||
github.com/filecoin-project/chain-validation v0.0.3/go.mod h1:NCEGFjcWRjb8akWFSOXvU6n2efkWIqAeOKU6o5WBGQw=
|
||||
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f h1:L2jaVU8TvWTx7iZPhlYvUE8vkoOnj778XuKavz8W36g=
|
||||
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f/go.mod h1:rCbpXPva2NKF9/J4X6sr7hbKBgQCxyFtRj7KOZqoIms=
|
||||
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5 h1:/MmWluswvDIbuPvBct4q6HeQgVm62O2DzWYTB38kt4A=
|
||||
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc h1:cODZD2YzpTUtrOSxbEnWFcQHidNRZiRdvLxySjGvG/M=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc/go.mod h1:KsFPWjF+UUYl6n9A+qbg4bjFgAOneicFZtDH/LQEX2U=
|
||||
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0 h1:rQ7GTJbWE5XqUCPiBgs4gbTKD2wC7MynZhSaf7ZzlUM=
|
||||
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0/go.mod h1:PAZ5tvSfMfWE327osqFXKm7cBpCpBk2Nh0qKsJUmjjk=
|
||||
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8=
|
||||
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce h1:Jdejrx6XVSTRy2PiX08HCU5y68p3wx2hNMJJc/J7kZY=
|
||||
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce/go.mod h1:b14UWxhxVCAjrQUYvVGrQRRsjAh79wXYejw9RbUcAww=
|
||||
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8 h1:g3oodvSz+Ou+ObwcVBB2wyt8SHdWpwzMiNJ19U1zZNA=
|
||||
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8/go.mod h1:c8NTjvFVy1Ud02mmGDjOiMeawY2t6ALfrrdvAB01FQc=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.1 h1:gV7bs5YaqlgpGFMiLxInGK2L1FyCXUE0rimz4L7ghoE=
|
||||
github.com/filecoin-project/go-paramfetch v0.0.1/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.1 h1:yiLSEprWA1E43DFTSCXLSuCstYuDKiI6RCXiYz4GaRs=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.1/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689 h1:2cT5bhm/5I0RY+HBIPdRRrtjCwLj33Qx6DHRs9TCslY=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55 h1:XChPRKPZL+/N6a3ccLmjCJ7JrR+SFLFJDllv0BkxW4I=
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55/go.mod h1:ahsryULdwYoZ94K09HcfqX3QBwevWVldENSV/EdCbNg=
|
||||
github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIiWBRilQjQ+5IiwdQ=
|
||||
github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
@@ -142,6 +132,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
@@ -219,8 +211,9 @@ github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbR
|
||||
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
|
||||
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c h1:lN5IQA07VtLiTLAp/Scezp1ljFhXErC6yq4O1cu+yJ0=
|
||||
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I=
|
||||
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1 h1:Nq8xEW+2KZq7IkRlkOh0rTEUI8FgunhMoLj5EMkJzbQ=
|
||||
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1/go.mod h1:rmd887mJxQRDfndfDEY3Liyx8gQVyfFFRSHdsnDSAlk=
|
||||
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291 h1:Yy0dcFWw8oDV/WJ4S/rkMQRWnJ3tGr9EbgDDv2JhVQw=
|
||||
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291/go.mod h1:AG6sBpd2PWMccpAG7XLFBBQ/4rfBEtzUNeO2GSMesYk=
|
||||
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||
@@ -230,7 +223,6 @@ github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj
|
||||
github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
|
||||
github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
|
||||
github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
|
||||
github.com/ipfs/go-datastore v0.1.1 h1:F4k0TkTAZGLFzBOrVKDAvch6JZtuN4NHkfdcEZL50aI=
|
||||
github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
|
||||
github.com/ipfs/go-datastore v0.3.1 h1:SS1t869a6cctoSYmZXUk8eL6AzVXgASmKIWFNQkQ1jU=
|
||||
github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
|
||||
@@ -238,10 +230,9 @@ github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ
|
||||
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
|
||||
github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
|
||||
github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
|
||||
github.com/ipfs/go-ds-badger v0.0.7 h1:NMyh88Q50HG6/S2YD58DLkq0c0/ZQPMbSojONH+PRf4=
|
||||
github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk=
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521 h1:GSygNXXIRXFVntMR3Yr30imf40H9cYDAw72Qb0za2nE=
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521/go.mod h1:oDKWqmQbLyyml3mgMtHgB7qAf6cQOEsJPRLFzfwKQ5Q=
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d h1:/Pn9fMp6ih2M/3vDeA2H+8mkTFQSpLxFuIM1HJD3Zlw=
|
||||
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d/go.mod h1:sTQFaWUoW0OvhXzfHnQ9j39L6fdlqDkptDYcpC1XrYE=
|
||||
github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=
|
||||
github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=
|
||||
github.com/ipfs/go-filestore v0.0.2 h1:pcYwpjtXXwirtbjBXKVJM9CTa9F7/8v1EkfnDaHTO3s=
|
||||
@@ -289,15 +280,9 @@ github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dC
|
||||
github.com/ipfs/go-ipld-format v0.0.2 h1:OVAGlyYT6JPZ0pEfGntFPS40lfrDmaDbQwNHEY2G9Zs=
|
||||
github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k=
|
||||
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
|
||||
github.com/ipfs/go-log v1.0.0 h1:BW3LQIiZzpNyolt84yvKNCd3FU+AK4VDw1hnHR+1aiI=
|
||||
github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA=
|
||||
github.com/ipfs/go-log v1.0.1 h1:5lIEEOQTk/vd1WuPFBRqz2mcp+5G1fMVcW+Ib/H5Hfo=
|
||||
github.com/ipfs/go-log v1.0.1 h1:5lIEEOQTk/vd1WuPFBRqz2mcp+5G1fMVcW+Ib/H5Hfo=
|
||||
github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
|
||||
github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
|
||||
github.com/ipfs/go-log/v2 v2.0.1 h1:mnR9XFltezAtO8A6tj5U7nKkRzhEQNEw/wT11U2HhPM=
|
||||
github.com/ipfs/go-log/v2 v2.0.1 h1:mnR9XFltezAtO8A6tj5U7nKkRzhEQNEw/wT11U2HhPM=
|
||||
github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
|
||||
github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
|
||||
github.com/ipfs/go-log/v2 v2.0.2 h1:xguurydRdfKMJjKyxNXNU8lYP0VZH1NUwJRwUorjuEw=
|
||||
github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
|
||||
@@ -320,6 +305,7 @@ github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb h1:tmWYgjltxwM7PD
|
||||
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
|
||||
github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
|
||||
github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
|
||||
github.com/ipld/go-ipld-prime v0.0.1/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
|
||||
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785 h1:fASnkvtR+SmB2y453RxmDD3Uvd4LonVUgFGk9JoDaZs=
|
||||
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
|
||||
github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5 h1:lSip43rAdyGA+yRQuy6ju0ucZkWpYc1F2CTQtZTVW/4=
|
||||
@@ -457,7 +443,6 @@ github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4T
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.2/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.3 h1:wMgajt1uM2tMiqf4M+4qWKVyyFc8SfA+84VV9glZq1M=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.4 h1:d23fvq5oYMJ/lkkbO4oTwBp/JP+I/1m5gZJobNXCE/k=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs=
|
||||
@@ -479,7 +464,6 @@ github.com/libp2p/go-libp2p-routing-helpers v0.1.0/go.mod h1:oUs0h39vNwYtYXnQWOT
|
||||
github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0=
|
||||
github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
|
||||
github.com/libp2p/go-libp2p-secio v0.1.1/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
|
||||
github.com/libp2p/go-libp2p-secio v0.2.0 h1:ywzZBsWEEz2KNTn5RtzauEDq5RFEefPsttXYwAWqHng=
|
||||
github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=
|
||||
github.com/libp2p/go-libp2p-secio v0.2.1 h1:eNWbJTdyPA7NxhP7J3c5lT97DC5d+u+IldkgCYFTPVA=
|
||||
github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8=
|
||||
@@ -530,7 +514,6 @@ github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FW
|
||||
github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
|
||||
github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4=
|
||||
github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
|
||||
github.com/libp2p/go-stream-muxer v0.0.1 h1:Ce6e2Pyu+b5MC1k3eeFtAax0pW4gc6MosYSLV05UeLw=
|
||||
github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
|
||||
github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ=
|
||||
github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw=
|
||||
@@ -558,7 +541,6 @@ github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNA
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -716,7 +698,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
||||
github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg=
|
||||
github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
@@ -731,14 +712,14 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20190910031516-c1cbffdb01bb/go.mod h1:x
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190917003517-d78d67427694/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20191116002219-891f55cd449d/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20191212224538-d370462a7e8a/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20191216205031-b047b6acb3c0 h1:efb/4CnrubzNGqQOeHErxyQ6rIsJb7GcgeSDF7fqWeI=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20191216205031-b047b6acb3c0/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe h1:n7En1uyDtknjLRDXebWlPGJoHvwL8AkNcSQzuOoYYYQ=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8 h1:PHZv8Nu+95MBVNu3qSgg3ncxIv8hy4DzGAOBR9xYQRc=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
|
||||
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo=
|
||||
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
|
||||
github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
|
||||
github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDbeKFZInPUrAG+bjuJmUXONGdEFW7XL0SpTY1y4=
|
||||
@@ -760,12 +741,10 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50=
|
||||
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
|
||||
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
@@ -781,7 +760,6 @@ go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
|
||||
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
@@ -802,7 +780,6 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -811,7 +788,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
@@ -832,12 +808,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -868,7 +842,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA=
|
||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -891,7 +864,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200108195415-316d2f248479 h1:csuS+MHeEA2eWhyjQCMaPMq4z1+/PohkBSjJZHSIbOE=
|
||||
golang.org/x/tools v0.0.0-20200108195415-316d2f248479/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -911,7 +883,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package incrt
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
var log = logging.Logger("incrt")
|
||||
|
||||
var now = time.Now
|
||||
|
||||
type ReaderDeadline interface {
|
||||
Read([]byte) (int, error)
|
||||
SetReadDeadline(time.Time) error
|
||||
}
|
||||
|
||||
type incrt struct {
|
||||
rd ReaderDeadline
|
||||
|
||||
waitPerByte time.Duration
|
||||
wait time.Duration
|
||||
maxWait time.Duration
|
||||
}
|
||||
|
||||
// New creates an Incremental Reader Timeout, with minimum sustained speed of
|
||||
// minSpeed bytes per second and with maximum wait of maxWait
|
||||
func New(rd ReaderDeadline, minSpeed int64, maxWait time.Duration) io.Reader {
|
||||
return &incrt{
|
||||
rd: rd,
|
||||
waitPerByte: time.Second / time.Duration(minSpeed),
|
||||
wait: maxWait,
|
||||
maxWait: maxWait,
|
||||
}
|
||||
}
|
||||
|
||||
type errNoWait struct{}
|
||||
|
||||
func (err errNoWait) Error() string {
|
||||
return "wait time exceeded"
|
||||
}
|
||||
func (err errNoWait) Timeout() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (crt *incrt) Read(buf []byte) (int, error) {
|
||||
start := now()
|
||||
if crt.wait == 0 {
|
||||
return 0, errNoWait{}
|
||||
}
|
||||
|
||||
err := crt.rd.SetReadDeadline(start.Add(crt.wait))
|
||||
if err != nil {
|
||||
log.Warnf("unable to set daedline: %+v", err)
|
||||
}
|
||||
|
||||
n, err := crt.rd.Read(buf)
|
||||
|
||||
crt.rd.SetReadDeadline(time.Time{})
|
||||
if err == nil {
|
||||
dur := now().Sub(start)
|
||||
crt.wait -= dur
|
||||
crt.wait += time.Duration(n) * crt.waitPerByte
|
||||
if crt.wait < 0 {
|
||||
crt.wait = 0
|
||||
}
|
||||
if crt.wait > crt.maxWait {
|
||||
crt.wait = crt.maxWait
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
+17
-8
@@ -99,7 +99,7 @@ func (h handlers) register(namespace string, r interface{}) {
|
||||
// Handle
|
||||
|
||||
type rpcErrFunc func(w func(func(io.Writer)), req *request, code int, err error)
|
||||
type chanOut func(reflect.Value) interface{}
|
||||
type chanOut func(reflect.Value, int64) error
|
||||
|
||||
func (h handlers) handleReader(ctx context.Context, r io.Reader, w io.Writer, rpcError rpcErrFunc) {
|
||||
wf := func(cb func(io.Writer)) {
|
||||
@@ -222,16 +222,25 @@ func (h handlers) handle(ctx context.Context, req request, w func(func(io.Writer
|
||||
if handler.valOut != -1 {
|
||||
resp.Result = callResult[handler.valOut].Interface()
|
||||
}
|
||||
if resp.Result != nil && reflect.TypeOf(resp.Result).Kind() == reflect.Chan {
|
||||
// Channel responses are sent from channel control goroutine.
|
||||
// Sending responses here could cause deadlocks on writeLk, or allow
|
||||
// sending channel messages before this rpc call returns
|
||||
|
||||
w(func(w io.Writer) {
|
||||
if resp.Result != nil && reflect.TypeOf(resp.Result).Kind() == reflect.Chan {
|
||||
// this must happen in the writer callback, otherwise we may start sending
|
||||
// channel messages before we send this response
|
||||
|
||||
//noinspection GoNilness // already checked above
|
||||
resp.Result = chOut(callResult[handler.valOut])
|
||||
//noinspection GoNilness // already checked above
|
||||
err = chOut(callResult[handler.valOut], *req.ID)
|
||||
if err == nil {
|
||||
return // channel goroutine handles responding
|
||||
}
|
||||
|
||||
log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
|
||||
resp.Error = &respError{
|
||||
Code: 1,
|
||||
Message: err.(error).Error(),
|
||||
}
|
||||
}
|
||||
|
||||
w(func(w io.Writer) {
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
||||
@@ -375,5 +375,51 @@ func TestChan(t *testing.T) {
|
||||
serverHandler.wait <- struct{}{}
|
||||
_, ok = <-sub
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
|
||||
func TestControlChanDeadlock(t *testing.T) {
|
||||
for r := 0; r < 20; r++ {
|
||||
testControlChanDeadlock(t)
|
||||
}
|
||||
}
|
||||
|
||||
func testControlChanDeadlock(t *testing.T) {
|
||||
var client struct {
|
||||
Sub func(context.Context, int, int) (<-chan int, error)
|
||||
}
|
||||
|
||||
n := 5000
|
||||
|
||||
serverHandler := &ChanHandler{
|
||||
wait: make(chan struct{}, n),
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("ChanHandler", serverHandler)
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer closer()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
serverHandler.wait <- struct{}{}
|
||||
}
|
||||
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
|
||||
sub, err := client.Sub(ctx, 1, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < n; i++ {
|
||||
require.Equal(t, i+1, <-sub)
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = client.Sub(ctx, 2, -1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
+60
-24
@@ -11,6 +11,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const wsCancel = "xrpc.cancel"
|
||||
@@ -33,8 +34,10 @@ type frame struct {
|
||||
}
|
||||
|
||||
type outChanReg struct {
|
||||
id uint64
|
||||
ch reflect.Value
|
||||
reqID int64
|
||||
|
||||
chID uint64
|
||||
ch reflect.Value
|
||||
}
|
||||
|
||||
type wsConn struct {
|
||||
@@ -134,51 +137,80 @@ func (c *wsConn) sendRequest(req request) {
|
||||
// (forwards channel messages to client)
|
||||
func (c *wsConn) handleOutChans() {
|
||||
regV := reflect.ValueOf(c.registerCh)
|
||||
exitV := reflect.ValueOf(c.exiting)
|
||||
|
||||
cases := []reflect.SelectCase{
|
||||
{ // registration chan always 0
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: regV,
|
||||
},
|
||||
{ // exit chan always 1
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: exitV,
|
||||
},
|
||||
}
|
||||
internal := len(cases)
|
||||
var caseToID []uint64
|
||||
|
||||
for {
|
||||
chosen, val, ok := reflect.Select(cases)
|
||||
|
||||
if chosen == 0 { // control channel
|
||||
switch chosen {
|
||||
case 0: // registration channel
|
||||
if !ok {
|
||||
// control channel closed - signals closed connection
|
||||
// This shouldn't happen, instead the exiting channel should get closed
|
||||
log.Warn("control channel closed")
|
||||
return
|
||||
}
|
||||
|
||||
registration := val.Interface().(outChanReg)
|
||||
|
||||
caseToID = append(caseToID, registration.chID)
|
||||
cases = append(cases, reflect.SelectCase{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: registration.ch,
|
||||
})
|
||||
|
||||
c.nextWriter(func(w io.Writer) {
|
||||
resp := &response{
|
||||
Jsonrpc: "2.0",
|
||||
ID: registration.reqID,
|
||||
Result: registration.chID,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
continue
|
||||
case 1: // exiting channel
|
||||
if !ok {
|
||||
// exiting channel closed - signals closed connection
|
||||
//
|
||||
// We're not closing any channels as we're on receiving end.
|
||||
// Also, context cancellation below should take care of any running
|
||||
// requests
|
||||
return
|
||||
}
|
||||
|
||||
registration := val.Interface().(outChanReg)
|
||||
|
||||
caseToID = append(caseToID, registration.id)
|
||||
cases = append(cases, reflect.SelectCase{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: registration.ch,
|
||||
})
|
||||
|
||||
log.Warn("exiting channel received a message")
|
||||
continue
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// Output channel closed, cleanup, and tell remote that this happened
|
||||
|
||||
n := len(caseToID)
|
||||
n := len(cases) - 1
|
||||
if n > 0 {
|
||||
cases[chosen] = cases[n]
|
||||
caseToID[chosen-1] = caseToID[n-1]
|
||||
caseToID[chosen-internal] = caseToID[n-internal]
|
||||
}
|
||||
|
||||
id := caseToID[chosen-1]
|
||||
id := caseToID[chosen-internal]
|
||||
cases = cases[:n]
|
||||
caseToID = caseToID[:n-1]
|
||||
caseToID = caseToID[:n-internal]
|
||||
|
||||
c.sendRequest(request{
|
||||
Jsonrpc: "2.0",
|
||||
@@ -194,24 +226,29 @@ func (c *wsConn) handleOutChans() {
|
||||
Jsonrpc: "2.0",
|
||||
ID: nil, // notification
|
||||
Method: chValue,
|
||||
Params: []param{{v: reflect.ValueOf(caseToID[chosen-1])}, {v: val}},
|
||||
Params: []param{{v: reflect.ValueOf(caseToID[chosen-internal])}, {v: val}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// handleChanOut registers output channel for forwarding to client
|
||||
func (c *wsConn) handleChanOut(ch reflect.Value) interface{} {
|
||||
func (c *wsConn) handleChanOut(ch reflect.Value, req int64) error {
|
||||
c.spawnOutChanHandlerOnce.Do(func() {
|
||||
go c.handleOutChans()
|
||||
})
|
||||
id := atomic.AddUint64(&c.chanCtr, 1)
|
||||
|
||||
c.registerCh <- outChanReg{
|
||||
id: id,
|
||||
ch: ch,
|
||||
}
|
||||
select {
|
||||
case c.registerCh <- outChanReg{
|
||||
reqID: req,
|
||||
|
||||
return id
|
||||
chID: id,
|
||||
ch: ch,
|
||||
}:
|
||||
return nil
|
||||
case <-c.exiting:
|
||||
return xerrors.New("connection closing")
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
@@ -389,7 +426,6 @@ func (c *wsConn) handleWsConn(ctx context.Context) {
|
||||
c.chanHandlers = map[uint64]func(m []byte, ok bool){}
|
||||
|
||||
c.registerCh = make(chan outChanReg)
|
||||
defer close(c.registerCh)
|
||||
defer close(c.exiting)
|
||||
|
||||
// ////
|
||||
|
||||
+13
-7
@@ -1,12 +1,18 @@
|
||||
package lotuslog
|
||||
|
||||
import logging "github.com/ipfs/go-log/v2"
|
||||
import (
|
||||
"os"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
func SetupLogLevels() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
logging.SetLogLevel("dht", "ERROR")
|
||||
logging.SetLogLevel("swarm2", "WARN")
|
||||
logging.SetLogLevel("bitswap", "WARN")
|
||||
logging.SetLogLevel("pubsub", "WARN")
|
||||
logging.SetLogLevel("connmgr", "WARN")
|
||||
if _, set := os.LookupEnv("GOLOG_LOG_LEVEL"); !set {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
logging.SetLogLevel("dht", "ERROR")
|
||||
logging.SetLogLevel("swarm2", "WARN")
|
||||
logging.SetLogLevel("bitswap", "WARN")
|
||||
logging.SetLogLevel("pubsub", "WARN")
|
||||
logging.SetLogLevel("connmgr", "WARN")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
package statemachine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
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 *TestState) 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
|
||||
}
|
||||
|
||||
// t.A (uint64) (uint64)
|
||||
if len("A") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"A\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("A")))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("A")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.A))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.B (uint64) (uint64)
|
||||
if len("B") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"B\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("B")))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("B")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.B))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestState) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
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("TestState: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.A (uint64) (uint64)
|
||||
case "A":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.A = uint64(extra)
|
||||
// t.B (uint64) (uint64)
|
||||
case "B":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.B = uint64(extra)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (t *TestEvent) 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
|
||||
}
|
||||
|
||||
// t.A (string) (string)
|
||||
if len("A") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"A\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("A")))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("A")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.A) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.A was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.A)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.A)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Val (uint64) (uint64)
|
||||
if len("Val") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Val\" was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Val")))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Val")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Val))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestEvent) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
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("TestEvent: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.A (string) (string)
|
||||
case "A":
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.A = string(sval)
|
||||
}
|
||||
// t.Val (uint64) (uint64)
|
||||
case "Val":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Val = uint64(extra)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package statemachine
|
||||
|
||||
import "context"
|
||||
|
||||
type Context struct {
|
||||
ctx context.Context
|
||||
send func(evt interface{}) error
|
||||
}
|
||||
|
||||
func (ctx *Context) Context() context.Context {
|
||||
return ctx.ctx
|
||||
}
|
||||
|
||||
func (ctx *Context) Send(evt interface{}) error {
|
||||
return ctx.send(evt)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package statemachine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/go-statestore"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type StateHandler interface {
|
||||
// returns
|
||||
Plan(events []Event, user interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// StateGroup manages a group of state machines sharing the same logic
|
||||
type StateGroup struct {
|
||||
sts *statestore.StateStore
|
||||
hnd StateHandler
|
||||
stateType reflect.Type
|
||||
|
||||
lk sync.Mutex
|
||||
sms map[datastore.Key]*StateMachine
|
||||
}
|
||||
|
||||
// stateType: T - (MyStateStruct{})
|
||||
func New(ds datastore.Datastore, hnd StateHandler, stateType interface{}) *StateGroup {
|
||||
return &StateGroup{
|
||||
sts: statestore.New(ds),
|
||||
hnd: hnd,
|
||||
stateType: reflect.TypeOf(stateType),
|
||||
|
||||
sms: map[datastore.Key]*StateMachine{},
|
||||
}
|
||||
}
|
||||
|
||||
// Send sends an event to machine identified by `id`.
|
||||
// `evt` is going to be passed into StateHandler.Planner, in the events[].User param
|
||||
//
|
||||
// If a state machine with the specified id doesn't exits, it's created, and it's
|
||||
// state is set to zero-value of stateType provided in group constructor
|
||||
func (s *StateGroup) Send(id interface{}, evt interface{}) (err error) {
|
||||
s.lk.Lock()
|
||||
defer s.lk.Unlock()
|
||||
|
||||
sm, exist := s.sms[statestore.ToKey(id)]
|
||||
if !exist {
|
||||
sm, err = s.loadOrCreate(id)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("loadOrCreate state: %w", err)
|
||||
}
|
||||
s.sms[statestore.ToKey(id)] = sm
|
||||
}
|
||||
|
||||
return sm.send(Event{User: evt})
|
||||
}
|
||||
|
||||
func (s *StateGroup) loadOrCreate(name interface{}) (*StateMachine, error) {
|
||||
exists, err := s.sts.Has(name)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to check if state for %v exists: %w", name, err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
userState := reflect.New(s.stateType).Interface()
|
||||
|
||||
err = s.sts.Begin(name, userState)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("saving initial state: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
res := &StateMachine{
|
||||
planner: s.hnd.Plan,
|
||||
eventsIn: make(chan Event),
|
||||
|
||||
name: name,
|
||||
st: s.sts.Get(name),
|
||||
stateType: s.stateType,
|
||||
|
||||
stageDone: make(chan struct{}),
|
||||
closing: make(chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
|
||||
go res.run()
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Stop stops all state machines in this group
|
||||
func (s *StateGroup) Stop(ctx context.Context) error {
|
||||
s.lk.Lock()
|
||||
defer s.lk.Unlock()
|
||||
|
||||
for _, sm := range s.sms {
|
||||
if err := sm.stop(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List outputs states of all state machines in this group
|
||||
// out: *[]StateT
|
||||
func (s *StateGroup) List(out interface{}) error {
|
||||
return s.sts.List(out)
|
||||
}
|
||||
|
||||
// Get gets state for a single state machine
|
||||
func (s *StateGroup) Get(id interface{}) *statestore.StoredState {
|
||||
return s.sts.Get(id)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package statemachine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/filecoin-project/go-statestore"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
var log = logging.Logger("evtsm")
|
||||
|
||||
type Event struct {
|
||||
User interface{}
|
||||
}
|
||||
|
||||
// TODO: This probably should be returning an int indicating partial event processing
|
||||
// (or something like errPartial(nEvents))
|
||||
// returns func(ctx Context, st <T>) (func(*<T>), error), where <T> is the typeOf(User) param
|
||||
type Planner func(events []Event, user interface{}) (interface{}, error)
|
||||
|
||||
type StateMachine struct {
|
||||
planner Planner
|
||||
eventsIn chan Event
|
||||
|
||||
name interface{}
|
||||
st *statestore.StoredState
|
||||
stateType reflect.Type
|
||||
|
||||
stageDone chan struct{}
|
||||
closing chan struct{}
|
||||
closed chan struct{}
|
||||
|
||||
busy int32
|
||||
}
|
||||
|
||||
func (fsm *StateMachine) run() {
|
||||
defer close(fsm.closed)
|
||||
|
||||
var pendingEvents []Event
|
||||
|
||||
for {
|
||||
// NOTE: This requires at least one event to be sent to trigger a stage
|
||||
// This means that after restarting the state machine users of this
|
||||
// code must send a 'restart' event
|
||||
select {
|
||||
case evt := <-fsm.eventsIn:
|
||||
pendingEvents = append(pendingEvents, evt)
|
||||
case <-fsm.stageDone:
|
||||
if len(pendingEvents) == 0 {
|
||||
continue
|
||||
}
|
||||
case <-fsm.closing:
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapInt32(&fsm.busy, 0, 1) {
|
||||
var nextStep interface{}
|
||||
var ustate interface{}
|
||||
|
||||
err := fsm.mutateUser(func(user interface{}) (err error) {
|
||||
nextStep, err = fsm.planner(pendingEvents, user)
|
||||
ustate = user
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Executing event planner failed: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
pendingEvents = nil
|
||||
|
||||
if nextStep == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ctx := Context{
|
||||
ctx: context.TODO(),
|
||||
send: func(evt interface{}) error {
|
||||
return fsm.send(Event{User: evt})
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
res := reflect.ValueOf(nextStep).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(ustate).Elem()})
|
||||
|
||||
if res[0].Interface() != nil {
|
||||
log.Errorf("executing step: %+v", res[0].Interface().(error)) // TODO: propagate top level
|
||||
return
|
||||
}
|
||||
|
||||
atomic.StoreInt32(&fsm.busy, 0)
|
||||
fsm.stageDone <- struct{}{}
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fsm *StateMachine) mutateUser(cb func(user interface{}) error) error {
|
||||
mutt := reflect.FuncOf([]reflect.Type{reflect.PtrTo(fsm.stateType)}, []reflect.Type{reflect.TypeOf(new(error)).Elem()}, false)
|
||||
|
||||
mutf := reflect.MakeFunc(mutt, func(args []reflect.Value) (results []reflect.Value) {
|
||||
err := cb(args[0].Interface())
|
||||
return []reflect.Value{reflect.ValueOf(&err).Elem()}
|
||||
})
|
||||
|
||||
return fsm.st.Mutate(mutf.Interface())
|
||||
}
|
||||
|
||||
func (fsm *StateMachine) send(evt Event) error {
|
||||
fsm.eventsIn <- evt // TODO: ctx, at least
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fsm *StateMachine) stop(ctx context.Context) error {
|
||||
close(fsm.closing)
|
||||
|
||||
select {
|
||||
case <-fsm.closed:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package statemachine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-datastore"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
}
|
||||
|
||||
type testHandler struct {
|
||||
t *testing.T
|
||||
proceed chan struct{}
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (t *testHandler) Plan(events []Event, state interface{}) (interface{}, error) {
|
||||
return t.plan(events, state.(*TestState))
|
||||
}
|
||||
|
||||
func (t *testHandler) plan(events []Event, state *TestState) (func(Context, TestState) error, error) {
|
||||
for _, event := range events {
|
||||
e := event.User.(*TestEvent)
|
||||
switch e.A {
|
||||
case "restart":
|
||||
case "start":
|
||||
state.A = 1
|
||||
case "b":
|
||||
state.A = 2
|
||||
state.B = e.Val
|
||||
}
|
||||
}
|
||||
|
||||
switch state.A {
|
||||
case 1:
|
||||
return t.step0, nil
|
||||
case 2:
|
||||
return t.step1, nil
|
||||
default:
|
||||
t.t.Fatal(state.A)
|
||||
}
|
||||
panic("how?")
|
||||
}
|
||||
|
||||
func (t *testHandler) step0(ctx Context, st TestState) error {
|
||||
ctx.Send(&TestEvent{A: "b", Val: 55})
|
||||
<-t.proceed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testHandler) step1(ctx Context, st TestState) error {
|
||||
assert.Equal(t.t, uint64(2), st.A)
|
||||
|
||||
close(t.done)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
for i := 0; i < 1000; i++ { // run a few times to expose any races
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
th := &testHandler{t: t, done: make(chan struct{}), proceed: make(chan struct{})}
|
||||
close(th.proceed)
|
||||
smm := New(ds, th, TestState{})
|
||||
|
||||
if err := smm.Send(uint64(2), &TestEvent{A: "start"}); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
<-th.done
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersist(t *testing.T) {
|
||||
for i := 0; i < 1000; i++ { // run a few times to expose any races
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
th := &testHandler{t: t, done: make(chan struct{}), proceed: make(chan struct{})}
|
||||
smm := New(ds, th, TestState{})
|
||||
|
||||
if err := smm.Send(uint64(2), &TestEvent{A: "start"}); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
if err := smm.Stop(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
smm = New(ds, th, TestState{})
|
||||
if err := smm.Send(uint64(2), &TestEvent{A: "restart"}); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
close(th.proceed)
|
||||
|
||||
<-th.done
|
||||
}
|
||||
}
|
||||
|
||||
var _ StateHandler = &testHandler{}
|
||||
@@ -0,0 +1,11 @@
|
||||
package statemachine
|
||||
|
||||
type TestState struct {
|
||||
A uint64
|
||||
B uint64
|
||||
}
|
||||
|
||||
type TestEvent struct {
|
||||
A string
|
||||
Val uint64
|
||||
}
|
||||
+36
-6
@@ -2,6 +2,7 @@ package miner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -22,6 +24,11 @@ var log = logging.Logger("miner")
|
||||
type waitFunc func(ctx context.Context, baseTime uint64) error
|
||||
|
||||
func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
|
||||
arc, err := lru.NewARC(10000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Miner{
|
||||
api: api,
|
||||
epp: epp,
|
||||
@@ -32,6 +39,7 @@ func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
|
||||
|
||||
return nil
|
||||
},
|
||||
minedBlockHeights: arc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +56,8 @@ type Miner struct {
|
||||
waitFunc waitFunc
|
||||
|
||||
lastWork *MiningBase
|
||||
|
||||
minedBlockHeights *lru.ARCCache
|
||||
}
|
||||
|
||||
func (m *Miner) Addresses() ([]address.Address, error) {
|
||||
@@ -200,13 +210,33 @@ eventLoop:
|
||||
mWon[b.Header.Miner] = struct{}{}
|
||||
}
|
||||
for _, b := range blks {
|
||||
// TODO: this code was written to handle creating blocks for multiple miners.
|
||||
// However, we don't use that, and we probably never will. So even though this code will
|
||||
// never see different miners, i'm going to handle the caching as if it was going to.
|
||||
// We can clean it up later when we remove all the multiple miner logic.
|
||||
blkKey := fmt.Sprintf("%s-%d", b.Header.Miner, b.Header.Height)
|
||||
if _, ok := m.minedBlockHeights.Get(blkKey); ok {
|
||||
log.Warnw("Created a block at the same height as another block we've created", "height", b.Header.Height, "miner", b.Header.Miner, "parents", b.Header.Parents)
|
||||
continue
|
||||
}
|
||||
|
||||
m.minedBlockHeights.Add(blkKey, true)
|
||||
if err := m.api.SyncSubmitBlock(ctx, b); err != nil {
|
||||
log.Errorf("failed to submit newly mined block: %s", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*base.nullRounds)), 0)
|
||||
time.Sleep(time.Until(nextRound))
|
||||
|
||||
select {
|
||||
case <-time.After(time.Until(nextRound)):
|
||||
case <-m.stop:
|
||||
stopping := m.stopping
|
||||
m.stop = nil
|
||||
m.stopping = nil
|
||||
close(stopping)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,13 +386,13 @@ func (m *Miner) computeTicket(ctx context.Context, addr address.Address, base *M
|
||||
}
|
||||
|
||||
func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof *types.EPostProof, pending []*types.SignedMessage) (*types.BlockMsg, error) {
|
||||
msgs, err := selectMessages(context.TODO(), m.api.StateGetActor, base, pending)
|
||||
msgs, err := SelectMessages(context.TODO(), m.api.StateGetActor, base.ts, pending)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("message filtering failed: %w", err)
|
||||
}
|
||||
|
||||
if len(msgs) > build.BlockMessageLimit {
|
||||
log.Error("selectMessages returned too many messages: ", len(msgs))
|
||||
log.Error("SelectMessages returned too many messages: ", len(msgs))
|
||||
msgs = msgs[:build.BlockMessageLimit]
|
||||
}
|
||||
|
||||
@@ -374,7 +404,7 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
|
||||
return m.api.MinerCreateBlock(context.TODO(), addr, base.ts, ticket, proof, msgs, nheight, uint64(uts))
|
||||
}
|
||||
|
||||
type actorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error)
|
||||
type ActorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error)
|
||||
|
||||
func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
|
||||
for _, msg := range msgs {
|
||||
@@ -385,7 +415,7 @@ func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
|
||||
return out
|
||||
}
|
||||
|
||||
func selectMessages(ctx context.Context, al actorLookup, base *MiningBase, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
|
||||
func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
|
||||
out := make([]*types.SignedMessage, 0, build.BlockMessageLimit)
|
||||
inclNonces := make(map[address.Address]uint64)
|
||||
inclBalances := make(map[address.Address]types.BigInt)
|
||||
@@ -401,7 +431,7 @@ func selectMessages(ctx context.Context, al actorLookup, base *MiningBase, msgs
|
||||
from := msg.Message.From
|
||||
|
||||
if _, ok := inclNonces[from]; !ok {
|
||||
act, err := al(ctx, from, base.ts)
|
||||
act, err := al(ctx, from, ts)
|
||||
if err != nil {
|
||||
log.Warnf("failed to check message sender balance, skipping message: %+v", err)
|
||||
continue
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ func TestMessageFiltering(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
outmsgs, err := selectMessages(ctx, af, &MiningBase{}, wrapMsgs(msgs))
|
||||
outmsgs, err := SelectMessages(ctx, af, nil, wrapMsgs(msgs))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+10
-3
@@ -6,14 +6,21 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
func NewTestMiner(nextCh <-chan struct{}, addr address.Address) func(api.FullNode, gen.ElectionPoStProver) *Miner {
|
||||
return func(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
|
||||
arc, err := lru.NewARC(10000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
m := &Miner{
|
||||
api: api,
|
||||
waitFunc: chanWaiter(nextCh),
|
||||
epp: epp,
|
||||
api: api,
|
||||
waitFunc: chanWaiter(nextCh),
|
||||
epp: epp,
|
||||
minedBlockHeights: arc,
|
||||
}
|
||||
|
||||
if err := m.Register(addr); err != nil {
|
||||
|
||||
+2
-1
@@ -50,6 +50,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/paych"
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sealing"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
@@ -245,7 +246,7 @@ func Online() Option {
|
||||
ApplyIf(func(s *Settings) bool { return s.nodeType == repo.StorageMiner },
|
||||
Override(new(sectorbuilder.Interface), modules.SectorBuilder),
|
||||
Override(new(*sectorblocks.SectorBlocks), sectorblocks.NewSectorBlocks),
|
||||
Override(new(storage.TicketFn), modules.SealTicketGen),
|
||||
Override(new(sealing.TicketFn), modules.SealTicketGen),
|
||||
Override(new(*storage.Miner), modules.StorageMiner),
|
||||
|
||||
Override(new(dtypes.StagingBlockstore), modules.StagingBlockstore),
|
||||
|
||||
@@ -3,6 +3,7 @@ package full
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -18,10 +19,12 @@ import (
|
||||
"github.com/ipfs/go-path"
|
||||
"github.com/ipfs/go-path/resolver"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/prometheus/common/log"
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -83,6 +86,10 @@ func (a *ChainAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) (*api
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error) {
|
||||
return a.Chain.GetPath(ctx, from, to)
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainGetParentMessages(ctx context.Context, bcid cid.Cid) ([]api.Message, error) {
|
||||
b, err := a.Chain.GetBlock(bcid)
|
||||
if err != nil {
|
||||
@@ -308,3 +315,34 @@ func (a *ChainAPI) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Mess
|
||||
|
||||
return cm.VMMessage(), nil
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainExport(ctx context.Context, ts *types.TipSet) (<-chan []byte, error) {
|
||||
r, w := io.Pipe()
|
||||
out := make(chan []byte)
|
||||
go func() {
|
||||
defer w.Close()
|
||||
if err := a.Chain.Export(ctx, ts, w); err != nil {
|
||||
log.Errorf("chain export call failed: %s", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
for {
|
||||
buf := make([]byte, 4096)
|
||||
n, err := r.Read(buf)
|
||||
if err != nil {
|
||||
log.Errorf("chain export pipe read failed: %s", err)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case out <- buf[:n]:
|
||||
case <-ctx.Done():
|
||||
log.Warnf("export writer failed: %s", ctx.Err())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (a *StateAPI) StatePledgeCollateral(ctx context.Context, ts *types.TipSet)
|
||||
return types.BigFromBytes(ret.Return), nil
|
||||
}
|
||||
|
||||
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
|
||||
return a.StateManager.Call(ctx, msg, ts)
|
||||
}
|
||||
|
||||
@@ -400,3 +400,7 @@ func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message,
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *StateAPI) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
|
||||
return stmgr.ComputeState(ctx, a.StateManager, height, msgs, ts)
|
||||
}
|
||||
|
||||
+12
-1
@@ -160,6 +160,16 @@ func (sm *StorageMinerAPI) SectorsStatus(ctx context.Context, sid uint64) (api.S
|
||||
deals[i] = piece.DealID
|
||||
}
|
||||
|
||||
log := make([]api.SectorLog, len(info.Log))
|
||||
for i, l := range info.Log {
|
||||
log[i] = api.SectorLog{
|
||||
Kind: l.Kind,
|
||||
Timestamp: l.Timestamp,
|
||||
Trace: l.Trace,
|
||||
Message: l.Message,
|
||||
}
|
||||
}
|
||||
|
||||
return api.SectorInfo{
|
||||
SectorID: sid,
|
||||
State: info.State,
|
||||
@@ -172,6 +182,7 @@ func (sm *StorageMinerAPI) SectorsStatus(ctx context.Context, sid uint64) (api.S
|
||||
Retries: info.Nonce,
|
||||
|
||||
LastErr: info.LastErr,
|
||||
Log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -206,7 +217,7 @@ func (sm *StorageMinerAPI) SectorsRefs(context.Context) (map[string][]api.Sealed
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) SectorsUpdate(ctx context.Context, id uint64, state api.SectorState) error {
|
||||
return sm.Miner.UpdateSectorState(ctx, id, storage.NonceIncrement, state)
|
||||
return sm.Miner.ForceSectorState(ctx, id, state)
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) WorkerQueue(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) {
|
||||
|
||||
@@ -5,7 +5,16 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
dtgraphsync "github.com/filecoin-project/go-data-transfer/impl/graphsync"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
retrievalimpl "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
paramfetch "github.com/filecoin-project/go-paramfetch"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/go-statestore"
|
||||
"github.com/ipfs/go-bitswap"
|
||||
"github.com/ipfs/go-bitswap/network"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
@@ -23,25 +32,16 @@ import (
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
dtgraphsync "github.com/filecoin-project/go-data-transfer/impl/graphsync"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
retrievalimpl "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/go-statestore"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/markets/retrievaladapter"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/modules/helpers"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
|
||||
"github.com/filecoin-project/lotus/markets/retrievaladapter"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sealing"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
@@ -98,7 +98,7 @@ func SectorBuilderConfig(storagePath string, threads uint, noprecommit, nocommit
|
||||
}
|
||||
}
|
||||
|
||||
func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb sectorbuilder.Interface, tktFn storage.TicketFn) (*storage.Miner, error) {
|
||||
func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb sectorbuilder.Interface, tktFn sealing.TicketFn) (*storage.Miner, error) {
|
||||
maddr, err := minerAddrFromDS(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -241,7 +241,7 @@ func SectorBuilder(cfg *sectorbuilder.Config, ds dtypes.MetadataDS) (*sectorbuil
|
||||
return sb, nil
|
||||
}
|
||||
|
||||
func SealTicketGen(api api.FullNode) storage.TicketFn {
|
||||
func SealTicketGen(api api.FullNode) sealing.TicketFn {
|
||||
return func(ctx context.Context) (*sectorbuilder.SealTicket, error) {
|
||||
ts, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -393,6 +393,12 @@ func TestAPIRPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAPIDealFlow(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestDealFlow(t, mockSbBuilder, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -42,7 +42,7 @@ type PeerMgr struct {
|
||||
maxFilPeers int
|
||||
minFilPeers int
|
||||
|
||||
expanding bool
|
||||
expanding chan struct{}
|
||||
|
||||
h host.Host
|
||||
dht *dht.IpfsDHT
|
||||
@@ -56,7 +56,8 @@ func NewPeerMgr(h host.Host, dht *dht.IpfsDHT, bootstrap dtypes.BootstrapPeers)
|
||||
dht: dht,
|
||||
bootstrappers: bootstrap,
|
||||
|
||||
peers: make(map[peer.ID]time.Duration),
|
||||
peers: make(map[peer.ID]time.Duration),
|
||||
expanding: make(chan struct{}, 1),
|
||||
|
||||
maxFilPeers: MaxFilPeers,
|
||||
minFilPeers: MinFilPeers,
|
||||
@@ -125,18 +126,19 @@ func (pmgr *PeerMgr) getPeerCount() int {
|
||||
}
|
||||
|
||||
func (pmgr *PeerMgr) expandPeers() {
|
||||
if pmgr.expanding {
|
||||
select {
|
||||
case pmgr.expanding <- struct{}{}:
|
||||
default:
|
||||
return
|
||||
}
|
||||
pmgr.expanding = true
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
pmgr.expanding = false
|
||||
}()
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
pmgr.doExpand(ctx)
|
||||
|
||||
<-pmgr.expanding
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,10 @@ func (s *fpostScheduler) runPost(ctx context.Context, eps uint64, ts *types.TipS
|
||||
return nil, xerrors.Errorf("getting sorted sector info: %w", err)
|
||||
}
|
||||
|
||||
log.Infow("running fPoSt", "chain-random", rand, "eps", eps, "height", ts.Height())
|
||||
log.Infow("running fPoSt",
|
||||
"chain-random", rand,
|
||||
"eps", eps,
|
||||
"height", ts.Height())
|
||||
|
||||
faults, err := s.checkFaults(ctx, ssi)
|
||||
if err != nil {
|
||||
@@ -129,6 +132,10 @@ func (s *fpostScheduler) runPost(ctx context.Context, eps uint64, ts *types.TipS
|
||||
var seed [32]byte
|
||||
copy(seed[:], rand)
|
||||
|
||||
log.Infow("generating fPoSt",
|
||||
"sectors", len(ssi.Values()),
|
||||
"faults", len(faults))
|
||||
|
||||
scandidates, proof, err := s.sb.GenerateFallbackPoSt(ssi, seed, faults)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("running post failed: %w", err)
|
||||
|
||||
@@ -70,6 +70,9 @@ func (s *fpostScheduler) run(ctx context.Context) {
|
||||
var lowest, highest *types.TipSet = s.cur, nil
|
||||
|
||||
for _, change := range changes {
|
||||
if change.Val == nil {
|
||||
log.Errorf("change.Val was nil")
|
||||
}
|
||||
switch change.Type {
|
||||
case store.HCRevert:
|
||||
lowest = change.Val
|
||||
@@ -111,6 +114,9 @@ func (s *fpostScheduler) revert(ctx context.Context, newLowest *types.TipSet) er
|
||||
}
|
||||
|
||||
func (s *fpostScheduler) update(ctx context.Context, new *types.TipSet) error {
|
||||
if new == nil {
|
||||
return xerrors.Errorf("no new tipset in fpostScheduler.update")
|
||||
}
|
||||
newEPS, start, err := s.shouldFallbackPost(ctx, new)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+30
-45
@@ -5,50 +5,42 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/filecoin-project/go-statestore"
|
||||
"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/events"
|
||||
"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/storage/sealing"
|
||||
)
|
||||
|
||||
var log = logging.Logger("storageminer")
|
||||
|
||||
const SectorStorePrefix = "/sectors"
|
||||
|
||||
type Miner struct {
|
||||
api storageMinerApi
|
||||
events *events.Events
|
||||
h host.Host
|
||||
api storageMinerApi
|
||||
h host.Host
|
||||
sb sectorbuilder.Interface
|
||||
ds datastore.Batching
|
||||
tktFn sealing.TicketFn
|
||||
|
||||
maddr address.Address
|
||||
worker address.Address
|
||||
|
||||
// Sealing
|
||||
sb sectorbuilder.Interface
|
||||
sectors *statestore.StateStore
|
||||
tktFn TicketFn
|
||||
|
||||
sectorIncoming chan *SectorInfo
|
||||
sectorUpdated chan sectorUpdate
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
sealing *sealing.Sealing
|
||||
}
|
||||
|
||||
type storageMinerApi interface {
|
||||
// Call a read only method on actors (no interaction with the chain required)
|
||||
StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateCall(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error)
|
||||
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
|
||||
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
|
||||
@@ -57,6 +49,7 @@ type storageMinerApi interface {
|
||||
StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
|
||||
|
||||
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error)
|
||||
|
||||
@@ -65,28 +58,25 @@ type storageMinerApi interface {
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
}
|
||||
|
||||
func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb sectorbuilder.Interface, tktFn TicketFn) (*Miner, error) {
|
||||
return &Miner{
|
||||
api: api,
|
||||
|
||||
maddr: addr,
|
||||
func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb sectorbuilder.Interface, tktFn sealing.TicketFn) (*Miner, error) {
|
||||
m := &Miner{
|
||||
api: api,
|
||||
h: h,
|
||||
sb: sb,
|
||||
ds: ds,
|
||||
tktFn: tktFn,
|
||||
|
||||
sectors: statestore.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix))),
|
||||
maddr: addr,
|
||||
}
|
||||
|
||||
sectorIncoming: make(chan *SectorInfo),
|
||||
sectorUpdated: make(chan sectorUpdate),
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
}, nil
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Miner) Run(ctx context.Context) error {
|
||||
@@ -94,32 +84,27 @@ func (m *Miner) Run(ctx context.Context) error {
|
||||
return xerrors.Errorf("miner preflight checks failed: %w", err)
|
||||
}
|
||||
|
||||
m.events = events.NewEvents(ctx, m.api)
|
||||
|
||||
fps := &fpostScheduler{
|
||||
api: m.api,
|
||||
sb: m.sb,
|
||||
api: m.api,
|
||||
sb: m.sb,
|
||||
|
||||
actor: m.maddr,
|
||||
worker: m.worker,
|
||||
}
|
||||
|
||||
go fps.run(ctx)
|
||||
if err := m.sectorStateLoop(ctx); err != nil {
|
||||
log.Errorf("%+v", err)
|
||||
return xerrors.Errorf("failed to startup sector state loop: %w", err)
|
||||
}
|
||||
|
||||
evts := events.NewEvents(ctx, m.api)
|
||||
m.sealing = sealing.New(m.api, evts, m.maddr, m.worker, m.ds, m.sb, m.tktFn)
|
||||
|
||||
go m.sealing.Run(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Miner) Stop(ctx context.Context) error {
|
||||
close(m.stop)
|
||||
select {
|
||||
case <-m.stopped:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
defer m.sealing.Stop(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Miner) runPreflightChecks(ctx context.Context) error {
|
||||
|
||||
@@ -207,6 +207,10 @@ func (sb *SBMock) GetPath(string, string) (string, error) {
|
||||
panic("nyi")
|
||||
}
|
||||
|
||||
func (sb *SBMock) CanCommit(sectorID uint64) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (sb *SBMock) WorkerStats() sectorbuilder.WorkerStats {
|
||||
panic("nyi")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/storage/sealing"
|
||||
)
|
||||
|
||||
// TODO: refactor this to be direct somehow
|
||||
|
||||
func (m *Miner) AllocatePiece(size uint64) (sectorID uint64, offset uint64, err error) {
|
||||
return m.sealing.AllocatePiece(size)
|
||||
}
|
||||
|
||||
func (m *Miner) SealPiece(ctx context.Context, size uint64, r io.Reader, sectorID uint64, dealID uint64) error {
|
||||
return m.sealing.SealPiece(ctx, size, r, sectorID, dealID)
|
||||
}
|
||||
|
||||
func (m *Miner) ListSectors() ([]sealing.SectorInfo, error) {
|
||||
return m.sealing.ListSectors()
|
||||
}
|
||||
|
||||
func (m *Miner) GetSectorInfo(sid uint64) (sealing.SectorInfo, error) {
|
||||
return m.sealing.GetSectorInfo(sid)
|
||||
}
|
||||
|
||||
func (m *Miner) PledgeSector() error {
|
||||
return m.sealing.PledgeSector()
|
||||
}
|
||||
|
||||
func (m *Miner) ForceSectorState(ctx context.Context, id uint64, state api.SectorState) error {
|
||||
return m.sealing.ForceSectorState(ctx, id, state)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
// TODO: For now we handle this by halting state execution, when we get jsonrpc reconnecting
|
||||
// We should implement some wait-for-api logic
|
||||
type ErrApi struct{ error }
|
||||
|
||||
type ErrInvalidDeals struct{ error }
|
||||
type ErrExpiredDeals struct{ error }
|
||||
|
||||
type ErrBadCommD struct{ error }
|
||||
type ErrExpiredTicket struct{ error }
|
||||
|
||||
// checkPieces validates that:
|
||||
// - Each piece han a corresponding on chain deal
|
||||
// - Piece commitments match with on chain deals
|
||||
// - Piece sizes match
|
||||
// - Deals aren't expired
|
||||
func checkPieces(ctx context.Context, si SectorInfo, api sealingApi) error {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
|
||||
}
|
||||
|
||||
for i, piece := range si.Pieces {
|
||||
deal, err := api.StateMarketStorageDeal(ctx, piece.DealID, nil)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("getting deal %d for piece %d: %w", piece.DealID, i, err)}
|
||||
}
|
||||
|
||||
if string(deal.PieceRef) != string(piece.CommP) {
|
||||
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with wrong CommP: %x != %x", i, len(si.Pieces), si.SectorID, piece.DealID, piece.CommP, deal.PieceRef)}
|
||||
}
|
||||
|
||||
if piece.Size != deal.PieceSize {
|
||||
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with different size: %d != %d", i, len(si.Pieces), si.SectorID, piece.DealID, piece.Size, deal.PieceSize)}
|
||||
}
|
||||
|
||||
if head.Height() >= deal.ProposalExpiration {
|
||||
return &ErrExpiredDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers expired deal %d - expires %d, head %d", i, len(si.Pieces), si.SectorID, piece.DealID, deal.ProposalExpiration, head.Height())}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkSeal checks that data commitment generated in the sealing process
|
||||
// matches pieces, and that the seal ticket isn't expired
|
||||
func checkSeal(ctx context.Context, maddr address.Address, si SectorInfo, api sealingApi) (err error) {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
|
||||
}
|
||||
|
||||
ssize, err := api.StateMinerSectorSize(ctx, maddr, head)
|
||||
if err != nil {
|
||||
return &ErrApi{err}
|
||||
}
|
||||
|
||||
ccparams, err := actors.SerializeParams(&actors.ComputeDataCommitmentParams{
|
||||
DealIDs: si.deals(),
|
||||
SectorSize: ssize,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("computing params for ComputeDataCommitment: %w", err)
|
||||
}
|
||||
|
||||
ccmt := &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
From: maddr,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(9999999999),
|
||||
Method: actors.SMAMethods.ComputeDataCommitment,
|
||||
Params: ccparams,
|
||||
}
|
||||
r, err := api.StateCall(ctx, ccmt, nil)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("calling ComputeDataCommitment: %w", err)}
|
||||
}
|
||||
if r.ExitCode != 0 {
|
||||
return &ErrBadCommD{xerrors.Errorf("receipt for ComputeDataCommitment had exit code %d", r.ExitCode)}
|
||||
}
|
||||
if string(r.Return) != string(si.CommD) {
|
||||
return &ErrBadCommD{xerrors.Errorf("on chain CommD differs from sector: %x != %x", r.Return, si.CommD)}
|
||||
}
|
||||
|
||||
if int64(head.Height())-int64(si.Ticket.BlockHeight+build.SealRandomnessLookback) > build.SealRandomnessLookbackLimit {
|
||||
return &ErrExpiredTicket{xerrors.Errorf("ticket expired: seal height: %d, head: %d", si.Ticket.BlockHeight+build.SealRandomnessLookback, head.Height())}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
)
|
||||
|
||||
func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface{}, error) {
|
||||
next, err := m.plan(events, user.(*SectorInfo))
|
||||
if err != nil || next == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(ctx statemachine.Context, si SectorInfo) error {
|
||||
err := next(ctx, si)
|
||||
if err != nil {
|
||||
log.Errorf("unhandled sector error (%d): %+v", si.SectorID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
var fsmPlanners = []func(events []statemachine.Event, state *SectorInfo) error{
|
||||
api.UndefinedSectorState: planOne(on(SectorStart{}, api.Packing)),
|
||||
api.Packing: planOne(on(SectorPacked{}, api.Unsealed)),
|
||||
api.Unsealed: planOne(
|
||||
on(SectorSealed{}, api.PreCommitting),
|
||||
on(SectorSealFailed{}, api.SealFailed),
|
||||
on(SectorPackingFailed{}, api.PackingFailed),
|
||||
),
|
||||
api.PreCommitting: planOne(
|
||||
on(SectorSealFailed{}, api.SealFailed),
|
||||
on(SectorPreCommitted{}, api.WaitSeed),
|
||||
on(SectorPreCommitFailed{}, api.PreCommitFailed),
|
||||
),
|
||||
api.WaitSeed: planOne(
|
||||
on(SectorSeedReady{}, api.Committing),
|
||||
on(SectorPreCommitFailed{}, api.PreCommitFailed),
|
||||
),
|
||||
api.Committing: planCommitting,
|
||||
api.CommitWait: planOne(
|
||||
on(SectorProving{}, api.Proving),
|
||||
on(SectorCommitFailed{}, api.CommitFailed),
|
||||
),
|
||||
|
||||
api.Proving: planOne(
|
||||
on(SectorFaultReported{}, api.FaultReported),
|
||||
on(SectorFaulty{}, api.Faulty),
|
||||
),
|
||||
|
||||
api.SealFailed: planOne(
|
||||
on(SectorRetrySeal{}, api.Unsealed),
|
||||
),
|
||||
api.PreCommitFailed: planOne(
|
||||
on(SectorRetryPreCommit{}, api.PreCommitting),
|
||||
on(SectorRetryWaitSeed{}, api.WaitSeed),
|
||||
on(SectorSealFailed{}, api.SealFailed),
|
||||
),
|
||||
|
||||
api.Faulty: planOne(
|
||||
on(SectorFaultReported{}, api.FaultReported),
|
||||
),
|
||||
api.FaultedFinal: final,
|
||||
}
|
||||
|
||||
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, error) {
|
||||
/////
|
||||
// First process all events
|
||||
|
||||
for _, event := range events {
|
||||
l := Log{
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Message: fmt.Sprintf("%+v", event),
|
||||
Kind: fmt.Sprintf("event;%T", event.User),
|
||||
}
|
||||
|
||||
if err, iserr := event.User.(xerrors.Formatter); iserr {
|
||||
l.Trace = fmt.Sprintf("%+v", err)
|
||||
}
|
||||
|
||||
state.Log = append(state.Log, l)
|
||||
}
|
||||
|
||||
p := fsmPlanners[state.State]
|
||||
if p == nil {
|
||||
return nil, xerrors.Errorf("planner for state %s not found", api.SectorStates[state.State])
|
||||
}
|
||||
|
||||
if err := p(events, state); err != nil {
|
||||
return nil, xerrors.Errorf("running planner for state %s failed: %w", api.SectorStates[state.State], err)
|
||||
}
|
||||
|
||||
/////
|
||||
// Now decide what to do next
|
||||
|
||||
/*
|
||||
|
||||
* Empty
|
||||
| |
|
||||
| v
|
||||
*<- Packing <- incoming
|
||||
| |
|
||||
| v
|
||||
*<- Unsealed <--> SealFailed
|
||||
| |
|
||||
| v
|
||||
* PreCommitting <--> PreCommitFailed
|
||||
| | ^
|
||||
| v |
|
||||
*<- WaitSeed ----------/
|
||||
| |||
|
||||
| vvv v--> SealCommitFailed
|
||||
*<- Committing
|
||||
| | ^--> CommitFailed
|
||||
| v ^
|
||||
*<- CommitWait ---/
|
||||
| |
|
||||
| v
|
||||
*<- Proving
|
||||
|
|
||||
v
|
||||
FailedUnrecoverable
|
||||
|
||||
UndefinedSectorState <- ¯\_(ツ)_/¯
|
||||
| ^
|
||||
*---------------------/
|
||||
|
||||
*/
|
||||
|
||||
switch state.State {
|
||||
// Happy path
|
||||
case api.Packing:
|
||||
return m.handlePacking, nil
|
||||
case api.Unsealed:
|
||||
return m.handleUnsealed, nil
|
||||
case api.PreCommitting:
|
||||
return m.handlePreCommitting, nil
|
||||
case api.WaitSeed:
|
||||
return m.handleWaitSeed, nil
|
||||
case api.Committing:
|
||||
return m.handleCommitting, nil
|
||||
case api.CommitWait:
|
||||
return m.handleCommitWait, nil
|
||||
case api.Proving:
|
||||
// TODO: track sector health / expiration
|
||||
log.Infof("Proving sector %d", state.SectorID)
|
||||
|
||||
// Handled failure modes
|
||||
case api.SealFailed:
|
||||
return m.handleSealFailed, nil
|
||||
case api.PreCommitFailed:
|
||||
return m.handlePreCommitFailed, nil
|
||||
case api.SealCommitFailed:
|
||||
log.Warnf("sector %d entered unimplemented state 'SealCommitFailed'", state.SectorID)
|
||||
case api.CommitFailed:
|
||||
log.Warnf("sector %d entered unimplemented state 'CommitFailed'", state.SectorID)
|
||||
|
||||
// Faults
|
||||
case api.Faulty:
|
||||
return m.handleFaulty, nil
|
||||
case api.FaultReported:
|
||||
return m.handleFaultReported, nil
|
||||
|
||||
// Fatal errors
|
||||
case api.UndefinedSectorState:
|
||||
log.Error("sector update with undefined state!")
|
||||
case api.FailedUnrecoverable:
|
||||
log.Errorf("sector %d failed unrecoverably", state.SectorID)
|
||||
default:
|
||||
log.Errorf("unexpected sector update state: %d", state.State)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func planCommitting(events []statemachine.Event, state *SectorInfo) error {
|
||||
for _, event := range events {
|
||||
switch e := event.User.(type) {
|
||||
case globalMutator:
|
||||
if e.applyGlobal(state) {
|
||||
return nil
|
||||
}
|
||||
case SectorCommitted: // the normal case
|
||||
e.apply(state)
|
||||
state.State = api.CommitWait
|
||||
case SectorSeedReady: // seed changed :/
|
||||
if e.seed.Equals(&state.Seed) {
|
||||
log.Warnf("planCommitting: got SectorSeedReady, but the seed didn't change")
|
||||
continue // or it didn't!
|
||||
}
|
||||
log.Warnf("planCommitting: commit Seed changed")
|
||||
e.apply(state)
|
||||
state.State = api.Committing
|
||||
return nil
|
||||
case SectorComputeProofFailed:
|
||||
state.State = api.SealCommitFailed
|
||||
case SectorSealFailed:
|
||||
state.State = api.CommitFailed
|
||||
default:
|
||||
return xerrors.Errorf("planCommitting got event of unknown type %T, events: %+v", event.User, events)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Sealing) restartSectors(ctx context.Context) error {
|
||||
trackedSectors, err := m.ListSectors()
|
||||
if err != nil {
|
||||
log.Errorf("loading sector list: %+v", err)
|
||||
}
|
||||
|
||||
for _, sector := range trackedSectors {
|
||||
if err := m.sectors.Send(sector.SectorID, SectorRestart{}); err != nil {
|
||||
log.Errorf("restarting sector %d: %+v", sector.SectorID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Grab on-chain sector set and diff with trackedSectors
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Sealing) ForceSectorState(ctx context.Context, id uint64, state api.SectorState) error {
|
||||
return m.sectors.Send(id, SectorForceState{state})
|
||||
}
|
||||
|
||||
func final(events []statemachine.Event, state *SectorInfo) error {
|
||||
return xerrors.Errorf("didn't expect any events in state %s, got %+v", api.SectorStates[state.State], events)
|
||||
}
|
||||
|
||||
func on(mut mutator, next api.SectorState) func() (mutator, api.SectorState) {
|
||||
return func() (mutator, api.SectorState) {
|
||||
return mut, next
|
||||
}
|
||||
}
|
||||
|
||||
func planOne(ts ...func() (mut mutator, next api.SectorState)) func(events []statemachine.Event, state *SectorInfo) error {
|
||||
return func(events []statemachine.Event, state *SectorInfo) error {
|
||||
if len(events) != 1 {
|
||||
for _, event := range events {
|
||||
if gm, ok := event.User.(globalMutator); ok {
|
||||
gm.applyGlobal(state)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return xerrors.Errorf("planner for state %s only has a plan for a single event only, got %+v", api.SectorStates[state.State], events)
|
||||
}
|
||||
|
||||
if gm, ok := events[0].User.(globalMutator); ok {
|
||||
gm.applyGlobal(state)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, t := range ts {
|
||||
mut, next := t()
|
||||
|
||||
if reflect.TypeOf(events[0].User) != reflect.TypeOf(mut) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err, iserr := events[0].User.(error); iserr {
|
||||
log.Warnf("sector %d got error event %T: %+v", state.SectorID, events[0].User, err)
|
||||
}
|
||||
|
||||
events[0].User.(mutator).apply(state)
|
||||
state.State = next
|
||||
return nil
|
||||
}
|
||||
|
||||
return xerrors.Errorf("planner for state %s received unexpected event %T (%+v)", api.SectorStates[state.State], events[0].User, events[0])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
)
|
||||
|
||||
type mutator interface {
|
||||
apply(state *SectorInfo)
|
||||
}
|
||||
|
||||
// globalMutator is an event which can apply in every state
|
||||
type globalMutator interface {
|
||||
// applyGlobal applies the event to the state. If if returns true,
|
||||
// event processing should be interrupted
|
||||
applyGlobal(state *SectorInfo) bool
|
||||
}
|
||||
|
||||
// Global events
|
||||
|
||||
type SectorRestart struct{}
|
||||
|
||||
func (evt SectorRestart) applyGlobal(*SectorInfo) bool { return false }
|
||||
|
||||
type SectorFatalError struct{ error }
|
||||
|
||||
func (evt SectorFatalError) applyGlobal(state *SectorInfo) bool {
|
||||
log.Errorf("Fatal error on sector %d: %+v", state.SectorID, evt.error)
|
||||
// TODO: Do we want to mark the state as unrecoverable?
|
||||
// I feel like this should be a softer error, where the user would
|
||||
// be able to send a retry event of some kind
|
||||
return true
|
||||
}
|
||||
|
||||
type SectorForceState struct {
|
||||
state api.SectorState
|
||||
}
|
||||
|
||||
func (evt SectorForceState) applyGlobal(state *SectorInfo) bool {
|
||||
state.State = evt.state
|
||||
return true
|
||||
}
|
||||
|
||||
// Normal path
|
||||
|
||||
type SectorStart struct {
|
||||
id uint64
|
||||
pieces []Piece
|
||||
}
|
||||
|
||||
func (evt SectorStart) apply(state *SectorInfo) {
|
||||
state.SectorID = evt.id
|
||||
state.Pieces = evt.pieces
|
||||
}
|
||||
|
||||
type SectorPacked struct{ pieces []Piece }
|
||||
|
||||
func (evt SectorPacked) apply(state *SectorInfo) {
|
||||
state.Pieces = append(state.Pieces, evt.pieces...)
|
||||
}
|
||||
|
||||
type SectorPackingFailed struct{ error }
|
||||
|
||||
func (evt SectorPackingFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorSealed struct {
|
||||
commR []byte
|
||||
commD []byte
|
||||
ticket SealTicket
|
||||
}
|
||||
|
||||
func (evt SectorSealed) apply(state *SectorInfo) {
|
||||
state.CommD = evt.commD
|
||||
state.CommR = evt.commR
|
||||
state.Ticket = evt.ticket
|
||||
}
|
||||
|
||||
type SectorSealFailed struct{ error }
|
||||
|
||||
func (evt SectorSealFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorPreCommitFailed struct{ error }
|
||||
|
||||
func (evt SectorPreCommitFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorPreCommitted struct {
|
||||
message cid.Cid
|
||||
}
|
||||
|
||||
func (evt SectorPreCommitted) apply(state *SectorInfo) {
|
||||
state.PreCommitMessage = &evt.message
|
||||
}
|
||||
|
||||
type SectorSeedReady struct {
|
||||
seed SealSeed
|
||||
}
|
||||
|
||||
func (evt SectorSeedReady) apply(state *SectorInfo) {
|
||||
state.Seed = evt.seed
|
||||
}
|
||||
|
||||
type SectorComputeProofFailed struct{ error }
|
||||
|
||||
type SectorCommitFailed struct{ error }
|
||||
|
||||
func (evt SectorCommitFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorCommitted struct {
|
||||
message cid.Cid
|
||||
proof []byte
|
||||
}
|
||||
|
||||
func (evt SectorCommitted) apply(state *SectorInfo) {
|
||||
state.Proof = evt.proof
|
||||
state.CommitMessage = &evt.message
|
||||
}
|
||||
|
||||
type SectorProving struct{}
|
||||
|
||||
func (evt SectorProving) apply(*SectorInfo) {}
|
||||
|
||||
// Failed state recovery
|
||||
|
||||
type SectorRetrySeal struct{}
|
||||
|
||||
func (evt SectorRetrySeal) apply(state *SectorInfo) {}
|
||||
|
||||
type SectorRetryPreCommit struct{}
|
||||
|
||||
func (evt SectorRetryPreCommit) apply(state *SectorInfo) {}
|
||||
|
||||
type SectorRetryWaitSeed struct{}
|
||||
|
||||
func (evt SectorRetryWaitSeed) apply(state *SectorInfo) {}
|
||||
|
||||
// Faults
|
||||
|
||||
type SectorFaulty struct{}
|
||||
|
||||
func (evt SectorFaulty) apply(state *SectorInfo) {}
|
||||
|
||||
type SectorFaultReported struct{ reportMsg cid.Cid }
|
||||
|
||||
func (evt SectorFaultReported) apply(state *SectorInfo) {
|
||||
state.FaultReportMsg = &evt.reportMsg
|
||||
}
|
||||
|
||||
type SectorFaultedFinal struct{}
|
||||
@@ -0,0 +1,85 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
_ = logging.SetLogLevel("*", "INFO")
|
||||
}
|
||||
|
||||
func (t *test) planSingle(evt interface{}) {
|
||||
_, err := t.s.plan([]statemachine.Event{{evt}}, t.state)
|
||||
require.NoError(t.t, err)
|
||||
}
|
||||
|
||||
type test struct {
|
||||
s *Sealing
|
||||
t *testing.T
|
||||
state *SectorInfo
|
||||
|
||||
next func(statemachine.Context, SectorInfo) error
|
||||
}
|
||||
|
||||
func TestHappyPath(t *testing.T) {
|
||||
m := test{
|
||||
s: &Sealing{},
|
||||
t: t,
|
||||
state: &SectorInfo{State: api.Packing},
|
||||
}
|
||||
|
||||
m.planSingle(SectorPacked{})
|
||||
require.Equal(m.t, m.state.State, api.Unsealed)
|
||||
|
||||
m.planSingle(SectorSealed{})
|
||||
require.Equal(m.t, m.state.State, api.PreCommitting)
|
||||
|
||||
m.planSingle(SectorPreCommitted{})
|
||||
require.Equal(m.t, m.state.State, api.WaitSeed)
|
||||
|
||||
m.planSingle(SectorSeedReady{})
|
||||
require.Equal(m.t, m.state.State, api.Committing)
|
||||
|
||||
m.planSingle(SectorCommitted{})
|
||||
require.Equal(m.t, m.state.State, api.CommitWait)
|
||||
|
||||
m.planSingle(SectorProving{})
|
||||
require.Equal(m.t, m.state.State, api.Proving)
|
||||
}
|
||||
|
||||
func TestSeedRevert(t *testing.T) {
|
||||
m := test{
|
||||
s: &Sealing{},
|
||||
t: t,
|
||||
state: &SectorInfo{State: api.Packing},
|
||||
}
|
||||
|
||||
m.planSingle(SectorPacked{})
|
||||
require.Equal(m.t, m.state.State, api.Unsealed)
|
||||
|
||||
m.planSingle(SectorSealed{})
|
||||
require.Equal(m.t, m.state.State, api.PreCommitting)
|
||||
|
||||
m.planSingle(SectorPreCommitted{})
|
||||
require.Equal(m.t, m.state.State, api.WaitSeed)
|
||||
|
||||
m.planSingle(SectorSeedReady{})
|
||||
require.Equal(m.t, m.state.State, api.Committing)
|
||||
|
||||
_, err := m.s.plan([]statemachine.Event{{SectorSeedReady{seed: SealSeed{BlockHeight: 5}}}, {SectorCommitted{}}}, m.state)
|
||||
require.NoError(t, err)
|
||||
require.Equal(m.t, m.state.State, api.Committing)
|
||||
|
||||
// not changing the seed this time
|
||||
_, err = m.s.plan([]statemachine.Event{{SectorSeedReady{seed: SealSeed{BlockHeight: 5}}}, {SectorCommitted{}}}, m.state)
|
||||
require.Equal(m.t, m.state.State, api.CommitWait)
|
||||
|
||||
m.planSingle(SectorProving{})
|
||||
require.Equal(m.t, m.state.State, api.Proving)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package storage
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func (m *Miner) pledgeSector(ctx context.Context, sectorID uint64, existingPieceSizes []uint64, sizes ...uint64) ([]Piece, error) {
|
||||
func (m *Sealing) pledgeSector(ctx context.Context, sectorID uint64, existingPieceSizes []uint64, sizes ...uint64) ([]Piece, error) {
|
||||
if len(sizes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (m *Miner) pledgeSector(ctx context.Context, sectorID uint64, existingPiece
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *Miner) PledgeSector() error {
|
||||
func (m *Sealing) PledgeSector() error {
|
||||
go func() {
|
||||
ctx := context.TODO() // we can't use the context from command which invokes
|
||||
// this, as we run everything here async, and it's cancelled when the
|
||||
@@ -0,0 +1,135 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/events"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/padreader"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
)
|
||||
|
||||
const SectorStorePrefix = "/sectors"
|
||||
|
||||
var log = logging.Logger("sectors")
|
||||
|
||||
type TicketFn func(context.Context) (*sectorbuilder.SealTicket, error)
|
||||
|
||||
type sealingApi interface { // TODO: trim down
|
||||
// Call a read only method on actors (no interaction with the chain required)
|
||||
StateCall(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error)
|
||||
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
|
||||
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
|
||||
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
|
||||
StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
|
||||
|
||||
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error)
|
||||
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
}
|
||||
|
||||
type Sealing struct {
|
||||
api sealingApi
|
||||
events *events.Events
|
||||
|
||||
maddr address.Address
|
||||
worker address.Address
|
||||
|
||||
sb sectorbuilder.Interface
|
||||
sectors *statemachine.StateGroup
|
||||
tktFn TicketFn
|
||||
}
|
||||
|
||||
func New(api sealingApi, events *events.Events, maddr address.Address, worker address.Address, ds datastore.Batching, sb sectorbuilder.Interface, tktFn TicketFn) *Sealing {
|
||||
s := &Sealing{
|
||||
api: api,
|
||||
events: events,
|
||||
|
||||
maddr: maddr,
|
||||
worker: worker,
|
||||
sb: sb,
|
||||
tktFn: tktFn,
|
||||
}
|
||||
|
||||
s.sectors = statemachine.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix)), s, SectorInfo{})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *Sealing) Run(ctx context.Context) error {
|
||||
if err := m.restartSectors(ctx); err != nil {
|
||||
log.Errorf("%+v", err)
|
||||
return xerrors.Errorf("failed load sector states: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Sealing) Stop(ctx context.Context) error {
|
||||
return m.sectors.Stop(ctx)
|
||||
}
|
||||
|
||||
func (m *Sealing) AllocatePiece(size uint64) (sectorID uint64, offset uint64, err error) {
|
||||
if padreader.PaddedSize(size) != size {
|
||||
return 0, 0, xerrors.Errorf("cannot allocate unpadded piece")
|
||||
}
|
||||
|
||||
sid, err := m.sb.AcquireSectorId() // TODO: Put more than one thing in a sector
|
||||
if err != nil {
|
||||
return 0, 0, xerrors.Errorf("acquiring sector ID: %w", err)
|
||||
}
|
||||
|
||||
// offset hard-coded to 0 since we only put one thing in a sector for now
|
||||
return sid, 0, nil
|
||||
}
|
||||
|
||||
func (m *Sealing) SealPiece(ctx context.Context, size uint64, r io.Reader, sectorID uint64, dealID uint64) error {
|
||||
log.Infof("Seal piece for deal %d", dealID)
|
||||
|
||||
ppi, err := m.sb.AddPiece(size, sectorID, r, []uint64{})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("adding piece to sector: %w", err)
|
||||
}
|
||||
|
||||
return m.newSector(ctx, sectorID, dealID, ppi)
|
||||
}
|
||||
|
||||
func (m *Sealing) newSector(ctx context.Context, sid uint64, dealID uint64, ppi sectorbuilder.PublicPieceInfo) error {
|
||||
return m.sectors.Send(sid, SectorStart{
|
||||
id: sid,
|
||||
pieces: []Piece{
|
||||
{
|
||||
DealID: dealID,
|
||||
|
||||
Size: ppi.Size,
|
||||
CommP: ppi.CommP[:],
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
)
|
||||
|
||||
func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) error {
|
||||
log.Infow("performing filling up rest of the sector...", "sector", sector.SectorID)
|
||||
|
||||
var allocated uint64
|
||||
for _, piece := range sector.Pieces {
|
||||
allocated += piece.Size
|
||||
}
|
||||
|
||||
ubytes := sectorbuilder.UserBytesForSectorSize(m.sb.SectorSize())
|
||||
|
||||
if allocated > ubytes {
|
||||
return xerrors.Errorf("too much data in sector: %d > %d", allocated, ubytes)
|
||||
}
|
||||
|
||||
fillerSizes, err := fillersFromRem(ubytes - allocated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fillerSizes) > 0 {
|
||||
log.Warnf("Creating %d filler pieces for sector %d", len(fillerSizes), sector.SectorID)
|
||||
}
|
||||
|
||||
pieces, err := m.pledgeSector(ctx.Context(), sector.SectorID, sector.existingPieces(), fillerSizes...)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("filling up the sector (%v): %w", fillerSizes, err)
|
||||
}
|
||||
|
||||
return ctx.Send(SectorPacked{pieces: pieces})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleUnsealed(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if err := checkPieces(ctx.Context(), sector, m.api); err != nil { // Sanity check state
|
||||
switch err.(type) {
|
||||
case *ErrApi:
|
||||
log.Errorf("handleUnsealed: api error, not proceeding: %+v", err)
|
||||
return nil
|
||||
case *ErrInvalidDeals:
|
||||
return ctx.Send(SectorPackingFailed{xerrors.Errorf("invalid deals in sector: %w", err)})
|
||||
case *ErrExpiredDeals: // Probably not much we can do here, maybe re-pack the sector?
|
||||
return ctx.Send(SectorPackingFailed{xerrors.Errorf("expired deals in sector: %w", err)})
|
||||
default:
|
||||
return xerrors.Errorf("checkPieces sanity check error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infow("performing sector replication...", "sector", sector.SectorID)
|
||||
ticket, err := m.tktFn(ctx.Context())
|
||||
if err != nil {
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("getting ticket failed: %w", err)})
|
||||
}
|
||||
|
||||
rspco, err := m.sb.SealPreCommit(ctx.Context(), sector.SectorID, *ticket, sector.pieceInfos())
|
||||
if err != nil {
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("seal pre commit failed: %w", err)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorSealed{
|
||||
commD: rspco.CommD[:],
|
||||
commR: rspco.CommR[:],
|
||||
ticket: SealTicket{
|
||||
BlockHeight: ticket.BlockHeight,
|
||||
TicketBytes: ticket.TicketBytes[:],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if err := checkSeal(ctx.Context(), m.maddr, sector, m.api); err != nil {
|
||||
switch err.(type) {
|
||||
case *ErrApi:
|
||||
log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err)
|
||||
return nil
|
||||
case *ErrBadCommD: // TODO: Should this just back to packing? (not really needed since handleUnsealed will do that too)
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
|
||||
case *ErrExpiredTicket:
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
|
||||
default:
|
||||
return xerrors.Errorf("checkSeal sanity check error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
params := &actors.SectorPreCommitInfo{
|
||||
SectorNumber: sector.SectorID,
|
||||
|
||||
CommR: sector.CommR,
|
||||
SealEpoch: sector.Ticket.BlockHeight,
|
||||
DealIDs: sector.deals(),
|
||||
}
|
||||
enc, aerr := actors.SerializeParams(params)
|
||||
if aerr != nil {
|
||||
return ctx.Send(SectorPreCommitFailed{xerrors.Errorf("could not serialize commit sector parameters: %w", aerr)})
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: m.maddr,
|
||||
From: m.worker,
|
||||
Method: actors.MAMethods.PreCommitSector,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
|
||||
GasLimit: types.NewInt(1000000 /* i dont know help */),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
log.Info("submitting precommit for sector: ", sector.SectorID)
|
||||
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
|
||||
if err != nil {
|
||||
return ctx.Send(SectorPreCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorPreCommitted{message: smsg.Cid()})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleWaitSeed(ctx statemachine.Context, sector SectorInfo) error {
|
||||
// would be ideal to just use the events.Called handler, but it wouldnt be able to handle individual message timeouts
|
||||
log.Info("Sector precommitted: ", sector.SectorID)
|
||||
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.PreCommitMessage)
|
||||
if err != nil {
|
||||
return ctx.Send(SectorPreCommitFailed{err})
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
log.Error("sector precommit failed: ", mw.Receipt.ExitCode)
|
||||
err := xerrors.Errorf("sector precommit failed: %d", mw.Receipt.ExitCode)
|
||||
return ctx.Send(SectorPreCommitFailed{err})
|
||||
}
|
||||
log.Info("precommit message landed on chain: ", sector.SectorID)
|
||||
|
||||
randHeight := mw.TipSet.Height() + build.InteractivePoRepDelay - 1 // -1 because of how the messages are applied
|
||||
log.Infof("precommit for sector %d made it on chain, will start proof computation at height %d", sector.SectorID, randHeight)
|
||||
|
||||
err = m.events.ChainAt(func(ectx context.Context, ts *types.TipSet, curH uint64) error {
|
||||
rand, err := m.api.ChainGetRandomness(ectx, ts.Key(), int64(randHeight))
|
||||
if err != nil {
|
||||
err = xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)
|
||||
|
||||
ctx.Send(SectorFatalError{error: err})
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Send(SectorSeedReady{seed: SealSeed{
|
||||
BlockHeight: randHeight,
|
||||
TicketBytes: rand,
|
||||
}})
|
||||
|
||||
return nil
|
||||
}, func(ctx context.Context, ts *types.TipSet) error {
|
||||
log.Warn("revert in interactive commit sector step")
|
||||
// TODO: need to cancel running process and restart...
|
||||
return nil
|
||||
}, build.InteractivePoRepConfidence, mw.TipSet.Height()+build.InteractivePoRepDelay)
|
||||
if err != nil {
|
||||
log.Warn("waitForPreCommitMessage ChainAt errored: ", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) error {
|
||||
log.Info("scheduling seal proof computation...")
|
||||
|
||||
proof, err := m.sb.SealCommit(ctx.Context(), sector.SectorID, sector.Ticket.SB(), sector.Seed.SB(), sector.pieceInfos(), sector.rspco())
|
||||
if err != nil {
|
||||
return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("computing seal proof failed: %w", err)})
|
||||
}
|
||||
|
||||
// TODO: Consider splitting states and persist proof for faster recovery
|
||||
|
||||
params := &actors.SectorProveCommitInfo{
|
||||
Proof: proof,
|
||||
SectorID: sector.SectorID,
|
||||
DealIDs: sector.deals(),
|
||||
}
|
||||
|
||||
enc, aerr := actors.SerializeParams(params)
|
||||
if aerr != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("could not serialize commit sector parameters: %w", aerr)})
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: m.maddr,
|
||||
From: m.worker,
|
||||
Method: actors.MAMethods.ProveCommitSector,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
|
||||
GasLimit: types.NewInt(1000000 /* i dont know help */),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
// TODO: check seed / ticket are up to date
|
||||
|
||||
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
|
||||
if err != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorCommitted{
|
||||
proof: proof,
|
||||
message: smsg.Cid(),
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if sector.CommitMessage == nil {
|
||||
log.Errorf("sector %d entered commit wait state without a message cid", sector.SectorID)
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("entered commit wait with no commit cid")})
|
||||
}
|
||||
|
||||
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.CommitMessage)
|
||||
if err != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("failed to wait for porep inclusion: %w", err)})
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("submitting sector proof failed (exit=%d, msg=%s) (t:%x; s:%x(%d); p:%x)", mw.Receipt.ExitCode, sector.CommitMessage, sector.Ticket.TicketBytes, sector.Seed.TicketBytes, sector.Seed.BlockHeight, sector.Proof)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorProving{})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleFaulty(ctx statemachine.Context, sector SectorInfo) error {
|
||||
// TODO: check if the fault has already been reported, and that this sector is even valid
|
||||
|
||||
// TODO: coalesce faulty sector reporting
|
||||
bf := types.NewBitField()
|
||||
bf.Set(sector.SectorID)
|
||||
|
||||
enc, aerr := actors.SerializeParams(&actors.DeclareFaultsParams{bf})
|
||||
if aerr != nil {
|
||||
return xerrors.Errorf("failed to serialize declare fault params: %w", aerr)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: m.maddr,
|
||||
From: m.worker,
|
||||
Method: actors.MAMethods.DeclareFaults,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
|
||||
GasLimit: types.NewInt(1000000 /* i dont know help */),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to push declare faults message to network: %w", err)
|
||||
}
|
||||
|
||||
return ctx.Send(SectorFaultReported{reportMsg: smsg.Cid()})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleFaultReported(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if sector.FaultReportMsg == nil {
|
||||
return xerrors.Errorf("entered fault reported state without a FaultReportMsg cid")
|
||||
}
|
||||
|
||||
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.FaultReportMsg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to wait for fault declaration: %w", err)
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
log.Errorf("UNHANDLED: declaring sector fault failed (exit=%d, msg=%s) (id: %d)", mw.Receipt.ExitCode, *sector.FaultReportMsg, sector.SectorID)
|
||||
return xerrors.Errorf("UNHANDLED: submitting fault declaration failed (exit %d)", mw.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return ctx.Send(SectorFaultedFinal{})
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/lib/statemachine"
|
||||
)
|
||||
|
||||
const minRetryTime = 1 * time.Minute
|
||||
|
||||
func failedCooldown(ctx statemachine.Context, sector SectorInfo) error {
|
||||
retryStart := time.Unix(int64(sector.Log[len(sector.Log)-1].Timestamp), 0).Add(minRetryTime)
|
||||
if len(sector.Log) > 0 && !time.Now().After(retryStart) {
|
||||
log.Infof("%s(%d), waiting %s before retrying", api.SectorStates[sector.State], sector.SectorID, time.Until(retryStart))
|
||||
select {
|
||||
case <-time.After(time.Until(retryStart)):
|
||||
case <-ctx.Context().Done():
|
||||
return ctx.Context().Err()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Sealing) checkPreCommitted(ctx statemachine.Context, sector SectorInfo) (*actors.PreCommittedSector, bool) {
|
||||
act, err := m.api.StateGetActor(ctx.Context(), m.maddr, nil)
|
||||
if err != nil {
|
||||
log.Errorf("handleSealFailed(%d): temp error: %+v", sector.SectorID, err)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
st, err := m.api.ChainReadObj(ctx.Context(), act.Head)
|
||||
if err != nil {
|
||||
log.Errorf("handleSealFailed(%d): temp error: %+v", sector.SectorID, err)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
var state actors.StorageMinerActorState
|
||||
if err := state.UnmarshalCBOR(bytes.NewReader(st)); err != nil {
|
||||
log.Errorf("handleSealFailed(%d): temp error: unmarshaling miner state: %+v", sector.SectorID, err)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
pci, found := state.PreCommittedSectors[fmt.Sprint(sector.SectorID)]
|
||||
if found {
|
||||
// TODO: If not expired yet, we can just try reusing sealticket
|
||||
log.Warnf("sector %d found in miner preseal array", sector.SectorID)
|
||||
return pci, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *Sealing) handleSealFailed(ctx statemachine.Context, sector SectorInfo) error {
|
||||
|
||||
if _, is := m.checkPreCommitted(ctx, sector); is {
|
||||
// TODO: Remove this after we can re-precommit
|
||||
return nil // noop, for now
|
||||
}
|
||||
|
||||
if err := failedCooldown(ctx, sector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.Send(SectorRetrySeal{})
|
||||
}
|
||||
|
||||
func (m *Sealing) handlePreCommitFailed(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if err := checkSeal(ctx.Context(), m.maddr, sector, m.api); err != nil {
|
||||
switch err.(type) {
|
||||
case *ErrApi:
|
||||
log.Errorf("handlePreCommitFailed: api error, not proceeding: %+v", err)
|
||||
return nil
|
||||
case *ErrBadCommD: // TODO: Should this just back to packing? (not really needed since handleUnsealed will do that too)
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
|
||||
case *ErrExpiredTicket:
|
||||
return ctx.Send(SectorSealFailed{xerrors.Errorf("ticket expired error: %w", err)})
|
||||
default:
|
||||
return xerrors.Errorf("checkSeal sanity check error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if pci, is := m.checkPreCommitted(ctx, sector); is && pci != nil {
|
||||
if sector.PreCommitMessage != nil {
|
||||
log.Warn("sector %d is precommitted on chain, but we don't have precommit message", sector.SectorID)
|
||||
return nil // TODO: SeedWait needs this currently
|
||||
}
|
||||
|
||||
if string(pci.Info.CommR) != string(sector.CommR) {
|
||||
log.Warn("sector %d is precommitted on chain, with different CommR: %x != %x", sector.SectorID, pci.Info.CommR, sector.CommR)
|
||||
return nil // TODO: remove when the actor allows re-precommit
|
||||
}
|
||||
|
||||
// TODO: we could compare more things, but I don't think we really need to
|
||||
// CommR tells us that CommD (and CommPs), and the ticket are all matching
|
||||
|
||||
if err := failedCooldown(ctx, sector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.Send(SectorRetryWaitSeed{})
|
||||
}
|
||||
|
||||
if sector.PreCommitMessage != nil {
|
||||
log.Warn("retrying precommit even though the message failed to apply")
|
||||
}
|
||||
|
||||
if err := failedCooldown(ctx, sector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.Send(SectorRetryPreCommit{})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user