Merge remote-tracking branch 'origin/master' into feat/async-validateblk

This commit is contained in:
Łukasz Magiera 2019-11-11 18:44:45 +01:00
commit 6fe5921bb6
173 changed files with 11251 additions and 3669 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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
View 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)
}

View File

@ -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
View 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
View 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
}

View File

@ -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
View 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
}

View File

@ -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
View 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
}

View File

@ -1 +1 @@
/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWQ3NBPgTbXFRGnmpZUpYBSPJtwJfxuAFHBuWJ8LumCYf2
/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWLM9S94b9mnHD2fbWvQzXudoapHjbBbFA1T4QVft65Kym

View File

@ -1 +1 @@
/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWGU8C1mFsEtz4bXmHUH3kQTnQnxVy8cigwGV94qCpYJw7
/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWB4pVpuRAcW2873V6m3uPWevKcZ1VWr1FGPUTwDZUN5mg

Binary file not shown.

View File

@ -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
}

View File

@ -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() {

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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},

View File

@ -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,
})

View 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) {
}
*/

View File

@ -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")

View File

@ -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)
}
}

View File

@ -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")
}

View File

@ -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

View File

@ -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,

View 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
}

View File

@ -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)
}

View File

@ -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
View 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
}

View File

@ -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() {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View 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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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{},

View File

@ -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) {

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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
View 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
}

View File

@ -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

View File

@ -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,
}

View File

@ -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)

View File

@ -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
}

View File

@ -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],

View File

@ -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")

View File

@ -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

View File

@ -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,

View File

@ -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
}

View File

@ -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())

View File

@ -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
}

View File

@ -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())
}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,
}

View File

@ -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}
}

View File

@ -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()
},
}

View File

@ -122,6 +122,7 @@ var Commands = []*cli.Command{
sendCmd,
stateCmd,
syncCmd,
unregisterMinerCmd,
versionCmd,
walletCmd,
}

View File

@ -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
View 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)
},
}

View File

@ -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)
}

View File

@ -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
},
}

View File

@ -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
},
}

View File

@ -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)
}

View 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
}

View 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())
}

View 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>

View File

@ -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>

View 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;
}

View 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>

View File

@ -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
}

View File

@ -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,

View File

@ -18,6 +18,8 @@ const FlagStorageRepo = "storagerepo"
func main() {
logging.SetLogLevel("*", "INFO")
logging.SetLogLevel("swarm", "WARN")
local := []*cli.Command{
runCmd,
initCmd,

View File

@ -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 {

View File

@ -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",

View File

@ -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)
}

View File

@ -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>&nbsp;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
})

View File

@ -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
}

View File

@ -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

@ -1 +1 @@
Subproject commit 10fef3cbfa8670d9e9cdffb0cd8dfd4f6f6fef07
Subproject commit 98479d3c79620f18783da0c2c6a15f8b8eb4fa2e

@ -1 +1 @@
Subproject commit 32e85ba52d9ccf3fc5715bcf53265b042020ee82
Subproject commit d94675a704d5f4c4bed30ec16538352f4d8cdc4b

View File

@ -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