Merge remote-tracking branch 'origin/master' into feat/async-validateblk
This commit is contained in:
commit
6fe5921bb6
.circleci
MakefileREADME.mdapi
build
chain
actors
actor_init.goactor_miner.goactor_multisig.goactor_multisig_test.goactor_paych_test.goactor_storagemarket.goactor_storagepower.goactor_storagepower_test.goactors.goactors_test.gocbor_gen.goharness2_test.go
blocksync
deals
cbor_gen.goclient.goclient_states.goclient_utils.gohandler_states.goprovider.goprovider_asks.goprovider_states.goprovider_utils.gostate_store.gotypes.govouchers.go
events
gen
market
state
stmgr
store
sub
sync.gosync_test.gotypes
vm
cli
cmd
lotus-fountain
lotus-storage-miner
lotus-townhall
lotus
extern
gen
@ -6,6 +6,7 @@ executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: circleci/golang:1.13
|
||||
resource_class: 2xlarge
|
||||
|
||||
commands:
|
||||
install-deps:
|
||||
@ -15,6 +16,8 @@ commands:
|
||||
prepare:
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo apt-get update
|
||||
- run: sudo apt-get install ocl-icd-opencl-dev
|
||||
- run: git submodule sync
|
||||
- run: git submodule update --init
|
||||
download-params:
|
||||
@ -22,14 +25,14 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore parameters cache
|
||||
keys:
|
||||
- 'v2-lotus-params-{{ checksum "build/proof-params/parameters.json" }}'
|
||||
- 'v2-lotus-params-'
|
||||
- 'v15-lotus-params-{{ checksum "build/proof-params/parameters.json" }}'
|
||||
- 'v15-lotus-params-'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
- run: ./lotus fetch-params
|
||||
- save_cache:
|
||||
name: Save parameters cache
|
||||
key: 'v2-lotus-params-{{ checksum "build/proof-params/parameters.json" }}'
|
||||
key: 'v15-lotus-params-{{ checksum "build/proof-params/parameters.json" }}'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
|
||||
@ -155,7 +158,7 @@ jobs:
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
version: 2.1
|
||||
ci:
|
||||
jobs:
|
||||
- lint-changes:
|
||||
|
2
Makefile
2
Makefile
@ -100,7 +100,7 @@ townhall:
|
||||
rm -f townhall
|
||||
go build -o townhall ./cmd/lotus-townhall
|
||||
(cd ./cmd/lotus-townhall/townhall && npm i && npm run build)
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec townhall -i ./cmd/lotus-townhall
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec townhall -i ./cmd/lotus-townhall -i ./build
|
||||
.PHONY: townhall
|
||||
|
||||
fountain:
|
||||
|
57
README.md
57
README.md
@ -21,7 +21,35 @@ In order to run lotus, please do the following:
|
||||
- bzr (some go dependency needs this)
|
||||
- jq
|
||||
- pkg-config
|
||||
- opencl-icd-loader
|
||||
- opencl driver (like nvidia-opencl on arch) (for GPU acceleration)
|
||||
- opencl-headers (build)
|
||||
- rustup (proofs build)
|
||||
- llvm (proofs build)
|
||||
- clang (proofs build)
|
||||
|
||||
Arch (run):
|
||||
```sh
|
||||
sudo pacman -Syu opencl-icd-loader
|
||||
```
|
||||
|
||||
Arch (build):
|
||||
```sh
|
||||
sudo pacman -Syu go gcc git bzr jq pkg-config opencl-icd-loader opencl-headers
|
||||
```
|
||||
|
||||
Ubuntu / Debian (run):
|
||||
```sh
|
||||
sudo apt update
|
||||
sudo apt install mesa-opencl-icd ocl-icd-opencl-dev
|
||||
```
|
||||
|
||||
Ubuntu (build):
|
||||
```sh
|
||||
sudo add-apt-repository ppa:longsleep/golang-backports
|
||||
sudo apt update
|
||||
sudo apt install golang-go gcc git bzr jq pkg-config mesa-opencl-icd ocl-icd-opencl-dev
|
||||
```
|
||||
|
||||
2. Clone this repo & `cd` into it
|
||||
```
|
||||
@ -31,7 +59,7 @@ $ cd lotus/
|
||||
|
||||
3. Build and install the source code
|
||||
```
|
||||
$ make
|
||||
$ make clean all
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
@ -61,16 +89,12 @@ $ lotus net peers | wc -l
|
||||
2 # number of peers
|
||||
```
|
||||
|
||||
[wait for the chain to finish syncing]
|
||||
|
||||
You can follow sync status with:
|
||||
Wait for the chain to finish syncing:
|
||||
```sh
|
||||
$ watch lotus sync status
|
||||
$ lotus sync wait
|
||||
```
|
||||
|
||||
then view latest block height along with other network metrics at the https://lotus-metrics.kittyhawk.wtf/chain.
|
||||
|
||||
[It may take a few minutes for the chain to finish syncing. You will see `Height: 0` until the full chain is synced and validated.]
|
||||
You can view latest block height along with other network metrics at the https://lotus-metrics.kittyhawk.wtf/chain.
|
||||
|
||||
### Basics
|
||||
|
||||
@ -92,19 +116,20 @@ $ lotus wallet balance [optional address (t3...)]
|
||||
|
||||
### Mining
|
||||
|
||||
Ensure that at least one BLS address (`t3..`) in your wallet has enough funds to
|
||||
cover pledge collateral:
|
||||
Ensure that at least one BLS address (`t3..`) in your wallet exists
|
||||
```sh
|
||||
$ lotus state pledge-collateral
|
||||
1234
|
||||
$ lotus wallet balance [t3...]
|
||||
8999
|
||||
$ lotus wallet list
|
||||
t3...
|
||||
```
|
||||
(Balance must be higher than the returned pledge collateral for the next step to work)
|
||||
With this address, go to https://lotus-faucet.kittyhawk.wtf/miner.html, and
|
||||
click `Create Miner`
|
||||
|
||||
Wait for a page telling you the address of the newly created storage miner to
|
||||
appear - It should be saying: `New storage miners address is: t0..`
|
||||
|
||||
Initialize storage miner:
|
||||
```sh
|
||||
$ lotus-storage-miner init --owner=t3...
|
||||
$ lotus-storage-miner init --actor=t01.. --owner=t3....
|
||||
```
|
||||
This command should return successfully after miner is setup on-chain (30-60s)
|
||||
|
||||
|
52
api/api_common.go
Normal file
52
api/api_common.go
Normal file
@ -0,0 +1,52 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
type Common interface {
|
||||
// Auth
|
||||
AuthVerify(ctx context.Context, token string) ([]Permission, error)
|
||||
AuthNew(ctx context.Context, perms []Permission) ([]byte, error)
|
||||
|
||||
// network
|
||||
|
||||
NetConnectedness(context.Context, peer.ID) (network.Connectedness, error)
|
||||
NetPeers(context.Context) ([]peer.AddrInfo, error)
|
||||
NetConnect(context.Context, peer.AddrInfo) error
|
||||
NetAddrsListen(context.Context) (peer.AddrInfo, error)
|
||||
NetDisconnect(context.Context, peer.ID) error
|
||||
|
||||
// ID returns peerID of libp2p node backing this API
|
||||
ID(context.Context) (peer.ID, error)
|
||||
|
||||
// Version provides information about API provider
|
||||
Version(context.Context) (Version, error)
|
||||
}
|
||||
|
||||
// Version provides various build-time information
|
||||
type Version struct {
|
||||
Version string
|
||||
|
||||
// APIVersion is a binary encoded semver version of the remote implementing
|
||||
// this api
|
||||
//
|
||||
// See APIVersion in build/version.go
|
||||
APIVersion uint32
|
||||
|
||||
// TODO: git commit / os / genesis cid?
|
||||
|
||||
// Seconds
|
||||
BlockDelay uint64
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
vM, vm, vp := build.VersionInts(v.APIVersion)
|
||||
return fmt.Sprintf("%s+api%d.%d.%d", v.Version, vM, vm, vp)
|
||||
}
|
@ -2,45 +2,17 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(SealedRef{})
|
||||
}
|
||||
|
||||
type Common interface {
|
||||
// Auth
|
||||
AuthVerify(ctx context.Context, token string) ([]Permission, error)
|
||||
AuthNew(ctx context.Context, perms []Permission) ([]byte, error)
|
||||
|
||||
// network
|
||||
|
||||
NetConnectedness(context.Context, peer.ID) (network.Connectedness, error)
|
||||
NetPeers(context.Context) ([]peer.AddrInfo, error)
|
||||
NetConnect(context.Context, peer.AddrInfo) error
|
||||
NetAddrsListen(context.Context) (peer.AddrInfo, error)
|
||||
NetDisconnect(context.Context, peer.ID) error
|
||||
|
||||
// ID returns peerID of libp2p node backing this API
|
||||
ID(context.Context) (peer.ID, error)
|
||||
|
||||
// Version provides information about API provider
|
||||
Version(context.Context) (Version, error)
|
||||
}
|
||||
|
||||
// FullNode API is a low-level interface to the Filecoin network full node
|
||||
type FullNode interface {
|
||||
Common
|
||||
@ -101,10 +73,11 @@ type FullNode interface {
|
||||
|
||||
// ClientImport imports file under the specified path into filestore
|
||||
ClientImport(ctx context.Context, path string) (cid.Cid, error)
|
||||
ClientStartDeal(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error)
|
||||
ClientStartDeal(ctx context.Context, data cid.Cid, miner address.Address, epochPrice types.BigInt, blocksDuration uint64) (*cid.Cid, error)
|
||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
|
||||
ClientListDeals(ctx context.Context) ([]DealInfo, error)
|
||||
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
|
||||
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error) // TODO: specify serialization mode we want (defaults to unixfs for now)
|
||||
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, path string) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error)
|
||||
|
||||
@ -122,16 +95,24 @@ type FullNode interface {
|
||||
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)
|
||||
|
||||
StateMinerSectors(context.Context, address.Address) ([]*SectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*SectorInfo, error)
|
||||
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error)
|
||||
StateMinerPower(context.Context, address.Address, *types.TipSet) (MinerPower, error)
|
||||
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error)
|
||||
StateMinerProvingPeriodEnd(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
|
||||
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
|
||||
StatePledgeCollateral(context.Context, *types.TipSet) (types.BigInt, error)
|
||||
StateWaitMsg(context.Context, cid.Cid) (*MsgWait, error)
|
||||
StateListMiners(context.Context, *types.TipSet) ([]address.Address, error)
|
||||
StateListActors(context.Context, *types.TipSet) ([]address.Address, error)
|
||||
StateMarketBalance(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error)
|
||||
StateMarketParticipants(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error)
|
||||
StateMarketDeals(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error)
|
||||
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
|
||||
|
||||
MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error
|
||||
// MarketFreeBalance
|
||||
|
||||
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
|
||||
PaychList(context.Context) ([]address.Address, error)
|
||||
@ -147,45 +128,6 @@ type FullNode interface {
|
||||
PaychVoucherSubmit(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error)
|
||||
}
|
||||
|
||||
// StorageMiner is a low-level interface to the Filecoin network storage miner node
|
||||
type StorageMiner interface {
|
||||
Common
|
||||
|
||||
ActorAddress(context.Context) (address.Address, error)
|
||||
|
||||
// Temp api for testing
|
||||
StoreGarbageData(context.Context) (uint64, error)
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
SectorsStatus(context.Context, uint64) (sectorbuilder.SectorSealingStatus, error)
|
||||
|
||||
// List all staged sectors
|
||||
SectorsList(context.Context) ([]uint64, error)
|
||||
|
||||
// Seal all staged sectors
|
||||
SectorsStagedSeal(context.Context) error
|
||||
|
||||
SectorsRefs(context.Context) (map[string][]SealedRef, error)
|
||||
}
|
||||
|
||||
// Version provides various build-time information
|
||||
type Version struct {
|
||||
Version string
|
||||
|
||||
// APIVersion is a binary encoded semver version of the remote implementing
|
||||
// this api
|
||||
//
|
||||
// See APIVersion in build/version.go
|
||||
APIVersion uint32
|
||||
|
||||
// TODO: git commit / os / genesis cid?
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
vM, vm, vp := build.VersionInts(v.APIVersion)
|
||||
return fmt.Sprintf("%s+api%d.%d.%d", v.Version, vM, vm, vp)
|
||||
}
|
||||
|
||||
type Import struct {
|
||||
Status filestore.Status
|
||||
Key cid.Cid
|
||||
@ -196,14 +138,13 @@ type Import struct {
|
||||
type DealInfo struct {
|
||||
ProposalCid cid.Cid
|
||||
State DealState
|
||||
Miner address.Address
|
||||
Provider address.Address
|
||||
|
||||
PieceRef cid.Cid
|
||||
CommP []byte
|
||||
PieceRef []byte // cid bytes
|
||||
Size uint64
|
||||
|
||||
TotalPrice types.BigInt
|
||||
Duration uint64
|
||||
PricePerEpoch types.BigInt
|
||||
Duration uint64
|
||||
}
|
||||
|
||||
type MsgWait struct {
|
||||
@ -223,7 +164,7 @@ type Message struct {
|
||||
Message *types.Message
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
type ChainSectorInfo struct {
|
||||
SectorID uint64
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
@ -271,12 +212,6 @@ type MinerPower struct {
|
||||
TotalPower types.BigInt
|
||||
}
|
||||
|
||||
type SealedRef struct {
|
||||
Piece string
|
||||
Offset uint64
|
||||
Size uint32
|
||||
}
|
||||
|
||||
type QueryOffer struct {
|
||||
Err string
|
||||
|
96
api/api_storage.go
Normal file
96
api/api_storage.go
Normal file
@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
)
|
||||
|
||||
// alias because cbor-gen doesn't like non-alias types
|
||||
type SectorState = uint64
|
||||
|
||||
const (
|
||||
UndefinedSectorState SectorState = iota
|
||||
|
||||
Empty // TODO: Is this useful
|
||||
Packing // sector not in sealStore, and not on chain
|
||||
|
||||
Unsealed // sealing / queued
|
||||
PreCommitting // on chain pre-commit
|
||||
PreCommitted // waiting for seed
|
||||
Committing
|
||||
Proving
|
||||
|
||||
SectorNoUpdate = UndefinedSectorState
|
||||
)
|
||||
|
||||
func SectorStateStr(s SectorState) string {
|
||||
switch s {
|
||||
case UndefinedSectorState:
|
||||
return "UndefinedSectorState"
|
||||
case Empty:
|
||||
return "Empty"
|
||||
case Packing:
|
||||
return "Packing"
|
||||
case Unsealed:
|
||||
return "Unsealed"
|
||||
case PreCommitting:
|
||||
return "PreCommitting"
|
||||
case PreCommitted:
|
||||
return "PreCommitted"
|
||||
case Committing:
|
||||
return "Committing"
|
||||
case Proving:
|
||||
return "Proving"
|
||||
}
|
||||
return fmt.Sprintf("<Unknown %d>", s)
|
||||
}
|
||||
|
||||
// StorageMiner is a low-level interface to the Filecoin network storage miner node
|
||||
type StorageMiner interface {
|
||||
Common
|
||||
|
||||
ActorAddress(context.Context) (address.Address, error)
|
||||
|
||||
// Temp api for testing
|
||||
StoreGarbageData(context.Context) error
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
SectorsStatus(context.Context, uint64) (SectorInfo, error)
|
||||
|
||||
// List all staged sectors
|
||||
SectorsList(context.Context) ([]uint64, error)
|
||||
|
||||
SectorsRefs(context.Context) (map[string][]SealedRef, error)
|
||||
|
||||
WorkerStats(context.Context) (WorkerStats, error)
|
||||
}
|
||||
|
||||
type WorkerStats struct {
|
||||
Free int
|
||||
Reserved int // for PoSt
|
||||
Total int
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID uint64
|
||||
State SectorState
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
Proof []byte
|
||||
Deals []uint64
|
||||
Ticket sectorbuilder.SealTicket
|
||||
Seed sectorbuilder.SealSeed
|
||||
}
|
||||
|
||||
type SealedRef struct {
|
||||
Piece string
|
||||
Offset uint64
|
||||
Size uint64
|
||||
}
|
||||
|
||||
type SealedRefs struct {
|
||||
Refs []SealedRef
|
||||
}
|
271
api/cbor_gen.go
Normal file
271
api/cbor_gen.go
Normal file
@ -0,0 +1,271 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
/* This file was generated by github.com/whyrusleeping/cbor-gen */
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Channel (address.Address) (struct)
|
||||
if err := t.Channel.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.ChannelMessage (cid.Cid) (struct)
|
||||
|
||||
if t.ChannelMessage == nil {
|
||||
if _, err := w.Write(cbg.CborNull); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCid(w, *t.ChannelMessage); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ChannelMessage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Vouchers)))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Vouchers {
|
||||
if err := v.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Channel (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.ChannelMessage (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
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.t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
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)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Piece (string) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Piece)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Piece)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Offset (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Offset))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Size (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Size))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Piece (string) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Piece = string(sval)
|
||||
}
|
||||
// t.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.t.Size (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.Size = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{129}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Refs ([]api.SealedRef) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Refs)))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Refs {
|
||||
if err := v.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Refs ([]api.SealedRef) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
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++ {
|
||||
|
||||
var v SealedRef
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Refs[i] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -3,11 +3,11 @@ package api
|
||||
import (
|
||||
"context"
|
||||
|
||||
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@ -81,24 +81,32 @@ type FullNodeStruct struct {
|
||||
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
|
||||
ClientFindData func(ctx context.Context, root cid.Cid) ([]QueryOffer, error) `perm:"read"`
|
||||
ClientStartDeal func(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order RetrievalOrder, path string) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) `perm:"read"`
|
||||
|
||||
StateMinerSectors func(context.Context, address.Address) ([]*SectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, *types.TipSet) ([]*SectorInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"`
|
||||
StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"`
|
||||
StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"`
|
||||
StateMinerProvingPeriodEnd func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateReplay func(context.Context, *types.TipSet, cid.Cid) (*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) (*ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateMinerSectors func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"`
|
||||
StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"`
|
||||
StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"`
|
||||
StateMinerProvingPeriodEnd 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"`
|
||||
StateReplay func(context.Context, *types.TipSet, cid.Cid) (*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) (*ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateMarketBalance func(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketParticipants func(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketDeals func(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error) `perm:"read"`
|
||||
StateMarketStorageDeal func(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error) `perm:"read"`
|
||||
|
||||
MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"`
|
||||
|
||||
PaychGet func(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error) `perm:"sign"`
|
||||
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
|
||||
@ -122,13 +130,13 @@ type StorageMinerStruct struct {
|
||||
Internal struct {
|
||||
ActorAddress func(context.Context) (address.Address, error) `perm:"read"`
|
||||
|
||||
StoreGarbageData func(context.Context) (uint64, error) `perm:"write"`
|
||||
StoreGarbageData func(context.Context) error `perm:"write"`
|
||||
|
||||
SectorsStatus func(context.Context, uint64) (sectorbuilder.SectorSealingStatus, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]uint64, error) `perm:"read"`
|
||||
SectorsStagedSeal func(context.Context) error `perm:"write"`
|
||||
SectorsStatus func(context.Context, uint64) (SectorInfo, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]uint64, error) `perm:"read"`
|
||||
SectorsRefs func(context.Context) (map[string][]SealedRef, error) `perm:"read"`
|
||||
|
||||
SectorsRefs func(context.Context) (map[string][]SealedRef, error) `perm:"read"`
|
||||
WorkerStats func(context.Context) (WorkerStats, error) `perm:"read"`
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +197,9 @@ func (c *FullNodeStruct) ClientFindData(ctx context.Context, root cid.Cid) ([]Qu
|
||||
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) {
|
||||
return c.Internal.ClientStartDeal(ctx, data, miner, price, blocksDuration)
|
||||
}
|
||||
func (c *FullNodeStruct) ClientGetDealInfo(ctx context.Context, deal cid.Cid) (*DealInfo, error) {
|
||||
return c.Internal.ClientGetDealInfo(ctx, deal)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]DealInfo, error) {
|
||||
return c.Internal.ClientListDeals(ctx)
|
||||
@ -334,11 +345,11 @@ func (c *FullNodeStruct) SyncSubmitBlock(ctx context.Context, blk *types.BlockMs
|
||||
return c.Internal.SyncSubmitBlock(ctx, blk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address) ([]*SectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr)
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*SectorInfo, error) {
|
||||
func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerProvingSet(ctx, addr, ts)
|
||||
}
|
||||
|
||||
@ -353,10 +364,15 @@ func (c *FullNodeStruct) StateMinerWorker(ctx context.Context, m address.Address
|
||||
func (c *FullNodeStruct) StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) {
|
||||
return c.Internal.StateMinerPeerID(ctx, m, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerProvingPeriodEnd(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
||||
return c.Internal.StateMinerProvingPeriodEnd(ctx, actor, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
||||
return c.Internal.StateMinerSectorSize(ctx, actor, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
return c.Internal.StateCall(ctx, msg, ts)
|
||||
}
|
||||
@ -388,6 +404,26 @@ func (c *FullNodeStruct) StateListActors(ctx context.Context, ts *types.TipSet)
|
||||
return c.Internal.StateListActors(ctx, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (actors.StorageParticipantBalance, error) {
|
||||
return c.Internal.StateMarketBalance(ctx, addr, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, ts *types.TipSet) (map[string]actors.StorageParticipantBalance, error) {
|
||||
return c.Internal.StateMarketParticipants(ctx, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, ts *types.TipSet) (map[string]actors.OnChainDeal, error) {
|
||||
return c.Internal.StateMarketDeals(ctx, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketStorageDeal(ctx context.Context, dealid uint64, ts *types.TipSet) (*actors.OnChainDeal, error) {
|
||||
return c.Internal.StateMarketStorageDeal(ctx, dealid, ts)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, amt)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error) {
|
||||
return c.Internal.PaychGet(ctx, from, to, ensureFunds)
|
||||
}
|
||||
@ -440,12 +476,12 @@ func (c *StorageMinerStruct) ActorAddress(ctx context.Context) (address.Address,
|
||||
return c.Internal.ActorAddress(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StoreGarbageData(ctx context.Context) (uint64, error) {
|
||||
func (c *StorageMinerStruct) StoreGarbageData(ctx context.Context) error {
|
||||
return c.Internal.StoreGarbageData(ctx)
|
||||
}
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid uint64) (sectorbuilder.SectorSealingStatus, error) {
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid uint64) (SectorInfo, error) {
|
||||
return c.Internal.SectorsStatus(ctx, sid)
|
||||
}
|
||||
|
||||
@ -454,15 +490,14 @@ func (c *StorageMinerStruct) SectorsList(ctx context.Context) ([]uint64, error)
|
||||
return c.Internal.SectorsList(ctx)
|
||||
}
|
||||
|
||||
// Seal all staged sectors
|
||||
func (c *StorageMinerStruct) SectorsStagedSeal(ctx context.Context) error {
|
||||
return c.Internal.SectorsStagedSeal(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) SectorsRefs(ctx context.Context) (map[string][]SealedRef, error) {
|
||||
return c.Internal.SectorsRefs(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerStats(ctx context.Context) (WorkerStats, error) {
|
||||
return c.Internal.WorkerStats(ctx)
|
||||
}
|
||||
|
||||
var _ Common = &CommonStruct{}
|
||||
var _ FullNode = &FullNodeStruct{}
|
||||
var _ StorageMiner = &StorageMinerStruct{}
|
||||
|
96
api/test/deals.go
Normal file
96
api/test/deals.go
Normal file
@ -0,0 +1,96 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
func TestDealFlow(t *testing.T, b APIBuilder) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
logging.SetAllLoggers(logging.LevelInfo)
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, []int{0})
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
r := io.LimitReader(rand.New(rand.NewSource(17)), 1000)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString("t0101")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("mining a block now")
|
||||
if err := n[0].MineOne(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
deal, err := client.ClientStartDeal(ctx, fcid, maddr, types.NewInt(40000000), 100)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||
time.Sleep(time.Second)
|
||||
loop:
|
||||
for {
|
||||
di, err := client.ClientGetDealInfo(ctx, *deal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch di.State {
|
||||
case api.DealRejected:
|
||||
t.Fatal("deal rejected")
|
||||
case api.DealFailed:
|
||||
t.Fatal("deal failed")
|
||||
case api.DealError:
|
||||
t.Fatal("deal errored")
|
||||
case api.DealComplete:
|
||||
fmt.Println("COMPLETE", di)
|
||||
break loop
|
||||
}
|
||||
fmt.Println("Deal state: ", api.DealStates[di.State])
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
26
api/types.go
26
api/types.go
@ -6,26 +6,36 @@ import (
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
type DealState int
|
||||
type DealState = uint64
|
||||
|
||||
const (
|
||||
DealUnknown = DealState(iota)
|
||||
DealRejected
|
||||
DealAccepted
|
||||
DealStarted
|
||||
DealUnknown = DealState(iota)
|
||||
DealRejected // Provider didn't like the proposal
|
||||
DealAccepted // Proposal accepted, data moved
|
||||
DealStaged // Data put into the sector
|
||||
DealSealing // Data in process of being sealed
|
||||
|
||||
DealFailed
|
||||
DealStaged
|
||||
DealSealing
|
||||
DealComplete
|
||||
|
||||
// Internal
|
||||
|
||||
DealError // deal failed with an unexpected error
|
||||
DealExpired
|
||||
|
||||
DealNoUpdate = DealUnknown
|
||||
)
|
||||
|
||||
var DealStates = []string{
|
||||
"DealUnknown",
|
||||
"DealRejected",
|
||||
"DealAccepted",
|
||||
"DealStaged",
|
||||
"DealSealing",
|
||||
"DealFailed",
|
||||
"DealComplete",
|
||||
"DealError",
|
||||
}
|
||||
|
||||
// TODO: check if this exists anywhere else
|
||||
type MultiaddrSlice []ma.Multiaddr
|
||||
|
||||
|
28
api/utils.go
Normal file
28
api/utils.go
Normal file
@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
|
||||
type Signer func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
|
||||
type Signable interface {
|
||||
Sign(context.Context, SignFunc) error
|
||||
}
|
||||
|
||||
func SignWith(ctx context.Context, signer Signer, addr address.Address, signable ...Signable) error {
|
||||
for _, s := range signable {
|
||||
err := s.Sign(ctx, func(ctx context.Context, b []byte) (*types.Signature, error) {
|
||||
return signer(ctx, addr, b)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1 +1 @@
|
||||
/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWQ3NBPgTbXFRGnmpZUpYBSPJtwJfxuAFHBuWJ8LumCYf2
|
||||
/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWLM9S94b9mnHD2fbWvQzXudoapHjbBbFA1T4QVft65Kym
|
||||
|
@ -1 +1 @@
|
||||
/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWGU8C1mFsEtz4bXmHUH3kQTnQnxVy8cigwGV94qCpYJw7
|
||||
/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWB4pVpuRAcW2873V6m3uPWevKcZ1VWr1FGPUTwDZUN5mg
|
||||
|
Binary file not shown.
@ -8,16 +8,20 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/xerrors"
|
||||
pb "gopkg.in/cheggaaa/pb.v1"
|
||||
)
|
||||
|
||||
var log = logging.Logger("build")
|
||||
|
||||
const gateway = "http://198.211.99.118/ipfs/"
|
||||
//const gateway = "http://198.211.99.118/ipfs/"
|
||||
const gateway = "https://ipfs.io/ipfs/"
|
||||
const paramdir = "/var/tmp/filecoin-proof-parameters"
|
||||
|
||||
type paramFile struct {
|
||||
@ -26,6 +30,13 @@ type paramFile struct {
|
||||
SectorSize uint64 `json:"sector_size"`
|
||||
}
|
||||
|
||||
type fetch struct {
|
||||
wg sync.WaitGroup
|
||||
fetchLk sync.Mutex
|
||||
|
||||
errs []error
|
||||
}
|
||||
|
||||
func GetParams(storage bool) error {
|
||||
if err := os.Mkdir(paramdir, 0755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
@ -38,49 +49,87 @@ func GetParams(storage bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ft := &fetch{}
|
||||
|
||||
for name, info := range params {
|
||||
if info.SectorSize != SectorSize {
|
||||
if !SupportedSectorSize(info.SectorSize) {
|
||||
continue
|
||||
}
|
||||
if !storage && strings.HasSuffix(name, ".params") {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := maybeFetch(name, info); err != nil {
|
||||
return err
|
||||
}
|
||||
ft.maybeFetchAsync(name, info)
|
||||
}
|
||||
|
||||
return nil
|
||||
return ft.wait()
|
||||
}
|
||||
|
||||
func maybeFetch(name string, info paramFile) error {
|
||||
path := filepath.Join(paramdir, name)
|
||||
f, err := os.Open(path)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
func (ft *fetch) maybeFetchAsync(name string, info paramFile) {
|
||||
ft.wg.Add(1)
|
||||
|
||||
h := blake2b.New512()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return err
|
||||
go func() {
|
||||
defer ft.wg.Done()
|
||||
|
||||
path := filepath.Join(paramdir, name)
|
||||
|
||||
err := ft.checkFile(path, info)
|
||||
if !os.IsNotExist(err) && err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sum := h.Sum(nil)
|
||||
strSum := hex.EncodeToString(sum[:16])
|
||||
if strSum == info.Digest {
|
||||
return nil
|
||||
}
|
||||
ft.fetchLk.Lock()
|
||||
defer ft.fetchLk.Unlock()
|
||||
|
||||
log.Warnf("Checksum mismatch in param file %s, %s != %s", name, strSum, info.Digest)
|
||||
if err := doFetch(path, info); err != nil {
|
||||
ft.errs = append(ft.errs, xerrors.Errorf("fetching file %s: %w", path, err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (ft *fetch) checkFile(path string, info paramFile) error {
|
||||
if os.Getenv("TRUST_PARAMS") == "1" {
|
||||
log.Warn("Assuming parameter files are ok. DO NOT USE IN PRODUCTION")
|
||||
return nil
|
||||
}
|
||||
|
||||
return doFetch(path, info)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := blake2b.New512()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sum := h.Sum(nil)
|
||||
strSum := hex.EncodeToString(sum[:16])
|
||||
if strSum == info.Digest {
|
||||
log.Infof("Parameter file %s is ok", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
return xerrors.Errorf("checksum mismatch in param file %s, %s != %s", path, strSum, info.Digest)
|
||||
}
|
||||
|
||||
func (ft *fetch) wait() error {
|
||||
ft.wg.Wait()
|
||||
return multierr.Combine(ft.errs...)
|
||||
}
|
||||
|
||||
func doFetch(out string, info paramFile) error {
|
||||
log.Infof("Fetching %s", out)
|
||||
gw := os.Getenv("IPFS_GATEWAY")
|
||||
if gw == "" {
|
||||
gw = gateway
|
||||
}
|
||||
log.Infof("Fetching %s from %s", out, gw)
|
||||
|
||||
resp, err := http.Get(gateway + info.Cid)
|
||||
resp, err := http.Get(gw + info.Cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package build
|
||||
|
||||
import "math/big"
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Core network constants
|
||||
|
||||
@ -10,7 +12,20 @@ import "math/big"
|
||||
const UnixfsChunkSize uint64 = 1 << 20
|
||||
const UnixfsLinksPerLevel = 1024
|
||||
|
||||
const SectorSize = 16 << 20
|
||||
var SectorSizes = []uint64{
|
||||
16 << 20,
|
||||
256 << 20,
|
||||
1 << 30,
|
||||
}
|
||||
|
||||
func SupportedSectorSize(ssize uint64) bool {
|
||||
for _, ss := range SectorSizes {
|
||||
if ssize == ss {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// /////
|
||||
// Payments
|
||||
@ -18,19 +33,11 @@ const SectorSize = 16 << 20
|
||||
// Blocks
|
||||
const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours
|
||||
|
||||
// Blocks
|
||||
const DealVoucherSkewLimit = 10
|
||||
|
||||
// Blocks
|
||||
const MinDealVoucherIncrement = ProvingPeriodDuration
|
||||
|
||||
const MaxVouchersPerDeal = 768 // roughly one voucher per 10h over a year
|
||||
|
||||
// /////
|
||||
// Consensus / Network
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 30
|
||||
const BlockDelay = 10
|
||||
|
||||
// Seconds
|
||||
const AllowableClockDrift = BlockDelay * 2
|
||||
@ -39,43 +46,68 @@ const AllowableClockDrift = BlockDelay * 2
|
||||
const ForkLengthThreshold = 100
|
||||
|
||||
// Blocks (e)
|
||||
const BlocksPerEpoch = 1
|
||||
const BlocksPerEpoch = 5
|
||||
|
||||
// Blocks
|
||||
const Finality = 500
|
||||
|
||||
// constants for Weight calculation
|
||||
// The ratio of weight contributed by short-term vs long-term factors in a given round
|
||||
const WRatioNum = int64(1)
|
||||
const WRatioDen = 2
|
||||
|
||||
// /////
|
||||
// Proofs / Mining
|
||||
// Proofs
|
||||
|
||||
// Blocks
|
||||
const RandomnessLookback = 20
|
||||
const ProvingPeriodDuration = 160
|
||||
|
||||
// PoStChallangeTime sets the window in which post computation should happen
|
||||
// Blocks
|
||||
const PoStChallangeTime = ProvingPeriodDuration - 6
|
||||
|
||||
// PoStRandomnessLookback is additional randomness lookback for PoSt computation
|
||||
// To compute randomness epoch in a given proving period:
|
||||
// RandH = PPE - PoStChallangeTime - PoStRandomnessLookback
|
||||
//
|
||||
// Blocks
|
||||
const PoStRandomnessLookback = 1
|
||||
|
||||
// Blocks
|
||||
const ProvingPeriodDuration = 40
|
||||
const SealRandomnessLookback = Finality
|
||||
|
||||
// Blocks
|
||||
const PoSTChallangeTime = 20
|
||||
const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000
|
||||
|
||||
// /////
|
||||
// Mining
|
||||
|
||||
// Blocks
|
||||
const EcRandomnessLookback = 300
|
||||
|
||||
const PowerCollateralProportion = 5
|
||||
const PerCapitaCollateralProportion = 1
|
||||
const CollateralPrecision = 1000
|
||||
|
||||
// Blocks
|
||||
const InteractivePoRepDelay = 10
|
||||
|
||||
// /////
|
||||
// Devnet settings
|
||||
|
||||
const TotalFilecoin = 2000000000
|
||||
const MiningRewardTotal = 1400000000
|
||||
const TotalFilecoin = 2_000_000_000
|
||||
const MiningRewardTotal = 1_400_000_000
|
||||
|
||||
const InitialRewardStr = "153856861913558700202"
|
||||
|
||||
var InitialReward *big.Int
|
||||
|
||||
const FilecoinPrecision = 1000000000000000000
|
||||
const FilecoinPrecision = 1_000_000_000_000_000_000
|
||||
|
||||
// six years
|
||||
// Blocks
|
||||
const HalvingPeriodBlocks = 6 * 365 * 24 * 60 * 2
|
||||
|
||||
// Blocks
|
||||
const AdjustmentPeriod = 7 * 24 * 60 * 2
|
||||
|
||||
// TODO: Move other important consts here
|
||||
|
||||
func init() {
|
||||
|
@ -1,82 +1,82 @@
|
||||
{
|
||||
"v12-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.params": {
|
||||
"cid": "QmZ6Y88jRbRjjYQzhh8o85bcUeChj7NGyo9yK6VbywhJ9F",
|
||||
"digest": "3d245479d9b5fc668d58c493da5f3ee1",
|
||||
"v15-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.params": {
|
||||
"cid": "QmT22f1Np1GpW29NXD7Zrv3Ae4poMYhmkDjyscqL8QrJXY",
|
||||
"digest": "989fd8d989e0f7f1fe21bb010cf1b231",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.vk": {
|
||||
"cid": "QmbGgBLMTCnRc1E1fsUCPyZE4SYzrtAYEWCqCtL55AgxE2",
|
||||
"digest": "29bd4c152096f878f41257b433159d81",
|
||||
"v15-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.vk": {
|
||||
"cid": "QmVqSdc23to4UwduCCb25223rpSccvtcgPMfRKY1qjucDc",
|
||||
"digest": "c6d258c37243b8544238a98100e3e399",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.params": {
|
||||
"cid": "Qmdm8vhWeRsZUUaHdysDr91gv6u6RFeC18hHxGnnzPrwcW",
|
||||
"digest": "c67fd415a65e6d1caf4278597cf3462e",
|
||||
"v15-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.params": {
|
||||
"cid": "QmRTCqgokEGTMfWVaSr7qFXTNotmpd2QBEi8RsvSQKmPLz",
|
||||
"digest": "ff77a5e270afc6e1c7fbc19e48348fac",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.vk": {
|
||||
"cid": "Qmc3xssw1syaZDZKmiM7TbCRnoDPfmD6V8ec1eTESidJQS",
|
||||
"digest": "870355c10000010b9a4ece80892acca2",
|
||||
"v15-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.vk": {
|
||||
"cid": "QmRssVAXRN3xp9VdSpTq1pNjkob3QiikoFZiM5hqrmh1VU",
|
||||
"digest": "b41f35ac26224258e366327716a835a4",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.params": {
|
||||
"cid": "QmZBvF2F9wTYKLBxWSCQKe34D3M7vkNNc7ou8mxnNhZkZc",
|
||||
"digest": "5d854e0ecfbd12cb7fa1247a6e6a0315",
|
||||
"v15-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.params": {
|
||||
"cid": "QmYNVRVzjXkuxJfnHTU5vmEcUBQf8dabXZ4m53SzqMkBv5",
|
||||
"digest": "d156b685e4a1fe3a1f7230b6a39b5ad4",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.vk": {
|
||||
"cid": "QmZfdHrnk2oN3Gx7hhjpRGXu8qY6FcqLjpHQ6jt1BBDg5R",
|
||||
"digest": "aca566faa466f05fb9d622bec39e4b6d",
|
||||
"v15-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.vk": {
|
||||
"cid": "QmaCEcsCFVuepMKdC5WURbr5ucEyLMNGxQaB7HqSnr2KGh",
|
||||
"digest": "06ff067ac78cdab5d7bbc82170882241",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.params": {
|
||||
"cid": "QmYdGGwQXpaBGTVWXqMFVXUP2CZhtsV29jxPkRm54ArAdT",
|
||||
"digest": "eb2d3c4cb7b32c87ead5326bcbd495f3",
|
||||
"v15-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.params": {
|
||||
"cid": "QmVuabRvJ797NwLisGKwRURASGxopBBgg4rfNsbZoSYzAc",
|
||||
"digest": "0e1ceb79a459a60508f480e5b1fed7ac",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v12-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.vk": {
|
||||
"cid": "QmeTtWQ2hCUq34BpHTy21jJqVqHbPJdNhQRqW4SF4ZNA7v",
|
||||
"digest": "c83eca165ba94233861227578d658a22",
|
||||
"v15-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.vk": {
|
||||
"cid": "QmdWENZBAbuUty1vVNn9vmvj1XbJ5UC8qzpcVD35s5AJxG",
|
||||
"digest": "1b755c74b9d6823c014f6a7ef76249f2",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-0ed875801b4a99e4a6b46e58703e6857277ce020510fc9080041f6cfd5e0d286.params": {
|
||||
"cid": "QmTkeyz3mfec4MqCbbiwuVWAQicY231zpdxSjdxN5U84PD",
|
||||
"digest": "cf7118ac2273e2ccb6b451a5acd5f6e0",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-0ed875801b4a99e4a6b46e58703e6857277ce020510fc9080041f6cfd5e0d286.vk": {
|
||||
"cid": "QmaGzJCwJNpQAD63QAKb8y4MKS1AMHeKyYzg3C5WyRSRRM",
|
||||
"digest": "288c792f4fe09c85f8f35673fb9d5ed0",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-5efcf852a15bd74808bc65d6f2df146de817baea96c96e3b752e6a3349957644.params": {
|
||||
"cid": "QmNSuxq15JPFCTehxVpgJydNZ79rpLoNwnLzQMGA9EziXg",
|
||||
"digest": "818cd9cc2e0e47210a05bd073847ab5a",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-5efcf852a15bd74808bc65d6f2df146de817baea96c96e3b752e6a3349957644.vk": {
|
||||
"cid": "Qmbc8LcydZXsVqQrkNMeLEu31Vxi1VigQGJ2ehytxWPALH",
|
||||
"digest": "a6636e2ee1a176161e022296bc045e79",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-6496c180c2eab89ee0638bc73879ced01daf512486eb38b9e1c9402ba578e010.params": {
|
||||
"cid": "QmUBpwbVwu5uzjTS8n1yesJ2QUaVbWJ8D9p2VaSsfjgAUr",
|
||||
"digest": "e7aa73a1b06290d30f567bfac8324bf1",
|
||||
"v15-stacked-proof-of-replication-0c0b444c6f31d11c8e98003cc99a3b938db26b77a296d4253cda8945c234266d.params": {
|
||||
"cid": "QmZDVpWTw5Eti5pE7N5z1Cmqsw8hPXhUcvG3cQuceK56LH",
|
||||
"digest": "6aa80306018ea1328f2d6faf8c080734",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-6496c180c2eab89ee0638bc73879ced01daf512486eb38b9e1c9402ba578e010.vk": {
|
||||
"cid": "Qme2uK8sQJUT3DzF291p2b91eFQzn7cKSJFiKVBsvrjTgt",
|
||||
"digest": "d318cd116803c8ccbd3ac4cded9400ad",
|
||||
"v15-stacked-proof-of-replication-0c0b444c6f31d11c8e98003cc99a3b938db26b77a296d4253cda8945c234266d.vk": {
|
||||
"cid": "QmaoXV7iVSJcfZ5qubYy7NBcXDSdnTzxH85d7M4bdDtfGZ",
|
||||
"digest": "f6832eb736faf2960e920d32e9780b12",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-a09b5cf44f640589b1b02cf823fa28269850342bcefa4878189b9b5c9ec4d2bb.params": {
|
||||
"cid": "QmTfhTnkFvbpFfw8UydFdnPCDfxgAxEcw4fRdGsELpcFnh",
|
||||
"digest": "906b6c0c9dc5bb581d9641c11b54e197",
|
||||
"v15-stacked-proof-of-replication-967b11bb59be11b7dc6f2b627520ba450a3aa50846dbbf886cb8b735fe25c4e7.params": {
|
||||
"cid": "QmbUW3a3q5DHBb7Ufk8iSbnSCZgbwpe3serqfwKmcTd11w",
|
||||
"digest": "64024e461b07c869df2463d33dd28035",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v15-stacked-proof-of-replication-967b11bb59be11b7dc6f2b627520ba450a3aa50846dbbf886cb8b735fe25c4e7.vk": {
|
||||
"cid": "Qme3QgBBE7hUgrK7G9ZfJhzkbvViN5HALFpFduYs5K1piv",
|
||||
"digest": "32496f4dc434b0ed9ef49cb62497a7d1",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v15-stacked-proof-of-replication-d01cd22091627b721c60a3375b5219af653fb9f6928c70aa7400587d396bc07a.params": {
|
||||
"cid": "QmZzgJmb8WXYDKxS22HDgnoBYcZzXDC7s2c2zsV7kouNZ9",
|
||||
"digest": "cd91f7ccb2ff57a06f3375946dcbdc68",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v15-stacked-proof-of-replication-d01cd22091627b721c60a3375b5219af653fb9f6928c70aa7400587d396bc07a.vk": {
|
||||
"cid": "QmRUMVzFnENbvyNb6aN2AJ2dnnewr1ESGA1UQLMVZZdsJM",
|
||||
"digest": "92fc84b76dbe69c731518aebcb82ac82",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v15-stacked-proof-of-replication-f464b92d805d03de6e2c20e2530135b2c8ec96045ec58f342d6feb90282bff8a.params": {
|
||||
"cid": "QmSixsGkxJXTAuFfWKy5aEXEDJEnpcb1GkdQVF8TCPWoHy",
|
||||
"digest": "f8339ae93478ded3840d0bc7efa19953",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v12-zigzag-proof-of-replication-a09b5cf44f640589b1b02cf823fa28269850342bcefa4878189b9b5c9ec4d2bb.vk": {
|
||||
"cid": "QmRDcUxfpPY9a1vR3T4vRgxHtWHyy9m3xuMQtj8P749r4e",
|
||||
"digest": "3632776cd23e376694c625390b9a73ea",
|
||||
"v15-stacked-proof-of-replication-f464b92d805d03de6e2c20e2530135b2c8ec96045ec58f342d6feb90282bff8a.vk": {
|
||||
"cid": "QmTMC8hdZ2TkZ9BFuzHzRLM9SuR2PQdUrSAwABeCuHyV2f",
|
||||
"digest": "f27f08ce1246ee6612c250bb12803ef1",
|
||||
"sector_size": 1024
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package build
|
||||
|
||||
// Version is the local build version, set by build system
|
||||
const Version = "0.0.0"
|
||||
const Version = "0.7.0"
|
||||
|
||||
// APIVersion is a hex semver version of the rpc api exposed
|
||||
//
|
||||
@ -12,7 +12,7 @@ const Version = "0.0.0"
|
||||
// R R H
|
||||
// |\vv/|
|
||||
// vv vv
|
||||
const APIVersion = 0x000001
|
||||
const APIVersion = 0x000701
|
||||
|
||||
const (
|
||||
MajorMask = 0xff0000
|
||||
|
@ -162,7 +162,7 @@ func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams)
|
||||
|
||||
func IsBuiltinActor(code cid.Cid) bool {
|
||||
switch code {
|
||||
case StorageMarketActorCodeCid, StorageMinerCodeCid, AccountActorCodeCid, InitActorCodeCid, MultisigActorCodeCid, PaymentChannelActorCodeCid:
|
||||
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@ -170,12 +170,11 @@ func IsBuiltinActor(code cid.Cid) bool {
|
||||
}
|
||||
|
||||
func IsSingletonActor(code cid.Cid) bool {
|
||||
return code == StorageMarketActorCodeCid || code == InitActorCodeCid
|
||||
return code == StoragePowerCodeCid || code == StorageMarketCodeCid || code == InitCodeCid
|
||||
}
|
||||
|
||||
func (ias *InitActorState) AddActor(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
||||
nid := ias.NextID
|
||||
ias.NextID++
|
||||
|
||||
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
||||
if err != nil {
|
||||
@ -195,6 +194,7 @@ func (ias *InitActorState) AddActor(cst *hamt.CborIpldStore, addr address.Addres
|
||||
return address.Undef, err
|
||||
}
|
||||
ias.AddressMap = ncid
|
||||
ias.NextID++
|
||||
|
||||
return NewIDAddress(nid)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package actors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
@ -18,27 +19,30 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const POST_SECTORS_COUNT = 8192
|
||||
|
||||
type StorageMinerActor struct{}
|
||||
|
||||
type StorageMinerActorState struct {
|
||||
// Contains mostly static info about this miner
|
||||
Info cid.Cid
|
||||
|
||||
// Collateral that is waiting to be withdrawn.
|
||||
DePledgedCollateral types.BigInt
|
||||
|
||||
// Time at which the depledged collateral may be withdrawn.
|
||||
DePledgeTime types.BigInt
|
||||
// PreCommittedSectors is the set of sectors that have been committed to but not
|
||||
// yet had their proofs submitted
|
||||
PreCommittedSectors map[string]*PreCommittedSector
|
||||
|
||||
// All sectors this miner has committed.
|
||||
Sectors cid.Cid
|
||||
|
||||
// TODO: Spec says 'StagedCommittedSectors', which one is it?
|
||||
|
||||
// Sectors this miner is currently mining. It is only updated
|
||||
// when a PoSt is submitted (not as each new sector commitment is added).
|
||||
ProvingSet cid.Cid
|
||||
|
||||
// TODO: these:
|
||||
// SectorTable
|
||||
// SectorExpirationQueue
|
||||
// ChallengeStatus
|
||||
|
||||
// Contains mostly static info about this miner
|
||||
Info cid.Cid
|
||||
|
||||
// Faulty sectors reported since last SubmitPost,
|
||||
// up to the current proving period's challenge time.
|
||||
CurrentFaultSet types.BitField
|
||||
@ -54,10 +58,6 @@ type StorageMinerActorState struct {
|
||||
// removal penalization is needed.
|
||||
NextDoneSet types.BitField
|
||||
|
||||
// Deals this miner has been slashed for since the last post submission.
|
||||
//TODO: unsupported map key type "Cid" (if you want to use struct keys, your atlas needs a transform to string)
|
||||
//ArbitratedDeals map[cid.Cid]struct{}
|
||||
|
||||
// Amount of power this miner has.
|
||||
Power types.BigInt
|
||||
|
||||
@ -89,63 +89,76 @@ type MinerInfo struct {
|
||||
PeerID peer.ID
|
||||
|
||||
// Amount of space in each sector committed to the network by this miner.
|
||||
SectorSize types.BigInt
|
||||
SectorSize uint64
|
||||
|
||||
// SubsectorCount
|
||||
}
|
||||
|
||||
type PreCommittedSector struct {
|
||||
Info SectorPreCommitInfo
|
||||
ReceivedEpoch uint64
|
||||
}
|
||||
|
||||
type StorageMinerConstructorParams struct {
|
||||
Owner address.Address
|
||||
Worker address.Address
|
||||
SectorSize types.BigInt
|
||||
SectorSize uint64
|
||||
PeerID peer.ID
|
||||
}
|
||||
|
||||
type maMethods struct {
|
||||
Constructor uint64
|
||||
CommitSector uint64
|
||||
SubmitPoSt uint64
|
||||
SlashStorageFault uint64
|
||||
GetCurrentProvingSet uint64
|
||||
ArbitrateDeal uint64
|
||||
DePledge uint64
|
||||
GetOwner uint64
|
||||
GetWorkerAddr uint64
|
||||
GetPower uint64
|
||||
GetPeerID uint64
|
||||
GetSectorSize uint64
|
||||
UpdatePeerID uint64
|
||||
ChangeWorker uint64
|
||||
IsSlashed uint64
|
||||
IsLate uint64
|
||||
PaymentVerifyInclusion uint64
|
||||
PaymentVerifySector uint64
|
||||
AddFaults uint64
|
||||
SlashConsensusFault uint64
|
||||
type SectorPreCommitInfo struct {
|
||||
SectorNumber uint64
|
||||
|
||||
CommR []byte // TODO: Spec says CID
|
||||
SealEpoch uint64
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
|
||||
type maMethods struct {
|
||||
Constructor uint64
|
||||
PreCommitSector uint64
|
||||
ProveCommitSector uint64
|
||||
SubmitPoSt uint64
|
||||
SlashStorageFault uint64
|
||||
GetCurrentProvingSet uint64
|
||||
ArbitrateDeal uint64
|
||||
DePledge uint64
|
||||
GetOwner uint64
|
||||
GetWorkerAddr uint64
|
||||
GetPower uint64
|
||||
GetPeerID uint64
|
||||
GetSectorSize uint64
|
||||
UpdatePeerID uint64
|
||||
ChangeWorker uint64
|
||||
IsSlashed uint64
|
||||
IsLate uint64
|
||||
DeclareFaults uint64
|
||||
SlashConsensusFault uint64
|
||||
}
|
||||
|
||||
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
||||
|
||||
func (sma StorageMinerActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: sma.StorageMinerConstructor,
|
||||
2: sma.CommitSector,
|
||||
3: sma.SubmitPoSt,
|
||||
//4: sma.SlashStorageFault,
|
||||
//5: sma.GetCurrentProvingSet,
|
||||
//6: sma.ArbitrateDeal,
|
||||
//7: sma.DePledge,
|
||||
8: sma.GetOwner,
|
||||
9: sma.GetWorkerAddr,
|
||||
10: sma.GetPower,
|
||||
11: sma.GetPeerID,
|
||||
12: sma.GetSectorSize,
|
||||
13: sma.UpdatePeerID,
|
||||
//14: sma.ChangeWorker,
|
||||
//15: sma.IsSlashed,
|
||||
//16: sma.IsLate,
|
||||
17: sma.PaymentVerifyInclusion,
|
||||
18: sma.PaymentVerifySector,
|
||||
19: sma.AddFaults,
|
||||
20: sma.SlashConsensusFault,
|
||||
2: sma.PreCommitSector,
|
||||
3: sma.ProveCommitSector,
|
||||
4: sma.SubmitPoSt,
|
||||
//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.IsLate,
|
||||
18: sma.DeclareFaults,
|
||||
19: sma.SlashConsensusFault,
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,16 +218,78 @@ func (sma StorageMinerActor) StorageMinerConstructor(act *types.Actor, vmctx typ
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type CommitSectorParams struct {
|
||||
SectorID uint64
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
CommRStar []byte
|
||||
Proof []byte
|
||||
func (sma StorageMinerActor) 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 commit 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(),
|
||||
}
|
||||
|
||||
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 StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContext, params *CommitSectorParams) ([]byte, ActorError) {
|
||||
ctx := context.TODO()
|
||||
func uintToStringKey(i uint64) string {
|
||||
buf := make([]byte, 10)
|
||||
n := binary.PutUvarint(buf, i)
|
||||
return string(buf[:n])
|
||||
}
|
||||
|
||||
type SectorProveCommitInfo struct {
|
||||
Proof []byte
|
||||
SectorID uint64
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor) 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
|
||||
@ -225,36 +300,53 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: this needs to get normalized to either the ID address or the actor address
|
||||
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 ok, err := ValidatePoRep(maddr, mi.SectorSize, params); err != nil {
|
||||
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.Wrap(err, "failed to compute data commitment")
|
||||
}
|
||||
|
||||
if ok, err := ValidatePoRep(maddr, mi.SectorSize, commD, us.Info.CommR, ticket, params.Proof, seed, params.SectorID); err != nil {
|
||||
return nil, err
|
||||
} else if !ok {
|
||||
return nil, aerrors.New(1, "bad proof!")
|
||||
}
|
||||
|
||||
// make sure the miner isnt trying to submit a pre-existing sector
|
||||
unique, err := SectorIsUnique(ctx, vmctx.Storage(), self.Sectors, params.SectorID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !unique {
|
||||
return nil, aerrors.New(2, "sector already committed!")
|
||||
}
|
||||
|
||||
// Power of the miner after adding this sector
|
||||
futurePower := types.BigAdd(self.Power, mi.SectorSize)
|
||||
collateralRequired := CollateralForPower(futurePower)
|
||||
|
||||
if act.Balance.LessThan(collateralRequired) {
|
||||
return nil, aerrors.New(3, "not enough collateral")
|
||||
return nil, aerrors.Newf(2, "porep proof was invalid (t:%x; s:%x(%d); p:%x)", ticket, seed, us.ReceivedEpoch+build.InteractivePoRepDelay, 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 := AddToSectorSet(ctx, vmctx.Storage(), self.Sectors, params.SectorID, params.CommR, params.CommD)
|
||||
nssroot, err := AddToSectorSet(ctx, vmctx.Storage(), self.Sectors, params.SectorID, us.Info.CommR, commD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -264,7 +356,7 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
// 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 'CommitSector' call take multiple sectors at a time.
|
||||
// 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.
|
||||
@ -286,7 +378,15 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
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, err
|
||||
}
|
||||
|
||||
type SubmitPoStParams struct {
|
||||
@ -346,7 +446,7 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
|
||||
|
||||
var seed [sectorbuilder.CommLen]byte
|
||||
{
|
||||
randHeight := currentProvingPeriodEnd - build.PoSTChallangeTime
|
||||
randHeight := currentProvingPeriodEnd - build.PoStChallangeTime - build.PoStRandomnessLookback
|
||||
if vmctx.BlockHeight() <= randHeight {
|
||||
// TODO: spec, retcode
|
||||
return nil, aerrors.Newf(1, "submit PoSt called outside submission window (%d < %d)", vmctx.BlockHeight(), randHeight)
|
||||
@ -393,7 +493,7 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
|
||||
|
||||
faults := self.CurrentFaultSet.All()
|
||||
|
||||
if ok, lerr := sectorbuilder.VerifyPost(mi.SectorSize.Uint64(),
|
||||
if ok, lerr := sectorbuilder.VerifyPost(mi.SectorSize,
|
||||
sectorbuilder.NewSortedSectorInfo(sectorInfos), seed, params.Proof,
|
||||
faults); !ok || lerr != nil {
|
||||
if lerr != nil {
|
||||
@ -426,14 +526,14 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
|
||||
|
||||
oldPower := self.Power
|
||||
self.Power = types.BigMul(types.NewInt(pss.Count-uint64(len(faults))),
|
||||
mi.SectorSize)
|
||||
types.NewInt(mi.SectorSize))
|
||||
|
||||
enc, err := SerializeParams(&UpdateStorageParams{Delta: types.BigSub(self.Power, oldPower)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(StorageMarketAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc)
|
||||
_, err = vmctx.Send(StoragePowerAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -477,6 +577,8 @@ func AddToSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID u
|
||||
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")
|
||||
}
|
||||
@ -511,8 +613,8 @@ func GetFromSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID
|
||||
return true, comms[0], comms[1], nil
|
||||
}
|
||||
|
||||
func ValidatePoRep(maddr address.Address, ssize types.BigInt, params *CommitSectorParams) (bool, ActorError) {
|
||||
ok, err := sectorbuilder.VerifySeal(ssize.Uint64(), params.CommR, params.CommD, params.CommRStar, maddr, params.SectorID, params.Proof)
|
||||
func ValidatePoRep(maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, ActorError) {
|
||||
ok, err := sectorbuilder.VerifySeal(ssize, commR, commD, maddr, ticket, seed, sectorID, proof)
|
||||
if err != nil {
|
||||
return false, aerrors.Absorb(err, 25, "verify seal failed")
|
||||
}
|
||||
@ -626,7 +728,7 @@ func (sma StorageMinerActor) GetSectorSize(act *types.Actor, vmctx types.VMConte
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mi.SectorSize.Bytes(), nil
|
||||
return types.NewInt(mi.SectorSize).Bytes(), nil
|
||||
}
|
||||
|
||||
type PaymentVerifyParams struct {
|
||||
@ -634,95 +736,17 @@ type PaymentVerifyParams struct {
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
type PieceInclVoucherData struct { // TODO: Update spec at https://github.com/filecoin-project/specs/blob/master/actors.md#paymentverify
|
||||
CommP []byte
|
||||
PieceSize types.BigInt
|
||||
}
|
||||
|
||||
type InclusionProof struct {
|
||||
Sector uint64 // for CommD, also verifies the sector is in sector set
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor) PaymentVerifyInclusion(act *types.Actor, vmctx types.VMContext, params *PaymentVerifyParams) ([]byte, ActorError) {
|
||||
// params.Extra - PieceInclVoucherData
|
||||
// params.Proof - InclusionProof
|
||||
|
||||
_, self, aerr := loadState(vmctx)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
mi, aerr := loadMinerInfo(vmctx, self)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
var voucherData PieceInclVoucherData
|
||||
if err := cbor.DecodeInto(params.Extra, &voucherData); err != nil {
|
||||
return nil, aerrors.Absorb(err, 2, "failed to decode storage voucher data for verification")
|
||||
}
|
||||
var proof InclusionProof
|
||||
if err := cbor.DecodeInto(params.Proof, &proof); err != nil {
|
||||
return nil, aerrors.Absorb(err, 3, "failed to decode storage payment proof")
|
||||
}
|
||||
|
||||
ok, _, commD, aerr := GetFromSectorSet(context.TODO(), vmctx.Storage(), self.Sectors, proof.Sector)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
if !ok {
|
||||
return nil, aerrors.New(4, "miner does not have required sector")
|
||||
}
|
||||
|
||||
ok, err := sectorbuilder.VerifyPieceInclusionProof(mi.SectorSize.Uint64(), voucherData.PieceSize.Uint64(), voucherData.CommP, commD, proof.Proof)
|
||||
if err != nil {
|
||||
return nil, aerrors.Absorb(err, 5, "verify piece inclusion proof failed")
|
||||
}
|
||||
if !ok {
|
||||
return nil, aerrors.New(6, "piece inclusion proof was invalid")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor) PaymentVerifySector(act *types.Actor, vmctx types.VMContext, params *PaymentVerifyParams) ([]byte, ActorError) {
|
||||
// params.Extra - BigInt - sector id
|
||||
// params.Proof - nil
|
||||
|
||||
_, self, aerr := loadState(vmctx)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
// TODO: ensure no sector ID reusability within related deal lifetime
|
||||
sector := types.BigFromBytes(params.Extra)
|
||||
|
||||
if len(params.Proof) > 0 {
|
||||
return nil, aerrors.New(1, "unexpected proof bytes")
|
||||
}
|
||||
|
||||
ok, _, _, aerr := GetFromSectorSet(context.TODO(), vmctx.Storage(), self.Sectors, sector.Uint64())
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
if !ok {
|
||||
return nil, aerrors.New(2, "miner does not have required sector")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type AddFaultsParams struct {
|
||||
type DeclareFaultsParams struct {
|
||||
Faults types.BitField
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor) AddFaults(act *types.Actor, vmctx types.VMContext, params *AddFaultsParams) ([]byte, ActorError) {
|
||||
func (sma StorageMinerActor) DeclareFaults(act *types.Actor, vmctx types.VMContext, params *DeclareFaultsParams) ([]byte, ActorError) {
|
||||
oldstate, self, aerr := loadState(vmctx)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
challengeHeight := self.ProvingPeriodEnd - build.PoSTChallangeTime
|
||||
challengeHeight := self.ProvingPeriodEnd - build.PoStChallangeTime
|
||||
|
||||
if vmctx.BlockHeight() < challengeHeight {
|
||||
// TODO: optimized bitfield methods
|
||||
@ -753,7 +777,7 @@ type MinerSlashConsensusFault struct {
|
||||
}
|
||||
|
||||
func (sma StorageMinerActor) SlashConsensusFault(act *types.Actor, vmctx types.VMContext, params *MinerSlashConsensusFault) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != StorageMarketAddress {
|
||||
if vmctx.Message().From != StoragePowerAddress {
|
||||
return nil, aerrors.New(1, "SlashConsensusFault may only be called by the storage market actor")
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,29 @@ type MultiSigActorState struct {
|
||||
Required uint64
|
||||
NextTxID uint64
|
||||
|
||||
InitialBalance types.BigInt
|
||||
StartingBlock uint64
|
||||
UnlockDuration uint64
|
||||
|
||||
//TODO: make this map/sharray/whatever
|
||||
Transactions []MTransaction
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) canSpend(act *types.Actor, amnt types.BigInt, height uint64) bool {
|
||||
if msas.UnlockDuration == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
offset := height - msas.StartingBlock
|
||||
if offset > msas.UnlockDuration {
|
||||
return true
|
||||
}
|
||||
|
||||
minBalance := types.BigDiv(msas.InitialBalance, types.NewInt(msas.UnlockDuration))
|
||||
minBalance = types.BigMul(minBalance, types.NewInt(offset))
|
||||
return !minBalance.LessThan(types.BigSub(act.Balance, amnt))
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) isSigner(addr address.Address) bool {
|
||||
for _, s := range msas.Signers {
|
||||
if s == addr {
|
||||
@ -29,11 +48,11 @@ func (msas MultiSigActorState) isSigner(addr address.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) getTransaction(txid uint64) *MTransaction {
|
||||
if txid > uint64(len(msas.Transactions)) {
|
||||
return nil
|
||||
func (msas MultiSigActorState) getTransaction(txid uint64) (*MTransaction, ActorError) {
|
||||
if txid >= uint64(len(msas.Transactions)) {
|
||||
return nil, aerrors.Newf(1, "could not get transaction (numbers of tx %d,want to get txid %d)", len(msas.Transactions), txid)
|
||||
}
|
||||
return &msas.Transactions[txid]
|
||||
return &msas.Transactions[txid], nil
|
||||
}
|
||||
|
||||
type MTransaction struct {
|
||||
@ -90,8 +109,9 @@ func (msa MultiSigActor) Exports() []interface{} {
|
||||
}
|
||||
|
||||
type MultiSigConstructorParams struct {
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
UnlockDuration uint64
|
||||
}
|
||||
|
||||
func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext,
|
||||
@ -100,6 +120,13 @@ func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext
|
||||
Signers: params.Signers,
|
||||
Required: params.Required,
|
||||
}
|
||||
|
||||
if params.UnlockDuration != 0 {
|
||||
self.InitialBalance = vmctx.Message().Value
|
||||
self.UnlockDuration = params.UnlockDuration
|
||||
self.StartingBlock = vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
head, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not put new head")
|
||||
@ -176,9 +203,16 @@ func (msa MultiSigActor) Propose(act *types.Actor, vmctx types.VMContext,
|
||||
}
|
||||
self.Transactions = append(self.Transactions, tx)
|
||||
}
|
||||
tx := self.getTransaction(txid)
|
||||
|
||||
tx, err := self.getTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.Required == 1 {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
@ -209,7 +243,11 @@ func (msa MultiSigActor) Approve(act *types.Actor, vmctx types.VMContext,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx := self.getTransaction(params.TxID)
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not approve")
|
||||
}
|
||||
@ -221,6 +259,9 @@ func (msa MultiSigActor) Approve(act *types.Actor, vmctx types.VMContext,
|
||||
}
|
||||
tx.Approved = append(tx.Approved, vmctx.Message().From)
|
||||
if uint64(len(tx.Approved)) >= self.Required {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
@ -240,7 +281,11 @@ func (msa MultiSigActor) Cancel(act *types.Actor, vmctx types.VMContext,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx := self.getTransaction(params.TxID)
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not cancel")
|
||||
}
|
||||
@ -376,6 +421,11 @@ func (msa MultiSigActor) ChangeRequirement(act *types.Actor, vmctx types.VMConte
|
||||
if params.Req < 1 {
|
||||
return nil, aerrors.New(5, "requirement must be at least 1")
|
||||
}
|
||||
|
||||
if params.Req > uint64(len(self.Signers)) {
|
||||
return nil, aerrors.New(6, "requirement must be at most the numbers of signers")
|
||||
}
|
||||
|
||||
self.Required = params.Req
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func TestMultiSigCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.MultisigActorCodeCid,
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.MultisigCodeCid,
|
||||
&actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
Required: 2,
|
||||
@ -49,7 +49,7 @@ func TestMultiSigOps(t *testing.T) {
|
||||
HarnessAddr(&sig1Addr, 100000),
|
||||
HarnessAddr(&sig2Addr, 100000),
|
||||
HarnessAddr(&outsideAddr, 100000),
|
||||
HarnessActor(&multSigAddr, &creatorAddr, actors.MultisigActorCodeCid,
|
||||
HarnessActor(&multSigAddr, &creatorAddr, actors.MultisigCodeCid,
|
||||
func() cbg.CBORMarshaler {
|
||||
return &actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
|
@ -18,7 +18,7 @@ func TestPaychCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelActorCodeCid,
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
@ -47,7 +47,7 @@ func TestPaychUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelActorCodeCid,
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
|
653
chain/actors/actor_storagemarket.go
Normal file
653
chain/actors/actor_storagemarket.go
Normal file
@ -0,0 +1,653 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
)
|
||||
|
||||
type StorageMarketActor struct{}
|
||||
|
||||
type smaMethods struct {
|
||||
Constructor uint64
|
||||
WithdrawBalance uint64
|
||||
AddBalance uint64
|
||||
CheckLockedBalance uint64
|
||||
PublishStorageDeals uint64
|
||||
HandleCronAction uint64
|
||||
SettleExpiredDeals uint64
|
||||
ProcessStorageDealsPayment uint64
|
||||
SlashStorageDealCollateral uint64
|
||||
GetLastExpirationFromDealIDs uint64
|
||||
ActivateStorageDeals uint64
|
||||
ComputeDataCommitment uint64
|
||||
}
|
||||
|
||||
var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||||
|
||||
func (sma StorageMarketActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
2: sma.WithdrawBalance,
|
||||
3: sma.AddBalance,
|
||||
// 4: sma.CheckLockedBalance,
|
||||
5: sma.PublishStorageDeals,
|
||||
// 6: sma.HandleCronAction,
|
||||
// 7: sma.SettleExpiredDeals,
|
||||
// 8: sma.ProcessStorageDealsPayment,
|
||||
// 9: sma.SlashStorageDealCollateral,
|
||||
// 10: sma.GetLastExpirationFromDealIDs,
|
||||
11: sma.ActivateStorageDeals, // TODO: move under PublishStorageDeals after specs team approves
|
||||
12: sma.ComputeDataCommitment,
|
||||
}
|
||||
}
|
||||
|
||||
type StorageParticipantBalance struct {
|
||||
Locked types.BigInt
|
||||
Available types.BigInt
|
||||
}
|
||||
|
||||
type StorageMarketState struct {
|
||||
Balances cid.Cid // hamt<addr, StorageParticipantBalance>
|
||||
Deals cid.Cid // amt<StorageDeal>
|
||||
|
||||
NextDealID uint64 // TODO: spec
|
||||
}
|
||||
|
||||
// TODO: Drop in favour of car storage
|
||||
type SerializationMode = uint64
|
||||
|
||||
const (
|
||||
SerializationUnixFSv0 = iota
|
||||
// IPLD / car
|
||||
)
|
||||
|
||||
type StorageDealProposal struct {
|
||||
PieceRef []byte // cid bytes // TODO: spec says to use cid.Cid, probably not a good idea
|
||||
PieceSize uint64
|
||||
PieceSerialization SerializationMode // Needs to be here as it tells how data in the sector maps to PieceRef cid
|
||||
|
||||
Client address.Address
|
||||
Provider address.Address
|
||||
|
||||
ProposalExpiration uint64
|
||||
Duration uint64 // TODO: spec
|
||||
|
||||
StoragePricePerEpoch types.BigInt
|
||||
StorageCollateral types.BigInt
|
||||
|
||||
ProposerSignature *types.Signature
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) TotalStoragePrice() types.BigInt {
|
||||
return types.BigMul(sdp.StoragePricePerEpoch, types.NewInt(sdp.Duration))
|
||||
}
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
|
||||
func (sdp *StorageDealProposal) Sign(ctx context.Context, sign SignFunc) error {
|
||||
if sdp.ProposerSignature != nil {
|
||||
return xerrors.New("signature already present in StorageDealProposal")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := sdp.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
sig, err := sign(ctx, buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sdp.ProposerSignature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) Verify() error {
|
||||
unsigned := *sdp
|
||||
unsigned.ProposerSignature = nil
|
||||
var buf bytes.Buffer
|
||||
if err := unsigned.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sdp.ProposerSignature.Verify(sdp.Client, buf.Bytes())
|
||||
}
|
||||
|
||||
func (d *StorageDeal) Sign(ctx context.Context, sign SignFunc) error {
|
||||
var buf bytes.Buffer
|
||||
if err := d.Proposal.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
sig, err := sign(ctx, buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CounterSignature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *StorageDeal) Verify(proposerWorker address.Address) error {
|
||||
var buf bytes.Buffer
|
||||
if err := d.Proposal.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.CounterSignature.Verify(proposerWorker, buf.Bytes())
|
||||
}
|
||||
|
||||
type StorageDeal struct {
|
||||
Proposal StorageDealProposal
|
||||
CounterSignature *types.Signature
|
||||
}
|
||||
|
||||
type OnChainDeal struct {
|
||||
Deal StorageDeal
|
||||
ActivationEpoch uint64 // 0 = inactive
|
||||
}
|
||||
|
||||
type WithdrawBalanceParams struct {
|
||||
Balance types.BigInt
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) WithdrawBalance(act *types.Actor, vmctx types.VMContext, params *WithdrawBalanceParams) ([]byte, ActorError) {
|
||||
// TODO: (spec) this should be 2-stage
|
||||
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
if balance.Available.LessThan(params.Balance) {
|
||||
return nil, aerrors.Newf(1, "can not withdraw more funds than available: %s > %s", params.Balance, b[0].Available)
|
||||
}
|
||||
|
||||
balance.Available = types.BigSub(balance.Available, params.Balance)
|
||||
|
||||
_, err = vmctx.Send(vmctx.Message().From, 0, params.Balance, nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "sending funds failed")
|
||||
}
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
balance.Available = types.BigAdd(balance.Available, vmctx.Message().Value)
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func setMarketBalances(vmctx types.VMContext, nd *hamt.Node, set map[address.Address]StorageParticipantBalance) (cid.Cid, ActorError) {
|
||||
for addr, b := range set {
|
||||
balance := b // to stop linter complaining
|
||||
if err := nd.Set(vmctx.Context(), string(addr.Bytes()), &balance); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "setting new balance")
|
||||
}
|
||||
}
|
||||
if err := nd.Flush(vmctx.Context()); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "flushing balance hamt")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(vmctx.Context(), nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to balances storage")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func GetMarketBalances(ctx context.Context, store *hamt.CborIpldStore, rcid cid.Cid, addrs ...address.Address) ([]StorageParticipantBalance, *hamt.Node, ActorError) {
|
||||
nd, err := hamt.LoadNode(ctx, store, rcid)
|
||||
if err != nil {
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
out := make([]StorageParticipantBalance, len(addrs))
|
||||
|
||||
for i, a := range addrs {
|
||||
var balance StorageParticipantBalance
|
||||
err = nd.Find(ctx, string(a.Bytes()), &balance)
|
||||
switch err {
|
||||
case hamt.ErrNotFound:
|
||||
out[i] = StorageParticipantBalance{
|
||||
Locked: types.NewInt(0),
|
||||
Available: types.NewInt(0),
|
||||
}
|
||||
case nil:
|
||||
out[i] = balance
|
||||
default:
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to do set lookup")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return out, nd, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) CheckLockedBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
type PublishStorageDealsParams struct {
|
||||
Deals []StorageDeal
|
||||
}
|
||||
|
||||
type PublishStorageDealResponse struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *PublishStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// todo: handle duplicate deals
|
||||
|
||||
if len(params.Deals) == 0 {
|
||||
return nil, aerrors.New(1, "no storage deals in params.Deals")
|
||||
}
|
||||
|
||||
out := PublishStorageDealResponse{
|
||||
DealIDs: make([]uint64, len(params.Deals)),
|
||||
}
|
||||
|
||||
workerBytes, aerr := vmctx.Send(params.Deals[0].Proposal.Provider, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
// TODO: REVIEW: Do we want to check if provider exists in the power actor?
|
||||
|
||||
for i, deal := range params.Deals {
|
||||
if err := self.validateDeal(vmctx, deal, providerWorker); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := deals.Set(self.NextDealID, &OnChainDeal{Deal: deal})
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal in deal AMT")
|
||||
}
|
||||
out.DealIDs[i] = self.NextDealID
|
||||
|
||||
self.NextDealID++
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
if err := out.MarshalCBOR(&outBuf); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "serialising output")
|
||||
}
|
||||
|
||||
return outBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (st *StorageMarketState) validateDeal(vmctx types.VMContext, deal StorageDeal, providerWorker address.Address) aerrors.ActorError {
|
||||
if vmctx.BlockHeight() > deal.Proposal.ProposalExpiration {
|
||||
return aerrors.New(1, "deal proposal already expired")
|
||||
}
|
||||
|
||||
if err := deal.Proposal.Verify(); err != nil {
|
||||
return aerrors.Absorb(err, 2, "verifying proposer signature")
|
||||
}
|
||||
|
||||
err := deal.Verify(providerWorker)
|
||||
if err != nil {
|
||||
return aerrors.Absorb(err, 2, "verifying provider signature")
|
||||
}
|
||||
|
||||
// TODO: maybe this is actually fine
|
||||
if vmctx.Message().From != providerWorker && vmctx.Message().From != deal.Proposal.Client {
|
||||
return aerrors.New(4, "message not sent by deal participant")
|
||||
}
|
||||
|
||||
// TODO: do some caching (changes gas so needs to be in spec too)
|
||||
b, bnd, aerr := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), st.Balances, deal.Proposal.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return aerrors.Wrap(aerr, "getting client, and provider balances")
|
||||
}
|
||||
clientBalance := b[0]
|
||||
providerBalance := b[1]
|
||||
|
||||
totalPrice := deal.Proposal.TotalStoragePrice()
|
||||
|
||||
if clientBalance.Available.LessThan(totalPrice) {
|
||||
return aerrors.Newf(5, "client doesn't have enough available funds to cover storage price; %d < %d", clientBalance.Available, totalPrice)
|
||||
}
|
||||
|
||||
clientBalance = lockFunds(clientBalance, totalPrice)
|
||||
|
||||
// TODO: REVIEW: Not clear who pays for this
|
||||
if providerBalance.Available.LessThan(deal.Proposal.StorageCollateral) {
|
||||
return aerrors.Newf(6, "provider doesn't have enough available funds to cover StorageCollateral; %d < %d", providerBalance.Available, deal.Proposal.StorageCollateral)
|
||||
}
|
||||
|
||||
providerBalance = lockFunds(providerBalance, deal.Proposal.StorageCollateral)
|
||||
|
||||
// TODO: piece checks (e.g. size > sectorSize)?
|
||||
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
deal.Proposal.Client: clientBalance,
|
||||
providerWorker: providerBalance,
|
||||
})
|
||||
if aerr != nil {
|
||||
return aerr
|
||||
}
|
||||
|
||||
st.Balances = bcid
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ActivateStorageDealsParams struct {
|
||||
Deals []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ActivateStorageDeals(act *types.Actor, vmctx types.VMContext, params *ActivateStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
for _, deal := range params.Deals {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(3, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if vmctx.Message().From != dealInfo.Deal.Proposal.Provider {
|
||||
return nil, aerrors.New(1, "ActivateStorageDeals can only be called by the deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.Deal.Proposal.ProposalExpiration {
|
||||
return nil, aerrors.New(2, "deal cannot be activated: proposal expired")
|
||||
}
|
||||
|
||||
if dealInfo.ActivationEpoch > 0 {
|
||||
// this probably can't happen in practice
|
||||
return nil, aerrors.New(3, "deal already active")
|
||||
}
|
||||
|
||||
dealInfo.ActivationEpoch = vmctx.BlockHeight()
|
||||
|
||||
if err := deals.Set(deal, &dealInfo); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal info in AMT failed")
|
||||
}
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr := vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ProcessStorageDealsPaymentParams struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ProcessStorageDealsPayment(act *types.Actor, vmctx types.VMContext, params *ProcessStorageDealsPaymentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// TODO: Would be nice if send could assert actor type
|
||||
workerBytes, aerr := vmctx.Send(vmctx.Message().From, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(2, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Deal.Proposal.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(3, "ProcessStorageDealsPayment can only be called by deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < dealInfo.ActivationEpoch {
|
||||
// TODO: This is probably fatal
|
||||
return nil, aerrors.New(4, "ActivationEpoch lower than block height")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.ActivationEpoch+dealInfo.Deal.Proposal.Duration {
|
||||
// Deal expired, miner should drop it
|
||||
// TODO: process payment for the remainder of last proving period
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// todo: check math (written on a plane, also tired)
|
||||
// TODO: division is hard, this more than likely has some off-by-one issue
|
||||
toPay := types.BigMul(dealInfo.Deal.Proposal.StoragePricePerEpoch, types.NewInt(build.ProvingPeriodDuration))
|
||||
|
||||
b, bnd, aerr := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, dealInfo.Deal.Proposal.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
clientBal := b[0]
|
||||
providerBal := b[1]
|
||||
|
||||
clientBal.Locked, providerBal.Available = transferFunds(clientBal.Locked, providerBal.Available, toPay)
|
||||
|
||||
// TODO: call set once
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
dealInfo.Deal.Proposal.Client: clientBal,
|
||||
providerWorker: providerBal,
|
||||
})
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
}
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func lockFunds(p StorageParticipantBalance, amt types.BigInt) StorageParticipantBalance {
|
||||
p.Available, p.Locked = transferFunds(p.Available, p.Locked, amt)
|
||||
return p
|
||||
}
|
||||
|
||||
func transferFunds(from, to, amt types.BigInt) (types.BigInt, types.BigInt) {
|
||||
// TODO: some asserts
|
||||
return types.BigSub(from, amt), types.BigAdd(to, amt)
|
||||
}
|
||||
|
||||
type ComputeDataCommitmentParams struct {
|
||||
DealIDs []uint64
|
||||
SectorSize uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ComputeDataCommitment(act *types.Actor, vmctx types.VMContext, params *ComputeDataCommitmentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
var pieces []sectorbuilder.PublicPieceInfo
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(3, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Deal.Proposal.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(4, "referenced deal was not from caller")
|
||||
}
|
||||
|
||||
var commP [32]byte
|
||||
copy(commP[:], dealInfo.Deal.Proposal.PieceRef)
|
||||
|
||||
pieces = append(pieces, sectorbuilder.PublicPieceInfo{
|
||||
Size: dealInfo.Deal.Proposal.PieceSize,
|
||||
CommP: commP,
|
||||
})
|
||||
}
|
||||
|
||||
commd, err := sectorbuilder.GenerateDataCommitment(params.SectorSize, pieces)
|
||||
if err != nil {
|
||||
return nil, aerrors.Absorb(err, 5, "failed to generate data commitment from pieces")
|
||||
}
|
||||
|
||||
return commd[:], nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) HandleCronAction(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SettleExpiredDeals(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SlashStorageDealCollateral(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) GetLastExpirationFromDealIDs(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
@ -53,12 +53,12 @@ type StoragePowerState struct {
|
||||
type CreateStorageMinerParams struct {
|
||||
Owner address.Address
|
||||
Worker address.Address
|
||||
SectorSize types.BigInt
|
||||
SectorSize uint64
|
||||
PeerID peer.ID
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VMContext, params *CreateStorageMinerParams) ([]byte, ActorError) {
|
||||
if !SupportedSectorSize(params.SectorSize) {
|
||||
if !build.SupportedSectorSize(params.SectorSize) {
|
||||
return nil, aerrors.New(1, "Unsupported sector size")
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VM
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(InitActorAddress, IAMethods.Exec, vmctx.Message().Value, encoded)
|
||||
ret, err := vmctx.Send(InitAddress, IAMethods.Exec, vmctx.Message().Value, encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -116,13 +116,6 @@ func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VM
|
||||
return naddr.Bytes(), nil
|
||||
}
|
||||
|
||||
func SupportedSectorSize(ssize types.BigInt) bool {
|
||||
if ssize.Uint64() == build.SectorSize {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ArbitrateConsensusFaultParams struct {
|
||||
Block1 *types.BlockHeader
|
||||
Block2 *types.BlockHeader
|
||||
@ -336,6 +329,7 @@ func powerLookup(ctx context.Context, vmctx types.VMContext, self *StoragePowerS
|
||||
return types.EmptyInt, aerrors.New(1, "miner not registered with storage power actor")
|
||||
}
|
||||
|
||||
// TODO: Use local amt
|
||||
ret, err := vmctx.Send(miner, MAMethods.GetPower, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return types.EmptyInt, aerrors.Wrap(err, "invoke Miner.GetPower")
|
||||
|
@ -37,12 +37,12 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
// cheating the bootstrapping problem
|
||||
cheatStorageMarketTotal(t, h.vm, h.cs.Blockstore())
|
||||
|
||||
ret, _ := h.InvokeWithValue(t, ownerAddr, StorageMarketAddress, SPAMethods.CreateStorageMiner,
|
||||
ret, _ := h.InvokeWithValue(t, ownerAddr, StoragePowerAddress, SPAMethods.CreateStorageMiner,
|
||||
types.NewInt(500000),
|
||||
&CreateStorageMinerParams{
|
||||
Owner: ownerAddr,
|
||||
Worker: workerAddr,
|
||||
SectorSize: types.NewInt(build.SectorSize),
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
@ -52,7 +52,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SPAMethods.IsMiner,
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsMiner,
|
||||
&IsMinerParam{Addr: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
@ -68,7 +68,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SPAMethods.PowerLookup,
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
power := types.BigFromBytes(ret.Return)
|
||||
@ -93,7 +93,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
signBlock(t, h.w, workerAddr, b1)
|
||||
signBlock(t, h.w, workerAddr, b2)
|
||||
|
||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SPAMethods.ArbitrateConsensusFault,
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
|
||||
&ArbitrateConsensusFaultParams{
|
||||
Block1: b1,
|
||||
Block2: b2,
|
||||
@ -102,13 +102,13 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SPAMethods.PowerLookup,
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
assert.Equal(t, ret.ExitCode, byte(1))
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StorageMarketAddress, SPAMethods.IsMiner, &IsMinerParam{minerAddr})
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsMiner, &IsMinerParam{minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
assert.Equal(t, ret.Return, cbg.CborBoolFalse)
|
||||
}
|
||||
@ -117,7 +117,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||
t.Helper()
|
||||
|
||||
sma, err := vm.StateTree().GetActor(StorageMarketAddress)
|
||||
sma, err := vm.StateTree().GetActor(StoragePowerAddress)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -138,7 +138,7 @@ func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||
|
||||
sma.Head = c
|
||||
|
||||
if err := vm.StateTree().SetActor(StorageMarketAddress, sma); err != nil {
|
||||
if err := vm.StateTree().SetActor(StoragePowerAddress, sma); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,18 @@ import (
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var AccountActorCodeCid cid.Cid
|
||||
var StorageMarketActorCodeCid cid.Cid
|
||||
var AccountCodeCid cid.Cid
|
||||
var StoragePowerCodeCid cid.Cid
|
||||
var StorageMarketCodeCid cid.Cid
|
||||
var StorageMinerCodeCid cid.Cid
|
||||
var MultisigActorCodeCid cid.Cid
|
||||
var InitActorCodeCid cid.Cid
|
||||
var PaymentChannelActorCodeCid cid.Cid
|
||||
var MultisigCodeCid cid.Cid
|
||||
var InitCodeCid cid.Cid
|
||||
var PaymentChannelCodeCid cid.Cid
|
||||
|
||||
var InitActorAddress = mustIDAddress(0)
|
||||
var InitAddress = mustIDAddress(0)
|
||||
var NetworkAddress = mustIDAddress(1)
|
||||
var StorageMarketAddress = mustIDAddress(2)
|
||||
var StoragePowerAddress = mustIDAddress(2)
|
||||
var StorageMarketAddress = mustIDAddress(3) // TODO: missing from spec
|
||||
var BurntFundsAddress = mustIDAddress(99)
|
||||
|
||||
func mustIDAddress(i uint64) address.Address {
|
||||
@ -37,10 +39,11 @@ func init() {
|
||||
return c
|
||||
}
|
||||
|
||||
AccountActorCodeCid = mustSum("account")
|
||||
StorageMarketActorCodeCid = mustSum("smarket")
|
||||
StorageMinerCodeCid = mustSum("sminer")
|
||||
MultisigActorCodeCid = mustSum("multisig")
|
||||
InitActorCodeCid = mustSum("init")
|
||||
PaymentChannelActorCodeCid = mustSum("paych")
|
||||
AccountCodeCid = mustSum("fil/1/account") // TODO: spec
|
||||
StoragePowerCodeCid = mustSum("fil/1/power")
|
||||
StorageMarketCodeCid = mustSum("fil/1/market")
|
||||
StorageMinerCodeCid = mustSum("fil/1/miner")
|
||||
MultisigCodeCid = mustSum("fil/1/multisig")
|
||||
InitCodeCid = mustSum("fil/1/init")
|
||||
PaymentChannelCodeCid = mustSum("fil/1/paych")
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func TestVMInvokeMethod(t *testing.T) {
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: InitActorAddress,
|
||||
To: InitAddress,
|
||||
From: from,
|
||||
Method: IAMethods.Exec,
|
||||
Params: enc,
|
||||
@ -118,7 +118,7 @@ func TestStorageMarketActorCreateMiner(t *testing.T) {
|
||||
params := &StorageMinerConstructorParams{
|
||||
Owner: maddr,
|
||||
Worker: maddr,
|
||||
SectorSize: types.NewInt(build.SectorSize),
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
}
|
||||
var err error
|
||||
@ -128,7 +128,7 @@ func TestStorageMarketActorCreateMiner(t *testing.T) {
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: StorageMarketAddress,
|
||||
To: StoragePowerAddress,
|
||||
From: from,
|
||||
Method: SPAMethods.CreateStorageMiner,
|
||||
Params: enc,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -210,7 +210,7 @@ func (h *Harness) CreateActor(t testing.TB, from address.Address,
|
||||
t.Helper()
|
||||
|
||||
return h.Apply(t, types.Message{
|
||||
To: actors.InitActorAddress,
|
||||
To: actors.InitAddress,
|
||||
From: from,
|
||||
Method: actors.IAMethods.Exec,
|
||||
Params: DumpObject(t,
|
||||
|
237
chain/blocksync/blocksync.go
Normal file
237
chain/blocksync/blocksync.go
Normal file
@ -0,0 +1,237 @@
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
var log = logging.Logger("blocksync")
|
||||
|
||||
type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream, error)
|
||||
|
||||
const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(BlockSyncRequest{})
|
||||
cbor.RegisterCborType(BlockSyncResponse{})
|
||||
cbor.RegisterCborType(BSTipSet{})
|
||||
}
|
||||
|
||||
type BlockSyncService struct {
|
||||
cs *store.ChainStore
|
||||
}
|
||||
|
||||
type BlockSyncRequest struct {
|
||||
Start []cid.Cid
|
||||
RequestLength uint64
|
||||
|
||||
Options uint64
|
||||
}
|
||||
|
||||
type BSOptions struct {
|
||||
IncludeBlocks bool
|
||||
IncludeMessages bool
|
||||
}
|
||||
|
||||
func ParseBSOptions(optfield uint64) *BSOptions {
|
||||
return &BSOptions{
|
||||
IncludeBlocks: optfield&(BSOptBlocks) != 0,
|
||||
IncludeMessages: optfield&(BSOptMessages) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
BSOptBlocks = 1 << 0
|
||||
BSOptMessages = 1 << 1
|
||||
)
|
||||
|
||||
type BlockSyncResponse struct {
|
||||
Chain []*BSTipSet
|
||||
|
||||
Status uint64
|
||||
Message string
|
||||
}
|
||||
|
||||
type BSTipSet struct {
|
||||
Blocks []*types.BlockHeader
|
||||
|
||||
BlsMessages []*types.Message
|
||||
BlsMsgIncludes [][]uint64
|
||||
|
||||
SecpkMessages []*types.SignedMessage
|
||||
SecpkMsgIncludes [][]uint64
|
||||
}
|
||||
|
||||
func NewBlockSyncService(cs *store.ChainStore) *BlockSyncService {
|
||||
return &BlockSyncService{
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) HandleStream(s inet.Stream) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "blocksync.HandleStream")
|
||||
defer span.End()
|
||||
|
||||
defer s.Close()
|
||||
|
||||
var req BlockSyncRequest
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &req); err != nil {
|
||||
log.Errorf("failed to read block sync request: %s", err)
|
||||
return
|
||||
}
|
||||
log.Infof("block sync request for: %s %d", req.Start, req.RequestLength)
|
||||
|
||||
resp, err := bss.processRequest(ctx, &req)
|
||||
if err != nil {
|
||||
log.Error("failed to process block sync request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, resp); err != nil {
|
||||
log.Error("failed to write back response for handle stream: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blocksync.ProcessRequest")
|
||||
defer span.End()
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
if len(req.Start) == 0 {
|
||||
return &BlockSyncResponse{
|
||||
Status: 204,
|
||||
Message: "no cids given in blocksync request",
|
||||
}, nil
|
||||
}
|
||||
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("blocks", opts.IncludeBlocks),
|
||||
trace.BoolAttribute("messages", opts.IncludeMessages),
|
||||
)
|
||||
|
||||
chain, err := bss.collectChainSegment(req.Start, req.RequestLength, opts)
|
||||
if err != nil {
|
||||
log.Warn("encountered error while responding to block sync request: ", err)
|
||||
return &BlockSyncResponse{
|
||||
Status: 203,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{
|
||||
Chain: chain,
|
||||
Status: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) collectChainSegment(start []cid.Cid, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
var bstips []*BSTipSet
|
||||
cur := start
|
||||
for {
|
||||
var bst BSTipSet
|
||||
ts, err := bss.cs.LoadTipSet(cur)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed loading tipset %s: %w", cur, err)
|
||||
}
|
||||
|
||||
if opts.IncludeMessages {
|
||||
bmsgs, bmincl, smsgs, smincl, err := bss.gatherMessages(ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("gather messages failed: %w", err)
|
||||
}
|
||||
|
||||
bst.BlsMessages = bmsgs
|
||||
bst.BlsMsgIncludes = bmincl
|
||||
bst.SecpkMessages = smsgs
|
||||
bst.SecpkMsgIncludes = smincl
|
||||
}
|
||||
|
||||
if opts.IncludeBlocks {
|
||||
bst.Blocks = ts.Blocks()
|
||||
}
|
||||
|
||||
bstips = append(bstips, &bst)
|
||||
|
||||
if uint64(len(bstips)) >= length || ts.Height() == 0 {
|
||||
return bstips, nil
|
||||
}
|
||||
|
||||
cur = ts.Parents()
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
blsmsgmap := make(map[cid.Cid]uint64)
|
||||
secpkmsgmap := make(map[cid.Cid]uint64)
|
||||
var secpkmsgs []*types.SignedMessage
|
||||
var blsmsgs []*types.Message
|
||||
var secpkincl, blsincl [][]uint64
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := bss.cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
bmi := make([]uint64, 0, len(bmsgs))
|
||||
for _, m := range bmsgs {
|
||||
i, ok := blsmsgmap[m.Cid()]
|
||||
if !ok {
|
||||
i = uint64(len(blsmsgs))
|
||||
blsmsgs = append(blsmsgs, m)
|
||||
blsmsgmap[m.Cid()] = i
|
||||
}
|
||||
|
||||
bmi = append(bmi, i)
|
||||
}
|
||||
blsincl = append(blsincl, bmi)
|
||||
|
||||
smi := make([]uint64, 0, len(smsgs))
|
||||
for _, m := range smsgs {
|
||||
i, ok := secpkmsgmap[m.Cid()]
|
||||
if !ok {
|
||||
i = uint64(len(secpkmsgs))
|
||||
secpkmsgs = append(secpkmsgs, m)
|
||||
secpkmsgmap[m.Cid()] = i
|
||||
}
|
||||
|
||||
smi = append(smi, i)
|
||||
}
|
||||
secpkincl = append(secpkincl, smi)
|
||||
}
|
||||
|
||||
return blsmsgs, blsincl, secpkmsgs, secpkincl, nil
|
||||
}
|
||||
|
||||
func bstsToFullTipSet(bts *BSTipSet) (*store.FullTipSet, error) {
|
||||
fts := &store.FullTipSet{}
|
||||
for i, b := range bts.Blocks {
|
||||
fb := &types.FullBlock{
|
||||
Header: b,
|
||||
}
|
||||
for _, mi := range bts.BlsMsgIncludes[i] {
|
||||
fb.BlsMessages = append(fb.BlsMessages, bts.BlsMessages[mi])
|
||||
}
|
||||
for _, mi := range bts.SecpkMsgIncludes[i] {
|
||||
fb.SecpkMessages = append(fb.SecpkMessages, bts.SecpkMessages[mi])
|
||||
}
|
||||
|
||||
fts.Blocks = append(fts.Blocks, fb)
|
||||
}
|
||||
|
||||
return fts, nil
|
||||
}
|
@ -1,252 +1,45 @@
|
||||
package chain
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
"github.com/ipfs/go-cid"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborrpc"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream, error)
|
||||
|
||||
const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(BlockSyncRequest{})
|
||||
cbor.RegisterCborType(BlockSyncResponse{})
|
||||
cbor.RegisterCborType(BSTipSet{})
|
||||
}
|
||||
|
||||
type BlockSyncService struct {
|
||||
cs *store.ChainStore
|
||||
}
|
||||
|
||||
type BlockSyncRequest struct {
|
||||
Start []cid.Cid
|
||||
RequestLength uint64
|
||||
|
||||
Options uint64
|
||||
}
|
||||
|
||||
type BSOptions struct {
|
||||
IncludeBlocks bool
|
||||
IncludeMessages bool
|
||||
}
|
||||
|
||||
func ParseBSOptions(optfield uint64) *BSOptions {
|
||||
return &BSOptions{
|
||||
IncludeBlocks: optfield&(BSOptBlocks) != 0,
|
||||
IncludeMessages: optfield&(BSOptMessages) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
BSOptBlocks = 1 << 0
|
||||
BSOptMessages = 1 << 1
|
||||
)
|
||||
|
||||
type BlockSyncResponse struct {
|
||||
Chain []*BSTipSet
|
||||
|
||||
Status uint64
|
||||
Message string
|
||||
}
|
||||
|
||||
type BSTipSet struct {
|
||||
Blocks []*types.BlockHeader
|
||||
|
||||
BlsMessages []*types.Message
|
||||
BlsMsgIncludes [][]uint64
|
||||
|
||||
SecpkMessages []*types.SignedMessage
|
||||
SecpkMsgIncludes [][]uint64
|
||||
}
|
||||
|
||||
func NewBlockSyncService(cs *store.ChainStore) *BlockSyncService {
|
||||
return &BlockSyncService{
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) HandleStream(s inet.Stream) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "blocksync.HandleStream")
|
||||
defer span.End()
|
||||
|
||||
defer s.Close()
|
||||
|
||||
var req BlockSyncRequest
|
||||
if err := cborrpc.ReadCborRPC(bufio.NewReader(s), &req); err != nil {
|
||||
log.Errorf("failed to read block sync request: %s", err)
|
||||
return
|
||||
}
|
||||
log.Infof("block sync request for: %s %d", req.Start, req.RequestLength)
|
||||
|
||||
resp, err := bss.processRequest(ctx, &req)
|
||||
if err != nil {
|
||||
log.Error("failed to process block sync request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cborrpc.WriteCborRPC(s, resp); err != nil {
|
||||
log.Error("failed to write back response for handle stream: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blocksync.ProcessRequest")
|
||||
defer span.End()
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
if len(req.Start) == 0 {
|
||||
return &BlockSyncResponse{
|
||||
Status: 204,
|
||||
Message: "no cids given in blocksync request",
|
||||
}, nil
|
||||
}
|
||||
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("blocks", opts.IncludeBlocks),
|
||||
trace.BoolAttribute("messages", opts.IncludeMessages),
|
||||
)
|
||||
|
||||
chain, err := bss.collectChainSegment(req.Start, req.RequestLength, opts)
|
||||
if err != nil {
|
||||
log.Error("encountered error while responding to block sync request: ", err)
|
||||
return &BlockSyncResponse{
|
||||
Status: 203,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{
|
||||
Chain: chain,
|
||||
Status: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) collectChainSegment(start []cid.Cid, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
var bstips []*BSTipSet
|
||||
cur := start
|
||||
for {
|
||||
var bst BSTipSet
|
||||
ts, err := bss.cs.LoadTipSet(cur)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.IncludeMessages {
|
||||
bmsgs, bmincl, smsgs, smincl, err := bss.gatherMessages(ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("gather messages failed: %w", err)
|
||||
}
|
||||
|
||||
bst.BlsMessages = bmsgs
|
||||
bst.BlsMsgIncludes = bmincl
|
||||
bst.SecpkMessages = smsgs
|
||||
bst.SecpkMsgIncludes = smincl
|
||||
}
|
||||
|
||||
if opts.IncludeBlocks {
|
||||
bst.Blocks = ts.Blocks()
|
||||
}
|
||||
|
||||
bstips = append(bstips, &bst)
|
||||
|
||||
if uint64(len(bstips)) >= length || ts.Height() == 0 {
|
||||
return bstips, nil
|
||||
}
|
||||
|
||||
cur = ts.Parents()
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
blsmsgmap := make(map[cid.Cid]uint64)
|
||||
secpkmsgmap := make(map[cid.Cid]uint64)
|
||||
var secpkmsgs []*types.SignedMessage
|
||||
var blsmsgs []*types.Message
|
||||
var secpkincl, blsincl [][]uint64
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := bss.cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
bmi := make([]uint64, 0, len(bmsgs))
|
||||
for _, m := range bmsgs {
|
||||
i, ok := blsmsgmap[m.Cid()]
|
||||
if !ok {
|
||||
i = uint64(len(blsmsgs))
|
||||
blsmsgs = append(blsmsgs, m)
|
||||
blsmsgmap[m.Cid()] = i
|
||||
}
|
||||
|
||||
bmi = append(bmi, i)
|
||||
}
|
||||
blsincl = append(blsincl, bmi)
|
||||
|
||||
smi := make([]uint64, 0, len(smsgs))
|
||||
for _, m := range smsgs {
|
||||
i, ok := secpkmsgmap[m.Cid()]
|
||||
if !ok {
|
||||
i = uint64(len(secpkmsgs))
|
||||
secpkmsgs = append(secpkmsgs, m)
|
||||
secpkmsgmap[m.Cid()] = i
|
||||
}
|
||||
|
||||
smi = append(smi, i)
|
||||
}
|
||||
secpkincl = append(secpkincl, smi)
|
||||
}
|
||||
|
||||
return blsmsgs, blsincl, secpkmsgs, secpkincl, nil
|
||||
}
|
||||
|
||||
type BlockSync struct {
|
||||
bserv bserv.BlockService
|
||||
newStream NewStreamFunc
|
||||
bserv bserv.BlockService
|
||||
host host.Host
|
||||
|
||||
syncPeersLk sync.Mutex
|
||||
syncPeers map[peer.ID]struct{}
|
||||
syncPeers *bsPeerTracker
|
||||
}
|
||||
|
||||
func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host) *BlockSync {
|
||||
return &BlockSync{
|
||||
bserv: bserv,
|
||||
newStream: h.NewStream,
|
||||
syncPeers: make(map[peer.ID]struct{}),
|
||||
host: h,
|
||||
syncPeers: newPeerTracker(),
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *BlockSync) getPeers() []peer.ID {
|
||||
bs.syncPeersLk.Lock()
|
||||
defer bs.syncPeersLk.Unlock()
|
||||
var out []peer.ID
|
||||
for p := range bs.syncPeers {
|
||||
out = append(out, p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse) error {
|
||||
switch res.Status {
|
||||
case 101: // Partial Response
|
||||
@ -274,22 +67,20 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int)
|
||||
)
|
||||
}
|
||||
|
||||
peers := bs.getPeers()
|
||||
perm := rand.Perm(len(peers))
|
||||
// TODO: round robin through these peers on error
|
||||
|
||||
req := &BlockSyncRequest{
|
||||
Start: tipset,
|
||||
RequestLength: uint64(count),
|
||||
Options: BSOptBlocks,
|
||||
}
|
||||
|
||||
peers := bs.getPeers()
|
||||
|
||||
var oerr error
|
||||
for _, p := range perm {
|
||||
res, err := bs.sendRequestToPeer(ctx, peers[p], req)
|
||||
for _, p := range peers {
|
||||
res, err := bs.sendRequestToPeer(ctx, p, req)
|
||||
if err != nil {
|
||||
oerr = err
|
||||
log.Warnf("BlockSync request failed for peer %s: %s", peers[p].String(), err)
|
||||
log.Warnf("BlockSync request failed for peer %s: %s", p.String(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -298,7 +89,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int)
|
||||
}
|
||||
oerr = bs.processStatus(req, res)
|
||||
if oerr != nil {
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", peers[p].String(), oerr)
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), oerr)
|
||||
}
|
||||
}
|
||||
return nil, xerrors.Errorf("GetBlocks failed with all peers: %w", oerr)
|
||||
@ -327,11 +118,11 @@ func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid)
|
||||
|
||||
return bstsToFullTipSet(bts)
|
||||
case 101: // Partial Response
|
||||
panic("not handled")
|
||||
return nil, xerrors.Errorf("partial responses are not handled")
|
||||
case 201: // req.Start not found
|
||||
return nil, fmt.Errorf("not found")
|
||||
case 202: // Go Away
|
||||
panic("not handled")
|
||||
return nil, xerrors.Errorf("received 'go away' response peer")
|
||||
case 203: // Internal Error
|
||||
return nil, fmt.Errorf("block sync peer errored: %q", res.Message)
|
||||
case 204: // Invalid Request
|
||||
@ -376,37 +167,28 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
return nil, xerrors.Errorf("GetChainMessages failed with all peers(%d): %w", len(peers), err)
|
||||
}
|
||||
|
||||
func bstsToFullTipSet(bts *BSTipSet) (*store.FullTipSet, error) {
|
||||
fts := &store.FullTipSet{}
|
||||
for i, b := range bts.Blocks {
|
||||
fb := &types.FullBlock{
|
||||
Header: b,
|
||||
}
|
||||
for _, mi := range bts.BlsMsgIncludes[i] {
|
||||
fb.BlsMessages = append(fb.BlsMessages, bts.BlsMessages[mi])
|
||||
}
|
||||
for _, mi := range bts.SecpkMsgIncludes[i] {
|
||||
fb.SecpkMessages = append(fb.SecpkMessages, bts.SecpkMessages[mi])
|
||||
}
|
||||
|
||||
fts.Blocks = append(fts.Blocks, fb)
|
||||
}
|
||||
|
||||
return fts, nil
|
||||
}
|
||||
|
||||
func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
s, err := bs.newStream(inet.WithNoDial(ctx, "should already have connection"), p, BlockSyncProtocolID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ctx, span := trace.StartSpan(ctx, "sendRequestToPeer")
|
||||
defer span.End()
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("peer", p.Pretty()),
|
||||
)
|
||||
}
|
||||
|
||||
if err := cborrpc.WriteCborRPC(s, req); err != nil {
|
||||
s, err := bs.host.NewStream(inet.WithNoDial(ctx, "should already have connection"), p, BlockSyncProtocolID)
|
||||
if err != nil {
|
||||
bs.RemovePeer(p)
|
||||
return nil, xerrors.Errorf("failed to open stream to peer: %w", err)
|
||||
}
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res BlockSyncResponse
|
||||
if err := cborrpc.ReadCborRPC(bufio.NewReader(s), &res); err != nil {
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -447,9 +229,15 @@ func (bs *BlockSync) GetBlock(ctx context.Context, c cid.Cid) (*types.BlockHeade
|
||||
}
|
||||
|
||||
func (bs *BlockSync) AddPeer(p peer.ID) {
|
||||
bs.syncPeersLk.Lock()
|
||||
defer bs.syncPeersLk.Unlock()
|
||||
bs.syncPeers[p] = struct{}{}
|
||||
bs.syncPeers.addPeer(p)
|
||||
}
|
||||
|
||||
func (bs *BlockSync) RemovePeer(p peer.ID) {
|
||||
bs.syncPeers.removePeer(p)
|
||||
}
|
||||
|
||||
func (bs *BlockSync) getPeers() []peer.ID {
|
||||
return bs.syncPeers.prefSortedPeers()
|
||||
}
|
||||
|
||||
func (bs *BlockSync) FetchMessagesByCids(ctx context.Context, cids []cid.Cid) ([]*types.Message, error) {
|
||||
@ -528,3 +316,83 @@ func (bs *BlockSync) fetchCids(ctx context.Context, cids []cid.Cid, cb func(int,
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type peerStats struct {
|
||||
successes int
|
||||
failures int
|
||||
firstSeen time.Time
|
||||
}
|
||||
|
||||
type bsPeerTracker struct {
|
||||
peers map[peer.ID]*peerStats
|
||||
lk sync.Mutex
|
||||
}
|
||||
|
||||
func newPeerTracker() *bsPeerTracker {
|
||||
return &bsPeerTracker{
|
||||
peers: make(map[peer.ID]*peerStats),
|
||||
}
|
||||
}
|
||||
func (bpt *bsPeerTracker) addPeer(p peer.ID) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
if _, ok := bpt.peers[p]; ok {
|
||||
return
|
||||
}
|
||||
bpt.peers[p] = &peerStats{
|
||||
firstSeen: time.Now(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
|
||||
// TODO: this could probably be cached, but as long as its not too many peers, fine for now
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
out := make([]peer.ID, 0, len(bpt.peers))
|
||||
for p := range bpt.peers {
|
||||
out = append(out, p)
|
||||
}
|
||||
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
pi := bpt.peers[out[i]]
|
||||
pj := bpt.peers[out[j]]
|
||||
if pi.successes > pj.successes {
|
||||
return true
|
||||
}
|
||||
if pi.failures < pj.successes {
|
||||
return true
|
||||
}
|
||||
return pi.firstSeen.Before(pj.firstSeen)
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) logSuccess(p peer.ID) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
if pi, ok := bpt.peers[p]; !ok {
|
||||
log.Warn("log success called on peer not in tracker")
|
||||
return
|
||||
} else {
|
||||
pi.successes++
|
||||
}
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) logFailure(p peer.ID) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
if pi, ok := bpt.peers[p]; !ok {
|
||||
log.Warn("log failure called on peer not in tracker")
|
||||
return
|
||||
} else {
|
||||
pi.failures++
|
||||
}
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) removePeer(p peer.ID) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
delete(bpt.peers, p)
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package chain
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
@ -15,11 +15,15 @@ import (
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *BlockSyncRequest) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Start ([]cid.Cid)
|
||||
// t.t.Start ([]cid.Cid) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Start)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -29,19 +33,20 @@ func (t *BlockSyncRequest) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.RequestLength (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.RequestLength)); err != nil {
|
||||
// t.t.RequestLength (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.RequestLength))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Options (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.Options)); err != nil {
|
||||
// t.t.Options (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Options))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockSyncRequest) UnmarshalCBOR(br io.Reader) error {
|
||||
func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -55,14 +60,14 @@ func (t *BlockSyncRequest) UnmarshalCBOR(br io.Reader) error {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Start ([]cid.Cid)
|
||||
// t.t.Start ([]cid.Cid) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.Start: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -80,7 +85,7 @@ func (t *BlockSyncRequest) UnmarshalCBOR(br io.Reader) error {
|
||||
t.Start[i] = c
|
||||
}
|
||||
|
||||
// t.t.RequestLength (uint64)
|
||||
// t.t.RequestLength (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -89,8 +94,8 @@ func (t *BlockSyncRequest) UnmarshalCBOR(br io.Reader) error {
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.RequestLength = extra
|
||||
// t.t.Options (uint64)
|
||||
t.RequestLength = uint64(extra)
|
||||
// t.t.Options (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -99,16 +104,20 @@ func (t *BlockSyncRequest) UnmarshalCBOR(br io.Reader) error {
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Options = extra
|
||||
t.Options = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Chain ([]*chain.BSTipSet)
|
||||
// t.t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Chain)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -118,12 +127,12 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.Status (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.Status)); err != nil {
|
||||
// t.t.Status (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Status))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Message (string)
|
||||
// t.t.Message (string) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Message)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -133,7 +142,8 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockSyncResponse) UnmarshalCBOR(br io.Reader) error {
|
||||
func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -147,14 +157,14 @@ func (t *BlockSyncResponse) UnmarshalCBOR(br io.Reader) error {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Chain ([]*chain.BSTipSet)
|
||||
// t.t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.Chain: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -164,6 +174,7 @@ func (t *BlockSyncResponse) UnmarshalCBOR(br io.Reader) error {
|
||||
t.Chain = make([]*BSTipSet, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v BSTipSet
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
@ -172,7 +183,7 @@ func (t *BlockSyncResponse) UnmarshalCBOR(br io.Reader) error {
|
||||
t.Chain[i] = &v
|
||||
}
|
||||
|
||||
// t.t.Status (uint64)
|
||||
// t.t.Status (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -181,39 +192,30 @@ func (t *BlockSyncResponse) UnmarshalCBOR(br io.Reader) error {
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Status = extra
|
||||
// t.t.Message (string)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if maj != cbg.MajTextString {
|
||||
return fmt.Errorf("expected cbor type 'text string' in input")
|
||||
}
|
||||
|
||||
if extra > 256*1024 {
|
||||
return fmt.Errorf("string in cbor input too long")
|
||||
}
|
||||
t.Status = uint64(extra)
|
||||
// t.t.Message (string) (string)
|
||||
|
||||
{
|
||||
buf := make([]byte, extra)
|
||||
if _, err := io.ReadFull(br, buf); err != nil {
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Message = string(buf)
|
||||
t.Message = string(sval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{133}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Blocks ([]*types.BlockHeader)
|
||||
// t.t.Blocks ([]*types.BlockHeader) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Blocks)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -223,7 +225,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.BlsMessages ([]*types.Message)
|
||||
// t.t.BlsMessages ([]*types.Message) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMessages)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -233,7 +235,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.BlsMsgIncludes ([][]uint64)
|
||||
// t.t.BlsMsgIncludes ([][]uint64) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMsgIncludes)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -248,7 +250,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.SecpkMessages ([]*types.SignedMessage)
|
||||
// t.t.SecpkMessages ([]*types.SignedMessage) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMessages)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -258,7 +260,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.SecpkMsgIncludes ([][]uint64)
|
||||
// t.t.SecpkMsgIncludes ([][]uint64) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMsgIncludes)))); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -275,7 +277,8 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
@ -289,14 +292,14 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Blocks ([]*types.BlockHeader)
|
||||
// t.t.Blocks ([]*types.BlockHeader) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.Blocks: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -306,6 +309,7 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.Blocks = make([]*types.BlockHeader, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.BlockHeader
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
@ -314,14 +318,14 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.Blocks[i] = &v
|
||||
}
|
||||
|
||||
// t.t.BlsMessages ([]*types.Message)
|
||||
// t.t.BlsMessages ([]*types.Message) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.BlsMessages: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -331,6 +335,7 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.BlsMessages = make([]*types.Message, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.Message
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
@ -339,14 +344,14 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.BlsMessages[i] = &v
|
||||
}
|
||||
|
||||
// t.t.BlsMsgIncludes ([][]uint64)
|
||||
// t.t.BlsMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.BlsMsgIncludes: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -366,7 +371,7 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.BlsMsgIncludes[i]: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -392,14 +397,14 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.t.SecpkMessages ([]*types.SignedMessage)
|
||||
// t.t.SecpkMessages ([]*types.SignedMessage) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.SecpkMessages: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -409,6 +414,7 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.SecpkMessages = make([]*types.SignedMessage, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedMessage
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
@ -417,14 +423,14 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
t.SecpkMessages[i] = &v
|
||||
}
|
||||
|
||||
// t.t.SecpkMsgIncludes ([][]uint64)
|
||||
// t.t.SecpkMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.SecpkMsgIncludes: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
@ -444,7 +450,7 @@ func (t *BSTipSet) UnmarshalCBOR(br io.Reader) error {
|
||||
return err
|
||||
}
|
||||
if extra > 8192 {
|
||||
return fmt.Errorf("array too large")
|
||||
return fmt.Errorf("t.SecpkMsgIncludes[i]: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
853
chain/deals/cbor_gen.go
Normal file
853
chain/deals/cbor_gen.go
Normal file
@ -0,0 +1,853 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
/* This file was generated by github.com/whyrusleeping/cbor-gen */
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *AskRequest) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{129}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Miner (address.Address) (struct)
|
||||
if err := t.Miner.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Miner (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Miner.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{129}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Ask (types.SignedStorageAsk) (struct)
|
||||
if err := t.Ask.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Ask (types.SignedStorageAsk) (struct)
|
||||
|
||||
{
|
||||
|
||||
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 {
|
||||
t.Ask = new(types.SignedStorageAsk)
|
||||
if err := t.Ask.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Proposal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{130}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.DealProposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.DealProposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Piece (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Piece); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Piece: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Proposal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 2 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.DealProposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
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 {
|
||||
t.DealProposal = new(actors.StorageDealProposal)
|
||||
if err := t.DealProposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.Piece (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Piece: %w", err)
|
||||
}
|
||||
|
||||
t.Piece = c
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Response) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{133}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Message (string) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Message)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Message)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Proposal (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Proposal); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
// t.t.StorageDeal (actors.StorageDeal) (struct)
|
||||
if err := t.StorageDeal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.PublishMessage (cid.Cid) (struct)
|
||||
|
||||
if t.PublishMessage == nil {
|
||||
if _, err := w.Write(cbg.CborNull); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCid(w, *t.PublishMessage); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.PublishMessage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Response) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 5 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.State (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.State = uint64(extra)
|
||||
// t.t.Message (string) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Message = string(sval)
|
||||
}
|
||||
// t.t.Proposal (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
t.Proposal = c
|
||||
|
||||
}
|
||||
// t.t.StorageDeal (actors.StorageDeal) (struct)
|
||||
|
||||
{
|
||||
|
||||
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 {
|
||||
t.StorageDeal = new(actors.StorageDeal)
|
||||
if err := t.StorageDeal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.PublishMessage (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
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.PublishMessage: %w", err)
|
||||
}
|
||||
|
||||
t.PublishMessage = &c
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SignedResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{130}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Response (deals.Response) (struct)
|
||||
if err := t.Response.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Signature (types.Signature) (struct)
|
||||
if err := t.Signature.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SignedResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 2 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Response (deals.Response) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Response.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.Signature (types.Signature) (struct)
|
||||
|
||||
{
|
||||
|
||||
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 {
|
||||
t.Signature = new(types.Signature)
|
||||
if err := t.Signature.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDealProposal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{136}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Data (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Data); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Data: %w", err)
|
||||
}
|
||||
|
||||
// t.t.PricePerEpoch (types.BigInt) (struct)
|
||||
if err := t.PricePerEpoch.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.ProposalExpiration (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.ProposalExpiration))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Duration (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Duration))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.ProviderAddress (address.Address) (struct)
|
||||
if err := t.ProviderAddress.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Client (address.Address) (struct)
|
||||
if err := t.Client.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.MinerWorker (address.Address) (struct)
|
||||
if err := t.MinerWorker.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.MinerID (peer.ID) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.MinerID)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.MinerID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDealProposal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 8 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Data (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Data: %w", err)
|
||||
}
|
||||
|
||||
t.Data = c
|
||||
|
||||
}
|
||||
// t.t.PricePerEpoch (types.BigInt) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.PricePerEpoch.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.ProposalExpiration (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.ProposalExpiration = uint64(extra)
|
||||
// t.t.Duration (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.Duration = uint64(extra)
|
||||
// t.t.ProviderAddress (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.ProviderAddress.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.Client (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Client.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.MinerWorker (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.MinerWorker.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.MinerID (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.MinerID = peer.ID(sval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDeal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{135}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.ProposalCid); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
// t.t.Proposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.Proposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Miner (peer.ID) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Miner)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Miner)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.MinerWorker (address.Address) (struct)
|
||||
if err := t.MinerWorker.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.DealID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.DealID))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.PublishMessage (cid.Cid) (struct)
|
||||
|
||||
if t.PublishMessage == nil {
|
||||
if _, err := w.Write(cbg.CborNull); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCid(w, *t.PublishMessage); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.PublishMessage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDeal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 7 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
t.ProposalCid = c
|
||||
|
||||
}
|
||||
// t.t.Proposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Proposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.State (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.State = uint64(extra)
|
||||
// t.t.Miner (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Miner = peer.ID(sval)
|
||||
}
|
||||
// t.t.MinerWorker (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.MinerWorker.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.DealID (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.DealID = uint64(extra)
|
||||
// t.t.PublishMessage (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
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.PublishMessage: %w", err)
|
||||
}
|
||||
|
||||
t.PublishMessage = &c
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *MinerDeal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{135}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Client (peer.ID) (string)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Client)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Client)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Proposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.Proposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.ProposalCid); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
// t.t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.Ref (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Ref); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Ref: %w", err)
|
||||
}
|
||||
|
||||
// t.t.DealID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.DealID))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.t.SectorID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SectorID))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *MinerDeal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 7 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.t.Client (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Client = peer.ID(sval)
|
||||
}
|
||||
// t.t.Proposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Proposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
t.ProposalCid = c
|
||||
|
||||
}
|
||||
// t.t.State (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.State = uint64(extra)
|
||||
// t.t.Ref (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Ref: %w", err)
|
||||
}
|
||||
|
||||
t.Ref = c
|
||||
|
||||
}
|
||||
// t.t.DealID (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.DealID = uint64(extra)
|
||||
// t.t.SectorID (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.SectorID = uint64(extra)
|
||||
return nil
|
||||
}
|
@ -2,12 +2,10 @@ package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
@ -17,47 +15,48 @@ import (
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/events"
|
||||
"github.com/filecoin-project/lotus/chain/market"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/lib/cborrpc"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/retrieval/discovery"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(ClientDeal{})
|
||||
cbor.RegisterCborType(actors.PieceInclVoucherData{}) // TODO: USE CBORGEN!
|
||||
cbor.RegisterCborType(types.SignedVoucher{})
|
||||
cbor.RegisterCborType(types.ModVerifyParams{})
|
||||
cbor.RegisterCborType(types.Signature{})
|
||||
cbor.RegisterCborType(actors.PaymentInfo{})
|
||||
cbor.RegisterCborType(api.PaymentInfo{})
|
||||
cbor.RegisterCborType(actors.InclusionProof{})
|
||||
}
|
||||
|
||||
var log = logging.Logger("deals")
|
||||
|
||||
type ClientDeal struct {
|
||||
ProposalCid cid.Cid
|
||||
Proposal StorageDealProposal
|
||||
Proposal actors.StorageDealProposal
|
||||
State api.DealState
|
||||
Miner peer.ID
|
||||
MinerWorker address.Address
|
||||
DealID uint64
|
||||
|
||||
PublishMessage *cid.Cid
|
||||
|
||||
s inet.Stream
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
sm *stmgr.StateManager
|
||||
chain *store.ChainStore
|
||||
h host.Host
|
||||
w *wallet.Wallet
|
||||
dag dtypes.ClientDAG
|
||||
discovery *discovery.Local
|
||||
events *events.Events
|
||||
fm *market.FundMgr
|
||||
|
||||
deals ClientStateStore
|
||||
deals *statestore.StateStore
|
||||
conns map[cid.Cid]inet.Stream
|
||||
|
||||
incoming chan ClientDeal
|
||||
incoming chan *ClientDeal
|
||||
updated chan clientDealUpdate
|
||||
|
||||
stop chan struct{}
|
||||
@ -68,20 +67,24 @@ type clientDealUpdate struct {
|
||||
newState api.DealState
|
||||
id cid.Cid
|
||||
err error
|
||||
mut func(*ClientDeal)
|
||||
}
|
||||
|
||||
func NewClient(sm *stmgr.StateManager, h host.Host, w *wallet.Wallet, ds dtypes.MetadataDS, dag dtypes.ClientDAG, discovery *discovery.Local) *Client {
|
||||
func NewClient(sm *stmgr.StateManager, chain *store.ChainStore, h host.Host, w *wallet.Wallet, ds dtypes.MetadataDS, dag dtypes.ClientDAG, discovery *discovery.Local, fm *market.FundMgr, chainapi full.ChainAPI) *Client {
|
||||
c := &Client{
|
||||
sm: sm,
|
||||
chain: chain,
|
||||
h: h,
|
||||
w: w,
|
||||
dag: dag,
|
||||
discovery: discovery,
|
||||
fm: fm,
|
||||
events: events.NewEvents(context.TODO(), &chainapi),
|
||||
|
||||
deals: ClientStateStore{StateStore{ds: namespace.Wrap(ds, datastore.NewKey("/deals/client"))}},
|
||||
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
|
||||
conns: map[cid.Cid]inet.Stream{},
|
||||
|
||||
incoming: make(chan ClientDeal, 16),
|
||||
incoming: make(chan *ClientDeal, 16),
|
||||
updated: make(chan clientDealUpdate, 16),
|
||||
|
||||
stop: make(chan struct{}),
|
||||
@ -108,7 +111,7 @@ func (c *Client) Run(ctx context.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) onIncoming(deal ClientDeal) {
|
||||
func (c *Client) onIncoming(deal *ClientDeal) {
|
||||
log.Info("incoming deal")
|
||||
|
||||
if _, ok := c.conns[deal.ProposalCid]; ok {
|
||||
@ -134,10 +137,13 @@ func (c *Client) onIncoming(deal ClientDeal) {
|
||||
}
|
||||
|
||||
func (c *Client) onUpdated(ctx context.Context, update clientDealUpdate) {
|
||||
log.Infof("Deal %s updated state to %d", update.id, update.newState)
|
||||
log.Infof("Client deal %s updated state to %s", update.id, api.DealStates[update.newState])
|
||||
var deal ClientDeal
|
||||
err := c.deals.MutateClient(update.id, func(d *ClientDeal) error {
|
||||
err := c.deals.Mutate(update.id, func(d *ClientDeal) error {
|
||||
d.State = update.newState
|
||||
if update.mut != nil {
|
||||
update.mut(d)
|
||||
}
|
||||
deal = *d
|
||||
return nil
|
||||
})
|
||||
@ -159,77 +165,81 @@ func (c *Client) onUpdated(ctx context.Context, update clientDealUpdate) {
|
||||
case api.DealStaged:
|
||||
c.handle(ctx, deal, c.staged, api.DealSealing)
|
||||
case api.DealSealing:
|
||||
c.handle(ctx, deal, c.sealing, api.DealComplete)
|
||||
c.handle(ctx, deal, c.sealing, api.DealNoUpdate)
|
||||
// TODO: DealComplete -> watch for faults, expiration, etc.
|
||||
}
|
||||
}
|
||||
|
||||
type ClientDealProposal struct {
|
||||
Data cid.Cid
|
||||
|
||||
TotalPrice types.BigInt
|
||||
Duration uint64
|
||||
PricePerEpoch types.BigInt
|
||||
ProposalExpiration uint64
|
||||
Duration uint64
|
||||
|
||||
Payment actors.PaymentInfo
|
||||
|
||||
MinerAddress address.Address
|
||||
ClientAddress address.Address
|
||||
MinerID peer.ID
|
||||
ProviderAddress address.Address
|
||||
Client address.Address
|
||||
MinerWorker address.Address
|
||||
MinerID peer.ID
|
||||
}
|
||||
|
||||
func (c *Client) VerifyParams(ctx context.Context, data cid.Cid) (*actors.PieceInclVoucherData, error) {
|
||||
commP, size, err := c.commP(ctx, data)
|
||||
func (c *Client) Start(ctx context.Context, p ClientDealProposal) (cid.Cid, error) {
|
||||
if err := c.fm.EnsureAvailable(ctx, p.Client, types.BigMul(p.PricePerEpoch, types.NewInt(p.Duration))); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("adding market funds failed: %w", err)
|
||||
}
|
||||
|
||||
commP, pieceSize, err := c.commP(ctx, p.Data)
|
||||
|
||||
dealProposal := &actors.StorageDealProposal{
|
||||
PieceRef: commP,
|
||||
PieceSize: uint64(pieceSize),
|
||||
PieceSerialization: actors.SerializationUnixFSv0,
|
||||
Client: p.Client,
|
||||
Provider: p.ProviderAddress,
|
||||
ProposalExpiration: p.ProposalExpiration,
|
||||
Duration: p.Duration,
|
||||
StoragePricePerEpoch: p.PricePerEpoch,
|
||||
StorageCollateral: types.NewInt(uint64(pieceSize)), // TODO: real calc
|
||||
}
|
||||
|
||||
if err := api.SignWith(ctx, c.w.Sign, p.Client, dealProposal); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("signing deal proposal failed: %w", err)
|
||||
}
|
||||
|
||||
proposalNd, err := cborutil.AsIpld(dealProposal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return cid.Undef, xerrors.Errorf("getting proposal node failed: %w", err)
|
||||
}
|
||||
|
||||
return &actors.PieceInclVoucherData{
|
||||
CommP: commP,
|
||||
PieceSize: types.NewInt(uint64(size)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Start(ctx context.Context, p ClientDealProposal, vd *actors.PieceInclVoucherData) (cid.Cid, error) {
|
||||
proposal := StorageDealProposal{
|
||||
PieceRef: p.Data,
|
||||
SerializationMode: SerializationUnixFs,
|
||||
CommP: vd.CommP[:],
|
||||
Size: vd.PieceSize.Uint64(),
|
||||
TotalPrice: p.TotalPrice,
|
||||
Duration: p.Duration,
|
||||
Payment: p.Payment,
|
||||
MinerAddress: p.MinerAddress,
|
||||
ClientAddress: p.ClientAddress,
|
||||
}
|
||||
|
||||
s, err := c.h.NewStream(ctx, p.MinerID, ProtocolID)
|
||||
s, err := c.h.NewStream(ctx, p.MinerID, DealProtocolID)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
return cid.Undef, xerrors.Errorf("connecting to storage provider failed: %w", err)
|
||||
}
|
||||
|
||||
if err := c.sendProposal(s, proposal, p.ClientAddress); err != nil {
|
||||
return cid.Undef, err
|
||||
proposal := &Proposal{
|
||||
DealProposal: dealProposal,
|
||||
Piece: p.Data,
|
||||
}
|
||||
|
||||
proposalNd, err := cbor.WrapObject(proposal, math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
if err := cborutil.WriteCborRPC(s, proposal); err != nil {
|
||||
s.Reset()
|
||||
return cid.Undef, xerrors.Errorf("sending proposal to storage provider failed: %w", err)
|
||||
}
|
||||
|
||||
deal := ClientDeal{
|
||||
deal := &ClientDeal{
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
Proposal: proposal,
|
||||
Proposal: *dealProposal,
|
||||
State: api.DealUnknown,
|
||||
Miner: p.MinerID,
|
||||
MinerWorker: p.MinerWorker,
|
||||
|
||||
s: s,
|
||||
}
|
||||
|
||||
// TODO: actually care about what happens with the deal after it was accepted
|
||||
c.incoming <- deal
|
||||
|
||||
// TODO: start tracking after the deal is sealed
|
||||
return deal.ProposalCid, c.discovery.AddPeer(p.Data, discovery.RetrievalPeer{
|
||||
Address: proposal.MinerAddress,
|
||||
Address: dealProposal.Provider,
|
||||
ID: deal.Miner,
|
||||
})
|
||||
}
|
||||
@ -243,12 +253,12 @@ func (c *Client) QueryAsk(ctx context.Context, p peer.ID, a address.Address) (*t
|
||||
req := &AskRequest{
|
||||
Miner: a,
|
||||
}
|
||||
if err := cborrpc.WriteCborRPC(s, req); err != nil {
|
||||
if err := cborutil.WriteCborRPC(s, req); err != nil {
|
||||
return nil, xerrors.Errorf("failed to send ask request: %w", err)
|
||||
}
|
||||
|
||||
var out AskResponse
|
||||
if err := cborrpc.ReadCborRPC(s, &out); err != nil {
|
||||
if err := cborutil.ReadCborRPC(s, &out); err != nil {
|
||||
return nil, xerrors.Errorf("failed to read ask response: %w", err)
|
||||
}
|
||||
|
||||
@ -268,7 +278,19 @@ func (c *Client) QueryAsk(ctx context.Context, p peer.ID, a address.Address) (*t
|
||||
}
|
||||
|
||||
func (c *Client) List() ([]ClientDeal, error) {
|
||||
return c.deals.ListClient()
|
||||
var out []ClientDeal
|
||||
if err := c.deals.List(&out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetDeal(d cid.Cid) (*ClientDeal, error) {
|
||||
var out ClientDeal
|
||||
if err := c.deals.Get(d, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (c *Client) Stop() {
|
||||
|
@ -1,103 +1,225 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
)
|
||||
|
||||
type clientHandlerFunc func(ctx context.Context, deal ClientDeal) error
|
||||
type clientHandlerFunc func(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error)
|
||||
|
||||
func (c *Client) handle(ctx context.Context, deal ClientDeal, cb clientHandlerFunc, next api.DealState) {
|
||||
go func() {
|
||||
err := cb(ctx, deal)
|
||||
mut, err := cb(ctx, deal)
|
||||
if err != nil {
|
||||
next = api.DealError
|
||||
}
|
||||
|
||||
if err == nil && next == api.DealNoUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: next,
|
||||
id: deal.ProposalCid,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) new(ctx context.Context, deal ClientDeal) error {
|
||||
func (c *Client) new(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
resp, err := c.readStorageDealResp(deal)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if err := c.disconnect(deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.State != api.DealAccepted {
|
||||
return xerrors.Errorf("deal wasn't accepted (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
log.Info("DEAL ACCEPTED!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) accepted(ctx context.Context, deal ClientDeal) error {
|
||||
/* data transfer happens */
|
||||
|
||||
resp, err := c.readStorageDealResp(deal)
|
||||
if err != nil {
|
||||
return err
|
||||
if resp.State != api.DealAccepted {
|
||||
return nil, xerrors.Errorf("deal wasn't accepted (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
if resp.State != api.DealStaged {
|
||||
return xerrors.Errorf("deal wasn't staged (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
log.Info("DEAL STAGED!")
|
||||
|
||||
return nil
|
||||
return func(info *ClientDeal) {
|
||||
info.PublishMessage = resp.PublishMessage
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) staged(ctx context.Context, deal ClientDeal) error {
|
||||
/* miner seals our data, hopefully */
|
||||
func (c *Client) accepted(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
log.Infow("DEAL ACCEPTED!")
|
||||
|
||||
resp, err := c.readStorageDealResp(deal)
|
||||
pubmsg, err := c.chain.GetMessage(*deal.PublishMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, xerrors.Errorf("getting deal pubsish message: %w", err)
|
||||
}
|
||||
|
||||
if resp.State != api.DealSealing {
|
||||
return xerrors.Errorf("deal wasn't sealed (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
log.Info("DEAL SEALED!")
|
||||
|
||||
ok, err := sectorbuilder.VerifyPieceInclusionProof(build.SectorSize, deal.Proposal.Size, deal.Proposal.CommP, resp.CommD, resp.PieceInclusionProof.ProofElements)
|
||||
pw, err := stmgr.GetMinerWorker(ctx, c.sm, nil, deal.Proposal.Provider)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("verifying piece inclusion proof in staged deal %s: %w", deal.ProposalCid, err)
|
||||
}
|
||||
if !ok {
|
||||
return xerrors.Errorf("verifying piece inclusion proof in staged deal %s failed", deal.ProposalCid)
|
||||
return nil, xerrors.Errorf("getting miner worker failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
if pubmsg.From != pw {
|
||||
return nil, xerrors.Errorf("deal wasn't published by storage provider: from=%s, provider=%s", pubmsg.From, deal.Proposal.Provider)
|
||||
}
|
||||
|
||||
if pubmsg.To != actors.StorageMarketAddress {
|
||||
return nil, xerrors.Errorf("deal publish message wasn't set to StorageMarket actor (to=%s)", pubmsg.To)
|
||||
}
|
||||
|
||||
if pubmsg.Method != actors.SMAMethods.PublishStorageDeals {
|
||||
return nil, xerrors.Errorf("deal publish message called incorrect method (method=%s)", pubmsg.Method)
|
||||
}
|
||||
|
||||
var params actors.PublishStorageDealsParams
|
||||
if err := params.UnmarshalCBOR(bytes.NewReader(pubmsg.Params)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dealIdx := -1
|
||||
for i, storageDeal := range params.Deals {
|
||||
// TODO: make it less hacky
|
||||
eq, err := cborutil.Equals(&deal.Proposal, &storageDeal.Proposal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if eq {
|
||||
dealIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if dealIdx == -1 {
|
||||
return nil, xerrors.Errorf("deal publish didn't contain our deal (message cid: %s)", deal.PublishMessage)
|
||||
}
|
||||
|
||||
// TODO: timeout
|
||||
_, ret, err := c.sm.WaitForMessage(ctx, *deal.PublishMessage)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("waiting for deal publish message: %w", err)
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return nil, xerrors.Errorf("deal publish failed: exit=%d", ret.ExitCode)
|
||||
}
|
||||
|
||||
var res actors.PublishStorageDealResponse
|
||||
if err := res.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(info *ClientDeal) {
|
||||
info.DealID = res.DealIDs[dealIdx]
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) sealing(ctx context.Context, deal ClientDeal) error {
|
||||
resp, err := c.readStorageDealResp(deal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (c *Client) staged(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
// TODO: Maybe wait for pre-commit
|
||||
|
||||
if resp.State != api.DealComplete {
|
||||
return xerrors.Errorf("deal wasn't complete (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
// TODO: look for the commit message on chain, negotiate better payment vouchers
|
||||
|
||||
log.Info("DEAL COMPLETE!!")
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Client) sealing(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
checkFunc := func(ts *types.TipSet) (done bool, more bool, err error) {
|
||||
sd, err := stmgr.GetStorageDeal(ctx, c.sm, deal.DealID, ts)
|
||||
if err != nil {
|
||||
// TODO: This may be fine for some errors
|
||||
return false, false, xerrors.Errorf("failed to look up deal on chain: %w", err)
|
||||
}
|
||||
|
||||
if sd.ActivationEpoch > 0 {
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
called := func(msg *types.Message, ts *types.TipSet, curH uint64) (more bool, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
err: xerrors.Errorf("handling applied event: %w", err),
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if msg == nil {
|
||||
log.Error("timed out waiting for deal activation... what now?")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var params actors.SectorProveCommitInfo
|
||||
if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, dealID := range params.DealIDs {
|
||||
if dealID == deal.DealID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sd, err := stmgr.GetStorageDeal(ctx, c.sm, deal.DealID, ts)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("failed to look up deal on chain: %w", err)
|
||||
}
|
||||
|
||||
if sd.ActivationEpoch == 0 {
|
||||
return false, xerrors.Errorf("deal wasn't active: deal=%d, parentState=%s, h=%d", deal.DealID, ts.ParentState(), ts.Height())
|
||||
}
|
||||
|
||||
log.Infof("Storage deal %d activated at epoch %d", deal.DealID, sd.ActivationEpoch)
|
||||
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revert := func(ctx context.Context, ts *types.TipSet) error {
|
||||
log.Warn("deal activation reverted; TODO: actually handle this!")
|
||||
// TODO: Just go back to DealSealing?
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.events.Called(checkFunc, called, revert, 3, build.SealRandomnessLookbackLimit, deal.Proposal.Provider, actors.MAMethods.ProveCommitSector); err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up called handler")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -2,18 +2,16 @@ package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"runtime"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/lib/cborrpc"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/padreader"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
)
|
||||
|
||||
func (c *Client) failDeal(id cid.Cid, cerr error) {
|
||||
@ -29,10 +27,10 @@ func (c *Client) failDeal(id cid.Cid, cerr error) {
|
||||
}
|
||||
|
||||
// TODO: store in some sort of audit log
|
||||
log.Errorf("deal %s failed: %s", id, cerr)
|
||||
log.Errorf("deal %s failed: %+v", id, cerr)
|
||||
}
|
||||
|
||||
func (c *Client) commP(ctx context.Context, data cid.Cid) ([]byte, int64, error) {
|
||||
func (c *Client) commP(ctx context.Context, data cid.Cid) ([]byte, uint64, error) {
|
||||
root, err := c.dag.Get(ctx, data)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get file root for deal: %s", err)
|
||||
@ -51,57 +49,52 @@ func (c *Client) commP(ctx context.Context, data cid.Cid) ([]byte, int64, error)
|
||||
return nil, 0, xerrors.New("unsupported unixfs type")
|
||||
}
|
||||
|
||||
size, err := uf.Size()
|
||||
s, err := uf.Size()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
commP, err := sectorbuilder.GeneratePieceCommitment(uf, uint64(size))
|
||||
pr, psize := padreader.New(uf, uint64(s))
|
||||
|
||||
commp, err := sectorbuilder.GeneratePieceCommitment(pr, psize)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, 0, xerrors.Errorf("generating CommP: %w", err)
|
||||
}
|
||||
|
||||
return commP[:], size, err
|
||||
return commp[:], psize, nil
|
||||
}
|
||||
|
||||
func (c *Client) sendProposal(s inet.Stream, proposal StorageDealProposal, from address.Address) error {
|
||||
log.Info("Sending deal proposal")
|
||||
|
||||
msg, err := cbor.DumpObject(proposal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig, err := c.w.Sign(context.TODO(), from, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedProposal := &SignedStorageDealProposal{
|
||||
Proposal: proposal,
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
return cborrpc.WriteCborRPC(s, signedProposal)
|
||||
}
|
||||
|
||||
func (c *Client) readStorageDealResp(deal ClientDeal) (*StorageDealResponse, error) {
|
||||
func (c *Client) readStorageDealResp(deal ClientDeal) (*Response, error) {
|
||||
s, ok := c.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
// TODO: Try to re-establish the connection using query protocol
|
||||
return nil, xerrors.Errorf("no connection to miner")
|
||||
}
|
||||
|
||||
var resp SignedStorageDealResponse
|
||||
if err := cborrpc.ReadCborRPC(s, &resp); err != nil {
|
||||
log.Errorw("failed to read StorageDealResponse message", "error", err)
|
||||
var resp SignedResponse
|
||||
if err := cborutil.ReadCborRPC(s, &resp); err != nil {
|
||||
log.Errorw("failed to read Response message", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: verify signature
|
||||
if err := resp.Verify(deal.MinerWorker); err != nil {
|
||||
return nil, xerrors.Errorf("verifying response signature failed", err)
|
||||
}
|
||||
|
||||
if resp.Response.Proposal != deal.ProposalCid {
|
||||
return nil, xerrors.New("miner responded to a wrong proposal")
|
||||
return nil, xerrors.Errorf("miner responded to a wrong proposal: %s != %s", resp.Response.Proposal, deal.ProposalCid)
|
||||
}
|
||||
|
||||
return &resp.Response, nil
|
||||
}
|
||||
|
||||
func (c *Client) disconnect(deal ClientDeal) error {
|
||||
s, ok := c.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.Close()
|
||||
delete(c.conns, deal.ProposalCid)
|
||||
return err
|
||||
}
|
||||
|
@ -1,313 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-sectorbuilder/sealing_state"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"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/types"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
type minerHandlerFunc func(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error)
|
||||
|
||||
func (h *Handler) handle(ctx context.Context, deal MinerDeal, cb minerHandlerFunc, next api.DealState) {
|
||||
go func() {
|
||||
mut, err := cb(ctx, deal)
|
||||
|
||||
if err == nil && next == api.DealNoUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case h.updated <- minerDealUpdate{
|
||||
newState: next,
|
||||
id: deal.ProposalCid,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-h.stop:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// ACCEPTED
|
||||
|
||||
func (h *Handler) checkVoucher(ctx context.Context, deal MinerDeal, voucher *types.SignedVoucher, lane uint64, maxClose uint64, amount types.BigInt) error {
|
||||
err := h.full.PaychVoucherCheckValid(ctx, deal.Proposal.Payment.PayChActor, voucher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if voucher.Extra == nil {
|
||||
return xerrors.New("voucher.Extra not set")
|
||||
}
|
||||
|
||||
if voucher.Extra.Actor != deal.Proposal.MinerAddress {
|
||||
return xerrors.Errorf("extra params actor didn't match miner address in proposal: '%s' != '%s'", voucher.Extra.Actor, deal.Proposal.MinerAddress)
|
||||
}
|
||||
|
||||
if voucher.Extra.Method != actors.MAMethods.PaymentVerifyInclusion {
|
||||
return xerrors.Errorf("expected extra method %d, got %d", actors.MAMethods.PaymentVerifyInclusion, voucher.Extra.Method)
|
||||
}
|
||||
|
||||
var inclChallenge actors.PieceInclVoucherData
|
||||
if err := cbor.DecodeInto(voucher.Extra.Data, &inclChallenge); err != nil {
|
||||
return xerrors.Errorf("failed to decode storage voucher data for verification: %w", err)
|
||||
}
|
||||
|
||||
if inclChallenge.PieceSize.Uint64() != deal.Proposal.Size {
|
||||
return xerrors.Errorf("paych challenge piece size didn't match deal proposal size: %d != %d", inclChallenge.PieceSize.Uint64(), deal.Proposal.Size)
|
||||
}
|
||||
|
||||
if !bytes.Equal(inclChallenge.CommP, deal.Proposal.CommP) {
|
||||
return xerrors.New("paych challenge commP didn't match deal proposal")
|
||||
}
|
||||
|
||||
if voucher.MinCloseHeight > maxClose {
|
||||
return xerrors.Errorf("MinCloseHeight too high (%d), max expected: %d", voucher.MinCloseHeight, maxClose)
|
||||
}
|
||||
|
||||
if voucher.TimeLock > maxClose {
|
||||
return xerrors.Errorf("TimeLock too high (%d), max expected: %d", voucher.TimeLock, maxClose)
|
||||
}
|
||||
|
||||
if len(voucher.Merges) > 0 {
|
||||
return xerrors.New("didn't expect any merges")
|
||||
}
|
||||
|
||||
if voucher.Amount.LessThan(amount) {
|
||||
return xerrors.Errorf("not enough funds in the voucher: %s < %s; vl=%d", voucher.Amount, amount, len(deal.Proposal.Payment.Vouchers))
|
||||
}
|
||||
|
||||
if voucher.Lane != lane {
|
||||
return xerrors.Errorf("expected all vouchers on lane %d, found voucher on lane %d", lane, voucher.Lane)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) consumeVouchers(ctx context.Context, deal MinerDeal) error {
|
||||
curHead, err := h.full.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(deal.Proposal.Payment.Vouchers) == 0 {
|
||||
return xerrors.Errorf("no payment vouchers for deal")
|
||||
}
|
||||
|
||||
increment := deal.Proposal.Duration / uint64(len(deal.Proposal.Payment.Vouchers))
|
||||
|
||||
startH := deal.Proposal.Payment.Vouchers[0].TimeLock - increment
|
||||
if startH > curHead.Height()+build.DealVoucherSkewLimit {
|
||||
return xerrors.Errorf("deal starts too far into the future: start=%d; h=%d; max=%d; inc=%d", startH, curHead.Height(), curHead.Height()+build.DealVoucherSkewLimit, increment)
|
||||
}
|
||||
|
||||
vspec := VoucherSpec(deal.Proposal.Duration, deal.Proposal.TotalPrice, startH, nil)
|
||||
|
||||
lane := deal.Proposal.Payment.Vouchers[0].Lane
|
||||
|
||||
for i, voucher := range deal.Proposal.Payment.Vouchers {
|
||||
maxClose := curHead.Height() + (increment * uint64(i+1)) + build.DealVoucherSkewLimit
|
||||
|
||||
if err := h.checkVoucher(ctx, deal, voucher, lane, maxClose, vspec[i].Amount); err != nil {
|
||||
return xerrors.Errorf("validating payment voucher %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
minPrice := types.BigMul(types.BigMul(h.pricePerByteBlock, types.NewInt(deal.Proposal.Size)), types.NewInt(deal.Proposal.Duration))
|
||||
if types.BigCmp(minPrice, deal.Proposal.TotalPrice) > 0 {
|
||||
return xerrors.Errorf("minimum price: %s", minPrice)
|
||||
}
|
||||
|
||||
prevAmt := types.NewInt(0)
|
||||
|
||||
for i, voucher := range deal.Proposal.Payment.Vouchers {
|
||||
delta, err := h.full.PaychVoucherAdd(ctx, deal.Proposal.Payment.PayChActor, voucher, nil, types.BigSub(vspec[i].Amount, prevAmt))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("consuming payment voucher %d: %w", i, err)
|
||||
}
|
||||
prevAmt = types.BigAdd(prevAmt, delta)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
switch deal.Proposal.SerializationMode {
|
||||
//case SerializationRaw:
|
||||
//case SerializationIPLD:
|
||||
case SerializationUnixFs:
|
||||
default:
|
||||
return nil, xerrors.Errorf("deal proposal with unsupported serialization: %s", deal.Proposal.SerializationMode)
|
||||
}
|
||||
|
||||
if deal.Proposal.Payment.ChannelMessage != nil {
|
||||
log.Info("waiting for channel message to appear on chain")
|
||||
if _, err := h.full.StateWaitMsg(ctx, *deal.Proposal.Payment.ChannelMessage); err != nil {
|
||||
return nil, xerrors.Errorf("waiting for paych message: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.consumeVouchers(ctx, deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("fetching data for a deal")
|
||||
err := h.sendSignedResponse(StorageDealResponse{
|
||||
State: api.DealAccepted,
|
||||
Message: "",
|
||||
Proposal: deal.ProposalCid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, merkledag.FetchGraph(ctx, deal.Ref, h.dag)
|
||||
}
|
||||
|
||||
// STAGED
|
||||
|
||||
func (h *Handler) staged(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
err := h.sendSignedResponse(StorageDealResponse{
|
||||
State: api.DealStaged,
|
||||
Proposal: deal.ProposalCid,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("Sending deal response failed: %s", err)
|
||||
}
|
||||
|
||||
root, err := h.dag.Get(ctx, deal.Ref)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get file root for deal: %s", err)
|
||||
}
|
||||
|
||||
// TODO: abstract this away into ReadSizeCloser + implement different modes
|
||||
n, err := unixfile.NewUnixfsFile(ctx, h.dag, root)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("cannot open unixfs file: %s", err)
|
||||
}
|
||||
|
||||
uf, ok := n.(sectorblocks.UnixfsReader)
|
||||
if !ok {
|
||||
// we probably got directory, unsupported for now
|
||||
return nil, xerrors.Errorf("unsupported unixfs file type")
|
||||
}
|
||||
|
||||
sectorID, err := h.secst.AddUnixfsPiece(deal.Proposal.PieceRef, uf, deal.Proposal.Duration)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("AddPiece failed: %s", err)
|
||||
}
|
||||
|
||||
log.Warnf("New Sector: %d", sectorID)
|
||||
return func(deal *MinerDeal) {
|
||||
deal.SectorID = sectorID
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SEALING
|
||||
|
||||
func getInclusionProof(ref string, status sectorbuilder.SectorSealingStatus) (PieceInclusionProof, error) {
|
||||
for i, p := range status.Pieces {
|
||||
if p.Key == ref {
|
||||
return PieceInclusionProof{
|
||||
Position: uint64(i),
|
||||
ProofElements: p.InclusionProof,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return PieceInclusionProof{}, xerrors.Errorf("pieceInclusionProof for %s in sector %d not found", ref, status.SectorID)
|
||||
}
|
||||
|
||||
func (h *Handler) waitSealed(ctx context.Context, deal MinerDeal) (sectorbuilder.SectorSealingStatus, error) {
|
||||
status, err := h.secst.WaitSeal(ctx, deal.SectorID)
|
||||
if err != nil {
|
||||
return sectorbuilder.SectorSealingStatus{}, err
|
||||
}
|
||||
|
||||
switch status.State {
|
||||
case sealing_state.Sealed:
|
||||
case sealing_state.Failed:
|
||||
return sectorbuilder.SectorSealingStatus{}, xerrors.Errorf("sealing sector %d for deal %s (ref=%s) failed: %s", deal.SectorID, deal.ProposalCid, deal.Ref, status.SealErrorMsg)
|
||||
case sealing_state.Pending:
|
||||
return sectorbuilder.SectorSealingStatus{}, xerrors.Errorf("sector status was 'pending' after call to WaitSeal (for sector %d)", deal.SectorID)
|
||||
case sealing_state.Sealing:
|
||||
return sectorbuilder.SectorSealingStatus{}, xerrors.Errorf("sector status was 'wait' after call to WaitSeal (for sector %d)", deal.SectorID)
|
||||
default:
|
||||
return sectorbuilder.SectorSealingStatus{}, xerrors.Errorf("unknown SealStatusCode: %d", status.SectorID)
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (h *Handler) sealing(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
status, err := h.waitSealed(ctx, deal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: don't hardcode unixfs
|
||||
ip, err := getInclusionProof(string(sectorblocks.SerializationUnixfs0)+deal.Ref.String(), status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proof := &actors.InclusionProof{
|
||||
Sector: deal.SectorID,
|
||||
Proof: ip.ProofElements,
|
||||
}
|
||||
proofB, err := cbor.DumpObject(proof)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// store proofs for channels
|
||||
for i, v := range deal.Proposal.Payment.Vouchers {
|
||||
if v.Extra.Method == actors.MAMethods.PaymentVerifyInclusion {
|
||||
// TODO: Set correct minAmount
|
||||
if _, err := h.full.PaychVoucherAdd(ctx, deal.Proposal.Payment.PayChActor, v, proofB, types.NewInt(0)); err != nil {
|
||||
return nil, xerrors.Errorf("storing payment voucher %d proof: %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = h.sendSignedResponse(StorageDealResponse{
|
||||
State: api.DealSealing,
|
||||
Proposal: deal.ProposalCid,
|
||||
PieceInclusionProof: ip,
|
||||
CommD: status.CommD[:],
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("Sending deal response failed: %s", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h *Handler) complete(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
mcid, err := h.commt.WaitCommit(ctx, deal.Proposal.MinerAddress, deal.SectorID)
|
||||
if err != nil {
|
||||
log.Warnf("Waiting for sector commitment message: %s", err)
|
||||
}
|
||||
|
||||
err = h.sendSignedResponse(StorageDealResponse{
|
||||
State: api.DealComplete,
|
||||
Proposal: deal.ProposalCid,
|
||||
|
||||
SectorCommitMessage: &mcid,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("Sending deal response failed: %s", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -2,58 +2,56 @@ package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/storage/commitment"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(MinerDeal{})
|
||||
}
|
||||
|
||||
type MinerDeal struct {
|
||||
Client peer.ID
|
||||
Proposal StorageDealProposal
|
||||
Proposal actors.StorageDealProposal
|
||||
ProposalCid cid.Cid
|
||||
State api.DealState
|
||||
|
||||
Ref cid.Cid
|
||||
|
||||
DealID uint64
|
||||
SectorID uint64 // Set when State >= DealStaged
|
||||
|
||||
s inet.Stream
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
type Provider struct {
|
||||
pricePerByteBlock types.BigInt // how much we want for storing one byte for one block
|
||||
minPieceSize uint64
|
||||
|
||||
ask *types.SignedStorageAsk
|
||||
askLk sync.Mutex
|
||||
|
||||
secst *sectorblocks.SectorBlocks
|
||||
commt *commitment.Tracker
|
||||
full api.FullNode
|
||||
secb *sectorblocks.SectorBlocks
|
||||
sminer *storage.Miner
|
||||
full api.FullNode
|
||||
|
||||
// TODO: Use a custom protocol or graphsync in the future
|
||||
// TODO: GC
|
||||
dag dtypes.StagingDAG
|
||||
|
||||
deals MinerStateStore
|
||||
deals *statestore.StateStore
|
||||
ds dtypes.MetadataDS
|
||||
|
||||
conns map[cid.Cid]inet.Stream
|
||||
@ -73,7 +71,7 @@ type minerDealUpdate struct {
|
||||
mut func(*MinerDeal)
|
||||
}
|
||||
|
||||
func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, commt *commitment.Tracker, dag dtypes.StagingDAG, fullNode api.FullNode) (*Handler, error) {
|
||||
func NewProvider(ds dtypes.MetadataDS, sminer *storage.Miner, secb *sectorblocks.SectorBlocks, dag dtypes.StagingDAG, fullNode api.FullNode) (*Provider, error) {
|
||||
addr, err := ds.Get(datastore.NewKey("miner-address"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -83,14 +81,14 @@ func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, commt *c
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := &Handler{
|
||||
secst: secst,
|
||||
commt: commt,
|
||||
dag: dag,
|
||||
full: fullNode,
|
||||
h := &Provider{
|
||||
sminer: sminer,
|
||||
dag: dag,
|
||||
full: fullNode,
|
||||
secb: secb,
|
||||
|
||||
pricePerByteBlock: types.NewInt(3), // TODO: allow setting
|
||||
minPieceSize: 1,
|
||||
minPieceSize: 256, // TODO: allow setting (BUT KEEP MIN 256! (because of how we fill sectors up))
|
||||
|
||||
conns: map[cid.Cid]inet.Stream{},
|
||||
|
||||
@ -101,7 +99,7 @@ func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, commt *c
|
||||
|
||||
actor: minerAddress,
|
||||
|
||||
deals: MinerStateStore{StateStore{ds: namespace.Wrap(ds, datastore.NewKey("/deals/client"))}},
|
||||
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
|
||||
ds: ds,
|
||||
}
|
||||
|
||||
@ -112,7 +110,7 @@ func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, commt *c
|
||||
if h.ask == nil {
|
||||
// TODO: we should be fine with this state, and just say it means 'not actively accepting deals'
|
||||
// for now... lets just set a price
|
||||
if err := h.SetPrice(types.NewInt(3), 1000000); err != nil {
|
||||
if err := h.SetPrice(types.NewInt(500_000_000), 1000000); err != nil {
|
||||
return nil, xerrors.Errorf("failed setting a default price: %w", err)
|
||||
}
|
||||
}
|
||||
@ -120,40 +118,40 @@ func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, commt *c
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (h *Handler) Run(ctx context.Context) {
|
||||
func (p *Provider) Run(ctx context.Context) {
|
||||
// TODO: restore state
|
||||
|
||||
go func() {
|
||||
defer log.Warn("quitting deal handler loop")
|
||||
defer close(h.stopped)
|
||||
defer log.Warn("quitting deal provider loop")
|
||||
defer close(p.stopped)
|
||||
|
||||
for {
|
||||
select {
|
||||
case deal := <-h.incoming: // DealAccepted
|
||||
h.onIncoming(deal)
|
||||
case update := <-h.updated: // DealStaged
|
||||
h.onUpdated(ctx, update)
|
||||
case <-h.stop:
|
||||
case deal := <-p.incoming: // DealAccepted
|
||||
p.onIncoming(deal)
|
||||
case update := <-p.updated: // DealStaged
|
||||
p.onUpdated(ctx, update)
|
||||
case <-p.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *Handler) onIncoming(deal MinerDeal) {
|
||||
func (p *Provider) onIncoming(deal MinerDeal) {
|
||||
log.Info("incoming deal")
|
||||
|
||||
h.conns[deal.ProposalCid] = deal.s
|
||||
p.conns[deal.ProposalCid] = deal.s
|
||||
|
||||
if err := h.deals.Begin(deal.ProposalCid, deal); err != nil {
|
||||
if err := p.deals.Begin(deal.ProposalCid, &deal); err != nil {
|
||||
// This can happen when client re-sends proposal
|
||||
h.failDeal(deal.ProposalCid, err)
|
||||
p.failDeal(deal.ProposalCid, err)
|
||||
log.Errorf("deal tracking failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
h.updated <- minerDealUpdate{
|
||||
p.updated <- minerDealUpdate{
|
||||
newState: api.DealAccepted,
|
||||
id: deal.ProposalCid,
|
||||
err: nil,
|
||||
@ -161,15 +159,15 @@ func (h *Handler) onIncoming(deal MinerDeal) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *Handler) onUpdated(ctx context.Context, update minerDealUpdate) {
|
||||
log.Infof("Deal %s updated state to %d", update.id, update.newState)
|
||||
func (p *Provider) onUpdated(ctx context.Context, update minerDealUpdate) {
|
||||
log.Infof("Deal %s updated state to %s", update.id, api.DealStates[update.newState])
|
||||
if update.err != nil {
|
||||
log.Errorf("deal %s failed: %s", update.id, update.err)
|
||||
h.failDeal(update.id, update.err)
|
||||
log.Errorf("deal %s (newSt: %d) failed: %+v", update.id, update.newState, update.err)
|
||||
p.failDeal(update.id, update.err)
|
||||
return
|
||||
}
|
||||
var deal MinerDeal
|
||||
err := h.deals.MutateMiner(update.id, func(d *MinerDeal) error {
|
||||
err := p.deals.Mutate(update.id, func(d *MinerDeal) error {
|
||||
d.State = update.newState
|
||||
if update.mut != nil {
|
||||
update.mut(d)
|
||||
@ -178,67 +176,61 @@ func (h *Handler) onUpdated(ctx context.Context, update minerDealUpdate) {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
h.failDeal(update.id, err)
|
||||
p.failDeal(update.id, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch update.newState {
|
||||
case api.DealAccepted:
|
||||
h.handle(ctx, deal, h.accept, api.DealStaged)
|
||||
p.handle(ctx, deal, p.accept, api.DealStaged)
|
||||
case api.DealStaged:
|
||||
h.handle(ctx, deal, h.staged, api.DealSealing)
|
||||
p.handle(ctx, deal, p.staged, api.DealSealing)
|
||||
case api.DealSealing:
|
||||
h.handle(ctx, deal, h.sealing, api.DealComplete)
|
||||
p.handle(ctx, deal, p.sealing, api.DealComplete)
|
||||
case api.DealComplete:
|
||||
h.handle(ctx, deal, h.complete, api.DealNoUpdate)
|
||||
p.handle(ctx, deal, p.complete, api.DealNoUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) newDeal(s inet.Stream, proposal StorageDealProposal) (MinerDeal, error) {
|
||||
// TODO: Review: Not signed?
|
||||
proposalNd, err := cbor.WrapObject(proposal, math.MaxUint64, -1)
|
||||
if err != nil {
|
||||
return MinerDeal{}, err
|
||||
}
|
||||
|
||||
ref, err := cid.Parse(proposal.PieceRef)
|
||||
func (p *Provider) newDeal(s inet.Stream, proposal Proposal) (MinerDeal, error) {
|
||||
proposalNd, err := cborutil.AsIpld(proposal.DealProposal)
|
||||
if err != nil {
|
||||
return MinerDeal{}, err
|
||||
}
|
||||
|
||||
return MinerDeal{
|
||||
Client: s.Conn().RemotePeer(),
|
||||
Proposal: proposal,
|
||||
Proposal: *proposal.DealProposal,
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
State: api.DealUnknown,
|
||||
|
||||
Ref: ref,
|
||||
Ref: proposal.Piece,
|
||||
|
||||
s: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *Handler) HandleStream(s inet.Stream) {
|
||||
func (p *Provider) HandleStream(s inet.Stream) {
|
||||
log.Info("Handling storage deal proposal!")
|
||||
|
||||
proposal, err := h.readProposal(s)
|
||||
proposal, err := p.readProposal(s)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
deal, err := h.newDeal(s, proposal.Proposal)
|
||||
deal, err := p.newDeal(s, proposal)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
log.Errorf("%+v", err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
h.incoming <- deal
|
||||
p.incoming <- deal
|
||||
}
|
||||
|
||||
func (h *Handler) Stop() {
|
||||
close(h.stop)
|
||||
<-h.stopped
|
||||
func (p *Provider) Stop() {
|
||||
close(p.stop)
|
||||
<-p.stopped
|
||||
}
|
@ -1,85 +1,85 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborrpc"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (h *Handler) SetPrice(p types.BigInt, ttlsecs int64) error {
|
||||
h.askLk.Lock()
|
||||
defer h.askLk.Unlock()
|
||||
func (p *Provider) SetPrice(price types.BigInt, ttlsecs int64) error {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
|
||||
var seqno uint64
|
||||
if h.ask != nil {
|
||||
seqno = h.ask.Ask.SeqNo + 1
|
||||
if p.ask != nil {
|
||||
seqno = p.ask.Ask.SeqNo + 1
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
ask := &types.StorageAsk{
|
||||
Price: p,
|
||||
Timestamp: now,
|
||||
Expiry: now + ttlsecs,
|
||||
Miner: h.actor,
|
||||
Price: price,
|
||||
Timestamp: uint64(now),
|
||||
Expiry: uint64(now + ttlsecs),
|
||||
Miner: p.actor,
|
||||
SeqNo: seqno,
|
||||
MinPieceSize: h.minPieceSize,
|
||||
MinPieceSize: p.minPieceSize,
|
||||
}
|
||||
|
||||
ssa, err := h.signAsk(ask)
|
||||
ssa, err := p.signAsk(ask)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return h.saveAsk(ssa)
|
||||
return p.saveAsk(ssa)
|
||||
}
|
||||
|
||||
func (h *Handler) getAsk(m address.Address) *types.SignedStorageAsk {
|
||||
h.askLk.Lock()
|
||||
defer h.askLk.Unlock()
|
||||
if m != h.actor {
|
||||
func (p *Provider) getAsk(m address.Address) *types.SignedStorageAsk {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
if m != p.actor {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.ask
|
||||
return p.ask
|
||||
}
|
||||
|
||||
func (h *Handler) HandleAskStream(s inet.Stream) {
|
||||
func (p *Provider) HandleAskStream(s inet.Stream) {
|
||||
defer s.Close()
|
||||
var ar AskRequest
|
||||
if err := cborrpc.ReadCborRPC(s, &ar); err != nil {
|
||||
if err := cborutil.ReadCborRPC(s, &ar); err != nil {
|
||||
log.Errorf("failed to read AskRequest from incoming stream: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := h.processAskRequest(&ar)
|
||||
resp := p.processAskRequest(&ar)
|
||||
|
||||
if err := cborrpc.WriteCborRPC(s, resp); err != nil {
|
||||
if err := cborutil.WriteCborRPC(s, resp); err != nil {
|
||||
log.Errorf("failed to write ask response: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) processAskRequest(ar *AskRequest) *AskResponse {
|
||||
func (p *Provider) processAskRequest(ar *AskRequest) *AskResponse {
|
||||
return &AskResponse{
|
||||
Ask: h.getAsk(ar.Miner),
|
||||
Ask: p.getAsk(ar.Miner),
|
||||
}
|
||||
}
|
||||
|
||||
var bestAskKey = datastore.NewKey("latest-ask")
|
||||
|
||||
func (h *Handler) tryLoadAsk() error {
|
||||
h.askLk.Lock()
|
||||
defer h.askLk.Unlock()
|
||||
func (p *Provider) tryLoadAsk() error {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
|
||||
err := h.loadAsk()
|
||||
err := p.loadAsk()
|
||||
if err != nil {
|
||||
if xerrors.Is(err, datastore.ErrNotFound) {
|
||||
log.Warn("no previous ask found, miner will not accept deals until a price is set")
|
||||
@ -91,33 +91,33 @@ func (h *Handler) tryLoadAsk() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) loadAsk() error {
|
||||
askb, err := h.ds.Get(datastore.NewKey("latest-ask"))
|
||||
func (p *Provider) loadAsk() error {
|
||||
askb, err := p.ds.Get(datastore.NewKey("latest-ask"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load most recent ask from disk: %w", err)
|
||||
}
|
||||
|
||||
var ssa types.SignedStorageAsk
|
||||
if err := cbor.DecodeInto(askb, &ssa); err != nil {
|
||||
if err := cborutil.ReadCborRPC(bytes.NewReader(askb), &ssa); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.ask = &ssa
|
||||
p.ask = &ssa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) signAsk(a *types.StorageAsk) (*types.SignedStorageAsk, error) {
|
||||
b, err := cbor.DumpObject(a)
|
||||
func (p *Provider) signAsk(a *types.StorageAsk) (*types.SignedStorageAsk, error) {
|
||||
b, err := cborutil.Dump(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
worker, err := h.getWorker(h.actor)
|
||||
worker, err := p.getWorker(p.actor)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get worker to sign ask: %w", err)
|
||||
}
|
||||
|
||||
sig, err := h.full.WalletSign(context.TODO(), worker, b)
|
||||
sig, err := p.full.WalletSign(context.TODO(), worker, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -128,29 +128,29 @@ func (h *Handler) signAsk(a *types.StorageAsk) (*types.SignedStorageAsk, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *Handler) saveAsk(a *types.SignedStorageAsk) error {
|
||||
b, err := cbor.DumpObject(a)
|
||||
func (p *Provider) saveAsk(a *types.SignedStorageAsk) error {
|
||||
b, err := cborutil.Dump(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.ds.Put(bestAskKey, b); err != nil {
|
||||
if err := p.ds.Put(bestAskKey, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.ask = a
|
||||
p.ask = a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) checkAskSignature(ask *types.SignedStorageAsk) error {
|
||||
tss := c.sm.ChainStore().GetHeaviestTipSet().ParentState()
|
||||
|
||||
w, err := stmgr.GetMinerWorker(context.TODO(), c.sm, tss, ask.Ask.Miner)
|
||||
w, err := stmgr.GetMinerWorkerRaw(context.TODO(), c.sm, tss, ask.Ask.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get worker for miner in ask", err)
|
||||
}
|
||||
|
||||
sigb, err := cbor.DumpObject(ask.Ask)
|
||||
sigb, err := cborutil.Dump(ask.Ask)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to re-serialize ask")
|
||||
}
|
210
chain/deals/provider_states.go
Normal file
210
chain/deals/provider_states.go
Normal file
@ -0,0 +1,210 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-merkledag"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/padreader"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
type providerHandlerFunc func(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error)
|
||||
|
||||
func (p *Provider) handle(ctx context.Context, deal MinerDeal, cb providerHandlerFunc, next api.DealState) {
|
||||
go func() {
|
||||
mut, err := cb(ctx, deal)
|
||||
|
||||
if err == nil && next == api.DealNoUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.updated <- minerDealUpdate{
|
||||
newState: next,
|
||||
id: deal.ProposalCid,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-p.stop:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// ACCEPTED
|
||||
func (p *Provider) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
switch deal.Proposal.PieceSerialization {
|
||||
//case SerializationRaw:
|
||||
//case SerializationIPLD:
|
||||
case actors.SerializationUnixFSv0:
|
||||
default:
|
||||
return nil, xerrors.Errorf("deal proposal with unsupported serialization: %s", deal.Proposal.PieceSerialization)
|
||||
}
|
||||
|
||||
head, err := p.full.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if head.Height() >= deal.Proposal.ProposalExpiration {
|
||||
return nil, xerrors.Errorf("deal proposal already expired")
|
||||
}
|
||||
|
||||
// TODO: check StorageCollateral
|
||||
|
||||
minPrice := types.BigDiv(types.BigMul(p.ask.Ask.Price, types.NewInt(deal.Proposal.PieceSize)), types.NewInt(1<<30))
|
||||
if deal.Proposal.StoragePricePerEpoch.LessThan(minPrice) {
|
||||
return nil, xerrors.Errorf("storage price per epoch less than asking price: %s < %s", deal.Proposal.StoragePricePerEpoch, minPrice)
|
||||
}
|
||||
|
||||
if deal.Proposal.PieceSize < p.ask.Ask.MinPieceSize {
|
||||
return nil, xerrors.Errorf("piece size less than minimum required size: %d < %d", deal.Proposal.PieceSize, p.ask.Ask.MinPieceSize)
|
||||
}
|
||||
|
||||
// check market funds
|
||||
clientMarketBalance, err := p.full.StateMarketBalance(ctx, deal.Proposal.Client, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting client market balance failed: %w", err)
|
||||
}
|
||||
|
||||
// This doesn't guarantee that the client won't withdraw / lock those funds
|
||||
// but it's a decent first filter
|
||||
if clientMarketBalance.Available.LessThan(deal.Proposal.TotalStoragePrice()) {
|
||||
return nil, xerrors.New("clientMarketBalance.Available too small")
|
||||
}
|
||||
|
||||
waddr, err := p.full.StateMinerWorker(ctx, deal.Proposal.Provider, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: check StorageCollateral (may be too large (or too small))
|
||||
if err := p.full.MarketEnsureAvailable(ctx, waddr, deal.Proposal.StorageCollateral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("publishing deal")
|
||||
|
||||
storageDeal := actors.StorageDeal{
|
||||
Proposal: deal.Proposal,
|
||||
}
|
||||
if err := api.SignWith(ctx, p.full.WalletSign, waddr, &storageDeal); err != nil {
|
||||
return nil, xerrors.Errorf("signing storage deal failed: ", err)
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&actors.PublishStorageDealsParams{
|
||||
Deals: []actors.StorageDeal{storageDeal},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing PublishStorageDeals params failed: ", err)
|
||||
}
|
||||
|
||||
// TODO: We may want this to happen after fetching data
|
||||
smsg, err := p.full.MpoolPushMessage(ctx, &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
From: waddr,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
Method: actors.SMAMethods.PublishStorageDeals,
|
||||
Params: params,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := p.full.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Receipt.ExitCode != 0 {
|
||||
return nil, xerrors.Errorf("publishing deal failed: exit %d", r.Receipt.ExitCode)
|
||||
}
|
||||
var resp actors.PublishStorageDealResponse
|
||||
if err := resp.UnmarshalCBOR(bytes.NewReader(r.Receipt.Return)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp.DealIDs) != 1 {
|
||||
return nil, xerrors.Errorf("got unexpected number of DealIDs from")
|
||||
}
|
||||
|
||||
log.Info("fetching data for a deal")
|
||||
mcid := smsg.Cid()
|
||||
err = p.sendSignedResponse(&Response{
|
||||
State: api.DealAccepted,
|
||||
|
||||
Proposal: deal.ProposalCid,
|
||||
PublishMessage: &mcid,
|
||||
StorageDeal: &storageDeal,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.disconnect(deal); err != nil {
|
||||
log.Warnf("closing client connection: %+v", err)
|
||||
}
|
||||
|
||||
return func(deal *MinerDeal) {
|
||||
deal.DealID = resp.DealIDs[0]
|
||||
}, merkledag.FetchGraph(ctx, deal.Ref, p.dag)
|
||||
}
|
||||
|
||||
// STAGED
|
||||
|
||||
func (p *Provider) staged(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
root, err := p.dag.Get(ctx, deal.Ref)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get file root for deal: %s", err)
|
||||
}
|
||||
|
||||
// TODO: abstract this away into ReadSizeCloser + implement different modes
|
||||
n, err := unixfile.NewUnixfsFile(ctx, p.dag, root)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("cannot open unixfs file: %s", err)
|
||||
}
|
||||
|
||||
uf, ok := n.(sectorblocks.UnixfsReader)
|
||||
if !ok {
|
||||
// we probably got directory, unsupported for now
|
||||
return nil, xerrors.Errorf("unsupported unixfs file type")
|
||||
}
|
||||
|
||||
// TODO: uf.Size() is user input, not trusted
|
||||
// This won't be useful / here after we migrate to putting CARs into sectors
|
||||
size, err := uf.Size()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting unixfs file size: %w", err)
|
||||
}
|
||||
if padreader.PaddedSize(uint64(size)) != deal.Proposal.PieceSize {
|
||||
return nil, xerrors.Errorf("deal.Proposal.PieceSize didn't match padded unixfs file size")
|
||||
}
|
||||
|
||||
sectorID, err := p.secb.AddUnixfsPiece(ctx, deal.Ref, uf, deal.DealID)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("AddPiece failed: %s", err)
|
||||
}
|
||||
log.Warnf("New Sector: %d", sectorID)
|
||||
|
||||
return func(deal *MinerDeal) {
|
||||
deal.SectorID = sectorID
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SEALING
|
||||
|
||||
func (p *Provider) sealing(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
// TODO: consider waiting for seal to happen
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Provider) complete(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
// TODO: observe sector lifecycle, status, expiration..
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -5,20 +5,18 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborrpc"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (h *Handler) failDeal(id cid.Cid, cerr error) {
|
||||
if err := h.deals.End(id); err != nil {
|
||||
func (p *Provider) failDeal(id cid.Cid, cerr error) {
|
||||
if err := p.deals.End(id); err != nil {
|
||||
log.Warnf("deals.End: %s", err)
|
||||
}
|
||||
|
||||
@ -27,18 +25,18 @@ func (h *Handler) failDeal(id cid.Cid, cerr error) {
|
||||
cerr = xerrors.Errorf("unknown error (fail called at %s:%d)", f, l)
|
||||
}
|
||||
|
||||
log.Errorf("deal %s failed: %s", id, cerr)
|
||||
log.Warnf("deal %s failed: %s", id, cerr)
|
||||
|
||||
err := h.sendSignedResponse(StorageDealResponse{
|
||||
err := p.sendSignedResponse(&Response{
|
||||
State: api.DealFailed,
|
||||
Message: cerr.Error(),
|
||||
Proposal: id,
|
||||
})
|
||||
|
||||
s, ok := h.conns[id]
|
||||
s, ok := p.conns[id]
|
||||
if ok {
|
||||
_ = s.Reset()
|
||||
delete(h.conns, id)
|
||||
delete(p.conns, id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -46,65 +44,77 @@ func (h *Handler) failDeal(id cid.Cid, cerr error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) readProposal(s inet.Stream) (proposal SignedStorageDealProposal, err error) {
|
||||
if err := cborrpc.ReadCborRPC(s, &proposal); err != nil {
|
||||
func (p *Provider) readProposal(s inet.Stream) (proposal Proposal, err error) {
|
||||
if err := cborutil.ReadCborRPC(s, &proposal); err != nil {
|
||||
log.Errorw("failed to read proposal message", "error", err)
|
||||
return SignedStorageDealProposal{}, err
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
// TODO: Validate proposal maybe
|
||||
// (and signature, obviously)
|
||||
if err := proposal.DealProposal.Verify(); err != nil {
|
||||
return proposal, xerrors.Errorf("verifying StorageDealProposal: %w", err)
|
||||
}
|
||||
|
||||
if proposal.Proposal.MinerAddress != h.actor {
|
||||
log.Errorf("proposal with wrong MinerAddress: %s", proposal.Proposal.MinerAddress)
|
||||
return SignedStorageDealProposal{}, err
|
||||
if proposal.DealProposal.Provider != p.actor {
|
||||
log.Errorf("proposal with wrong ProviderAddress: %s", proposal.DealProposal.Provider)
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Handler) sendSignedResponse(resp StorageDealResponse) error {
|
||||
s, ok := h.conns[resp.Proposal]
|
||||
func (p *Provider) sendSignedResponse(resp *Response) error {
|
||||
s, ok := p.conns[resp.Proposal]
|
||||
if !ok {
|
||||
return xerrors.New("couldn't send response: not connected")
|
||||
}
|
||||
|
||||
msg, err := cbor.DumpObject(&resp)
|
||||
msg, err := cborutil.Dump(resp)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("serializing response: %w", err)
|
||||
}
|
||||
|
||||
worker, err := h.getWorker(h.actor)
|
||||
worker, err := p.getWorker(p.actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := h.full.WalletSign(context.TODO(), worker, msg)
|
||||
sig, err := p.full.WalletSign(context.TODO(), worker, msg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to sign response message: %w", err)
|
||||
}
|
||||
|
||||
signedResponse := SignedStorageDealResponse{
|
||||
Response: resp,
|
||||
signedResponse := &SignedResponse{
|
||||
Response: *resp,
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
err = cborrpc.WriteCborRPC(s, signedResponse)
|
||||
err = cborutil.WriteCborRPC(s, signedResponse)
|
||||
if err != nil {
|
||||
// Assume client disconnected
|
||||
s.Close()
|
||||
delete(h.conns, resp.Proposal)
|
||||
delete(p.conns, resp.Proposal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) getWorker(miner address.Address) (address.Address, error) {
|
||||
func (p *Provider) disconnect(deal MinerDeal) error {
|
||||
s, ok := p.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.Close()
|
||||
delete(p.conns, deal.ProposalCid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Provider) getWorker(miner address.Address) (address.Address, error) {
|
||||
getworker := &types.Message{
|
||||
To: miner,
|
||||
From: miner,
|
||||
Method: actors.MAMethods.GetWorkerAddr,
|
||||
}
|
||||
r, err := h.full.StateCall(context.TODO(), getworker, nil)
|
||||
r, err := p.full.StateCall(context.TODO(), getworker, nil)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("getting worker address: %w", err)
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/query"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type StateStore struct {
|
||||
ds datastore.Datastore
|
||||
}
|
||||
|
||||
func (st *StateStore) Begin(i cid.Cid, state interface{}) error {
|
||||
k := datastore.NewKey(i.String())
|
||||
has, err := st.ds.Has(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if has {
|
||||
return xerrors.Errorf("Already tracking state for %s", i)
|
||||
}
|
||||
|
||||
b, err := cbor.DumpObject(state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return st.ds.Put(k, b)
|
||||
}
|
||||
|
||||
func (st *StateStore) End(i cid.Cid) error {
|
||||
k := datastore.NewKey(i.String())
|
||||
has, err := st.ds.Has(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !has {
|
||||
return xerrors.Errorf("No state for %s", i)
|
||||
}
|
||||
return st.ds.Delete(k)
|
||||
}
|
||||
|
||||
func (st *StateStore) mutate(i cid.Cid, mutator func([]byte) ([]byte, error)) error {
|
||||
k := datastore.NewKey(i.String())
|
||||
has, err := st.ds.Has(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !has {
|
||||
return xerrors.Errorf("No state for %s", i)
|
||||
}
|
||||
|
||||
cur, err := st.ds.Get(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mutated, err := mutator(cur)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return st.ds.Put(k, mutated)
|
||||
}
|
||||
|
||||
type MinerStateStore struct {
|
||||
StateStore
|
||||
}
|
||||
|
||||
func (st *MinerStateStore) MutateMiner(i cid.Cid, mutator func(*MinerDeal) error) error {
|
||||
return st.mutate(i, minerMutator(mutator))
|
||||
}
|
||||
|
||||
func minerMutator(m func(*MinerDeal) error) func([]byte) ([]byte, error) {
|
||||
return func(in []byte) ([]byte, error) {
|
||||
var deal MinerDeal
|
||||
err := cbor.DecodeInto(in, &deal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := m(&deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cbor.DumpObject(deal)
|
||||
}
|
||||
}
|
||||
|
||||
type ClientStateStore struct {
|
||||
StateStore
|
||||
}
|
||||
|
||||
func (st *ClientStateStore) MutateClient(i cid.Cid, mutator func(*ClientDeal) error) error {
|
||||
return st.mutate(i, clientMutator(mutator))
|
||||
}
|
||||
|
||||
func clientMutator(m func(*ClientDeal) error) func([]byte) ([]byte, error) {
|
||||
return func(in []byte) ([]byte, error) {
|
||||
var deal ClientDeal
|
||||
err := cbor.DecodeInto(in, &deal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := m(&deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cbor.DumpObject(deal)
|
||||
}
|
||||
}
|
||||
|
||||
func (st *ClientStateStore) ListClient() ([]ClientDeal, error) {
|
||||
var out []ClientDeal
|
||||
|
||||
res, err := st.ds.Query(query.Query{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
for {
|
||||
res, ok := res.NextSync()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
var deal ClientDeal
|
||||
err := cbor.DecodeInto(res.Value, &deal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, deal)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
@ -2,87 +2,50 @@ package deals
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(StorageDealProposal{})
|
||||
cbor.RegisterCborType(SignedStorageDealProposal{})
|
||||
const DealProtocolID = "/fil/storage/mk/1.0.1"
|
||||
const AskProtocolID = "/fil/storage/ask/1.0.1"
|
||||
|
||||
cbor.RegisterCborType(PieceInclusionProof{})
|
||||
type Proposal struct {
|
||||
DealProposal *actors.StorageDealProposal
|
||||
|
||||
cbor.RegisterCborType(StorageDealResponse{})
|
||||
cbor.RegisterCborType(SignedStorageDealResponse{})
|
||||
|
||||
cbor.RegisterCborType(AskRequest{})
|
||||
cbor.RegisterCborType(AskResponse{})
|
||||
Piece cid.Cid // Used for retrieving from the client
|
||||
}
|
||||
|
||||
const ProtocolID = "/fil/storage/mk/1.0.0"
|
||||
const AskProtocolID = "/fil/storage/ask/1.0.0"
|
||||
|
||||
type SerializationMode string
|
||||
|
||||
const (
|
||||
SerializationUnixFs = "UnixFs"
|
||||
SerializationRaw = "Raw"
|
||||
SerializationIPLD = "IPLD"
|
||||
)
|
||||
|
||||
type StorageDealProposal struct {
|
||||
PieceRef cid.Cid // TODO: port to spec
|
||||
SerializationMode SerializationMode
|
||||
CommP []byte
|
||||
|
||||
Size uint64
|
||||
TotalPrice types.BigInt
|
||||
Duration uint64
|
||||
|
||||
Payment actors.PaymentInfo
|
||||
|
||||
MinerAddress address.Address
|
||||
ClientAddress address.Address
|
||||
}
|
||||
|
||||
type SignedStorageDealProposal struct {
|
||||
Proposal StorageDealProposal
|
||||
|
||||
Signature *types.Signature
|
||||
}
|
||||
|
||||
// response
|
||||
|
||||
type PieceInclusionProof struct {
|
||||
Position uint64
|
||||
ProofElements []byte
|
||||
}
|
||||
|
||||
type StorageDealResponse struct {
|
||||
type Response struct {
|
||||
State api.DealState
|
||||
|
||||
// DealRejected / DealAccepted / DealFailed / DealStaged
|
||||
// DealProposalRejected
|
||||
Message string
|
||||
Proposal cid.Cid
|
||||
|
||||
// DealSealing
|
||||
PieceInclusionProof PieceInclusionProof
|
||||
CommD []byte // TODO: not in spec
|
||||
|
||||
// DealComplete
|
||||
SectorCommitMessage *cid.Cid
|
||||
// DealAccepted
|
||||
StorageDeal *actors.StorageDeal
|
||||
PublishMessage *cid.Cid
|
||||
}
|
||||
|
||||
type SignedStorageDealResponse struct {
|
||||
Response StorageDealResponse
|
||||
// TODO: Do we actually need this to be signed?
|
||||
type SignedResponse struct {
|
||||
Response Response
|
||||
|
||||
Signature *types.Signature
|
||||
}
|
||||
|
||||
func (r *SignedResponse) Verify(addr address.Address) error {
|
||||
b, err := cborutil.Dump(&r.Response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Signature.Verify(addr, b)
|
||||
}
|
||||
|
||||
type AskRequest struct {
|
||||
Miner address.Address
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func VoucherSpec(blocksDuration uint64, price types.BigInt, start uint64, extra *types.ModVerifyParams) []api.VoucherSpec {
|
||||
nVouchers := blocksDuration / build.MinDealVoucherIncrement
|
||||
if nVouchers < 1 {
|
||||
nVouchers = 1
|
||||
}
|
||||
if nVouchers > build.MaxVouchersPerDeal {
|
||||
nVouchers = build.MaxVouchersPerDeal
|
||||
}
|
||||
|
||||
hIncrements := blocksDuration / nVouchers
|
||||
vouchers := make([]api.VoucherSpec, nVouchers)
|
||||
|
||||
for i := uint64(0); i < nVouchers; i++ {
|
||||
vouchers[i] = api.VoucherSpec{
|
||||
Amount: types.BigDiv(types.BigMul(price, types.NewInt(i+1)), types.NewInt(nVouchers)),
|
||||
TimeLock: start + (hIncrements * (i + 1)),
|
||||
MinClose: start + (hIncrements * (i + 1)),
|
||||
Extra: extra,
|
||||
}
|
||||
}
|
||||
|
||||
return vouchers
|
||||
}
|
@ -18,8 +18,8 @@ import (
|
||||
var log = logging.Logger("events")
|
||||
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type HeightHandler func(ts *types.TipSet, curH uint64) error
|
||||
type RevertHandler func(ts *types.TipSet) error
|
||||
type HeightHandler func(ctx context.Context, ts *types.TipSet, curH uint64) error
|
||||
type RevertHandler func(ctx context.Context, ts *types.TipSet) error
|
||||
|
||||
type heightHandler struct {
|
||||
confidence int
|
||||
@ -59,6 +59,7 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
|
||||
heightEvents: heightEvents{
|
||||
tsc: tsc,
|
||||
ctx: ctx,
|
||||
gcConfidence: uint64(gcConfidence),
|
||||
|
||||
heightTriggers: map[uint64]*heightHandler{},
|
||||
@ -69,6 +70,7 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
calledEvents: calledEvents{
|
||||
cs: api,
|
||||
tsc: tsc,
|
||||
ctx: ctx,
|
||||
gcConfidence: uint64(gcConfidence),
|
||||
|
||||
confQueue: map[triggerH]map[msgH][]*queuedEvent{},
|
||||
|
@ -24,7 +24,7 @@ type triggerH = uint64
|
||||
|
||||
// `ts` is the tipset, in which the `msg` is included.
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type CalledHandler func(msg *types.Message, ts *types.TipSet, curH uint64) (bool, error)
|
||||
type CalledHandler func(msg *types.Message, ts *types.TipSet, curH uint64) (more bool, err error)
|
||||
|
||||
// CheckFunc is used for atomicity guarantees. If the condition the callbacks
|
||||
// wait for has already happened in tipset `ts`
|
||||
@ -56,6 +56,7 @@ type queuedEvent struct {
|
||||
type calledEvents struct {
|
||||
cs eventApi
|
||||
tsc *tipSetCache
|
||||
ctx context.Context
|
||||
gcConfidence uint64
|
||||
|
||||
lk sync.Mutex
|
||||
@ -114,7 +115,7 @@ func (e *calledEvents) handleReverts(ts *types.TipSet) {
|
||||
|
||||
trigger := e.triggers[event.trigger]
|
||||
|
||||
if err := trigger.revert(ts); err != nil {
|
||||
if err := trigger.revert(e.ctx, ts); err != nil {
|
||||
log.Errorf("reverting chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, ts.Height(), triggerH, err)
|
||||
}
|
||||
}
|
||||
@ -146,7 +147,11 @@ func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
|
||||
func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message, ts *types.TipSet) {
|
||||
trigger := e.triggers[triggerId]
|
||||
triggerH := ts.Height() + uint64(trigger.confidence)
|
||||
|
||||
// messages are not applied in the tipset they are included in
|
||||
appliedH := ts.Height() + 1
|
||||
|
||||
triggerH := appliedH + uint64(trigger.confidence)
|
||||
|
||||
byOrigH, ok := e.confQueue[triggerH]
|
||||
if !ok {
|
||||
@ -154,13 +159,13 @@ func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message,
|
||||
e.confQueue[triggerH] = byOrigH
|
||||
}
|
||||
|
||||
byOrigH[ts.Height()] = append(byOrigH[ts.Height()], &queuedEvent{
|
||||
byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{
|
||||
trigger: triggerId,
|
||||
h: ts.Height(),
|
||||
h: appliedH,
|
||||
msg: msg,
|
||||
})
|
||||
|
||||
e.revertQueue[ts.Height()] = append(e.revertQueue[ts.Height()], triggerH) // todo: dedupe?
|
||||
e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
|
||||
}
|
||||
|
||||
func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
|
@ -1,8 +1,11 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@ -17,16 +20,29 @@ type heightEvents struct {
|
||||
|
||||
htTriggerHeights map[triggerH][]triggerId
|
||||
htHeights map[msgH][]triggerId
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
ctx, span := trace.StartSpan(e.ctx, "events.HeightHeadChange")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("endHeight", int64(app[0].Height())))
|
||||
span.AddAttributes(trace.Int64Attribute("reverts", int64(len(rev))))
|
||||
span.AddAttributes(trace.Int64Attribute("applies", int64(len(app))))
|
||||
|
||||
for _, ts := range rev {
|
||||
// TODO: log error if h below gcconfidence
|
||||
// revert height-based triggers
|
||||
|
||||
revert := func(h uint64, ts *types.TipSet) {
|
||||
for _, tid := range e.htHeights[h] {
|
||||
err := e.heightTriggers[tid].revert(ts)
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
|
||||
err := e.heightTriggers[tid].revert(ctx, ts)
|
||||
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("reverting chain trigger (@H %d): %s", h, err)
|
||||
}
|
||||
@ -74,7 +90,13 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := hnd.handle(incTs, h); err != nil {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", false))
|
||||
|
||||
err = hnd.handle(ctx, incTs, h)
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (@H %d, called @ %d) failed: %s", triggerH, ts.Height(), err)
|
||||
}
|
||||
}
|
||||
@ -125,9 +147,16 @@ func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence
|
||||
}
|
||||
|
||||
e.lk.Unlock()
|
||||
if err := hnd(ts, bestH); err != nil {
|
||||
ctx, span := trace.StartSpan(e.ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", true))
|
||||
|
||||
err = hnd(ctx, ts, bestH)
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.lk.Lock()
|
||||
bestH = e.tsc.best().Height()
|
||||
}
|
||||
|
@ -46,11 +46,24 @@ func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet
|
||||
|
||||
func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
a, _ := address.NewFromString("t00")
|
||||
b, _ := address.NewFromString("t02")
|
||||
ts, err := types.NewTipSet([]*types.BlockHeader{
|
||||
{
|
||||
Height: h,
|
||||
Miner: a,
|
||||
|
||||
Tickets: []*types.Ticket{{[]byte{byte(h % 2)}}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
},
|
||||
{
|
||||
Height: h,
|
||||
Miner: b,
|
||||
|
||||
Tickets: []*types.Ticket{{[]byte{byte((h + 1) % 2)}}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
@ -183,12 +196,12 @@ func TestAt(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -248,12 +261,12 @@ func TestAtNullTrigger(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, uint64(6), ts.Height())
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -282,12 +295,12 @@ func TestAtNullConf(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -323,12 +336,12 @@ func TestAtStart(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -357,12 +370,12 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 11, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -385,16 +398,16 @@ func TestAtChained(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 10)
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -421,16 +434,16 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 10)
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -455,11 +468,11 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
applied = true
|
||||
require.Equal(t, 6, int(ts.Height()))
|
||||
return nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
@ -494,12 +507,13 @@ func TestCalled(t *testing.T) {
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
appliedMsg = msg
|
||||
appliedTs = ts
|
||||
appliedH = curH
|
||||
return more, nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 20, t0123, 5)
|
||||
@ -526,28 +540,28 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// create additional block so we are above confidence threshold
|
||||
|
||||
fcs.advance(0, 1, nil) // H=9 (confidence=3, apply)
|
||||
fcs.advance(0, 2, nil) // H=10 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(6), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaajq", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(9), appliedH)
|
||||
require.Equal(t, uint64(7), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(10), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(1), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
|
||||
// revert some blocks, keep the message
|
||||
|
||||
fcs.advance(3, 1, nil) // H=7 (confidence=1)
|
||||
fcs.advance(3, 1, nil) // H=8 (confidence=1)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert the message
|
||||
|
||||
fcs.advance(2, 1, nil) // H=6, we reverted ts with the msg
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@ -561,7 +575,7 @@ func TestCalled(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
fcs.advance(0, 4, map[int]cid.Cid{ // msg at H=7; H=10 (confidence=3)
|
||||
fcs.advance(0, 5, map[int]cid.Cid{ // (confidence=3)
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
@ -569,16 +583,16 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(7), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaajr", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(10), appliedH)
|
||||
require.Equal(t, uint64(9), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(12), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
|
||||
// revert and apply at different height
|
||||
|
||||
fcs.advance(4, 5, map[int]cid.Cid{ // msg at H=8; H=11 (confidence=3)
|
||||
fcs.advance(4, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
1: n2msg,
|
||||
})
|
||||
|
||||
@ -590,16 +604,16 @@ func TestCalled(t *testing.T) {
|
||||
reverted = false
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(8), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaajr", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(11), appliedH)
|
||||
require.Equal(t, uint64(11), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(14), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
|
||||
// call method again
|
||||
|
||||
fcs.advance(0, 4, map[int]cid.Cid{ // msg at H=12; H=15
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
@ -608,7 +622,7 @@ func TestCalled(t *testing.T) {
|
||||
applied = false
|
||||
|
||||
// send and revert below confidence, then cross confidence
|
||||
fcs.advance(0, 1, map[int]cid.Cid{ // msg at H=16; H=16
|
||||
fcs.advance(0, 2, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 3},
|
||||
@ -623,7 +637,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// test timeout (it's set to 20 in the call to `events.Called` above)
|
||||
|
||||
fcs.advance(0, 6, nil) // H=25
|
||||
fcs.advance(0, 6, nil)
|
||||
|
||||
require.Equal(t, false, applied) // not calling timeout as we received messages
|
||||
require.Equal(t, false, reverted)
|
||||
@ -631,7 +645,7 @@ func TestCalled(t *testing.T) {
|
||||
// test unregistering with more
|
||||
|
||||
more = false
|
||||
fcs.advance(0, 4, map[int]cid.Cid{ // msg at H=26; H=29
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 4}, // this signals we don't want more
|
||||
@ -643,7 +657,7 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 4, map[int]cid.Cid{ // msg at H=26; H=29
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 5},
|
||||
@ -693,7 +707,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
return false, nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
return nil
|
||||
}, 3, 20, t0123, 5)
|
||||
@ -728,7 +742,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
return false, nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
return nil
|
||||
}, 3, 20, t0123, 5)
|
||||
@ -765,21 +779,21 @@ func TestCalledOrder(t *testing.T) {
|
||||
switch at {
|
||||
case 0:
|
||||
require.Equal(t, uint64(1), msg.Nonce)
|
||||
require.Equal(t, uint64(3), ts.Height())
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
case 1:
|
||||
require.Equal(t, uint64(2), msg.Nonce)
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
default:
|
||||
t.Fatal("apply should only get called twice, at: ", at)
|
||||
}
|
||||
at++
|
||||
return true, nil
|
||||
}, func(ts *types.TipSet) error {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
switch at {
|
||||
case 2:
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
case 3:
|
||||
require.Equal(t, uint64(3), ts.Height())
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
default:
|
||||
t.Fatal("revert should only get called twice, at: ", at)
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func (m mybs) Get(c cid.Cid) (block.Block, error) {
|
||||
|
||||
func NewGenerator() (*ChainGen, error) {
|
||||
mr := repo.NewMemory(nil)
|
||||
lr, err := mr.Lock()
|
||||
lr, err := mr.Lock(repo.RepoStorageMiner)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("taking mem-repo lock failed: %w", err)
|
||||
}
|
||||
@ -206,14 +206,14 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add
|
||||
|
||||
st := pts.ParentState()
|
||||
|
||||
worker, err := stmgr.GetMinerWorker(ctx, cg.sm, st, m)
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, cg.sm, st, m)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, xerrors.Errorf("get miner worker: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, lastTicket.VRFProof)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, xerrors.Errorf("compute VRF: %w", err)
|
||||
}
|
||||
|
||||
tick := &types.Ticket{
|
||||
@ -252,7 +252,7 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
|
||||
|
||||
msgs, err := cg.getRandomMessages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, xerrors.Errorf("get random messages: %w", err)
|
||||
}
|
||||
|
||||
for len(blks) == 0 {
|
||||
@ -385,7 +385,7 @@ func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *t
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
return stmgr.GetMinerWorker(ctx, mca.sm, ts.ParentState(), maddr)
|
||||
return stmgr.GetMinerWorkerRaw(ctx, mca.sm, ts.ParentState(), maddr)
|
||||
}
|
||||
|
||||
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) {
|
||||
@ -393,9 +393,9 @@ func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*ty
|
||||
}
|
||||
|
||||
func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket, miner address.Address, a MiningCheckAPI) (bool, types.ElectionProof, error) {
|
||||
r, err := a.ChainGetRandomness(ctx, ts, ticks, build.RandomnessLookback)
|
||||
r, err := a.ChainGetRandomness(ctx, ts, ticks, build.EcRandomnessLookback)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return false, nil, xerrors.Errorf("chain get randomness: %w", err)
|
||||
}
|
||||
|
||||
mworker, err := a.StateMinerWorker(ctx, miner, ts)
|
||||
@ -413,7 +413,7 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket,
|
||||
return false, nil, xerrors.Errorf("failed to check power: %w", err)
|
||||
}
|
||||
|
||||
return types.PowerCmp(vrfout, types.BigMul(pow.MinerPower, types.NewInt(build.BlocksPerEpoch)), pow.TotalPower), vrfout, nil
|
||||
return types.PowerCmp(vrfout, pow.MinerPower, pow.TotalPower), vrfout, nil
|
||||
}
|
||||
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
|
@ -27,7 +27,7 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
|
||||
|
||||
height := parents.Height() + uint64(len(tickets))
|
||||
|
||||
worker, err := stmgr.GetMinerWorker(ctx, sm, st, miner)
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, miner)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
|
||||
}
|
||||
|
@ -5,6 +5,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
@ -12,14 +20,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
type GenesisBootstrap struct {
|
||||
@ -56,7 +56,7 @@ func SetupInitActor(bs bstore.Blockstore, addrs []address.Address) (*types.Actor
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: actors.InitActorCodeCid,
|
||||
Code: actors.InitCodeCid,
|
||||
Head: statecid,
|
||||
}
|
||||
|
||||
@ -85,10 +85,19 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.InitActorAddress, initact); err != nil {
|
||||
if err := state.SetActor(actors.InitAddress, initact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
spact, err := SetupStoragePowerActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
|
||||
if err := state.SetActor(actors.StoragePowerAddress, spact); err != nil {
|
||||
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
smact, err := SetupStorageMarketActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
@ -104,7 +113,7 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
||||
}
|
||||
|
||||
err = state.SetActor(actors.NetworkAddress, &types.Actor{
|
||||
Code: actors.AccountActorCodeCid,
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: netAmt,
|
||||
Head: emptyobject,
|
||||
})
|
||||
@ -113,7 +122,7 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
||||
}
|
||||
|
||||
err = state.SetActor(actors.BurntFundsAddress, &types.Actor{
|
||||
Code: actors.AccountActorCodeCid,
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: types.NewInt(0),
|
||||
Head: emptyobject,
|
||||
})
|
||||
@ -123,7 +132,7 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
||||
|
||||
for a, v := range actmap {
|
||||
err = state.SetActor(a, &types.Actor{
|
||||
Code: actors.AccountActorCodeCid,
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: v,
|
||||
Head: emptyobject,
|
||||
})
|
||||
@ -135,7 +144,7 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
func SetupStoragePowerActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
nd := hamt.NewNode(cst)
|
||||
emptyhamt, err := cst.Put(context.TODO(), nd)
|
||||
@ -154,7 +163,41 @@ func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: actors.StorageMarketActorCodeCid,
|
||||
Code: actors.StoragePowerCodeCid,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
nd := hamt.NewNode(cst)
|
||||
emptyHAMT, err := cst.Put(context.TODO(), nd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
|
||||
emptyAMT, err := amt.FromArray(blks, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
sms := &actors.StorageMarketState{
|
||||
Balances: emptyHAMT,
|
||||
Deals: emptyAMT,
|
||||
NextDealID: 0,
|
||||
}
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Actor{
|
||||
Code: actors.StorageMarketCodeCid,
|
||||
Head: stcid,
|
||||
Nonce: 0,
|
||||
Balance: types.NewInt(0),
|
||||
@ -196,13 +239,13 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
params := mustEnc(&actors.CreateStorageMinerParams{
|
||||
Owner: owner,
|
||||
Worker: worker,
|
||||
SectorSize: types.NewInt(build.SectorSize),
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: pid,
|
||||
})
|
||||
|
||||
// TODO: hardcoding 7000000 here is a little fragile, it changes any
|
||||
// time anyone changes the initial account allocations
|
||||
rval, err := doExecValue(ctx, vm, actors.StorageMarketAddress, owner, types.FromFil(6500), actors.SPAMethods.CreateStorageMiner, params)
|
||||
rval, err := doExecValue(ctx, vm, actors.StoragePowerAddress, owner, types.FromFil(6500), actors.SPAMethods.CreateStorageMiner, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
@ -216,7 +259,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
|
||||
params = mustEnc(&actors.UpdateStorageParams{Delta: types.NewInt(5000)})
|
||||
|
||||
_, err = doExec(ctx, vm, actors.StorageMarketAddress, maddr, actors.SPAMethods.UpdateStorage, params)
|
||||
_, err = doExec(ctx, vm, actors.StoragePowerAddress, maddr, actors.SPAMethods.UpdateStorage, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to update total storage: %w", err)
|
||||
}
|
||||
@ -329,11 +372,11 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
|
||||
log.Infof("Empty Genesis root: %s", emptyroot)
|
||||
|
||||
genesisticket := &types.Ticket{
|
||||
VRFProof: []byte("vrf proof"),
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
}
|
||||
|
||||
b := &types.BlockHeader{
|
||||
Miner: actors.InitActorAddress,
|
||||
Miner: actors.InitAddress,
|
||||
Tickets: []*types.Ticket{genesisticket},
|
||||
ElectionProof: []byte("the Genesis block"),
|
||||
Parents: []cid.Cid{},
|
||||
|
79
chain/market/fundmgr.go
Normal file
79
chain/market/fundmgr.go
Normal file
@ -0,0 +1,79 @@
|
||||
package market
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
)
|
||||
|
||||
type FundMgr struct {
|
||||
sm *stmgr.StateManager
|
||||
mpool full.MpoolAPI
|
||||
|
||||
lk sync.Mutex
|
||||
available map[address.Address]types.BigInt
|
||||
}
|
||||
|
||||
func NewFundMgr(sm *stmgr.StateManager, mpool full.MpoolAPI) *FundMgr {
|
||||
return &FundMgr{
|
||||
sm: sm,
|
||||
mpool: mpool,
|
||||
|
||||
available: map[address.Address]types.BigInt{},
|
||||
}
|
||||
}
|
||||
|
||||
func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
fm.lk.Lock()
|
||||
avail, ok := fm.available[addr]
|
||||
if !ok {
|
||||
bal, err := fm.sm.MarketBalance(ctx, addr, nil)
|
||||
if err != nil {
|
||||
fm.lk.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
avail = bal.Available
|
||||
}
|
||||
|
||||
toAdd := types.NewInt(0)
|
||||
avail = types.BigSub(avail, amt)
|
||||
if avail.LessThan(types.NewInt(0)) {
|
||||
// TODO: some rules around adding more to avoid doing stuff on-chain
|
||||
// all the time
|
||||
toAdd = types.BigSub(toAdd, avail)
|
||||
avail = types.NewInt(0)
|
||||
}
|
||||
fm.available[addr] = avail
|
||||
|
||||
fm.lk.Unlock()
|
||||
|
||||
smsg, err := fm.mpool.MpoolPushMessage(ctx, &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
From: addr,
|
||||
Value: toAdd,
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
Method: actors.SMAMethods.AddBalance,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, r, err := fm.sm.WaitForMessage(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.ExitCode != 0 {
|
||||
return xerrors.Errorf("adding funds to storage miner market actor failed: exit %d", r.ExitCode)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -68,7 +68,7 @@ func (st *StateTree) SetActor(addr address.Address, act *types.Actor) error {
|
||||
}
|
||||
|
||||
func (st *StateTree) lookupID(addr address.Address) (address.Address, error) {
|
||||
act, err := st.GetActor(actors.InitActorAddress)
|
||||
act, err := st.GetActor(actors.InitAddress)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("getting init actor: %w", err)
|
||||
}
|
||||
@ -143,7 +143,7 @@ func (st *StateTree) Snapshot() error {
|
||||
|
||||
func (st *StateTree) RegisterNewAddress(addr address.Address, act *types.Actor) (address.Address, error) {
|
||||
var out address.Address
|
||||
err := st.MutateActor(actors.InitActorAddress, func(initact *types.Actor) error {
|
||||
err := st.MutateActor(actors.InitAddress, func(initact *types.Actor) error {
|
||||
var ias actors.InitActorState
|
||||
if err := st.Store.Get(context.TODO(), initact.Head, &ias); err != nil {
|
||||
return err
|
||||
|
@ -27,7 +27,7 @@ func BenchmarkStateTreeSet(b *testing.B) {
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountActorCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -54,7 +54,7 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountActorCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -80,7 +80,7 @@ func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
err = st.SetActor(a, &types.Actor{
|
||||
Balance: types.NewInt(1258812523 + uint64(i)),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountActorCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Nonce: uint64(i),
|
||||
})
|
||||
if err != nil {
|
||||
@ -123,7 +123,7 @@ func TestSetCache(t *testing.T) {
|
||||
act := &types.Actor{
|
||||
Balance: types.NewInt(0),
|
||||
Code: actors.StorageMinerCodeCid,
|
||||
Head: actors.AccountActorCodeCid,
|
||||
Head: actors.AccountCodeCid,
|
||||
Nonce: 0,
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
@ -14,6 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*types.MessageReceipt, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw")
|
||||
defer span.End()
|
||||
|
||||
vmi, err := vm.NewVM(bstate, bheight, r, actors.NetworkAddress, sm.cs.Blockstore())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up vm: %w", err)
|
||||
@ -29,6 +33,14 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
|
||||
msg.Value = types.NewInt(0)
|
||||
}
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("gas_limit", int64(msg.GasLimit.Uint64())),
|
||||
trace.Int64Attribute("gas_price", int64(msg.GasPrice.Uint64())),
|
||||
trace.StringAttribute("value", msg.Value.String()),
|
||||
)
|
||||
}
|
||||
|
||||
fromActor, err := vmi.StateTree().GetActor(msg.From)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("call raw get actor: %s", err)
|
||||
|
@ -217,7 +217,10 @@ func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types
|
||||
func (sm *StateManager) GetBalance(addr address.Address, ts *types.TipSet) (types.BigInt, error) {
|
||||
act, err := sm.GetActor(addr, ts)
|
||||
if err != nil {
|
||||
return types.BigInt{}, xerrors.Errorf("get actor: %w", err)
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
return types.NewInt(0), nil
|
||||
}
|
||||
return types.EmptyInt, xerrors.Errorf("get actor: %w", err)
|
||||
}
|
||||
|
||||
return act.Balance, nil
|
||||
@ -459,3 +462,17 @@ func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (actors.StorageParticipantBalance, error) {
|
||||
var state actors.StorageMarketState
|
||||
if _, err := sm.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
||||
return actors.StorageParticipantBalance{}, err
|
||||
}
|
||||
cst := hamt.CSTFromBstore(sm.ChainStore().Blockstore())
|
||||
b, _, err := actors.GetMarketBalances(ctx, cst, state.Balances, addr)
|
||||
if err != nil {
|
||||
return actors.StorageParticipantBalance{}, err
|
||||
}
|
||||
|
||||
return b[0], nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package stmgr
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
@ -16,7 +18,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func GetMinerWorker(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
|
||||
func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
|
||||
recp, err := sm.CallRaw(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
@ -80,7 +82,7 @@ func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr add
|
||||
}
|
||||
ret, err := sm.Call(ctx, &types.Message{
|
||||
From: maddr,
|
||||
To: actors.StorageMarketAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.PowerLookup,
|
||||
Params: enc,
|
||||
}, ts)
|
||||
@ -95,8 +97,8 @@ func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr add
|
||||
}
|
||||
|
||||
ret, err := sm.Call(ctx, &types.Message{
|
||||
From: actors.StorageMarketAddress,
|
||||
To: actors.StorageMarketAddress,
|
||||
From: actors.StoragePowerAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.GetTotalStorage,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
@ -118,7 +120,7 @@ func GetMinerPeerID(ctx context.Context, sm *StateManager, ts *types.TipSet, mad
|
||||
Method: actors.MAMethods.GetPeerID,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("callRaw failed: %w", err)
|
||||
return "", xerrors.Errorf("call failed: %w", err)
|
||||
}
|
||||
|
||||
if recp.ExitCode != 0 {
|
||||
@ -128,6 +130,23 @@ func GetMinerPeerID(ctx context.Context, sm *StateManager, ts *types.TipSet, mad
|
||||
return peer.IDFromBytes(recp.Return)
|
||||
}
|
||||
|
||||
func GetMinerWorker(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (address.Address, error) {
|
||||
recp, err := sm.Call(ctx, &types.Message{
|
||||
To: maddr,
|
||||
From: maddr,
|
||||
Method: actors.MAMethods.GetWorkerAddr,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("call failed: %w", err)
|
||||
}
|
||||
|
||||
if recp.ExitCode != 0 {
|
||||
return address.Undef, xerrors.Errorf("getting miner peer ID failed (exit code %d)", recp.ExitCode)
|
||||
}
|
||||
|
||||
return address.NewFromBytes(recp.Return)
|
||||
}
|
||||
|
||||
func GetMinerProvingPeriodEnd(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
@ -138,7 +157,7 @@ func GetMinerProvingPeriodEnd(ctx context.Context, sm *StateManager, ts *types.T
|
||||
return mas.ProvingPeriodEnd, nil
|
||||
}
|
||||
|
||||
func GetMinerProvingSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.SectorInfo, error) {
|
||||
func GetMinerProvingSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
@ -148,7 +167,7 @@ func GetMinerProvingSet(ctx context.Context, sm *StateManager, ts *types.TipSet,
|
||||
return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.ProvingSet)
|
||||
}
|
||||
|
||||
func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.SectorInfo, error) {
|
||||
func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
@ -158,20 +177,56 @@ func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet,
|
||||
return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.Sectors)
|
||||
}
|
||||
|
||||
func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.Cid) ([]*api.SectorInfo, error) {
|
||||
func GetMinerSectorSize(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) {
|
||||
var mas actors.StorageMinerActorState
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
|
||||
var minfo actors.MinerInfo
|
||||
if err := cst.Get(ctx, mas.Info, &minfo); err != nil {
|
||||
return 0, xerrors.Errorf("failed to read miner info: %w", err)
|
||||
}
|
||||
|
||||
return minfo.SectorSize, nil
|
||||
}
|
||||
|
||||
func GetStorageDeal(ctx context.Context, sm *StateManager, dealId uint64, ts *types.TipSet) (*actors.OnChainDeal, error) {
|
||||
var state actors.StorageMarketState
|
||||
if _, err := sm.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blks := amt.WrapBlockstore(sm.ChainStore().Blockstore())
|
||||
da, err := amt.LoadAMT(blks, state.Deals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ocd actors.OnChainDeal
|
||||
if err := da.Get(dealId, &ocd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ocd, nil
|
||||
}
|
||||
|
||||
func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.Cid) ([]*api.ChainSectorInfo, error) {
|
||||
blks := amt.WrapBlockstore(bs)
|
||||
a, err := amt.LoadAMT(blks, ssc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sset []*api.SectorInfo
|
||||
var sset []*api.ChainSectorInfo
|
||||
if err := a.ForEach(func(i uint64, v *cbg.Deferred) error {
|
||||
var comms [][]byte
|
||||
if err := cbor.DecodeInto(v.Raw, &comms); err != nil {
|
||||
return err
|
||||
}
|
||||
sset = append(sset, &api.SectorInfo{
|
||||
sset = append(sset, &api.ChainSectorInfo{
|
||||
SectorID: i,
|
||||
CommR: comms[0],
|
||||
CommD: comms[1],
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
@ -49,14 +50,18 @@ type ChainStore struct {
|
||||
|
||||
reorgCh chan<- reorg
|
||||
headChangeNotifs []func(rev, app []*types.TipSet) error
|
||||
|
||||
mmCache *lru.ARCCache
|
||||
}
|
||||
|
||||
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore {
|
||||
c, _ := lru.NewARC(2048)
|
||||
cs := &ChainStore{
|
||||
bs: bs,
|
||||
ds: ds,
|
||||
bestTips: pubsub.New(64),
|
||||
tipsets: make(map[uint64][]cid.Cid),
|
||||
mmCache: c,
|
||||
}
|
||||
|
||||
cs.reorgCh = cs.reorgWorker(context.TODO())
|
||||
@ -226,7 +231,7 @@ func (cs *ChainStore) MaybeTakeHeavierTipSet(ctx context.Context, ts *types.TipS
|
||||
// TODO: don't do this for initial sync. Now that we don't have a
|
||||
// difference between 'bootstrap sync' and 'caught up' sync, we need
|
||||
// some other heuristic.
|
||||
return cs.takeHeaviestTipSet(ts)
|
||||
return cs.takeHeaviestTipSet(ctx, ts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -262,7 +267,10 @@ func (cs *ChainStore) reorgWorker(ctx context.Context) chan<- reorg {
|
||||
return out
|
||||
}
|
||||
|
||||
func (cs *ChainStore) takeHeaviestTipSet(ts *types.TipSet) error {
|
||||
func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet) error {
|
||||
ctx, span := trace.StartSpan(ctx, "takeHeaviestTipSet")
|
||||
defer span.End()
|
||||
|
||||
if cs.heaviest != nil { // buf
|
||||
if len(cs.reorgCh) > 0 {
|
||||
log.Warnf("Reorg channel running behind, %d reorgs buffered", len(cs.reorgCh))
|
||||
@ -275,6 +283,8 @@ func (cs *ChainStore) takeHeaviestTipSet(ts *types.TipSet) error {
|
||||
log.Warnf("no heaviest tipset found, using %s", ts.Cids())
|
||||
}
|
||||
|
||||
span.AddAttributes(trace.BoolAttribute("newHead", true))
|
||||
|
||||
log.Debugf("New heaviest tipset! %s", ts.Cids())
|
||||
cs.heaviest = ts
|
||||
|
||||
@ -291,7 +301,7 @@ func (cs *ChainStore) takeHeaviestTipSet(ts *types.TipSet) error {
|
||||
func (cs *ChainStore) SetHead(ts *types.TipSet) error {
|
||||
cs.heaviestLk.Lock()
|
||||
defer cs.heaviestLk.Unlock()
|
||||
return cs.takeHeaviestTipSet(ts)
|
||||
return cs.takeHeaviestTipSet(context.TODO(), ts)
|
||||
}
|
||||
|
||||
func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) {
|
||||
@ -627,10 +637,21 @@ func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]ChainMsg, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
type mmCids struct {
|
||||
bls []cid.Cid
|
||||
secpk []cid.Cid
|
||||
}
|
||||
|
||||
func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) {
|
||||
o, ok := cs.mmCache.Get(mmc)
|
||||
if ok {
|
||||
mmcids := o.(*mmCids)
|
||||
return mmcids.bls, mmcids.secpk, nil
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(cs.bs)
|
||||
var msgmeta types.MsgMeta
|
||||
if err := cst.Get(context.TODO(), b.Messages, &msgmeta); err != nil {
|
||||
if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to load msgmeta: %w", err)
|
||||
}
|
||||
|
||||
@ -644,6 +665,20 @@ func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message,
|
||||
return nil, nil, errors.Wrap(err, "loading secpk message cids for block")
|
||||
}
|
||||
|
||||
cs.mmCache.Add(mmc, &mmCids{
|
||||
bls: blscids,
|
||||
secpk: secpkcids,
|
||||
})
|
||||
|
||||
return blscids, secpkcids, nil
|
||||
}
|
||||
|
||||
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
blscids, secpkcids, err := cs.readMsgMetaCids(b.Messages)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
blsmsgs, err := cs.LoadMessagesFromCids(blscids)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "loading bls messages for block")
|
||||
|
@ -23,8 +23,8 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
|
||||
// >>> wFunction(totalPowerAtTipset(ts)) * 2^8 <<< + (wFunction(totalPowerAtTipset(ts)) * len(ts.blocks) * wRatio_num * 2^8) / (e * wRatio_den)
|
||||
|
||||
ret, err := cs.call(ctx, &types.Message{
|
||||
From: actors.StorageMarketAddress,
|
||||
To: actors.StorageMarketAddress,
|
||||
From: actors.StoragePowerAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
Method: actors.SPAMethods.GetTotalStorage,
|
||||
}, ts)
|
||||
if err != nil {
|
||||
@ -37,17 +37,17 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
|
||||
tpow := types.BigFromBytes(ret.Return)
|
||||
if tpow.GreaterThan(zero) {
|
||||
log2P = int64(tpow.BitLen() - 1)
|
||||
} else {
|
||||
// Not really expect to be here ...
|
||||
return types.EmptyInt, xerrors.Errorf("All power in the net is gone. You network might be disconnected, or the net is dead!")
|
||||
}
|
||||
|
||||
out.Add(out, big.NewInt(log2P*256))
|
||||
out.Add(out, big.NewInt(log2P<<8))
|
||||
|
||||
// (wFunction(totalPowerAtTipset(ts)) * len(ts.blocks) * wRatio_num * 2^8) / (e * wRatio_den)
|
||||
|
||||
wRatioNum := int64(1)
|
||||
wRatioDen := 2
|
||||
|
||||
eWeight := big.NewInt((log2P * int64(len(ts.Blocks())) * wRatioNum) << 8)
|
||||
eWeight.Div(eWeight, big.NewInt(int64(build.BlocksPerEpoch*wRatioDen)))
|
||||
eWeight := big.NewInt((log2P * int64(len(ts.Blocks())) * build.WRatioNum) << 8)
|
||||
eWeight.Div(eWeight, big.NewInt(int64(build.BlocksPerEpoch*build.WRatioDen)))
|
||||
out.Add(out, eWeight)
|
||||
|
||||
return types.BigInt{Int: out}, nil
|
||||
|
@ -44,7 +44,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
|
||||
return
|
||||
}
|
||||
|
||||
log.Infow("new block over pubsub", "cid", blk.Header.Cid(), "source", msg.GetFrom())
|
||||
log.Debugw("new block over pubsub", "cid", blk.Header.Cid(), "source", msg.GetFrom())
|
||||
s.InformNewBlock(msg.GetFrom(), &types.FullBlock{
|
||||
Header: blk.Header,
|
||||
BlsMessages: bmsgs,
|
||||
|
193
chain/sync.go
193
chain/sync.go
@ -2,6 +2,7 @@ package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/blocksync"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
@ -49,7 +51,7 @@ type Syncer struct {
|
||||
bad *BadBlockCache
|
||||
|
||||
// handle to the block sync service
|
||||
Bsync *BlockSync
|
||||
Bsync *blocksync.BlockSync
|
||||
|
||||
self peer.ID
|
||||
|
||||
@ -61,7 +63,7 @@ type Syncer struct {
|
||||
peerHeadsLk sync.Mutex
|
||||
}
|
||||
|
||||
func NewSyncer(sm *stmgr.StateManager, bsync *BlockSync, self peer.ID) (*Syncer, error) {
|
||||
func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, self peer.ID) (*Syncer, error) {
|
||||
gen, err := sm.ChainStore().GetGenesis()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -103,7 +105,7 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) {
|
||||
|
||||
if from == syncer.self {
|
||||
// TODO: this is kindof a hack...
|
||||
log.Info("got block from ourselves")
|
||||
log.Debug("got block from ourselves")
|
||||
|
||||
if err := syncer.Sync(ctx, fts.TipSet()); err != nil {
|
||||
log.Errorf("failed to sync our own block %s: %+v", fts.TipSet().Cids(), err)
|
||||
@ -111,14 +113,22 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
syncer.peerHeadsLk.Lock()
|
||||
syncer.peerHeads[from] = fts.TipSet()
|
||||
syncer.peerHeadsLk.Unlock()
|
||||
syncer.Bsync.AddPeer(from)
|
||||
|
||||
bestPweight := syncer.store.GetHeaviestTipSet().Blocks()[0].ParentWeight
|
||||
targetWeight := fts.TipSet().Blocks()[0].ParentWeight
|
||||
if targetWeight.LessThan(bestPweight) {
|
||||
log.Warn("incoming tipset does not appear to be better than our best chain, ignoring for now")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := syncer.Sync(ctx, fts.TipSet()); err != nil {
|
||||
log.Errorf("sync error: %+v", err)
|
||||
log.Errorf("sync error (curW=%s, targetW=%s): %+v", bestPweight, targetWeight, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -350,6 +360,12 @@ func (syncer *Syncer) tryLoadFullTipSet(cids []cid.Cid) (*store.FullTipSet, erro
|
||||
func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error {
|
||||
ctx, span := trace.StartSpan(ctx, "chain.Sync")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("tipset", fmt.Sprint(maybeHead.Cids())),
|
||||
trace.Int64Attribute("height", int64(maybeHead.Height())),
|
||||
)
|
||||
}
|
||||
|
||||
syncer.syncLock.Lock()
|
||||
defer syncer.syncLock.Unlock()
|
||||
@ -359,16 +375,22 @@ func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error {
|
||||
}
|
||||
|
||||
if err := syncer.collectChain(ctx, maybeHead); err != nil {
|
||||
span.AddAttributes(trace.StringAttribute("col_error", err.Error()))
|
||||
return xerrors.Errorf("collectChain failed: %w", err)
|
||||
}
|
||||
|
||||
if err := syncer.store.PutTipSet(ctx, maybeHead); err != nil {
|
||||
span.AddAttributes(trace.StringAttribute("put_error", err.Error()))
|
||||
return xerrors.Errorf("failed to put synced tipset to chainstore: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPermanent(err error) bool {
|
||||
return !errors.Is(err, ErrTemporal)
|
||||
}
|
||||
|
||||
func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validateTipSet")
|
||||
defer span.End()
|
||||
@ -380,7 +402,9 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
|
||||
|
||||
for _, b := range fts.Blocks {
|
||||
if err := syncer.ValidateBlock(ctx, b); err != nil {
|
||||
syncer.bad.Add(b.Cid())
|
||||
if isPermanent(err) {
|
||||
syncer.bad.Add(b.Cid())
|
||||
}
|
||||
return xerrors.Errorf("validating block %s: %w", b.Cid(), err)
|
||||
}
|
||||
|
||||
@ -399,7 +423,7 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b
|
||||
}
|
||||
|
||||
ret, err := syncer.sm.Call(ctx, &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
From: maddr,
|
||||
Method: actors.SPAMethods.IsMiner,
|
||||
Params: enc,
|
||||
@ -445,6 +469,8 @@ func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Addre
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrTemporal = errors.New("temporal error")
|
||||
|
||||
// Should match up with 'Semantical Validation' in validation.md in the spec
|
||||
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validateBlock")
|
||||
@ -457,9 +483,32 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err)
|
||||
}
|
||||
|
||||
// fast checks first
|
||||
stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
|
||||
}
|
||||
|
||||
if stateroot != h.ParentStateRoot {
|
||||
msgs, err := syncer.store.MessagesForTipset(baseTs)
|
||||
if err != nil {
|
||||
log.Error("failed to load messages for tipset during tipset state mismatch error: ", err)
|
||||
} else {
|
||||
log.Warn("Messages for tipset with mismatching state:")
|
||||
for i, m := range msgs {
|
||||
mm := m.VMMessage()
|
||||
log.Warnf("Message[%d]: from=%s to=%s method=%d params=%x", i, mm.From, mm.To, mm.Method, mm.Params)
|
||||
}
|
||||
}
|
||||
|
||||
return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot)
|
||||
}
|
||||
|
||||
if precp != h.ParentMessageReceipts {
|
||||
return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
|
||||
}
|
||||
|
||||
if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) {
|
||||
return xerrors.Errorf("block was from the future")
|
||||
return xerrors.Errorf("block was from the future: %w", ErrTemporal)
|
||||
}
|
||||
|
||||
if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) {
|
||||
@ -467,84 +516,44 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets))
|
||||
}
|
||||
|
||||
waddrPromise := async.Any(func() (interface{}, error) {
|
||||
stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
|
||||
}
|
||||
|
||||
if stateroot != h.ParentStateRoot {
|
||||
return nil, xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot)
|
||||
}
|
||||
|
||||
if precp != h.ParentMessageReceipts {
|
||||
return nil, xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
|
||||
}
|
||||
|
||||
waddr, err := stmgr.GetMinerWorker(ctx, syncer.sm, stateroot, h.Miner)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("GetMinerWorker failed: %w", err)
|
||||
}
|
||||
return waddr, nil
|
||||
})
|
||||
|
||||
minerCheck := async.Err(func() error {
|
||||
if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil {
|
||||
return xerrors.Errorf("minerIsValid failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
waddri, err := waddrPromise.AwaitContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil {
|
||||
return xerrors.Errorf("minerIsValid failed: %w", err)
|
||||
}
|
||||
waddr := waddri.(address.Address)
|
||||
|
||||
blockSigCheck := async.Err(func() error {
|
||||
if err := h.CheckBlockSignature(ctx, waddr); err != nil {
|
||||
return xerrors.Errorf("check block signature failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, stateroot, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err)
|
||||
}
|
||||
|
||||
tktsCheck := async.Err(func() error {
|
||||
if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil {
|
||||
return xerrors.Errorf("validating block tickets failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err := h.CheckBlockSignature(ctx, waddr); err != nil {
|
||||
return xerrors.Errorf("check block signature failed: %w", err)
|
||||
}
|
||||
|
||||
eproofCheck := async.Err(func() error {
|
||||
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.RandomnessLookback)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
|
||||
}
|
||||
if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil {
|
||||
return xerrors.Errorf("validating block tickets failed: %w", err)
|
||||
}
|
||||
|
||||
if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil {
|
||||
return xerrors.Errorf("checking eproof failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.EcRandomnessLookback)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
|
||||
}
|
||||
|
||||
winnerCheck := async.Err(func() error {
|
||||
mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed getting power: %w", err)
|
||||
}
|
||||
if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil {
|
||||
return xerrors.Errorf("checking eproof failed: %w", err)
|
||||
}
|
||||
|
||||
if !types.PowerCmp(h.ElectionProof, mpow, tpow) {
|
||||
return xerrors.Errorf("miner created a block but was not a winner")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed getting power: %w", err)
|
||||
}
|
||||
|
||||
msgsCheck := async.Err(func() error {
|
||||
if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil {
|
||||
return xerrors.Errorf("block had invalid messages: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !types.PowerCmp(h.ElectionProof, mpow, tpow) {
|
||||
return xerrors.Errorf("miner created a block but was not a winner")
|
||||
}
|
||||
|
||||
if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil {
|
||||
return xerrors.Errorf("block had invalid messages: %w", err)
|
||||
}
|
||||
|
||||
await := []async.ErrorFuture{
|
||||
minerCheck,
|
||||
@ -705,6 +714,15 @@ func (syncer *Syncer) collectHeaders(ctx context.Context, from *types.TipSet, to
|
||||
trace.Int64Attribute("toHeight", int64(to.Height())),
|
||||
)
|
||||
|
||||
for _, pcid := range from.Parents() {
|
||||
if syncer.bad.Has(pcid) {
|
||||
for _, b := range from.Cids() {
|
||||
syncer.bad.Add(b)
|
||||
}
|
||||
return nil, xerrors.Errorf("chain linked to block marked previously as bad (%s, %s)", from.Cids(), pcid)
|
||||
}
|
||||
}
|
||||
|
||||
blockSet := []*types.TipSet{from}
|
||||
|
||||
at := from.Parents()
|
||||
@ -747,6 +765,8 @@ loop:
|
||||
|
||||
log.Errorf("failed to get blocks: %+v", err)
|
||||
|
||||
span.AddAttributes(trace.StringAttribute("error", err.Error()))
|
||||
|
||||
// This error will only be logged above,
|
||||
return nil, xerrors.Errorf("failed to get blocks: %w", err)
|
||||
}
|
||||
@ -779,6 +799,13 @@ loop:
|
||||
log.Warnf("(fork detected) synced header chain (%s - %d) does not link to our best block (%s - %d)", from.Cids(), from.Height(), to.Cids(), to.Height())
|
||||
fork, err := syncer.syncFork(ctx, last, to)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, ErrForkTooLong) {
|
||||
// TODO: we're marking this block bad in the same way that we mark invalid blocks bad. Maybe distinguish?
|
||||
log.Warn("adding forked chain to our bad tipset cache")
|
||||
for _, b := range from.Blocks() {
|
||||
syncer.bad.Add(b.Cid())
|
||||
}
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to sync fork: %w", err)
|
||||
}
|
||||
|
||||
@ -788,6 +815,8 @@ loop:
|
||||
return blockSet, nil
|
||||
}
|
||||
|
||||
var ErrForkTooLong = fmt.Errorf("fork longer than threshold")
|
||||
|
||||
func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) {
|
||||
tips, err := syncer.Bsync.GetBlocks(ctx, from.Parents(), build.ForkLengthThreshold)
|
||||
if err != nil {
|
||||
@ -814,7 +843,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *type
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, xerrors.Errorf("fork was longer than our threshold")
|
||||
return nil, ErrForkTooLong
|
||||
}
|
||||
|
||||
func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*types.TipSet) error {
|
||||
@ -897,7 +926,7 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
return nil
|
||||
}
|
||||
|
||||
func persistMessages(bs bstore.Blockstore, bst *BSTipSet) error {
|
||||
func persistMessages(bs bstore.Blockstore, bst *blocksync.BSTipSet) error {
|
||||
for _, m := range bst.BlsMessages {
|
||||
//log.Infof("putting BLS message: %s", m.Cid())
|
||||
if _, err := store.PutMessage(bs, m); err != nil {
|
||||
@ -930,6 +959,8 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error
|
||||
return err
|
||||
}
|
||||
|
||||
span.AddAttributes(trace.Int64Attribute("syncChainLength", int64(len(headers))))
|
||||
|
||||
if !headers[0].Equals(ts) {
|
||||
log.Errorf("collectChain headers[0] should be equal to sync target. Its not: %s != %s", headers[0].Cids(), ts.Cids())
|
||||
}
|
||||
@ -951,7 +982,7 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error
|
||||
}
|
||||
|
||||
syncer.syncState.SetStage(api.StageSyncComplete)
|
||||
log.Infow("new tipset", "height", ts.Height(), "tipset", types.LogCids(ts.Cids()))
|
||||
log.Debugw("new tipset", "height", ts.Height(), "tipset", types.LogCids(ts.Cids()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
@ -390,7 +391,7 @@ func TestSyncBadTimestamp(t *testing.T) {
|
||||
|
||||
base := tu.g.CurTipset
|
||||
tu.g.Timestamper = func(pts *types.TipSet, tl int) uint64 {
|
||||
return pts.MinTimestamp() + 2
|
||||
return pts.MinTimestamp() + (build.BlockDelay / 2)
|
||||
}
|
||||
|
||||
fmt.Println("BASE: ", base.Cids())
|
||||
|
@ -16,10 +16,12 @@ type SignedStorageAsk struct {
|
||||
}
|
||||
|
||||
type StorageAsk struct {
|
||||
Price BigInt
|
||||
// Price per GiB / Epoch
|
||||
Price BigInt
|
||||
|
||||
MinPieceSize uint64
|
||||
Miner address.Address
|
||||
Timestamp int64
|
||||
Expiry int64
|
||||
Timestamp uint64
|
||||
Expiry uint64
|
||||
SeqNo uint64
|
||||
}
|
||||
|
@ -32,3 +32,20 @@ func TestBigIntSerializationRoundTrip(t *testing.T) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilRoundTrip(t *testing.T) {
|
||||
testValues := []string{
|
||||
"0", "1", "1.001", "100.10001", "101100", "5000.01", "5000",
|
||||
}
|
||||
|
||||
for _, v := range testValues {
|
||||
fval, err := ParseFIL(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fval.String() != v {
|
||||
t.Fatal("mismatch in values!", v, fval.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,16 @@ package types
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"math/big"
|
||||
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"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/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
)
|
||||
|
||||
@ -159,23 +160,32 @@ func CidArrsEqual(a, b []cid.Cid) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var blocksPerEpoch = NewInt(build.BlocksPerEpoch)
|
||||
|
||||
func PowerCmp(eproof ElectionProof, mpow, totpow BigInt) bool {
|
||||
|
||||
/*
|
||||
Need to check that
|
||||
h(vrfout) / 2^256 < minerPower / totalPower
|
||||
(h(vrfout) + 1) / (max(h) + 1) <= e * minerPower / totalPower
|
||||
max(h) == 2^256-1
|
||||
which in terms of integer math means:
|
||||
(h(vrfout) + 1) * totalPower <= e * minerPower * 2^256
|
||||
in 2^256 space, it is equivalent to:
|
||||
h(vrfout) * totalPower < e * minerPower * 2^256
|
||||
*/
|
||||
|
||||
h := sha256.Sum256(eproof)
|
||||
|
||||
// 2^256
|
||||
rden := BigInt{big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil)}
|
||||
lhs := BigFromBytes(h[:]).Int
|
||||
lhs = lhs.Mul(lhs, totpow.Int)
|
||||
|
||||
top := BigMul(rden, mpow)
|
||||
out := BigDiv(top, totpow)
|
||||
// rhs = minerPower * 2^256
|
||||
// rhs = minerPower << 256
|
||||
rhs := new(big.Int).Lsh(mpow.Int, 256)
|
||||
rhs = rhs.Mul(rhs, blocksPerEpoch.Int)
|
||||
|
||||
hp := BigFromBytes(h[:])
|
||||
return hp.LessThan(out)
|
||||
// h(vrfout) * totalPower < e * minerPower * 2^256?
|
||||
return lhs.Cmp(rhs) == -1
|
||||
}
|
||||
|
||||
func (t *Ticket) Equals(ot *Ticket) bool {
|
||||
|
@ -27,7 +27,7 @@ func testBlockHeader(t testing.TB) *BlockHeader {
|
||||
ElectionProof: []byte("cats won the election"),
|
||||
Tickets: []*Ticket{
|
||||
&Ticket{
|
||||
VRFProof: []byte("vrf proof"),
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
},
|
||||
},
|
||||
Parents: []cid.Cid{c, c},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,19 @@ type FIL BigInt
|
||||
|
||||
func (f FIL) String() string {
|
||||
r := new(big.Rat).SetFrac(f.Int, big.NewInt(build.FilecoinPrecision))
|
||||
return strings.TrimRight(r.FloatString(18), "0")
|
||||
if r.Sign() == 0 {
|
||||
return "0"
|
||||
}
|
||||
return strings.TrimRight(strings.TrimRight(r.FloatString(18), "0"), ".")
|
||||
}
|
||||
|
||||
func (f FIL) Format(s fmt.State, ch rune) {
|
||||
switch ch {
|
||||
case 's', 'v':
|
||||
fmt.Fprint(s, f.String())
|
||||
default:
|
||||
f.Int.Format(s, ch)
|
||||
}
|
||||
}
|
||||
|
||||
func ParseFIL(s string) (FIL, error) {
|
||||
|
@ -4,10 +4,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
var log = logging.Logger("types")
|
||||
@ -20,14 +22,14 @@ type TipSet struct {
|
||||
|
||||
// why didnt i just export the fields? Because the struct has methods with the
|
||||
// same names already
|
||||
type expTipSet struct {
|
||||
type ExpTipSet struct {
|
||||
Cids []cid.Cid
|
||||
Blocks []*BlockHeader
|
||||
Height uint64
|
||||
}
|
||||
|
||||
func (ts *TipSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(expTipSet{
|
||||
return json.Marshal(ExpTipSet{
|
||||
Cids: ts.cids,
|
||||
Blocks: ts.blks,
|
||||
Height: ts.height,
|
||||
@ -35,7 +37,7 @@ func (ts *TipSet) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (ts *TipSet) UnmarshalJSON(b []byte) error {
|
||||
var ets expTipSet
|
||||
var ets ExpTipSet
|
||||
if err := json.Unmarshal(b, &ets); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -50,6 +52,34 @@ func (ts *TipSet) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *TipSet) MarshalCBOR(w io.Writer) error {
|
||||
if ts == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
return (&ExpTipSet{
|
||||
Cids: ts.cids,
|
||||
Blocks: ts.blks,
|
||||
Height: ts.height,
|
||||
}).MarshalCBOR(w)
|
||||
}
|
||||
|
||||
func (ts *TipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
var ets ExpTipSet
|
||||
if err := ets.UnmarshalCBOR(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ots, err := NewTipSet(ets.Blocks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*ts = *ots
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tipsetSortFunc(blks []*BlockHeader) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
ti := blks[i].LastTicket()
|
||||
@ -74,9 +104,15 @@ func NewTipSet(blks []*BlockHeader) (*TipSet, error) {
|
||||
if b.Height != blks[0].Height {
|
||||
return nil, fmt.Errorf("cannot create tipset with mismatching heights")
|
||||
}
|
||||
|
||||
for i, cid := range b.Parents {
|
||||
if cid != blks[0].Parents[i] {
|
||||
return nil, fmt.Errorf("cannot create tipset with mismatching parents")
|
||||
}
|
||||
}
|
||||
|
||||
ts.cids = append(ts.cids, b.Cid())
|
||||
|
||||
// TODO: ensure the same parents
|
||||
}
|
||||
ts.height = blks[0].Height
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
cborrpc "github.com/filecoin-project/lotus/lib/cborutil"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
@ -46,13 +47,13 @@ func (sv *SignedVoucher) EncodedString() (string, error) {
|
||||
func (sv *SignedVoucher) Equals(other *SignedVoucher) bool {
|
||||
// TODO: make this less bad
|
||||
|
||||
selfB, err := cbor.DumpObject(sv)
|
||||
selfB, err := cborrpc.Dump(sv)
|
||||
if err != nil {
|
||||
log.Errorf("SignedVoucher.Equals: dump self: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
otherB, err := cbor.DumpObject(other)
|
||||
otherB, err := cborrpc.Dump(other)
|
||||
if err != nil {
|
||||
log.Errorf("SignedVoucher.Equals: dump other: %s", err)
|
||||
return false
|
||||
|
@ -2,6 +2,7 @@ package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
@ -29,11 +30,12 @@ func newInvoker() *invoker {
|
||||
}
|
||||
|
||||
// add builtInCode using: register(cid, singleton)
|
||||
inv.register(actors.InitActorCodeCid, actors.InitActor{}, actors.InitActorState{})
|
||||
inv.register(actors.StorageMarketActorCodeCid, actors.StoragePowerActor{}, actors.StoragePowerState{})
|
||||
inv.register(actors.InitCodeCid, actors.InitActor{}, actors.InitActorState{})
|
||||
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.MultisigActorCodeCid, actors.MultiSigActor{}, actors.MultiSigActorState{})
|
||||
inv.register(actors.PaymentChannelActorCodeCid, actors.PaymentChannelActor{}, actors.PaymentChannelActorState{})
|
||||
inv.register(actors.MultisigCodeCid, actors.MultiSigActor{}, actors.MultiSigActorState{})
|
||||
inv.register(actors.PaymentChannelCodeCid, actors.PaymentChannelActor{}, actors.PaymentChannelActorState{})
|
||||
|
||||
return inv
|
||||
}
|
||||
@ -42,7 +44,8 @@ func (inv *invoker) Invoke(act *types.Actor, vmctx types.VMContext, method uint6
|
||||
|
||||
code, ok := inv.builtInCode[act.Code]
|
||||
if !ok {
|
||||
return nil, aerrors.Newf(255, "no code for actor %s", act.Code)
|
||||
log.Errorf("no code for actor %s", act.Code)
|
||||
return nil, aerrors.Newf(255, "no code for actor %s(%d)(%s)", act.Code, method, hex.EncodeToString(params))
|
||||
}
|
||||
if method >= uint64(len(code)) || code[method] == nil {
|
||||
return nil, aerrors.Newf(255, "no method %d on actor", method)
|
||||
|
@ -66,7 +66,7 @@ func NewBLSAccountActor(st *state.StateTree, addr address.Address) (*types.Actor
|
||||
}
|
||||
|
||||
nact := &types.Actor{
|
||||
Code: actors.AccountActorCodeCid,
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: types.NewInt(0),
|
||||
Head: c,
|
||||
}
|
||||
@ -76,7 +76,7 @@ func NewBLSAccountActor(st *state.StateTree, addr address.Address) (*types.Actor
|
||||
|
||||
func NewSecp256k1AccountActor(st *state.StateTree, addr address.Address) (*types.Actor, aerrors.ActorError) {
|
||||
nact := &types.Actor{
|
||||
Code: actors.AccountActorCodeCid,
|
||||
Code: actors.AccountCodeCid,
|
||||
Balance: types.NewInt(0),
|
||||
Head: EmptyObjectCid,
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ func (vmc *VMContext) ChargeGas(amount uint64) aerrors.ActorError {
|
||||
}
|
||||
|
||||
func (vmc *VMContext) StateTree() (types.StateTree, aerrors.ActorError) {
|
||||
if vmc.msg.To != actors.InitActorAddress {
|
||||
if vmc.msg.To != actors.InitAddress {
|
||||
return nil, aerrors.Escalate(fmt.Errorf("only init actor can access state tree directly"), "invalid use of StateTree")
|
||||
}
|
||||
|
||||
@ -215,7 +215,7 @@ func ResolveToKeyAddr(state types.StateTree, cst *hamt.CborIpldStore, addr addre
|
||||
return address.Undef, aerrors.Newf(1, "failed to find actor: %s", addr)
|
||||
}
|
||||
|
||||
if act.Code != actors.AccountActorCodeCid {
|
||||
if act.Code != actors.AccountCodeCid {
|
||||
return address.Undef, aerrors.New(1, "address was not for an account actor")
|
||||
}
|
||||
|
||||
@ -462,7 +462,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
return nil, xerrors.Errorf("fatal error: %w", actorErr)
|
||||
}
|
||||
if actorErr != nil {
|
||||
log.Warnf("[from=%s,n=%d,h=%d] Send actor error: %+v", msg.From, msg.Nonce, vm.blockHeight, actorErr)
|
||||
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)
|
||||
}
|
||||
|
||||
var errcode uint8
|
||||
@ -488,6 +488,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
return nil, xerrors.Errorf("getting block miner actor (%s) failed: %w", vm.blockMiner, err)
|
||||
}
|
||||
|
||||
// TODO: support multiple blocks in a tipset
|
||||
// TODO: actually wire this up (miner is undef for now)
|
||||
gasReward := types.BigMul(msg.GasPrice, gasUsed)
|
||||
if err := Transfer(gasHolder, miner, gasReward); err != nil {
|
||||
return nil, xerrors.Errorf("failed to give miner gas reward: %w", err)
|
||||
@ -630,6 +632,7 @@ func depositFunds(act *types.Actor, amt types.BigInt) {
|
||||
}
|
||||
|
||||
var miningRewardTotal = types.FromFil(build.MiningRewardTotal)
|
||||
var blocksPerEpoch = types.NewInt(build.BlocksPerEpoch)
|
||||
|
||||
// MiningReward returns correct mining reward
|
||||
// coffer is amount of FIL in NetworkAddress
|
||||
@ -637,5 +640,6 @@ func MiningReward(remainingReward types.BigInt) types.BigInt {
|
||||
ci := big.NewInt(0).Set(remainingReward.Int)
|
||||
res := ci.Mul(ci, build.InitialReward)
|
||||
res = res.Div(res, miningRewardTotal.Int)
|
||||
res = res.Div(res, blocksPerEpoch.Int)
|
||||
return types.BigInt{res}
|
||||
}
|
||||
|
@ -2,14 +2,17 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
actors "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@ -25,6 +28,7 @@ var clientCmd = &cli.Command{
|
||||
clientFindCmd,
|
||||
clientRetrieveCmd,
|
||||
clientQueryAskCmd,
|
||||
clientListDeals,
|
||||
},
|
||||
}
|
||||
|
||||
@ -101,8 +105,7 @@ var clientDealCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: parse bigint
|
||||
price, err := strconv.ParseInt(cctx.Args().Get(2), 10, 32)
|
||||
price, err := types.ParseFIL(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -112,7 +115,7 @@ var clientDealCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
proposal, err := api.ClientStartDeal(ctx, data, miner, types.NewInt(uint64(price)), uint64(dur))
|
||||
proposal, err := api.ClientStartDeal(ctx, data, miner, types.BigInt(price), uint64(dur))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -164,7 +167,7 @@ var clientFindCmd = &cli.Command{
|
||||
fmt.Printf("ERR %s@%s: %s\n", offer.Miner, offer.MinerPeerID, offer.Err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("RETRIEVAL %s@%s-%sfil-%db\n", offer.Miner, offer.MinerPeerID, offer.MinPrice, offer.Size)
|
||||
fmt.Printf("RETRIEVAL %s@%s-%sfil-%db\n", offer.Miner, offer.MinerPeerID, types.FIL(offer.MinPrice), offer.Size)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -230,11 +233,12 @@ var clientRetrieveCmd = &cli.Command{
|
||||
order := offers[0].Order()
|
||||
order.Client = payer
|
||||
|
||||
err = api.ClientRetrieve(ctx, order, cctx.Args().Get(1))
|
||||
if err == nil {
|
||||
fmt.Println("Success")
|
||||
if err := api.ClientRetrieve(ctx, order, cctx.Args().Get(1)); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
||||
fmt.Println("Success")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -308,20 +312,46 @@ var clientQueryAskCmd = &cli.Command{
|
||||
}
|
||||
|
||||
fmt.Printf("Ask: %s\n", maddr)
|
||||
fmt.Printf("Price per Byte: %s\n", ask.Ask.Price)
|
||||
fmt.Printf("Price per GigaByte: %s\n", types.FIL(ask.Ask.Price))
|
||||
|
||||
size := cctx.Int64("size")
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("Price per Block: %s\n", types.BigMul(ask.Ask.Price, types.NewInt(uint64(size))))
|
||||
perEpoch := types.BigDiv(types.BigMul(ask.Ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
|
||||
fmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))
|
||||
|
||||
duration := cctx.Int64("duration")
|
||||
if duration == 0 {
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("Total Price: %s\n", types.BigMul(types.BigMul(ask.Ask.Price, types.NewInt(uint64(size))), types.NewInt(uint64(duration))))
|
||||
fmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var clientListDeals = &cli.Command{
|
||||
Name: "list-deals",
|
||||
Usage: "List storage market deals",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
deals, err := api.ClientListDeals(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "DealCid\tProvider\tState\tPieceRef\tSize\tPrice\tDuration\n")
|
||||
for _, d := range deals {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%x\t%d\t%s\t%d\n", d.ProposalCid, d.Provider, lapi.DealStates[d.State], d.PieceRef, d.Size, d.PricePerEpoch, d.Duration)
|
||||
}
|
||||
return w.Flush()
|
||||
},
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ var Commands = []*cli.Command{
|
||||
sendCmd,
|
||||
stateCmd,
|
||||
syncCmd,
|
||||
unregisterMinerCmd,
|
||||
versionCmd,
|
||||
walletCmd,
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ var createMinerCmd = &cli.Command{
|
||||
createMinerArgs := actors.CreateStorageMinerParams{
|
||||
Worker: worker,
|
||||
Owner: owner,
|
||||
SectorSize: types.NewInt(ssize),
|
||||
SectorSize: ssize,
|
||||
PeerID: pid,
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ var createMinerCmd = &cli.Command{
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
From: addr,
|
||||
Method: actors.SPAMethods.CreateStorageMiner,
|
||||
Params: params,
|
||||
|
32
cli/miner.go
Normal file
32
cli/miner.go
Normal file
@ -0,0 +1,32 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
var unregisterMinerCmd = &cli.Command{
|
||||
Name: "unregister-miner",
|
||||
Usage: "Manually unregister miner actor",
|
||||
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 pass address of miner to unregister")
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.MinerUnregister(ctx, maddr)
|
||||
},
|
||||
}
|
@ -9,8 +9,14 @@ import (
|
||||
var fetchParamCmd = &cli.Command{
|
||||
Name: "fetch-params",
|
||||
Usage: "Fetch proving parameters",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "only-verify-keys",
|
||||
Usage: "only download the verify keys",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if err := build.GetParams(true); err != nil {
|
||||
if err := build.GetParams(!cctx.Bool("only-verify-keys")); err != nil {
|
||||
return xerrors.Errorf("fetching proof parameters: %w", err)
|
||||
}
|
||||
|
||||
|
41
cli/state.go
41
cli/state.go
@ -20,6 +20,7 @@ var stateCmd = &cli.Command{
|
||||
statePledgeCollateralCmd,
|
||||
stateListActorsCmd,
|
||||
stateListMinersCmd,
|
||||
stateGetActorCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ var stateSectorsCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
sectors, err := api.StateMinerSectors(ctx, maddr)
|
||||
sectors, err := api.StateMinerSectors(ctx, maddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -197,7 +198,7 @@ var statePledgeCollateralCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(types.FIL(coll).String())
|
||||
fmt.Println(types.FIL(coll))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -251,3 +252,39 @@ var stateListActorsCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stateGetActorCmd = &cli.Command{
|
||||
Name: "get-actor",
|
||||
Usage: "Print actor information",
|
||||
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 pass address of actor to get")
|
||||
}
|
||||
|
||||
addr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a, err := api.StateGetActor(ctx, addr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Address:\t%s\n", addr)
|
||||
fmt.Printf("Balance:\t%s\n", types.FIL(a.Balance))
|
||||
fmt.Printf("Nonce:\t\t%d\n", a.Nonce)
|
||||
fmt.Printf("Code:\t\t%s\n", a.Code)
|
||||
fmt.Printf("Head:\t\t%s\n", a.Head)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ var walletBalance = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", types.FIL(balance).String())
|
||||
fmt.Printf("%s\n", types.FIL(balance))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -231,7 +231,7 @@ var walletImport = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("imported key %s successfully!", addr)
|
||||
fmt.Printf("imported key %s successfully!\n", addr)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -3,16 +3,21 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
logging "github.com/ipfs/go-log"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"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/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
@ -20,7 +25,7 @@ import (
|
||||
|
||||
var log = logging.Logger("main")
|
||||
|
||||
var sendPerRequest = types.NewInt(500_000_000)
|
||||
var sendPerRequest, _ = types.ParseFIL("0.005")
|
||||
|
||||
func main() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
@ -88,6 +93,22 @@ var runCmd = &cli.Command{
|
||||
ctx: ctx,
|
||||
api: nodeApi,
|
||||
from: from,
|
||||
limiter: NewLimiter(LimiterConfig{
|
||||
TotalRate: time.Second,
|
||||
TotalBurst: 20,
|
||||
IPRate: time.Minute,
|
||||
IPBurst: 5,
|
||||
WalletRate: 15 * time.Minute,
|
||||
WalletBurst: 1,
|
||||
}),
|
||||
colLimiter: NewLimiter(LimiterConfig{
|
||||
TotalRate: time.Second,
|
||||
TotalBurst: 20,
|
||||
IPRate: 10 * time.Minute,
|
||||
IPBurst: 2,
|
||||
WalletRate: 1 * time.Hour,
|
||||
WalletBurst: 1,
|
||||
}),
|
||||
}
|
||||
|
||||
http.Handle("/", http.FileServer(rice.MustFindBox("site").HTTPBox()))
|
||||
@ -110,6 +131,9 @@ type handler struct {
|
||||
api api.FullNode
|
||||
|
||||
from address.Address
|
||||
|
||||
limiter *Limiter
|
||||
colLimiter *Limiter
|
||||
}
|
||||
|
||||
func (h *handler) send(w http.ResponseWriter, r *http.Request) {
|
||||
@ -120,8 +144,41 @@ func (h *handler) send(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Limit based on wallet address
|
||||
limiter := h.limiter.GetWalletLimiter(to.String())
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// Limit based on IP
|
||||
|
||||
reqIP := r.Header.Get("X-Real-IP")
|
||||
if reqIP == "" {
|
||||
h, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Errorf("could not get ip from: %s, err: %s", r.RemoteAddr, err)
|
||||
}
|
||||
reqIP = h
|
||||
}
|
||||
if i := net.ParseIP(reqIP); i != nil && i.IsLoopback() {
|
||||
log.Errorf("rate limiting localhost: %s", reqIP)
|
||||
}
|
||||
|
||||
limiter = h.limiter.GetIPLimiter(reqIP)
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// General limiter to allow throttling all messages that can make it into the mpool
|
||||
if !h.limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
smsg, err := h.api.MpoolPushMessage(h.ctx, &types.Message{
|
||||
Value: sendPerRequest,
|
||||
Value: types.BigInt(sendPerRequest),
|
||||
From: h.from,
|
||||
To: to,
|
||||
|
||||
@ -138,6 +195,138 @@ func (h *handler) send(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
// todo
|
||||
owner, err := address.NewFromString(r.FormValue("address"))
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
ssize, err := strconv.ParseInt(r.FormValue("sectorSize"), 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if owner.Protocol() != address.BLS {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("Miner address must use BLS"))
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("mkactor on %s", owner)
|
||||
|
||||
// Limit based on wallet address
|
||||
limiter := h.colLimiter.GetWalletLimiter(owner.String())
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// Limit based on IP
|
||||
limiter = h.colLimiter.GetIPLimiter(r.RemoteAddr)
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// General limiter owner allow throttling all messages that can make it into the mpool
|
||||
if !h.colLimiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
collateral, err := h.api.StatePledgeCollateral(r.Context(), nil)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
smsg, err := h.api.MpoolPushMessage(h.ctx, &types.Message{
|
||||
Value: types.BigInt(sendPerRequest),
|
||||
From: h.from,
|
||||
To: owner,
|
||||
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000),
|
||||
})
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("pushfunds: " + err.Error()))
|
||||
return
|
||||
}
|
||||
log.Infof("push funds to %s: %s", owner, smsg.Cid())
|
||||
|
||||
mw, err := h.api.StateWaitMsg(r.Context(), smsg.Cid())
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(xerrors.Errorf("create storage miner failed: exit code %d", mw.Receipt.ExitCode).Error()))
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("sendto %s ok", owner)
|
||||
|
||||
params, err := actors.SerializeParams(&actors.CreateStorageMinerParams{
|
||||
Owner: owner,
|
||||
Worker: owner,
|
||||
SectorSize: uint64(ssize),
|
||||
PeerID: peer.ID("SETME"),
|
||||
})
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: actors.StoragePowerAddress,
|
||||
From: h.from,
|
||||
Value: collateral,
|
||||
|
||||
Method: actors.SPAMethods.CreateStorageMiner,
|
||||
Params: params,
|
||||
|
||||
GasLimit: types.NewInt(10000000),
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
|
||||
signed, err := h.api.MpoolPushMessage(r.Context(), createStorageMinerMsg)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("smc %s", owner)
|
||||
|
||||
mw, err = h.api.StateWaitMsg(r.Context(), signed.Cid())
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(xerrors.Errorf("create storage miner failed: exit code %d", mw.Receipt.ExitCode).Error()))
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := address.NewFromBytes(mw.Receipt.Return)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(200)
|
||||
fmt.Fprintf(w, "New storage miners address is: %s\n", addr)
|
||||
fmt.Fprintf(w, "Run lotus-storage-miner init --actor=%s --owner=%s", addr, owner)
|
||||
}
|
||||
|
94
cmd/lotus-fountain/rate_limiter.go
Normal file
94
cmd/lotus-fountain/rate_limiter.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type Limiter struct {
|
||||
control *rate.Limiter
|
||||
|
||||
ips map[string]*rate.Limiter
|
||||
wallets map[string]*rate.Limiter
|
||||
mu *sync.RWMutex
|
||||
|
||||
config LimiterConfig
|
||||
}
|
||||
|
||||
type LimiterConfig struct {
|
||||
TotalRate time.Duration
|
||||
TotalBurst int
|
||||
|
||||
IPRate time.Duration
|
||||
IPBurst int
|
||||
|
||||
WalletRate time.Duration
|
||||
WalletBurst int
|
||||
}
|
||||
|
||||
func NewLimiter(c LimiterConfig) *Limiter {
|
||||
return &Limiter{
|
||||
control: rate.NewLimiter(rate.Every(c.TotalRate), c.TotalBurst),
|
||||
mu: &sync.RWMutex{},
|
||||
ips: make(map[string]*rate.Limiter),
|
||||
wallets: make(map[string]*rate.Limiter),
|
||||
|
||||
config: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Limiter) Allow() bool {
|
||||
return i.control.Allow()
|
||||
}
|
||||
|
||||
func (i *Limiter) AddIPLimiter(ip string) *rate.Limiter {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
limiter := rate.NewLimiter(rate.Every(i.config.IPRate), i.config.IPBurst)
|
||||
|
||||
i.ips[ip] = limiter
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *Limiter) GetIPLimiter(ip string) *rate.Limiter {
|
||||
i.mu.Lock()
|
||||
limiter, exists := i.ips[ip]
|
||||
|
||||
if !exists {
|
||||
i.mu.Unlock()
|
||||
return i.AddIPLimiter(ip)
|
||||
}
|
||||
|
||||
i.mu.Unlock()
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *Limiter) AddWalletLimiter(addr string) *rate.Limiter {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
limiter := rate.NewLimiter(rate.Every(i.config.WalletRate), i.config.WalletBurst)
|
||||
|
||||
i.wallets[addr] = limiter
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *Limiter) GetWalletLimiter(wallet string) *rate.Limiter {
|
||||
i.mu.Lock()
|
||||
limiter, exists := i.wallets[wallet]
|
||||
|
||||
if !exists {
|
||||
i.mu.Unlock()
|
||||
return i.AddWalletLimiter(wallet)
|
||||
}
|
||||
|
||||
i.mu.Unlock()
|
||||
|
||||
return limiter
|
||||
}
|
38
cmd/lotus-fountain/rate_limiter_test.go
Normal file
38
cmd/lotus-fountain/rate_limiter_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRateLimit(t *testing.T) {
|
||||
limiter := NewLimiter(LimiterConfig{
|
||||
TotalRate: time.Second,
|
||||
TotalBurst: 20,
|
||||
IPRate: time.Second,
|
||||
IPBurst: 1,
|
||||
WalletRate: time.Second,
|
||||
WalletBurst: 1,
|
||||
})
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
assert.True(t, limiter.Allow())
|
||||
}
|
||||
|
||||
assert.False(t, limiter.Allow())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
assert.True(t, limiter.Allow())
|
||||
|
||||
assert.True(t, limiter.GetIPLimiter("127.0.0.1").Allow())
|
||||
assert.False(t, limiter.GetIPLimiter("127.0.0.1").Allow())
|
||||
time.Sleep(time.Second)
|
||||
assert.True(t, limiter.GetIPLimiter("127.0.0.1").Allow())
|
||||
|
||||
assert.True(t, limiter.GetWalletLimiter("abc123").Allow())
|
||||
assert.False(t, limiter.GetWalletLimiter("abc123").Allow())
|
||||
time.Sleep(time.Second)
|
||||
assert.True(t, limiter.GetWalletLimiter("abc123").Allow())
|
||||
}
|
29
cmd/lotus-fountain/site/funds.html
Normal file
29
cmd/lotus-fountain/site/funds.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sending Funds - Lotus Fountain</title>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="Index">
|
||||
<div class="Index-nodes">
|
||||
<div class="Index-node">
|
||||
[SENDING FUNDS]
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<form action='/send' method='get'>
|
||||
<span>Enter destination address:</span>
|
||||
<input type='text' name='address' style="width: 300px">
|
||||
<button type='submit'>Send Funds</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
<a href="index.html">[Back]</a>
|
||||
<span style="float: right">Not dispensing real Filecoin tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -2,19 +2,26 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Lotus Fountain</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'monospace';
|
||||
background: #1f1f1f;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<form action='/send' method='get'>
|
||||
<span>Enter destination address:</span>
|
||||
<input type='text' name='address' style="width: 300px">
|
||||
<button type='submit'>Send</button>
|
||||
</form>
|
||||
<div class="Index">
|
||||
<div class="Index-nodes">
|
||||
<div class="Index-node">
|
||||
[LOTUS DEVNET FAUCET]
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<a href="funds.html">[Send Funds]</a>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<a href="miner.html">[Create Miner]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
<span style="float: right">Not dispensing real Filecoin tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
56
cmd/lotus-fountain/site/main.css
Normal file
56
cmd/lotus-fountain/site/main.css
Normal file
@ -0,0 +1,56 @@
|
||||
body {
|
||||
font-family: 'monospace';
|
||||
background: #1f1f1f;
|
||||
color: #f0f0f0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.Index {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #1a1a1a;
|
||||
color: #f0f0f0;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 40vw auto;
|
||||
grid-template-rows: auto auto auto 3em;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
"footer footer footer";
|
||||
}
|
||||
.Index-footer {
|
||||
background: #2a2a2a;
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
.Index-footer > div {
|
||||
padding-left: 0.7em;
|
||||
padding-top: 0.7em;
|
||||
}
|
||||
|
||||
.Index-nodes {
|
||||
grid-area: main;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
.Index-node {
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
background: #1f1f1f;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #30a00a;
|
||||
}
|
48
cmd/lotus-fountain/site/miner.html
Normal file
48
cmd/lotus-fountain/site/miner.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Creating Storage Miner - Lotus Fountain</title>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="Index">
|
||||
<div class="Index-nodes">
|
||||
<div class="Index-node">
|
||||
[CREATING STORAGE MINER]
|
||||
</div>
|
||||
<div class="Index-node" id="formnd">
|
||||
<form id="f" action='/mkminer' method='POST'>
|
||||
<span>Enter destination address:</span>
|
||||
<input type='text' name='address' style="width: 300px">
|
||||
<select name="sectorSize">
|
||||
<option selected value="1073741824">1GiB sectors</option>
|
||||
<option value="268435456">256MiB sectors</option>
|
||||
<option value="16777216">16MiB sectors</option>
|
||||
</select>
|
||||
<button type='submit'>Create Miner</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="plswait" style="display: none" class="Index-node">
|
||||
<b>Waiting for transaction on chain..</b>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<span>When creating storage miner, DO NOT REFRESH THE PAGE, wait for it to load. This can take more than 5min.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
<a href="index.html">[Back]</a>
|
||||
<span style="float: right">Not dispensing real Filecoin tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let f = document.getElementById('f')
|
||||
f.onsubmit = ev => {
|
||||
document.getElementById('plswait').style.display = 'block'
|
||||
document.getElementById('formnd').style.display = 'none'
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -6,8 +6,8 @@ import (
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
sectorstate "github.com/filecoin-project/go-sectorbuilder/sealing_state"
|
||||
"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"
|
||||
)
|
||||
@ -37,6 +37,14 @@ var infoCmd = &cli.Command{
|
||||
|
||||
fmt.Printf("Miner: %s\n", maddr)
|
||||
|
||||
// Sector size
|
||||
sizeByte, err := api.StateMinerSectorSize(ctx, maddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Sector Size: %s\n", sizeStr(sizeByte))
|
||||
|
||||
pow, err := api.StateMinerPower(ctx, maddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -45,40 +53,63 @@ var infoCmd = &cli.Command{
|
||||
percI := types.BigDiv(types.BigMul(pow.MinerPower, types.NewInt(1000)), pow.TotalPower)
|
||||
fmt.Printf("Power: %s / %s (%0.2f%%)\n", pow.MinerPower, pow.TotalPower, float64(percI.Int64())/1000*100)
|
||||
|
||||
// TODO: indicate whether the post worker is in use
|
||||
wstat, err := nodeApi.WorkerStats(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Worker use: %d / %d (+%d)\n", wstat.Total-wstat.Reserved-wstat.Free, wstat.Total, wstat.Reserved)
|
||||
|
||||
ppe, err := api.StateMinerProvingPeriodEnd(ctx, maddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ppe != 0 {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pdiff := int64(ppe - head.Height())
|
||||
pdifft := pdiff * build.BlockDelay
|
||||
fmt.Printf("Proving Period: %d, in %d Blocks (~%dm %ds)\n", ppe, pdiff, pdifft/60, pdifft%60)
|
||||
} else {
|
||||
fmt.Printf("Proving Period: Not Proving\n")
|
||||
}
|
||||
|
||||
sinfo, err := sectorsInfo(ctx, nodeApi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Sealed Sectors:\t", sinfo.SealedCount)
|
||||
fmt.Println("Sealing Sectors:\t", sinfo.SealingCount)
|
||||
fmt.Println("Pending Sectors:\t", sinfo.PendingCount)
|
||||
fmt.Println("Failed Sectors:\t", sinfo.FailedCount)
|
||||
fmt.Println("Sectors: ", sinfo)
|
||||
|
||||
// TODO: grab actr state / info
|
||||
// * Sector size
|
||||
// * Sealed sectors (count / bytes)
|
||||
// * Power
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
type SectorsInfo struct {
|
||||
TotalCount int
|
||||
SealingCount int
|
||||
FailedCount int
|
||||
SealedCount int
|
||||
PendingCount int
|
||||
var Units = []string{"B", "KiB", "MiB", "GiB", "TiB"}
|
||||
|
||||
func sizeStr(size uint64) string {
|
||||
i := 0
|
||||
unitSize := float64(size)
|
||||
for unitSize >= 1024 && i < len(Units)-1 {
|
||||
unitSize = unitSize / 1024
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf("%g %s", unitSize, Units[i])
|
||||
}
|
||||
|
||||
func sectorsInfo(ctx context.Context, napi api.StorageMiner) (*SectorsInfo, error) {
|
||||
func sectorsInfo(ctx context.Context, napi api.StorageMiner) (map[string]int, error) {
|
||||
sectors, err := napi.SectorsList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := SectorsInfo{
|
||||
TotalCount: len(sectors),
|
||||
out := map[string]int{
|
||||
"Total": len(sectors),
|
||||
}
|
||||
for _, s := range sectors {
|
||||
st, err := napi.SectorsStatus(ctx, s)
|
||||
@ -86,18 +117,8 @@ func sectorsInfo(ctx context.Context, napi api.StorageMiner) (*SectorsInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch st.State {
|
||||
case sectorstate.Sealed:
|
||||
out.SealedCount++
|
||||
case sectorstate.Pending:
|
||||
out.PendingCount++
|
||||
case sectorstate.Sealing:
|
||||
out.SealingCount++
|
||||
case sectorstate.Failed:
|
||||
out.FailedCount++
|
||||
case sectorstate.Unknown:
|
||||
}
|
||||
out[api.SectorStateStr(st.State)]++
|
||||
}
|
||||
|
||||
return &out, nil
|
||||
return out, nil
|
||||
}
|
||||
|
@ -49,6 +49,11 @@ var initCmd = &cli.Command{
|
||||
Aliases: []string{"o"},
|
||||
Usage: "owner key to use",
|
||||
},
|
||||
&cli.Uint64Flag{
|
||||
Name: "sector-size",
|
||||
Usage: "specify sector size to use",
|
||||
Value: build.SectorSizes[0],
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
log.Info("Initializing lotus storage miner")
|
||||
@ -96,7 +101,7 @@ var initCmd = &cli.Command{
|
||||
|
||||
log.Info("Initializing repo")
|
||||
|
||||
if err := r.Init(); err != nil {
|
||||
if err := r.Init(repo.RepoStorageMiner); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -117,7 +122,7 @@ var initCmd = &cli.Command{
|
||||
}
|
||||
|
||||
func storageMinerInit(ctx context.Context, cctx *cli.Context, api api.FullNode, r repo.Repo) error {
|
||||
lr, err := r.Lock()
|
||||
lr, err := r.Lock(repo.RepoStorageMiner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -276,6 +281,8 @@ func createStorageMiner(ctx context.Context, api api.FullNode, peerid peer.ID, c
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
ssize := cctx.Uint64("sector-size")
|
||||
|
||||
worker := owner
|
||||
if cctx.String("worker") != "" {
|
||||
worker, err = address.NewFromString(cctx.String("worker"))
|
||||
@ -295,7 +302,7 @@ func createStorageMiner(ctx context.Context, api api.FullNode, peerid peer.ID, c
|
||||
params, err := actors.SerializeParams(&actors.CreateStorageMinerParams{
|
||||
Owner: owner,
|
||||
Worker: worker,
|
||||
SectorSize: types.NewInt(build.SectorSize),
|
||||
SectorSize: ssize,
|
||||
PeerID: peerid,
|
||||
})
|
||||
if err != nil {
|
||||
@ -303,7 +310,7 @@ func createStorageMiner(ctx context.Context, api api.FullNode, peerid peer.ID, c
|
||||
}
|
||||
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
To: actors.StoragePowerAddress,
|
||||
From: owner,
|
||||
Value: collateral,
|
||||
|
||||
|
@ -18,6 +18,8 @@ const FlagStorageRepo = "storagerepo"
|
||||
|
||||
func main() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
logging.SetLogLevel("swarm", "WARN")
|
||||
|
||||
local := []*cli.Command{
|
||||
runCmd,
|
||||
initCmd,
|
||||
|
@ -8,13 +8,12 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
|
||||
"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"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/auth"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
@ -32,12 +31,20 @@ var runCmd = &cli.Command{
|
||||
Name: "api",
|
||||
Value: "2345",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "enable-gpu-proving",
|
||||
Usage: "Enable use of GPU for mining operations",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if err := build.GetParams(true); err != nil {
|
||||
return xerrors.Errorf("fetching proof parameters: %w", err)
|
||||
}
|
||||
|
||||
if !cctx.Bool("enable-gpu-proving") {
|
||||
os.Setenv("BELLMAN_NO_GPU", "true")
|
||||
}
|
||||
|
||||
nodeApi, ncloser, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -77,7 +84,7 @@ var runCmd = &cli.Command{
|
||||
}
|
||||
return lr.SetAPIEndpoint(apima)
|
||||
}),
|
||||
node.Override(new(*sectorbuilder.SectorBuilderConfig), modules.SectorBuilderConfig(storageRepoPath)),
|
||||
node.Override(new(*sectorbuilder.Config), modules.SectorBuilderConfig(storageRepoPath, 5)), // TODO: grab worker count from config
|
||||
node.Override(new(api.FullNode), nodeApi),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
)
|
||||
|
||||
@ -20,13 +21,7 @@ var storeGarbageCmd = &cli.Command{
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
sectorId, err := nodeApi.StoreGarbageData(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(sectorId)
|
||||
return nil
|
||||
return nodeApi.StoreGarbageData(ctx)
|
||||
},
|
||||
}
|
||||
|
||||
@ -36,7 +31,6 @@ var sectorsCmd = &cli.Command{
|
||||
Subcommands: []*cli.Command{
|
||||
sectorsStatusCmd,
|
||||
sectorsStagedListCmd,
|
||||
sectorsStagedSealCmd,
|
||||
sectorsRefsCmd,
|
||||
},
|
||||
}
|
||||
@ -67,13 +61,15 @@ var sectorsStatusCmd = &cli.Command{
|
||||
}
|
||||
|
||||
fmt.Printf("SectorID:\t%d\n", status.SectorID)
|
||||
fmt.Printf("Status:\t%s\n", status.State.String())
|
||||
fmt.Printf("SealErrorMsg:\t%q\n", status.SealErrorMsg)
|
||||
fmt.Printf("Status:\t%s\n", api.SectorStateStr(status.State))
|
||||
fmt.Printf("CommD:\t\t%x\n", status.CommD)
|
||||
fmt.Printf("CommR:\t\t%x\n", status.CommR)
|
||||
fmt.Printf("CommR*:\t\t%x\n", status.CommRStar)
|
||||
fmt.Printf("Ticket:\t\t%x\n", status.Ticket.TicketBytes)
|
||||
fmt.Printf("TicketH:\t\t%d\n", status.Ticket.BlockHeight)
|
||||
fmt.Printf("Seed:\t\t%x\n", status.Seed.TicketBytes)
|
||||
fmt.Printf("SeedH:\t\t%d\n", status.Seed.BlockHeight)
|
||||
fmt.Printf("Proof:\t\t%x\n", status.Proof)
|
||||
fmt.Printf("Pieces:\t\t%v\n", status.Pieces)
|
||||
fmt.Printf("Deals:\t\t%v\n", status.Deals)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -101,21 +97,6 @@ var sectorsStagedListCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsStagedSealCmd = &cli.Command{
|
||||
Name: "seal-staged", // TODO: nest this under a 'staged' subcommand? idk
|
||||
Usage: "Seal staged sectors",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
return nodeApi.SectorsStagedSeal(ctx)
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsRefsCmd = &cli.Command{
|
||||
Name: "refs",
|
||||
Usage: "List References to sectors",
|
||||
|
@ -1,9 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/ipfs/go-car"
|
||||
"github.com/ipfs/go-datastore"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@ -14,11 +19,26 @@ import (
|
||||
pnet "github.com/libp2p/go-libp2p-pnet"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/addrutil"
|
||||
"github.com/filecoin-project/lotus/node/modules/lp2p"
|
||||
)
|
||||
|
||||
const topic = "/fil/headnotifs/bafy2bzacea77zxnepp7wuqqgpj7xcw2ywwmmcmtrbjghhv4g2dildogpv6roi"
|
||||
var topic = "/fil/headnotifs/"
|
||||
|
||||
func init() {
|
||||
genBytes := build.MaybeGenesis()
|
||||
bs := blockstore.NewBlockstore(datastore.NewMapDatastore())
|
||||
|
||||
c, err := car.LoadCar(bs, bytes.NewReader(genBytes))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(c.Roots) != 1 {
|
||||
panic("expected genesis file to have one root")
|
||||
}
|
||||
|
||||
fmt.Printf("Genesis CID: %s\n", c.Roots[0])
|
||||
topic = topic + c.Roots[0].String()
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
WriteBufferSize: 1024,
|
||||
@ -48,9 +68,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pi, err := addrutil.ParseAddresses(ctx, []string{
|
||||
"/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWGU8C1mFsEtz4bXmHUH3kQTnQnxVy8cigwGV94qCpYJw7",
|
||||
})
|
||||
pi, err := build.BuiltinBootstrap()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -1,6 +1,15 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
|
||||
function colForH(besth, height) {
|
||||
const diff = besth - height
|
||||
if(diff === 0) return '#6f6'
|
||||
if(diff === 1) return '#df4'
|
||||
if(diff < 4) return '#ff0'
|
||||
if(diff < 10) return '#f60'
|
||||
return '#f00'
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -21,18 +30,18 @@ class App extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let best = Object.keys(this.state).map(k => this.state[k]).reduce((p, n) => p > n.Height ? p : n.Height, -1)
|
||||
console.log(best)
|
||||
let besth = Object.keys(this.state).map(k => this.state[k]).reduce((p, n) => p > n.Height ? p : n.Height, -1)
|
||||
let bestw = Object.keys(this.state).map(k => this.state[k]).reduce((p, n) => p > n.Weight ? p : n.Weight, -1)
|
||||
|
||||
return <table>{Object.keys(this.state).map(k => [k, this.state[k]]).map(([k, v]) => {
|
||||
|
||||
let mnrs = v.Blocks.map(b => <span> m:{b.Miner}</span>)
|
||||
let l = [<td>{k}</td>, <td>{v.NodeName}</td>, <td>{v.Height}</td>, <td>{mnrs}</td>]
|
||||
if (best !== v.Height) {
|
||||
l = <tr style={{color: '#f00'}}>{l}</tr>
|
||||
} else {
|
||||
let l = [<td>{k}</td>,
|
||||
<td>{v.NodeName}</td>,
|
||||
<td style={{color: bestw !== v.Weight ? '#f00' : '#afa'}}>{v.Weight}({bestw - v.Weight})</td>,
|
||||
<td style={{color: colForH(besth, v.Height)}}>{v.Height}({besth - v.Height})</td>,
|
||||
<td>{mnrs}</td>]
|
||||
l = <tr>{l}</tr>
|
||||
}
|
||||
|
||||
return l
|
||||
})
|
||||
|
@ -4,9 +4,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
@ -53,7 +54,7 @@ var DaemonCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Init(); err != nil && err != repo.ErrRepoExists {
|
||||
if err := r.Init(repo.RepoFullNode); err != nil && err != repo.ErrRepoExists {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@ import (
|
||||
func main() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
logging.SetLogLevel("dht", "ERROR")
|
||||
logging.SetLogLevel("swarm", "WARN")
|
||||
|
||||
local := []*cli.Command{
|
||||
DaemonCmd,
|
||||
}
|
||||
|
2
extern/go-bls-sigs
vendored
2
extern/go-bls-sigs
vendored
@ -1 +1 @@
|
||||
Subproject commit 10fef3cbfa8670d9e9cdffb0cd8dfd4f6f6fef07
|
||||
Subproject commit 98479d3c79620f18783da0c2c6a15f8b8eb4fa2e
|
2
extern/go-sectorbuilder
vendored
2
extern/go-sectorbuilder
vendored
@ -1 +1 @@
|
||||
Subproject commit 32e85ba52d9ccf3fc5715bcf53265b042020ee82
|
||||
Subproject commit d94675a704d5f4c4bed30ec16538352f4d8cdc4b
|
111
gen/main.go
111
gen/main.go
@ -4,9 +4,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
gen "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/blocksync"
|
||||
"github.com/filecoin-project/lotus/chain/deals"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/paych"
|
||||
"github.com/filecoin-project/lotus/retrieval"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -22,23 +29,58 @@ func main() {
|
||||
types.Actor{},
|
||||
types.MessageReceipt{},
|
||||
types.BlockMsg{},
|
||||
types.SignedStorageAsk{},
|
||||
types.StorageAsk{},
|
||||
types.ExpTipSet{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
/*
|
||||
err = gen.WriteTupleEncodersToFile("./chain/cbor_gen.go", "chain",
|
||||
chain.BlockSyncRequest{},
|
||||
chain.BlockSyncResponse{},
|
||||
chain.BSTipSet{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
*/
|
||||
err = gen.WriteTupleEncodersToFile("./paych/cbor_gen.go", "paych",
|
||||
paych.VoucherInfo{},
|
||||
paych.ChannelInfo{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./api/cbor_gen.go", "api",
|
||||
api.PaymentInfo{},
|
||||
api.SealedRef{},
|
||||
api.SealedRefs{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./retrieval/cbor_gen.go", "retrieval",
|
||||
retrieval.RetParams{},
|
||||
|
||||
retrieval.Query{},
|
||||
retrieval.QueryResponse{},
|
||||
retrieval.Unixfs0Offer{},
|
||||
retrieval.DealProposal{},
|
||||
retrieval.DealResponse{},
|
||||
retrieval.Block{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./chain/blocksync/cbor_gen.go", "blocksync",
|
||||
blocksync.BlockSyncRequest{},
|
||||
blocksync.BlockSyncResponse{},
|
||||
blocksync.BSTipSet{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./chain/actors/cbor_gen.go", "actors",
|
||||
actors.InitActorState{},
|
||||
@ -46,11 +88,10 @@ func main() {
|
||||
actors.AccountActorState{},
|
||||
actors.StorageMinerActorState{},
|
||||
actors.StorageMinerConstructorParams{},
|
||||
actors.CommitSectorParams{},
|
||||
actors.SectorPreCommitInfo{},
|
||||
actors.PreCommittedSector{},
|
||||
actors.MinerInfo{},
|
||||
actors.SubmitPoStParams{},
|
||||
actors.PieceInclVoucherData{},
|
||||
actors.InclusionProof{},
|
||||
actors.PaymentVerifyParams{},
|
||||
actors.UpdatePeerIDParams{},
|
||||
actors.MultiSigActorState{},
|
||||
@ -75,6 +116,44 @@ func main() {
|
||||
actors.ArbitrateConsensusFaultParams{},
|
||||
actors.PledgeCollateralParams{},
|
||||
actors.MinerSlashConsensusFault{},
|
||||
actors.StorageParticipantBalance{},
|
||||
actors.StorageMarketState{},
|
||||
actors.WithdrawBalanceParams{},
|
||||
actors.StorageDealProposal{},
|
||||
actors.StorageDeal{},
|
||||
actors.PublishStorageDealsParams{},
|
||||
actors.PublishStorageDealResponse{},
|
||||
actors.ActivateStorageDealsParams{},
|
||||
actors.ProcessStorageDealsPaymentParams{},
|
||||
actors.OnChainDeal{},
|
||||
actors.ComputeDataCommitmentParams{},
|
||||
actors.SectorProveCommitInfo{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./chain/deals/cbor_gen.go", "deals",
|
||||
deals.AskRequest{},
|
||||
deals.AskResponse{},
|
||||
deals.Proposal{},
|
||||
deals.Response{},
|
||||
deals.SignedResponse{},
|
||||
deals.ClientDealProposal{},
|
||||
deals.ClientDeal{},
|
||||
deals.MinerDeal{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = gen.WriteTupleEncodersToFile("./storage/cbor_gen.go", "storage",
|
||||
storage.SealTicket{},
|
||||
storage.SealSeed{},
|
||||
storage.Piece{},
|
||||
storage.SectorInfo{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user