Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d11e375f82 | ||
|
|
7b8ee40275 |
@@ -225,8 +225,9 @@ jobs:
|
||||
- run:
|
||||
name: Install jq
|
||||
command: |
|
||||
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output /usr/local/bin/jq
|
||||
chmod +x /usr/local/bin/jq
|
||||
mkdir $HOME/.bin
|
||||
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output $HOME/.bin/jq
|
||||
chmod +x $HOME/.bin/jq
|
||||
- restore_cache:
|
||||
name: restore go mod and cargo cache
|
||||
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: Sealing Issues
|
||||
about: Create a report for help with sealing (commit) failures.
|
||||
title: ''
|
||||
labels: 'sealing'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please provide all the information requested here to help us troubleshoot "commit failed" issues.
|
||||
If the information requested is missing, we will probably have to just ask you to provide it anyway,
|
||||
before we can help debug.
|
||||
|
||||
**Describe the problem**
|
||||
|
||||
A brief description of the problem you encountered while proving (sealing) a sector.
|
||||
|
||||
Including what commands you ran, and a description of your setup, is very helpful.
|
||||
|
||||
**Sectors list**
|
||||
|
||||
The output of `./lotus-storage-miner sectors list`.
|
||||
|
||||
**Sectors status**
|
||||
|
||||
The output of `./lotus-storage-miner sectors status --log <sectorId>` for the failed sector(s).
|
||||
|
||||
**Lotus storage miner logs**
|
||||
|
||||
Please go through the logs of your storage miner, and include screenshots of any error-like messages you find.
|
||||
|
||||
**Version**
|
||||
|
||||
The output of `./lotus --version`.
|
||||
@@ -103,19 +103,6 @@ install:
|
||||
install -C ./lotus-storage-miner /usr/local/bin/lotus-storage-miner
|
||||
install -C ./lotus-seal-worker /usr/local/bin/lotus-seal-worker
|
||||
|
||||
install-services: install
|
||||
mkdir -p /usr/local/lib/systemd/system
|
||||
install -C -m 0644 ./scripts/lotus-daemon.service /usr/local/lib/systemd/system/lotus-daemon.service
|
||||
install -C -m 0644 ./scripts/lotus-miner.service /usr/local/lib/systemd/system/lotus-miner.service
|
||||
systemctl daemon-reload
|
||||
@echo
|
||||
@echo "lotus and lotus-miner services installed. Don't forget to 'systemctl enable lotus|lotus-miner' for it to be enabled on startup."
|
||||
|
||||
clean-services:
|
||||
rm -f /usr/local/lib/systemd/system/lotus-daemon.service
|
||||
rm -f /usr/local/lib/systemd/system/lotus-miner.service
|
||||
systemctl daemon-reload
|
||||
|
||||
# TOOLS
|
||||
|
||||
lotus-seed: $(BUILD_DEPS)
|
||||
|
||||
+5
-9
@@ -4,18 +4,17 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
type Permission = string
|
||||
|
||||
type Common interface {
|
||||
// Auth
|
||||
AuthVerify(ctx context.Context, token string) ([]auth.Permission, error)
|
||||
AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error)
|
||||
AuthVerify(ctx context.Context, token string) ([]Permission, error)
|
||||
AuthNew(ctx context.Context, perms []Permission) ([]byte, error)
|
||||
|
||||
// network
|
||||
|
||||
@@ -34,9 +33,6 @@ type Common interface {
|
||||
|
||||
LogList(context.Context) ([]string, error)
|
||||
LogSetLevel(context.Context, string, string) error
|
||||
|
||||
// trigger graceful shutdown
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
// Version provides various build-time information
|
||||
|
||||
+9
-45
@@ -28,18 +28,13 @@ type FullNode interface {
|
||||
|
||||
// TODO: TipSetKeys
|
||||
|
||||
// MethodGroup: Chain
|
||||
// The Chain method group contains methods for interacting with the
|
||||
// blockchain, but that do not require any form of state computation
|
||||
// chain
|
||||
|
||||
// ChainNotify returns channel with chain head updates
|
||||
// First message is guaranteed to be of len == 1, and type == 'current'
|
||||
ChainNotify(context.Context) (<-chan []*HeadChange, error)
|
||||
// ChainHead returns the current head of the chain
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
// ChainGetRandomness is used to sample the chain for randomness
|
||||
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
// ChainGetBlock returns the block specified by the given CID
|
||||
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
|
||||
@@ -57,23 +52,14 @@ type FullNode interface {
|
||||
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error)
|
||||
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
|
||||
|
||||
// MethodGroup: Sync
|
||||
// The Sync method group contains methods for interacting with and
|
||||
// observing the lotus sync service
|
||||
|
||||
// SyncState returns the current status of the lotus sync system
|
||||
// syncer
|
||||
SyncState(context.Context) (*SyncState, error)
|
||||
// SyncSubmitBlock can be used to submit a newly created block to the
|
||||
// network through this node
|
||||
SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) error
|
||||
SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error)
|
||||
SyncMarkBad(ctx context.Context, bcid cid.Cid) error
|
||||
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
|
||||
|
||||
// MethodGroup: Mpool
|
||||
// The Mpool methods are for interacting with the message pool. The message pool
|
||||
// manages all incoming and outgoing 'messages' going over the network.
|
||||
|
||||
// messages
|
||||
MpoolPending(context.Context, types.TipSetKey) ([]*types.SignedMessage, error)
|
||||
MpoolPush(context.Context, *types.SignedMessage) (cid.Cid, error)
|
||||
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error) // get nonce, sign, push
|
||||
@@ -81,14 +67,16 @@ type FullNode interface {
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
MpoolEstimateGasPrice(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// MethodGroup: Miner
|
||||
// FullNodeStruct
|
||||
|
||||
// miner
|
||||
|
||||
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error)
|
||||
MinerCreateBlock(context.Context, *BlockTemplate) (*types.BlockMsg, error)
|
||||
|
||||
// // UX ?
|
||||
|
||||
// MethodGroup: Wallet
|
||||
// wallet
|
||||
|
||||
WalletNew(context.Context, crypto.SigType) (address.Address, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
@@ -104,20 +92,14 @@ type FullNode interface {
|
||||
|
||||
// Other
|
||||
|
||||
// MethodGroup: Client
|
||||
// The Client methods all have to do with interacting with the storage and
|
||||
// retrieval markets as a client
|
||||
|
||||
// ClientImport imports file under the specified path into filestore
|
||||
ClientImport(ctx context.Context, ref FileRef) (cid.Cid, error)
|
||||
// ClientStartDeal proposes a deal with a miner
|
||||
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error)
|
||||
// ClientGetDeal info returns the latest information about a given deal
|
||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
|
||||
ClientListDeals(ctx context.Context) ([]DealInfo, error)
|
||||
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
|
||||
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref *FileRef) error
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref FileRef) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
|
||||
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
|
||||
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
|
||||
@@ -130,9 +112,6 @@ type FullNode interface {
|
||||
|
||||
//ClientListAsks() []Ask
|
||||
|
||||
// MethodGroup: State
|
||||
// The State methods are used to query, inspect, and interact with chain state
|
||||
|
||||
// if tipset is nil, we'll use heaviest
|
||||
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
|
||||
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
|
||||
@@ -147,10 +126,7 @@ type FullNode interface {
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
|
||||
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
|
||||
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
|
||||
// Returns all non-expired Faults that occur within lookback epochs of the given tipset
|
||||
StateAllMinerFaults(ctx context.Context, lookback abi.ChainEpoch, ts types.TipSetKey) ([]*Fault, error)
|
||||
StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error)
|
||||
StateMinerInitialPledgeCollateral(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error)
|
||||
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
|
||||
@@ -170,10 +146,6 @@ type FullNode interface {
|
||||
StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error)
|
||||
StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*ComputeStateOutput, error)
|
||||
|
||||
// MethodGroup: Msig
|
||||
// The Msig methods are used to interact with multisig wallets on the
|
||||
// filecoin network
|
||||
|
||||
MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
MsigCreate(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error)
|
||||
MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
@@ -183,9 +155,6 @@ type FullNode interface {
|
||||
MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error)
|
||||
// MarketFreeBalance
|
||||
|
||||
// MethodGroup: Paych
|
||||
// The Paych methods are for interacting with and managing payment channels
|
||||
|
||||
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
|
||||
PaychList(context.Context) ([]address.Address, error)
|
||||
PaychStatus(context.Context, address.Address) (*PaychStatus, error)
|
||||
@@ -455,8 +424,3 @@ const (
|
||||
MsigApprove MsigProposeResponse = iota
|
||||
MsigCancel
|
||||
)
|
||||
|
||||
type Fault struct {
|
||||
Miner address.Address
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
@@ -1,38 +1,111 @@
|
||||
package apistruct
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
)
|
||||
|
||||
type permKey int
|
||||
|
||||
var permCtxKey permKey
|
||||
|
||||
const (
|
||||
// When changing these, update docs/API.md too
|
||||
|
||||
PermRead auth.Permission = "read" // default
|
||||
PermWrite auth.Permission = "write"
|
||||
PermSign auth.Permission = "sign" // Use wallet keys for signing
|
||||
PermAdmin auth.Permission = "admin" // Manage permissions
|
||||
PermRead api.Permission = "read" // default
|
||||
PermWrite api.Permission = "write"
|
||||
PermSign api.Permission = "sign" // Use wallet keys for signing
|
||||
PermAdmin api.Permission = "admin" // Manage permissions
|
||||
)
|
||||
|
||||
var AllPermissions = []auth.Permission{PermRead, PermWrite, PermSign, PermAdmin}
|
||||
var DefaultPerms = []auth.Permission{PermRead}
|
||||
var AllPermissions = []api.Permission{PermRead, PermWrite, PermSign, PermAdmin}
|
||||
var defaultPerms = []api.Permission{PermRead}
|
||||
|
||||
func WithPerm(ctx context.Context, perms []api.Permission) context.Context {
|
||||
return context.WithValue(ctx, permCtxKey, perms)
|
||||
}
|
||||
|
||||
func PermissionedStorMinerAPI(a api.StorageMiner) api.StorageMiner {
|
||||
var out StorageMinerStruct
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
|
||||
permissionedAny(a, &out.Internal)
|
||||
permissionedAny(a, &out.CommonStruct.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func PermissionedFullAPI(a api.FullNode) api.FullNode {
|
||||
var out FullNodeStruct
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
|
||||
permissionedAny(a, &out.Internal)
|
||||
permissionedAny(a, &out.CommonStruct.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func PermissionedWorkerAPI(a api.WorkerApi) api.WorkerApi {
|
||||
var out WorkerStruct
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
permissionedAny(a, &out.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func HasPerm(ctx context.Context, perm api.Permission) bool {
|
||||
callerPerms, ok := ctx.Value(permCtxKey).([]api.Permission)
|
||||
if !ok {
|
||||
callerPerms = defaultPerms
|
||||
}
|
||||
|
||||
for _, callerPerm := range callerPerms {
|
||||
if callerPerm == perm {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func permissionedAny(in interface{}, out interface{}) {
|
||||
rint := reflect.ValueOf(out).Elem()
|
||||
ra := reflect.ValueOf(in)
|
||||
|
||||
for f := 0; f < rint.NumField(); f++ {
|
||||
field := rint.Type().Field(f)
|
||||
requiredPerm := api.Permission(field.Tag.Get("perm"))
|
||||
if requiredPerm == "" {
|
||||
panic("missing 'perm' tag on " + field.Name) // ok
|
||||
}
|
||||
|
||||
// Validate perm tag
|
||||
ok := false
|
||||
for _, perm := range AllPermissions {
|
||||
if requiredPerm == perm {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
panic("unknown 'perm' tag on " + field.Name) // ok
|
||||
}
|
||||
|
||||
fn := ra.MethodByName(field.Name)
|
||||
|
||||
rint.Field(f).Set(reflect.MakeFunc(field.Type, func(args []reflect.Value) (results []reflect.Value) {
|
||||
ctx := args[0].Interface().(context.Context)
|
||||
if HasPerm(ctx, requiredPerm) {
|
||||
return fn.Call(args)
|
||||
}
|
||||
|
||||
err := xerrors.Errorf("missing permission to invoke '%s' (need '%s')", field.Name, requiredPerm)
|
||||
rerr := reflect.ValueOf(&err).Elem()
|
||||
|
||||
if field.Type.NumOut() == 2 {
|
||||
return []reflect.Value{
|
||||
reflect.Zero(field.Type.Out(0)),
|
||||
rerr,
|
||||
}
|
||||
} else {
|
||||
return []reflect.Value{rerr}
|
||||
}
|
||||
}))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+8
-26
@@ -9,8 +9,6 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
@@ -32,8 +30,8 @@ var _ = AllPermissions
|
||||
|
||||
type CommonStruct struct {
|
||||
Internal struct {
|
||||
AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"read"`
|
||||
AuthNew func(ctx context.Context, perms []auth.Permission) ([]byte, error) `perm:"admin"`
|
||||
AuthVerify func(ctx context.Context, token string) ([]api.Permission, error) `perm:"read"`
|
||||
AuthNew func(ctx context.Context, perms []api.Permission) ([]byte, error) `perm:"admin"`
|
||||
|
||||
NetConnectedness func(context.Context, peer.ID) (network.Connectedness, error) `perm:"read"`
|
||||
NetPeers func(context.Context) ([]peer.AddrInfo, error) `perm:"read"`
|
||||
@@ -47,8 +45,6 @@ type CommonStruct struct {
|
||||
|
||||
LogList func(context.Context) ([]string, error) `perm:"write"`
|
||||
LogSetLevel func(context.Context, string, string) error `perm:"write"`
|
||||
|
||||
Shutdown func(context.Context) error `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +108,7 @@ type FullNodeStruct struct {
|
||||
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
ClientCalcCommP func(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) `perm:"read"`
|
||||
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
|
||||
@@ -124,9 +120,7 @@ type FullNodeStruct struct {
|
||||
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"`
|
||||
StateMinerInfo func(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error) `perm:"read"`
|
||||
StateMinerDeadlines func(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) `perm:"read"`
|
||||
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
|
||||
StateAllMinerFaults func(context.Context, abi.ChainEpoch, types.TipSetKey) ([]*api.Fault, error) `perm:"read"`
|
||||
StateMinerRecoveries func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
|
||||
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error) `perm:"read"`
|
||||
StateMinerInitialPledgeCollateral func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateMinerAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateSectorPreCommitInfo func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"`
|
||||
@@ -245,11 +239,11 @@ type WorkerStruct struct {
|
||||
|
||||
// CommonStruct
|
||||
|
||||
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
|
||||
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]api.Permission, error) {
|
||||
return c.Internal.AuthVerify(ctx, token)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) {
|
||||
func (c *CommonStruct) AuthNew(ctx context.Context, perms []api.Permission) ([]byte, error) {
|
||||
return c.Internal.AuthNew(ctx, perms)
|
||||
}
|
||||
|
||||
@@ -295,10 +289,6 @@ func (c *CommonStruct) LogSetLevel(ctx context.Context, group, level string) err
|
||||
return c.Internal.LogSetLevel(ctx, group, level)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) Shutdown(ctx context.Context) error {
|
||||
return c.Internal.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// FullNodeStruct
|
||||
|
||||
func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) {
|
||||
@@ -328,7 +318,7 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
|
||||
return c.Internal.ClientListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, ref)
|
||||
}
|
||||
|
||||
@@ -543,18 +533,10 @@ func (c *FullNodeStruct) StateMinerDeadlines(ctx context.Context, m address.Addr
|
||||
return c.Internal.StateMinerDeadlines(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
|
||||
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) ([]abi.SectorNumber, error) {
|
||||
return c.Internal.StateMinerFaults(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateAllMinerFaults(ctx context.Context, cutoff abi.ChainEpoch, endTsk types.TipSetKey) ([]*api.Fault, error) {
|
||||
return c.Internal.StateAllMinerFaults(ctx, cutoff, endTsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerRecoveries(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
|
||||
return c.Internal.StateMinerRecoveries(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerInitialPledgeCollateral(ctx context.Context, maddr address.Address, snum abi.SectorNumber, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StateMinerInitialPledgeCollateral(ctx, maddr, snum, tsk)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"net/http"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
)
|
||||
|
||||
// NewCommonRPC creates a new http jsonrpc client.
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var ExampleValues = map[reflect.Type]interface{}{
|
||||
reflect.TypeOf(auth.Permission("")): auth.Permission("write"),
|
||||
reflect.TypeOf(""): "string value",
|
||||
reflect.TypeOf(uint64(42)): uint64(42),
|
||||
reflect.TypeOf(byte(7)): byte(7),
|
||||
reflect.TypeOf([]byte{}): []byte("byte array"),
|
||||
}
|
||||
|
||||
func addExample(v interface{}) {
|
||||
ExampleValues[reflect.TypeOf(v)] = v
|
||||
}
|
||||
|
||||
func init() {
|
||||
c, err := cid.Decode("bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ExampleValues[reflect.TypeOf(c)] = c
|
||||
|
||||
c2, err := cid.Decode("bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tsk := types.NewTipSetKey(c, c2)
|
||||
|
||||
ExampleValues[reflect.TypeOf(tsk)] = tsk
|
||||
|
||||
addr, err := address.NewIDAddress(1234)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ExampleValues[reflect.TypeOf(addr)] = addr
|
||||
|
||||
pid, err := peer.IDB58Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addExample(pid)
|
||||
|
||||
addExample(bitfield.NewFromSet([]uint64{5}))
|
||||
addExample(abi.RegisteredProof_StackedDRG32GiBPoSt)
|
||||
addExample(abi.ChainEpoch(10101))
|
||||
addExample(crypto.SigTypeBLS)
|
||||
addExample(int64(9))
|
||||
addExample(abi.MethodNum(1))
|
||||
addExample(exitcode.ExitCode(0))
|
||||
addExample(crypto.DomainSeparationTag_ElectionProofProduction)
|
||||
addExample(true)
|
||||
addExample(abi.UnpaddedPieceSize(1024))
|
||||
addExample(abi.UnpaddedPieceSize(1024).Padded())
|
||||
addExample(abi.DealID(5432))
|
||||
addExample(filestore.StatusFileChanged)
|
||||
addExample(abi.SectorNumber(9))
|
||||
addExample(abi.SectorSize(32 * 1024 * 1024 * 1024))
|
||||
addExample(api.MpoolChange(0))
|
||||
addExample(network.Connected)
|
||||
addExample(dtypes.NetworkName("lotus"))
|
||||
addExample(api.SyncStateStage(1))
|
||||
addExample(build.APIVersion)
|
||||
addExample(api.PCHInbound)
|
||||
addExample(time.Minute)
|
||||
addExample(&types.ExecutionResult{
|
||||
Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message),
|
||||
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt),
|
||||
})
|
||||
addExample(map[string]types.Actor{
|
||||
"t01236": exampleValue(reflect.TypeOf(types.Actor{})).(types.Actor),
|
||||
})
|
||||
addExample(map[string]api.MarketDeal{
|
||||
"t026363": exampleValue(reflect.TypeOf(api.MarketDeal{})).(api.MarketDeal),
|
||||
})
|
||||
addExample(map[string]api.MarketBalance{
|
||||
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{})).(api.MarketBalance),
|
||||
})
|
||||
|
||||
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// because reflect.TypeOf(maddr) returns the concrete type...
|
||||
ExampleValues[reflect.TypeOf(struct{ A multiaddr.Multiaddr }{}).Field(0).Type] = maddr
|
||||
|
||||
}
|
||||
|
||||
func exampleValue(t reflect.Type) interface{} {
|
||||
v, ok := ExampleValues[t]
|
||||
if ok {
|
||||
return v
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Slice:
|
||||
out := reflect.New(t).Elem()
|
||||
reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem())))
|
||||
return out.Interface()
|
||||
case reflect.Chan:
|
||||
return exampleValue(t.Elem())
|
||||
case reflect.Struct:
|
||||
es := exampleStruct(t)
|
||||
v := reflect.ValueOf(es).Elem().Interface()
|
||||
ExampleValues[t] = v
|
||||
return v
|
||||
case reflect.Array:
|
||||
out := reflect.New(t).Elem()
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem())))
|
||||
}
|
||||
return out.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
if t.Elem().Kind() == reflect.Struct {
|
||||
es := exampleStruct(t.Elem())
|
||||
//ExampleValues[t] = es
|
||||
return es
|
||||
}
|
||||
case reflect.Interface:
|
||||
return struct{}{}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("No example value for type: %s", t))
|
||||
}
|
||||
|
||||
func exampleStruct(t reflect.Type) interface{} {
|
||||
ns := reflect.New(t)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if strings.Title(f.Name) == f.Name {
|
||||
ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type)))
|
||||
}
|
||||
}
|
||||
|
||||
return ns.Interface()
|
||||
}
|
||||
|
||||
type Visitor struct {
|
||||
Methods map[string]ast.Node
|
||||
}
|
||||
|
||||
func (v *Visitor) Visit(node ast.Node) ast.Visitor {
|
||||
st, ok := node.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
|
||||
if st.Name.Name != "FullNode" {
|
||||
return nil
|
||||
}
|
||||
|
||||
iface := st.Type.(*ast.InterfaceType)
|
||||
for _, m := range iface.Methods.List {
|
||||
if len(m.Names) > 0 {
|
||||
v.Methods[m.Names[0].Name] = m
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
const noComment = "There are not yet any comments for this method."
|
||||
|
||||
func parseApiASTInfo() (map[string]string, map[string]string) {
|
||||
|
||||
fset := token.NewFileSet()
|
||||
pkgs, err := parser.ParseDir(fset, "./api", nil, parser.AllErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Println("parse error: ", err)
|
||||
}
|
||||
|
||||
ap := pkgs["api"]
|
||||
|
||||
f := ap.Files["api/api_full.go"]
|
||||
|
||||
cmap := ast.NewCommentMap(fset, f, f.Comments)
|
||||
|
||||
v := &Visitor{make(map[string]ast.Node)}
|
||||
ast.Walk(v, pkgs["api"])
|
||||
|
||||
groupDocs := make(map[string]string)
|
||||
out := make(map[string]string)
|
||||
for mn, node := range v.Methods {
|
||||
cs := cmap.Filter(node).Comments()
|
||||
if len(cs) == 0 {
|
||||
out[mn] = noComment
|
||||
} else {
|
||||
for _, c := range cs {
|
||||
if strings.HasPrefix(c.Text(), "MethodGroup:") {
|
||||
parts := strings.Split(c.Text(), "\n")
|
||||
groupName := strings.TrimSpace(parts[0][12:])
|
||||
comment := strings.Join(parts[1:], "\n")
|
||||
groupDocs[groupName] = comment
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
last := cs[len(cs)-1].Text()
|
||||
if !strings.HasPrefix(last, "MethodGroup:") {
|
||||
out[mn] = last
|
||||
} else {
|
||||
out[mn] = noComment
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, groupDocs
|
||||
}
|
||||
|
||||
type MethodGroup struct {
|
||||
GroupName string
|
||||
Header string
|
||||
Methods []*Method
|
||||
}
|
||||
|
||||
type Method struct {
|
||||
Comment string
|
||||
Name string
|
||||
InputExample string
|
||||
ResponseExample string
|
||||
}
|
||||
|
||||
func methodGroupFromName(mn string) string {
|
||||
i := strings.IndexFunc(mn[1:], func(r rune) bool {
|
||||
return unicode.IsUpper(r)
|
||||
})
|
||||
if i < 0 {
|
||||
return ""
|
||||
}
|
||||
return mn[:i+1]
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
comments, groupComments := parseApiASTInfo()
|
||||
|
||||
groups := make(map[string]*MethodGroup)
|
||||
|
||||
var api struct{ api.FullNode }
|
||||
t := reflect.TypeOf(api)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
m := t.Method(i)
|
||||
|
||||
groupName := methodGroupFromName(m.Name)
|
||||
|
||||
g, ok := groups[groupName]
|
||||
if !ok {
|
||||
g = new(MethodGroup)
|
||||
g.Header = groupComments[groupName]
|
||||
g.GroupName = groupName
|
||||
groups[groupName] = g
|
||||
}
|
||||
|
||||
var args []interface{}
|
||||
ft := m.Func.Type()
|
||||
for j := 2; j < ft.NumIn(); j++ {
|
||||
inp := ft.In(j)
|
||||
args = append(args, exampleValue(inp))
|
||||
}
|
||||
|
||||
v, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outv := exampleValue(ft.Out(0))
|
||||
|
||||
ov, err := json.Marshal(outv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g.Methods = append(g.Methods, &Method{
|
||||
Name: m.Name,
|
||||
Comment: comments[m.Name],
|
||||
InputExample: string(v),
|
||||
ResponseExample: string(ov),
|
||||
})
|
||||
}
|
||||
|
||||
var groupslice []*MethodGroup
|
||||
for _, g := range groups {
|
||||
groupslice = append(groupslice, g)
|
||||
}
|
||||
|
||||
sort.Slice(groupslice, func(i, j int) bool {
|
||||
return groupslice[i].GroupName < groupslice[j].GroupName
|
||||
})
|
||||
|
||||
for _, g := range groupslice {
|
||||
fmt.Printf("## %s\n", g.GroupName)
|
||||
fmt.Printf("%s\n\n", g.Header)
|
||||
|
||||
sort.Slice(g.Methods, func(i, j int) bool {
|
||||
return g.Methods[i].Name < g.Methods[j].Name
|
||||
})
|
||||
|
||||
for _, m := range g.Methods {
|
||||
fmt.Printf("### %s\n", m.Name)
|
||||
fmt.Printf("%s\n\n", m.Comment)
|
||||
|
||||
fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
|
||||
fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -200,7 +200,7 @@ func testRetrieval(t *testing.T, ctx context.Context, err error, client *impl.Fu
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ref := &api.FileRef{
|
||||
ref := api.FileRef{
|
||||
Path: filepath.Join(rpath, "ret"),
|
||||
IsCAR: carExport,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/dns4/bootstrap-0-sin.fil-test.net/tcp/1347/p2p/12D3KooWKNF7vNFEhnvB45E9mw2B5z6t419W3ziZPLdUDVnLLKGs
|
||||
/ip4/86.109.15.57/tcp/1347/p2p/12D3KooWKNF7vNFEhnvB45E9mw2B5z6t419W3ziZPLdUDVnLLKGs
|
||||
/dns4/bootstrap-0-dfw.fil-test.net/tcp/1347/p2p/12D3KooWECJTm7RUPyGfNbRwm6y2fK4wA7EB8rDJtWsq5AKi7iDr
|
||||
/ip4/139.178.84.45/tcp/1347/p2p/12D3KooWECJTm7RUPyGfNbRwm6y2fK4wA7EB8rDJtWsq5AKi7iDr
|
||||
/dns4/bootstrap-0-fra.fil-test.net/tcp/1347/p2p/12D3KooWC7MD6m7iNCuDsYtNr7xVtazihyVUizBbhmhEiyMAm9ym
|
||||
/ip4/136.144.49.17/tcp/1347/p2p/12D3KooWC7MD6m7iNCuDsYtNr7xVtazihyVUizBbhmhEiyMAm9ym
|
||||
/dns4/bootstrap-1-sin.fil-test.net/tcp/1347/p2p/12D3KooWD8eYqsKcEMFax6EbWN3rjA7qFsxCez2rmN8dWqkzgNaN
|
||||
/ip4/86.109.15.55/tcp/1347/p2p/12D3KooWD8eYqsKcEMFax6EbWN3rjA7qFsxCez2rmN8dWqkzgNaN
|
||||
/dns4/bootstrap-1-dfw.fil-test.net/tcp/1347/p2p/12D3KooWLB3RR8frLAmaK4ntHC2dwrAjyGzQgyUzWxAum1FxyyqD
|
||||
/ip4/139.178.84.41/tcp/1347/p2p/12D3KooWLB3RR8frLAmaK4ntHC2dwrAjyGzQgyUzWxAum1FxyyqD
|
||||
/dns4/bootstrap-1-fra.fil-test.net/tcp/1347/p2p/12D3KooWGPDJAw3HW4uVU3JEQBfFaZ1kdpg4HvvwRMVpUYbzhsLQ
|
||||
/ip4/136.144.49.131/tcp/1347/p2p/12D3KooWGPDJAw3HW4uVU3JEQBfFaZ1kdpg4HvvwRMVpUYbzhsLQ
|
||||
/dns4/t01000.miner.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWQfrGdBE8N2RzcnuHfyWZ4MBKMYZ6z1oPdhEbFxSNo1du
|
||||
/ip4/34.217.110.132/tcp/1347/p2p/12D3KooWQfrGdBE8N2RzcnuHfyWZ4MBKMYZ6z1oPdhEbFxSNo1du
|
||||
/dns4/peer0.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWKmHh5mQofRhFr6f6qsT4ksL7qUtd2BWC24wPHVFL9gej
|
||||
/ip4/54.187.182.170/tcp/1347/p2p/12D3KooWKmHh5mQofRhFr6f6qsT4ksL7qUtd2BWC24wPHVFL9gej
|
||||
/dns4/peer1.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWCWWtn3GMFVSn2PY7k9K7QkQTVA6p6wojUr5PgS5h1xtK
|
||||
/ip4/52.24.84.39/tcp/1347/p2p/12D3KooWCWWtn3GMFVSn2PY7k9K7QkQTVA6p6wojUr5PgS5h1xtK
|
||||
|
||||
Binary file not shown.
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -15,7 +14,6 @@ func init() {
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
// Seconds
|
||||
|
||||
@@ -25,7 +25,7 @@ func DefaultSectorSize() abi.SectorSize {
|
||||
}
|
||||
|
||||
sort.Slice(szs, func(i, j int) bool {
|
||||
return szs[i] < szs[j]
|
||||
return szs[i] < szs[i]
|
||||
})
|
||||
|
||||
return szs[0]
|
||||
@@ -109,10 +109,6 @@ const BadBlockCacheSize = 1 << 15
|
||||
// 10 block reorg.
|
||||
const BlsSignatureCacheSize = 40000
|
||||
|
||||
// Size of signature verification cache
|
||||
// 32k keeps the cache around 10MB in size, max
|
||||
const VerifSigCacheSize = 32000
|
||||
|
||||
// ///////
|
||||
// Limits
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(1024 << 30)
|
||||
power.ConsensusMinerMinPower = big.NewInt(2 << 30)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG32GiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG64GiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG512MiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG32GiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG64GiBSeal: {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ type eventApi interface {
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // optional / for CalledMsg
|
||||
}
|
||||
@@ -164,11 +163,6 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
|
||||
if err := e.headChange(rev, app); err != nil {
|
||||
log.Warnf("headChange failed: %s", err)
|
||||
}
|
||||
|
||||
// sync with fake chainstore (for tests)
|
||||
if fcs, ok := e.api.(interface{ notifDone() }); ok {
|
||||
fcs.notifDone()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -125,13 +125,7 @@ func (e *calledEvents) handleReverts(ts *types.TipSet) {
|
||||
}
|
||||
|
||||
func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
pts, err := e.cs.ChainGetTipSet(e.ctx, ts.Parents()) // we actually care about messages in the parent tipset here
|
||||
if err != nil {
|
||||
log.Errorf("getting parent tipset in checkNewCalls: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
e.messagesForTs(pts, func(msg *types.Message) {
|
||||
e.messagesForTs(ts, func(msg *types.Message) {
|
||||
// TODO: provide receipts
|
||||
|
||||
for tid, matchFns := range e.matchers {
|
||||
@@ -160,7 +154,8 @@ func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message, ts *types.TipSet) {
|
||||
trigger := e.triggers[triggerId]
|
||||
|
||||
appliedH := ts.Height()
|
||||
// messages are not applied in the tipset they are included in
|
||||
appliedH := ts.Height() + 1
|
||||
|
||||
triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
|
||||
|
||||
|
||||
+30
-149
@@ -3,20 +3,21 @@ package events
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -39,17 +40,9 @@ type fakeCS struct {
|
||||
msgs map[cid.Cid]fakeMsg
|
||||
blkMsgs map[cid.Cid]cid.Cid
|
||||
|
||||
sync sync.Mutex
|
||||
|
||||
tipsets map[types.TipSetKey]*types.TipSet
|
||||
|
||||
sub func(rev, app []*types.TipSet)
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetTipSet(ctx context.Context, key types.TipSetKey) (*types.TipSet, error) {
|
||||
return fcs.tipsets[key], nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -62,7 +55,7 @@ func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msgcid cid.Cid) *types.TipSet {
|
||||
func makeTs(t *testing.T, h abi.ChainEpoch, msgcid cid.Cid) *types.TipSet {
|
||||
a, _ := address.NewFromString("t00")
|
||||
b, _ := address.NewFromString("t02")
|
||||
var ts, err = types.NewTipSet([]*types.BlockHeader{
|
||||
@@ -70,8 +63,6 @@ func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msg
|
||||
Height: h,
|
||||
Miner: a,
|
||||
|
||||
Parents: parents,
|
||||
|
||||
Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
@@ -85,8 +76,6 @@ func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msg
|
||||
Height: h,
|
||||
Miner: b,
|
||||
|
||||
Parents: parents,
|
||||
|
||||
Ticket: &types.Ticket{VRFProof: []byte{byte((h + 1) % 2)}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
@@ -98,11 +87,6 @@ func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msg
|
||||
},
|
||||
})
|
||||
|
||||
if fcs.tipsets == nil {
|
||||
fcs.tipsets = map[types.TipSetKey]*types.TipSet{}
|
||||
}
|
||||
fcs.tipsets[ts.Key()] = ts
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
return ts
|
||||
@@ -196,7 +180,7 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
continue
|
||||
}
|
||||
|
||||
ts := fcs.makeTs(fcs.t, fcs.tsc.best().Key().Cids(), fcs.h, mc)
|
||||
ts := makeTs(fcs.t, fcs.h, mc)
|
||||
require.NoError(fcs.t, fcs.tsc.add(ts))
|
||||
|
||||
if hasMsgs {
|
||||
@@ -206,17 +190,8 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
apps = append(apps, ts)
|
||||
}
|
||||
|
||||
fcs.sync.Lock()
|
||||
|
||||
fcs.sub(revs, apps)
|
||||
|
||||
fcs.sync.Lock()
|
||||
fcs.sync.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) notifDone() {
|
||||
fcs.sync.Unlock()
|
||||
time.Sleep(100 * time.Millisecond) // TODO: :c
|
||||
}
|
||||
|
||||
var _ eventApi = &fakeCS{}
|
||||
@@ -227,7 +202,7 @@ func TestAt(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -292,7 +267,7 @@ func TestAtDoubleTrigger(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -334,7 +309,7 @@ func TestAtNullTrigger(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -368,7 +343,7 @@ func TestAtNullConf(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -407,7 +382,7 @@ func TestAtStart(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -441,7 +416,7 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -471,7 +446,7 @@ func TestAtChained(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -505,7 +480,7 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -539,7 +514,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -577,7 +552,7 @@ func TestCalled(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -653,7 +628,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert the message
|
||||
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg execution, but not the msg itself
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@@ -667,17 +642,10 @@ func TestCalled(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // (n2msg confidence=1)
|
||||
fcs.advance(0, 5, map[int]cid.Cid{ // (confidence=3)
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
require.Equal(t, true, applied) // msg from H=7, which had reverted execution
|
||||
require.Equal(t, false, reverted)
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 2, nil) // (confidence=3)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
@@ -691,7 +659,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert and apply at different height
|
||||
|
||||
fcs.advance(8, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
fcs.advance(4, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
1: n2msg,
|
||||
})
|
||||
|
||||
@@ -703,9 +671,9 @@ func TestCalled(t *testing.T) {
|
||||
reverted = false
|
||||
applied = false
|
||||
|
||||
require.Equal(t, abi.ChainEpoch(7), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(11), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(14), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
@@ -729,7 +697,7 @@ func TestCalled(t *testing.T) {
|
||||
}),
|
||||
})
|
||||
|
||||
fcs.advance(2, 5, nil) // H=19, but message reverted
|
||||
fcs.advance(1, 4, nil) // H=19, but message reverted
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@@ -789,7 +757,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -829,7 +797,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events = NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -863,7 +831,7 @@ func TestCalledOrder(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -926,7 +894,7 @@ func TestCalledNull(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -981,90 +949,3 @@ func TestCalledNull(t *testing.T) {
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
}
|
||||
|
||||
func TestRemoveTriggersOnMessage(t *testing.T) {
|
||||
fcs := &fakeCS{
|
||||
t: t,
|
||||
h: 1,
|
||||
|
||||
msgs: map[cid.Cid]fakeMsg{},
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
t0123, err := address.NewFromString("t0123")
|
||||
require.NoError(t, err)
|
||||
|
||||
more := true
|
||||
var applied, reverted bool
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
fmt.Println(msg == nil)
|
||||
fmt.Println(curH)
|
||||
applied = true
|
||||
return more, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=5, applied at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied TS & message TS
|
||||
fcs.advance(3, 1, nil) // H=6 (tipset message applied in reverted, AND message reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold, but message not applied
|
||||
// as it was reverted
|
||||
fcs.advance(0, 5, nil) // H=11 (confidence=3, apply)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message again (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=12, applied at H=13; H=15 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 2},
|
||||
},
|
||||
}),
|
||||
})
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied height TS, but don't remove message trigger
|
||||
fcs.advance(2, 1, nil) // H=13 (tipset message applied in reverted, by tipset with message not reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold
|
||||
fcs.advance(0, 4, nil) // H=18 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
@@ -18,7 +17,6 @@ func init() {
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
func testGeneration(t testing.TB, n int, msgs int, sectors int) {
|
||||
|
||||
@@ -5,10 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
@@ -22,7 +20,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
@@ -218,72 +215,9 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
}
|
||||
}
|
||||
|
||||
vregroot, err := address.NewIDAddress(80)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vrst, err := cst.Put(ctx, &account.State{Address: RootVerifierAddr})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = state.SetActor(vregroot, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: types.NewInt(0),
|
||||
Head: vrst,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setting account from actmap: %w", err)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot cid.Cid, template genesis.Template) (cid.Cid, error) {
|
||||
verifNeeds := make(map[address.Address]abi.PaddedPieceSize)
|
||||
var sum abi.PaddedPieceSize
|
||||
for _, m := range template.Miners {
|
||||
for _, s := range m.Sectors {
|
||||
amt := s.Deal.PieceSize
|
||||
verifNeeds[s.Deal.Client] += amt
|
||||
sum += amt
|
||||
}
|
||||
}
|
||||
|
||||
verifier, err := address.NewIDAddress(80)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
vm, err := vm.NewVM(stateroot, 0, &fakeRand{}, cs.Blockstore(), &fakedSigSyscalls{cs.VMSys()})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
|
||||
}
|
||||
|
||||
_, err = doExecValue(ctx, vm, builtin.VerifiedRegistryActorAddr, RootVerifierAddr, types.NewInt(0), builtin.MethodsVerifiedRegistry.AddVerifier, mustEnc(&verifreg.AddVerifierParams{
|
||||
Address: verifier,
|
||||
Allowance: abi.NewStoragePower(int64(sum)), // eh, close enough
|
||||
|
||||
}))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to failed to create verifier: %w", err)
|
||||
}
|
||||
|
||||
for c, amt := range verifNeeds {
|
||||
_, err := doExecValue(ctx, vm, builtin.VerifiedRegistryActorAddr, verifier, types.NewInt(0), builtin.MethodsVerifiedRegistry.AddVerifiedClient, mustEnc(&verifreg.AddVerifiedClientParams{
|
||||
Address: c,
|
||||
Allowance: abi.NewStoragePower(int64(amt)),
|
||||
}))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to add verified client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return vm.Flush(ctx)
|
||||
}
|
||||
|
||||
func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys runtime.Syscalls, template genesis.Template) (*GenesisBootstrap, error) {
|
||||
st, err := MakeInitialStateTree(ctx, bs, template)
|
||||
if err != nil {
|
||||
@@ -297,13 +231,6 @@ func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys runtime.Sys
|
||||
|
||||
// temp chainstore
|
||||
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys)
|
||||
|
||||
// Verify PreSealed Data
|
||||
stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to verify presealed data: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err = SetupStorageMiners(ctx, cs, stateroot, template.Miners)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage miners failed: %w", err)
|
||||
|
||||
@@ -130,7 +130,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
|
||||
params := &market.PublishStorageDealsParams{}
|
||||
for _, preseal := range m.Sectors {
|
||||
preseal.Deal.VerifiedDeal = true
|
||||
params.Deals = append(params.Deals, market.ClientDealProposal{
|
||||
Proposal: preseal.Deal,
|
||||
ClientSignature: crypto.Signature{Type: crypto.SigTypeBLS}, // TODO: do we want to sign these? Or do we want to fake signatures for genesis setup?
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
@@ -46,10 +45,6 @@ func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesi
|
||||
}
|
||||
}
|
||||
|
||||
if err := amap.Set(context.TODO(), string(RootVerifierAddr.Bytes()), 80); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := amap.Flush(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
@@ -14,26 +15,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
var RootVerifierAddr address.Address
|
||||
|
||||
var RootVerifierID address.Address
|
||||
|
||||
func init() {
|
||||
k, err := address.NewFromString("t3qfoulel6fy6gn3hjmbhpdpf6fs5aqjb5fkurhtwvgssizq4jey5nw4ptq5up6h7jk7frdvvobv52qzmgjinq")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
RootVerifierAddr = k
|
||||
|
||||
idk, err := address.NewFromString("t080")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
RootVerifierID = idk
|
||||
}
|
||||
|
||||
func SetupVerifiedRegistryActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
@@ -42,7 +23,11 @@ func SetupVerifiedRegistryActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sms := verifreg.ConstructState(h, RootVerifierID)
|
||||
var r [32]byte // TODO: grab from genesis template
|
||||
_, _ = rand.Read(r[:])
|
||||
k, _ := address.NewSecp256k1Address(r[:])
|
||||
|
||||
sms := verifreg.ConstructState(h, k)
|
||||
|
||||
stcid, err := cst.Put(context.TODO(), sms)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
@@ -88,8 +87,6 @@ type MessagePool struct {
|
||||
localMsgs datastore.Datastore
|
||||
|
||||
netName dtypes.NetworkName
|
||||
|
||||
sigValCache *lru.TwoQueueCache
|
||||
}
|
||||
|
||||
type msgSet struct {
|
||||
@@ -183,8 +180,6 @@ func (mpp *mpoolProvider) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error)
|
||||
|
||||
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) {
|
||||
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
|
||||
verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
|
||||
|
||||
mp := &MessagePool{
|
||||
closer: make(chan struct{}),
|
||||
repubTk: time.NewTicker(build.BlockDelay * 10 * time.Second),
|
||||
@@ -193,7 +188,6 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
|
||||
minGasPrice: types.NewInt(0),
|
||||
maxTxPoolSize: 5000,
|
||||
blsSigCache: cache,
|
||||
sigValCache: verifcache,
|
||||
changes: lps.New(50),
|
||||
localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)),
|
||||
api: api,
|
||||
@@ -319,6 +313,12 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
|
||||
}
|
||||
|
||||
func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
return mp.addTs(m, mp.curTs)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
|
||||
// big messages are bad, anti DOS
|
||||
if m.Size() > 32*1024 {
|
||||
return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig)
|
||||
@@ -332,53 +332,11 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
||||
return ErrMessageValueTooHigh
|
||||
}
|
||||
|
||||
if err := mp.VerifyMsgSig(m); err != nil {
|
||||
if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
|
||||
log.Warnf("mpooladd signature verification failed: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
return mp.addTs(m, mp.curTs)
|
||||
}
|
||||
|
||||
func sigCacheKey(m *types.SignedMessage) (string, error) {
|
||||
switch m.Signature.Type {
|
||||
case crypto.SigTypeBLS:
|
||||
if len(m.Signature.Data) < 90 {
|
||||
return "", fmt.Errorf("bls signature too short")
|
||||
}
|
||||
|
||||
return string(m.Cid().Bytes()) + string(m.Signature.Data[64:]), nil
|
||||
case crypto.SigTypeSecp256k1:
|
||||
return string(m.Cid().Bytes()), nil
|
||||
default:
|
||||
return "", xerrors.Errorf("unrecognized signature type: %d", m.Signature.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
|
||||
sck, err := sigCacheKey(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, ok := mp.sigValCache.Get(sck)
|
||||
if ok {
|
||||
// already validated, great
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mp.sigValCache.Add(sck, struct{}{})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
|
||||
snonce, err := mp.getStateNonce(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -41,7 +40,6 @@ func init() {
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
const testForkHeight = 40
|
||||
|
||||
@@ -610,9 +610,9 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for,
|
||||
// either way, no reason to lookback, it ain't there
|
||||
if act.Nonce == 0 || act.Nonce < m.VMMessage().Nonce {
|
||||
if act.Nonce < m.VMMessage().Nonce {
|
||||
// nonce on chain is before message nonce we're looking for, its
|
||||
// not going to be here
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -248,24 +248,24 @@ func GetMinerDeadlines(ctx context.Context, sm *StateManager, ts *types.TipSet,
|
||||
return mas.LoadDeadlines(sm.cs.Store(ctx))
|
||||
}
|
||||
|
||||
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*abi.BitField, error) {
|
||||
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]abi.SectorNumber, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get faults) failed to load miner actor state: %w", err)
|
||||
return nil, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return mas.Faults, nil
|
||||
}
|
||||
|
||||
func GetMinerRecoveries(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*abi.BitField, error) {
|
||||
var mas miner.State
|
||||
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
|
||||
faults, err := mas.Faults.All(miner.SectorsMax)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get recoveries) failed to load miner actor state: %w", err)
|
||||
return nil, xerrors.Errorf("reading fault bit set: %w", err)
|
||||
}
|
||||
|
||||
return mas.Recoveries, nil
|
||||
out := make([]abi.SectorNumber, len(faults))
|
||||
for i, fault := range faults {
|
||||
out[i] = abi.SectorNumber(fault)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GetStorageDeal(ctx context.Context, sm *StateManager, dealId abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
@@ -26,7 +25,6 @@ func init() {
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
func BenchmarkGetRandomness(b *testing.B) {
|
||||
|
||||
+15
-196
@@ -1,45 +1,30 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
miner "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
connmgr "github.com/libp2p/go-libp2p-core/connmgr"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/bufbstore"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
)
|
||||
|
||||
var log = logging.Logger("sub")
|
||||
|
||||
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager) {
|
||||
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager, bv *BlockValidator) {
|
||||
for {
|
||||
msg, err := bsub.Next(ctx)
|
||||
if err != nil {
|
||||
@@ -67,13 +52,15 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
|
||||
log.Debug("about to fetch messages for block from pubsub")
|
||||
bmsgs, err := s.Bsync.FetchMessagesByCids(context.TODO(), blk.BlsMessages)
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s; source: %s", err, src)
|
||||
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s; flagging source %s", err, src)
|
||||
bv.flagPeer(src)
|
||||
return
|
||||
}
|
||||
|
||||
smsgs, err := s.Bsync.FetchSignedMessagesByCids(context.TODO(), blk.SecpkMessages)
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s; source: %s", err, src)
|
||||
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s; flagging source %s", err, src)
|
||||
bv.flagPeer(src)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -102,25 +89,15 @@ type BlockValidator struct {
|
||||
recvBlocks *blockReceiptCache
|
||||
|
||||
blacklist func(peer.ID)
|
||||
|
||||
// necessary for block validation
|
||||
chain *store.ChainStore
|
||||
stmgr *stmgr.StateManager
|
||||
|
||||
mx sync.Mutex
|
||||
keycache map[string]address.Address
|
||||
}
|
||||
|
||||
func NewBlockValidator(chain *store.ChainStore, stmgr *stmgr.StateManager, blacklist func(peer.ID)) *BlockValidator {
|
||||
func NewBlockValidator(blacklist func(peer.ID)) *BlockValidator {
|
||||
p, _ := lru.New2Q(4096)
|
||||
return &BlockValidator{
|
||||
peers: p,
|
||||
killThresh: 10,
|
||||
blacklist: blacklist,
|
||||
recvBlocks: newBlockReceiptCache(),
|
||||
chain: chain,
|
||||
stmgr: stmgr,
|
||||
keycache: make(map[string]address.Address),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,191 +120,35 @@ func (bv *BlockValidator) flagPeer(p peer.ID) {
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
|
||||
// track validation time
|
||||
begin := time.Now()
|
||||
defer func() {
|
||||
end := time.Now()
|
||||
log.Infof("block validation time: %s", end.Sub(begin))
|
||||
}()
|
||||
|
||||
stats.Record(ctx, metrics.BlockReceived.M(1))
|
||||
|
||||
recordFailure := func(what string) {
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, what))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
bv.flagPeer(pid)
|
||||
}
|
||||
|
||||
// make sure the block can be decoded
|
||||
blk, err := types.DecodeBlockMsg(msg.GetData())
|
||||
if err != nil {
|
||||
log.Error("got invalid block over pubsub: ", err)
|
||||
recordFailure("invalid")
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "invalid"))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
bv.flagPeer(pid)
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
// check the message limit constraints
|
||||
if len(blk.BlsMessages)+len(blk.SecpkMessages) > build.BlockMessageLimit {
|
||||
log.Warnf("received block with too many messages over pubsub")
|
||||
recordFailure("too_many_messages")
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "too_many_messages"))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
bv.flagPeer(pid)
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
// make sure we have a signature
|
||||
if blk.Header.BlockSig == nil {
|
||||
log.Warnf("received block without a signature over pubsub")
|
||||
recordFailure("missing_signature")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
// validate the block meta: the Message CID in the header must match the included messages
|
||||
err = bv.validateMsgMeta(ctx, blk)
|
||||
if err != nil {
|
||||
log.Warnf("error validating message metadata: %s", err)
|
||||
recordFailure("invalid_block_meta")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
// we want to ensure that it is a block from a known miner; we reject blocks from unknown miners
|
||||
// to prevent spam attacks.
|
||||
// the logic works as follows: we lookup the miner in the chain for its key.
|
||||
// if we can find it then it's a known miner and we can validate the signature.
|
||||
// if we can't find it, we check whether we are (near) synced in the chain.
|
||||
// if we are not synced we cannot validate the block and we must ignore it.
|
||||
// if we are synced and the miner is unknown, then the block is rejcected.
|
||||
key, err := bv.getMinerWorkerKey(ctx, blk)
|
||||
if err != nil {
|
||||
if bv.isChainNearSynced() {
|
||||
log.Warnf("received block message from unknown miner over pubsub; rejecting message")
|
||||
recordFailure("unknown_miner")
|
||||
return pubsub.ValidationReject
|
||||
} else {
|
||||
log.Warnf("cannot validate block message; unknown miner in unsynced chain")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
}
|
||||
|
||||
err = sigs.CheckBlockSignature(blk.Header, ctx, key)
|
||||
if err != nil {
|
||||
log.Errorf("block signature verification failed: %s", err)
|
||||
recordFailure("signature_verification_failed")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
// it's a good block! make sure we've only seen it once
|
||||
if bv.recvBlocks.add(blk.Header.Cid()) > 0 {
|
||||
// TODO: once these changes propagate to the network, we can consider
|
||||
// dropping peers who send us the same block multiple times
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
// all good, accept the block
|
||||
msg.ValidatorData = blk
|
||||
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
|
||||
return pubsub.ValidationAccept
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) isChainNearSynced() bool {
|
||||
ts := bv.chain.GetHeaviestTipSet()
|
||||
timestamp := ts.MinTimestamp()
|
||||
now := time.Now().UnixNano()
|
||||
cutoff := uint64(now) - uint64(6*time.Hour)
|
||||
return timestamp > cutoff
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) validateMsgMeta(ctx context.Context, msg *types.BlockMsg) error {
|
||||
var bcids, scids []cbg.CBORMarshaler
|
||||
for _, m := range msg.BlsMessages {
|
||||
c := cbg.CborCid(m)
|
||||
bcids = append(bcids, &c)
|
||||
}
|
||||
|
||||
for _, m := range msg.SecpkMessages {
|
||||
c := cbg.CborCid(m)
|
||||
scids = append(scids, &c)
|
||||
}
|
||||
|
||||
// TODO there has to be a simpler way to do this without the blockstore dance
|
||||
bs := cbor.NewCborStore(bstore.NewBlockstore(dstore.NewMapDatastore()))
|
||||
|
||||
bmroot, err := amt.FromArray(ctx, bs, bcids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smroot, err := amt.FromArray(ctx, bs, scids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mrcid, err := bs.Put(ctx, &types.MsgMeta{
|
||||
BlsMessages: bmroot,
|
||||
SecpkMessages: smroot,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Header.Messages != mrcid {
|
||||
return fmt.Errorf("messages didn't match root cid in header")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) getMinerWorkerKey(ctx context.Context, msg *types.BlockMsg) (address.Address, error) {
|
||||
addr := msg.Header.Miner
|
||||
|
||||
bv.mx.Lock()
|
||||
key, ok := bv.keycache[addr.String()]
|
||||
bv.mx.Unlock()
|
||||
if ok {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// TODO I have a feeling all this can be simplified by cleverer DI to use the API
|
||||
ts := bv.chain.GetHeaviestTipSet()
|
||||
st, _, err := bv.stmgr.TipSetState(ctx, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore())
|
||||
cst := cbor.NewCborStore(buf)
|
||||
state, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
act, err := state.GetActor(addr)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
blk, err := bv.chain.Blockstore().Get(act.Head)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
aso := blk.RawData()
|
||||
|
||||
var mst miner.State
|
||||
err = mst.UnmarshalCBOR(bytes.NewReader(aso))
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
worker := mst.Info.Worker
|
||||
key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
bv.mx.Lock()
|
||||
bv.keycache[addr.String()] = key
|
||||
bv.mx.Unlock()
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
type blockReceiptCache struct {
|
||||
blocks *lru.TwoQueueCache
|
||||
}
|
||||
@@ -376,12 +197,10 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
||||
tag.Insert(metrics.FailureType, "add"),
|
||||
)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
switch {
|
||||
case xerrors.Is(err, messagepool.ErrBroadcastAnyway):
|
||||
return pubsub.ValidationIgnore
|
||||
default:
|
||||
return pubsub.ValidationReject
|
||||
if xerrors.Is(err, messagepool.ErrBroadcastAnyway) {
|
||||
return pubsub.ValidationAccept
|
||||
}
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
stats.Record(ctx, metrics.MessageValidationSuccess.M(1))
|
||||
return pubsub.ValidationAccept
|
||||
|
||||
+39
-7
@@ -31,6 +31,7 @@ import (
|
||||
amt "github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
@@ -841,13 +842,44 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
return xerrors.Errorf("failed to load base state tree: %w", err)
|
||||
}
|
||||
|
||||
checkMsg := func(msg types.ChainMsg) error {
|
||||
m := msg.VMMessage()
|
||||
|
||||
checkMsg := func(m *types.Message) error {
|
||||
// Phase 1: syntactic validation, as defined in the spec
|
||||
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
|
||||
if err := m.ValidForBlockInclusion(minGas); err != nil {
|
||||
return err
|
||||
|
||||
if m.Version != 0 {
|
||||
return xerrors.New("'Version' unsupported")
|
||||
}
|
||||
|
||||
if m.To == address.Undef {
|
||||
return xerrors.New("'To' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.From == address.Undef {
|
||||
return xerrors.New("'From' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.Value.LessThan(big.Zero()) {
|
||||
return xerrors.New("'Value' field cannot be negative")
|
||||
}
|
||||
|
||||
if m.Value.GreaterThan(types.TotalFilecoinInt) {
|
||||
return xerrors.New("'Value' field cannot be greater than total filecoin supply")
|
||||
}
|
||||
|
||||
if len(m.Params) != 0 && m.Method == 0 {
|
||||
return xerrors.New("'Params' field should be empty if no 'Method' is being called")
|
||||
}
|
||||
|
||||
if m.GasPrice.LessThan(big.Zero()) {
|
||||
return xerrors.New("'GasPrice' field cannot be negative")
|
||||
}
|
||||
|
||||
if m.GasLimit > build.BlockGasLimit {
|
||||
return xerrors.New("'GasLimit' field cannot be greater than a block's gas limit")
|
||||
}
|
||||
|
||||
// since prices might vary with time, this is technically semantic validation
|
||||
if m.GasLimit < vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(m.ChainLength()) {
|
||||
return xerrors.New("'GasLimit' field cannot be less than the cost of storing a message on chain")
|
||||
}
|
||||
|
||||
// Phase 2: (Partial) semantic validation:
|
||||
@@ -887,7 +919,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
|
||||
var secpkCids []cbg.CBORMarshaler
|
||||
for i, m := range b.SecpkMessages {
|
||||
if err := checkMsg(m); err != nil {
|
||||
if err := checkMsg(&m.Message); err != nil {
|
||||
return xerrors.Errorf("block had invalid secpk message at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
@@ -38,7 +37,6 @@ func init() {
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
const source = 0
|
||||
|
||||
@@ -69,9 +69,6 @@ type BlockHeader struct {
|
||||
BlockSig *crypto.Signature // 13
|
||||
|
||||
ForkSignaling uint64 // 14
|
||||
|
||||
// internal
|
||||
validated bool // true if the signature has been validated
|
||||
}
|
||||
|
||||
func (b *BlockHeader) ToStorageBlock() (block.Block, error) {
|
||||
@@ -127,14 +124,6 @@ func (blk *BlockHeader) SigningBytes() ([]byte, error) {
|
||||
return blkcopy.Serialize()
|
||||
}
|
||||
|
||||
func (blk *BlockHeader) SetValidated() {
|
||||
blk.validated = true
|
||||
}
|
||||
|
||||
func (blk *BlockHeader) IsValidated() bool {
|
||||
return blk.validated
|
||||
}
|
||||
|
||||
type MsgMeta struct {
|
||||
BlsMessages cid.Cid
|
||||
SecpkMessages cid.Cid
|
||||
|
||||
@@ -2,9 +2,7 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -65,62 +63,6 @@ func TestBlockHeaderSerialization(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInteropBH(t *testing.T) {
|
||||
newAddr, err := address.NewSecp256k1Address([]byte("address0"))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mcid, err := cid.Parse("bafy2bzaceaxyj7xq27gc2747adjcirpxx52tt7owqx6z6kckun7tqivvoym4y")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
posts := []abi.PoStProof{
|
||||
{abi.RegisteredProof_StackedDRG2KiBWinningPoSt, []byte{0x07}},
|
||||
}
|
||||
|
||||
bh := &BlockHeader{
|
||||
Miner: newAddr,
|
||||
Ticket: &Ticket{[]byte{0x01, 0x02, 0x03}},
|
||||
ElectionProof: &ElectionProof{[]byte{0x0a, 0x0b}},
|
||||
BeaconEntries: []BeaconEntry{
|
||||
{
|
||||
Round: 5,
|
||||
Data: []byte{0x0c},
|
||||
//prevRound: 0,
|
||||
},
|
||||
},
|
||||
Height: 2,
|
||||
Messages: mcid,
|
||||
ParentMessageReceipts: mcid,
|
||||
Parents: []cid.Cid{mcid},
|
||||
ParentWeight: NewInt(1000),
|
||||
ForkSignaling: 3,
|
||||
ParentStateRoot: mcid,
|
||||
Timestamp: 1,
|
||||
WinPoStProof: posts,
|
||||
BlockSig: &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: []byte{0x3},
|
||||
},
|
||||
BLSAggregate: &crypto.Signature{},
|
||||
}
|
||||
|
||||
bhsb, err := bh.SigningBytes()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// acquired from go-filecoin
|
||||
gfc := "8f5501d04cb15021bf6bd003073d79e2238d4e61f1ad22814301020381420a0b818205410c818209410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f603"
|
||||
if gfc != hex.EncodeToString(bhsb) {
|
||||
t.Fatal("not equal!")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBlockHeaderMarshal(b *testing.B) {
|
||||
bh := testBlockHeader(b)
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
type ExecutionResult struct {
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
Duration time.Duration
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
|
||||
Subcalls []*ExecutionResult
|
||||
}
|
||||
|
||||
@@ -4,13 +4,10 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/multiformats/go-multihash"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
)
|
||||
@@ -124,40 +121,3 @@ func (m *Message) VMMessage() *Message {
|
||||
func (m *Message) Equals(o *Message) bool {
|
||||
return m.Cid() == o.Cid()
|
||||
}
|
||||
|
||||
func (m *Message) ValidForBlockInclusion(minGas int64) error {
|
||||
if m.Version != 0 {
|
||||
return xerrors.New("'Version' unsupported")
|
||||
}
|
||||
|
||||
if m.To == address.Undef {
|
||||
return xerrors.New("'To' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.From == address.Undef {
|
||||
return xerrors.New("'From' address cannot be empty")
|
||||
}
|
||||
|
||||
if m.Value.LessThan(big.Zero()) {
|
||||
return xerrors.New("'Value' field cannot be negative")
|
||||
}
|
||||
|
||||
if m.Value.GreaterThan(TotalFilecoinInt) {
|
||||
return xerrors.New("'Value' field cannot be greater than total filecoin supply")
|
||||
}
|
||||
|
||||
if m.GasPrice.LessThan(big.Zero()) {
|
||||
return xerrors.New("'GasPrice' field cannot be negative")
|
||||
}
|
||||
|
||||
if m.GasLimit > build.BlockGasLimit {
|
||||
return xerrors.New("'GasLimit' field cannot be greater than a block's gas limit")
|
||||
}
|
||||
|
||||
// since prices might vary with time, this is technically semantic validation
|
||||
if m.GasLimit < minGas {
|
||||
return xerrors.New("'GasLimit' field cannot be less than the cost of storing a message on chain")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/puppet"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
vtypes "github.com/filecoin-project/chain-validation/chain/types"
|
||||
vdrivers "github.com/filecoin-project/chain-validation/drivers"
|
||||
vstate "github.com/filecoin-project/chain-validation/state"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
@@ -24,13 +24,12 @@ import (
|
||||
// Applier applies messages to state trees and storage.
|
||||
type Applier struct {
|
||||
stateWrapper *StateWrapper
|
||||
syscalls runtime.Syscalls
|
||||
}
|
||||
|
||||
var _ vstate.Applier = &Applier{}
|
||||
|
||||
func NewApplier(sw *StateWrapper, syscalls runtime.Syscalls) *Applier {
|
||||
return &Applier{sw, syscalls}
|
||||
func NewApplier(sw *StateWrapper) *Applier {
|
||||
return &Applier{sw}
|
||||
}
|
||||
|
||||
func (a *Applier) ApplyMessage(epoch abi.ChainEpoch, message *vtypes.Message) (vtypes.ApplyMessageResult, error) {
|
||||
@@ -66,7 +65,7 @@ func (a *Applier) ApplySignedMessage(epoch abi.ChainEpoch, msg *vtypes.SignedMes
|
||||
}
|
||||
|
||||
func (a *Applier) ApplyTipSetMessages(epoch abi.ChainEpoch, blocks []vtypes.BlockMessagesInfo, rnd vstate.RandomnessSource) (vtypes.ApplyTipSetResult, error) {
|
||||
cs := store.NewChainStore(a.stateWrapper.bs, a.stateWrapper.ds, a.syscalls)
|
||||
cs := store.NewChainStore(a.stateWrapper.bs, a.stateWrapper.ds, vdrivers.NewChainValidationSyscalls())
|
||||
sm := stmgr.NewStateManager(cs)
|
||||
|
||||
var bms []stmgr.BlockMessages
|
||||
@@ -135,7 +134,7 @@ func (a *Applier) applyMessage(epoch abi.ChainEpoch, lm types.ChainMsg) (vtypes.
|
||||
ctx := context.TODO()
|
||||
base := a.stateWrapper.Root()
|
||||
|
||||
lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, a.syscalls)
|
||||
lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, vdrivers.NewChainValidationSyscalls())
|
||||
// need to modify the VM invoker to add the puppet actor
|
||||
chainValInvoker := vm.NewInvoker()
|
||||
chainValInvoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"context"
|
||||
|
||||
vstate "github.com/filecoin-project/chain-validation/state"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
acrypto "github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
)
|
||||
|
||||
type Factories struct {
|
||||
@@ -16,15 +18,26 @@ func NewFactories() *Factories {
|
||||
return &Factories{}
|
||||
}
|
||||
|
||||
func (f *Factories) NewStateAndApplier(syscalls runtime.Syscalls) (vstate.VMWrapper, vstate.Applier) {
|
||||
func (f *Factories) NewStateAndApplier() (vstate.VMWrapper, vstate.Applier) {
|
||||
st := NewState()
|
||||
return st, NewApplier(st, syscalls)
|
||||
return st, NewApplier(st)
|
||||
}
|
||||
|
||||
func (f *Factories) NewKeyManager() vstate.KeyManager {
|
||||
return newKeyManager()
|
||||
}
|
||||
|
||||
type fakeRandSrc struct {
|
||||
}
|
||||
|
||||
func (r fakeRandSrc) Randomness(_ context.Context, _ acrypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) (abi.Randomness, error) {
|
||||
return abi.Randomness("sausages"), nil
|
||||
}
|
||||
|
||||
func (f *Factories) NewRandomnessSource() vstate.RandomnessSource {
|
||||
return &fakeRandSrc{}
|
||||
}
|
||||
|
||||
func (f *Factories) NewValidationConfig() vstate.ValidationConfig {
|
||||
trackGas := true
|
||||
checkExit := true
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package vectors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func LoadVector(t *testing.T, f string, out interface{}) {
|
||||
@@ -46,40 +42,5 @@ func TestBlockHeaderVectors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMessageSigningVectors(t *testing.T) {
|
||||
var msvs []MessageSigningVector
|
||||
LoadVector(t, "message_signing.json", &msvs)
|
||||
|
||||
for i, msv := range msvs {
|
||||
smsg := &types.SignedMessage{
|
||||
Message: *msv.Unsigned,
|
||||
Signature: *msv.Signature,
|
||||
}
|
||||
|
||||
if smsg.Cid().String() != msv.Cid {
|
||||
t.Fatalf("cid of message in vector %d mismatches", i)
|
||||
}
|
||||
|
||||
// TODO: check signature
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsignedMessageVectors(t *testing.T) {
|
||||
var msvs []UnsignedMessageVector
|
||||
LoadVector(t, "unsigned_messages.json", &msvs)
|
||||
|
||||
for i, msv := range msvs {
|
||||
b, err := msv.Message.Serialize()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dec, err := hex.DecodeString(msv.HexCbor)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, dec) {
|
||||
t.Fatalf("serialization vector %d mismatches bytes", i)
|
||||
}
|
||||
}
|
||||
// TODO:
|
||||
}
|
||||
|
||||
+3
-9
@@ -5,8 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
@@ -273,9 +271,7 @@ func (rt *Runtime) DeleteActor(addr address.Address) {
|
||||
panic(aerrors.Fatalf("failed to get actor: %s", err))
|
||||
}
|
||||
if !act.Balance.IsZero() {
|
||||
if err := rt.vm.transfer(rt.Message().Receiver(), builtin.BurntFundsActorAddr, act.Balance); err != nil {
|
||||
panic(aerrors.Fatalf("failed to transfer balance to burnt funds actor: %s", err))
|
||||
}
|
||||
rt.vm.transfer(rt.Message().Receiver(), builtin.BurntFundsActorAddr, act.Balance)
|
||||
}
|
||||
|
||||
if err := rt.state.DeleteActor(rt.Message().Receiver()); err != nil {
|
||||
@@ -368,7 +364,6 @@ func (rs *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMars
|
||||
}
|
||||
|
||||
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
|
||||
start := time.Now()
|
||||
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
@@ -408,9 +403,8 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
|
||||
}
|
||||
|
||||
er := types.ExecutionResult{
|
||||
Msg: msg,
|
||||
MsgRct: &mr,
|
||||
Duration: time.Since(start),
|
||||
Msg: msg,
|
||||
MsgRct: &mr,
|
||||
}
|
||||
|
||||
if errSend != nil {
|
||||
|
||||
@@ -225,10 +225,10 @@ func (ss *syscallShim) VerifySeal(info abi.SealVerifyInfo) error {
|
||||
}
|
||||
|
||||
ticket := []byte(info.Randomness)
|
||||
proof := []byte(info.Proof)
|
||||
proof := []byte(info.OnChain.Proof)
|
||||
seed := []byte(info.InteractiveRandomness)
|
||||
|
||||
log.Debugf("Verif r:%x; d:%x; m:%s; t:%x; s:%x; N:%d; p:%x", info.SealedCID, info.UnsealedCID, miner, ticket, seed, info.SectorID.Number, proof)
|
||||
log.Debugf("Verif r:%x; d:%x; m:%s; t:%x; s:%x; N:%d; p:%x", info.OnChain.SealedCID, info.UnsealedCID, miner, ticket, seed, info.SectorID.Number, proof)
|
||||
|
||||
//func(ctx context.Context, maddr address.Address, ssize abi.SectorSize, commD, commR, ticket, proof, seed []byte, sectorID abi.SectorNumber)
|
||||
ok, err := ss.verifier.VerifySeal(info)
|
||||
|
||||
+8
-8
@@ -210,7 +210,7 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
|
||||
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
|
||||
if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to transfer funds"), nil
|
||||
return nil, aerrors.Absorb(err, 1, "failed to transfer funds"), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,36 +602,36 @@ func (vm *VM) incrementNonce(addr address.Address) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (vm *VM) transfer(from, to address.Address, amt types.BigInt) aerrors.ActorError {
|
||||
func (vm *VM) transfer(from, to address.Address, amt types.BigInt) error {
|
||||
if from == to {
|
||||
return nil
|
||||
}
|
||||
|
||||
if amt.LessThan(types.NewInt(0)) {
|
||||
return aerrors.Newf(exitcode.SysErrForbidden, "attempted to transfer negative value: %s", amt)
|
||||
return xerrors.Errorf("attempted to transfer negative value")
|
||||
}
|
||||
|
||||
f, err := vm.cstate.GetActor(from)
|
||||
if err != nil {
|
||||
return aerrors.Fatalf("transfer failed when retrieving sender actor: %s", err)
|
||||
return xerrors.Errorf("transfer failed when retrieving sender actor")
|
||||
}
|
||||
|
||||
t, err := vm.cstate.GetActor(to)
|
||||
if err != nil {
|
||||
return aerrors.Fatalf("transfer failed when retrieving receiver actor: %s", err)
|
||||
return xerrors.Errorf("transfer failed when retrieving receiver actor")
|
||||
}
|
||||
|
||||
if err := deductFunds(f, amt); err != nil {
|
||||
return aerrors.Newf(exitcode.SysErrInsufficientFunds, "transfer failed when deducting funds: %s", err)
|
||||
return err
|
||||
}
|
||||
depositFunds(t, amt)
|
||||
|
||||
if err := vm.cstate.SetActor(from, f); err != nil {
|
||||
return aerrors.Fatalf("transfer failed when setting receiver actor: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := vm.cstate.SetActor(to, t); err != nil {
|
||||
return aerrors.Fatalf("transfer failed when setting sender actor: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+2
-4
@@ -6,8 +6,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
@@ -47,7 +45,7 @@ var authCreateAdminToken = &cli.Command{
|
||||
perm := cctx.String("perm")
|
||||
idx := 0
|
||||
for i, p := range apistruct.AllPermissions {
|
||||
if auth.Permission(perm) == p {
|
||||
if perm == p {
|
||||
idx = i + 1
|
||||
}
|
||||
}
|
||||
@@ -95,7 +93,7 @@ var authApiInfoToken = &cli.Command{
|
||||
perm := cctx.String("perm")
|
||||
idx := 0
|
||||
for i, p := range apistruct.AllPermissions {
|
||||
if auth.Permission(perm) == p {
|
||||
if perm == p {
|
||||
idx = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
@@ -522,12 +521,6 @@ var chainGetCmd = &cli.Command{
|
||||
cbu = new(power.CronEvent)
|
||||
case "account-state":
|
||||
cbu = new(account.State)
|
||||
case "miner-state":
|
||||
cbu = new(miner.State)
|
||||
case "power-state":
|
||||
cbu = new(power.State)
|
||||
case "market-state":
|
||||
cbu = new(market.State)
|
||||
default:
|
||||
return fmt.Errorf("unknown type: %q", t)
|
||||
}
|
||||
|
||||
+10
-21
@@ -384,7 +384,7 @@ var clientRetrieveCmd = &cli.Command{
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := &lapi.FileRef{
|
||||
ref := lapi.FileRef{
|
||||
Path: cctx.Args().Get(1),
|
||||
IsCAR: cctx.Bool("car"),
|
||||
}
|
||||
@@ -501,27 +501,16 @@ var clientListDeals = &cli.Command{
|
||||
}
|
||||
|
||||
var deals []deal
|
||||
for _, v := range localDeals {
|
||||
if v.DealID == 0 {
|
||||
deals = append(deals, deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: market.DealState{
|
||||
SectorStartEpoch: -1,
|
||||
LastUpdatedEpoch: -1,
|
||||
SlashEpoch: -1,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
onChain, err := api.StateMarketStorageDeal(ctx, v.DealID, head.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deals = append(deals, deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: onChain.State,
|
||||
})
|
||||
for idx := range localDeals {
|
||||
onChain, err := api.StateMarketStorageDeal(ctx, localDeals[idx].DealID, head.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deals = append(deals, deal{
|
||||
LocalDeal: localDeals[idx],
|
||||
OnChainDealState: onChain.State,
|
||||
})
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
|
||||
+18
-24
@@ -16,10 +16,9 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/client"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
@@ -211,33 +210,28 @@ func ReqContext(cctx *cli.Context) context.Context {
|
||||
}
|
||||
|
||||
var CommonCommands = []*cli.Command{
|
||||
netCmd,
|
||||
authCmd,
|
||||
fetchParamCmd,
|
||||
netCmd,
|
||||
versionCmd,
|
||||
logCmd,
|
||||
waitApiCmd,
|
||||
fetchParamCmd,
|
||||
versionCmd,
|
||||
}
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
withCategory("basic", sendCmd),
|
||||
withCategory("basic", walletCmd),
|
||||
withCategory("basic", clientCmd),
|
||||
withCategory("basic", multisigCmd),
|
||||
withCategory("basic", paychCmd),
|
||||
withCategory("developer", authCmd),
|
||||
withCategory("developer", mpoolCmd),
|
||||
withCategory("developer", stateCmd),
|
||||
withCategory("developer", chainCmd),
|
||||
withCategory("developer", logCmd),
|
||||
withCategory("developer", waitApiCmd),
|
||||
withCategory("developer", fetchParamCmd),
|
||||
withCategory("network", netCmd),
|
||||
withCategory("network", syncCmd),
|
||||
authCmd,
|
||||
chainCmd,
|
||||
clientCmd,
|
||||
fetchParamCmd,
|
||||
mpoolCmd,
|
||||
multisigCmd,
|
||||
netCmd,
|
||||
paychCmd,
|
||||
sendCmd,
|
||||
stateCmd,
|
||||
syncCmd,
|
||||
versionCmd,
|
||||
}
|
||||
|
||||
func withCategory(cat string, cmd *cli.Command) *cli.Command {
|
||||
cmd.Category = cat
|
||||
return cmd
|
||||
walletCmd,
|
||||
logCmd,
|
||||
waitApiCmd,
|
||||
}
|
||||
|
||||
+9
-82
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -494,12 +493,6 @@ var stateGetDealSetCmd = &cli.Command{
|
||||
var stateListMinersCmd = &cli.Command{
|
||||
Name: "list-miners",
|
||||
Usage: "list all miners in the network",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "sort-by",
|
||||
Usage: "criteria to sort miners by (none, num-deals)",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
@@ -519,26 +512,6 @@ var stateListMinersCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
switch cctx.String("sort-by") {
|
||||
case "num-deals":
|
||||
ndm, err := getDealsCounts(ctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(miners, func(i, j int) bool {
|
||||
return ndm[miners[i]] > ndm[miners[j]]
|
||||
})
|
||||
|
||||
for i := 0; i < 50 && i < len(miners); i++ {
|
||||
fmt.Printf("%s %d\n", miners[i], ndm[miners[i]])
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unrecognized sorting order")
|
||||
case "", "none":
|
||||
}
|
||||
|
||||
for _, m := range miners {
|
||||
fmt.Println(m.String())
|
||||
}
|
||||
@@ -547,22 +520,6 @@ var stateListMinersCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func getDealsCounts(ctx context.Context, lapi api.FullNode) (map[address.Address]int, error) {
|
||||
allDeals, err := lapi.StateMarketDeals(ctx, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[address.Address]int)
|
||||
for _, d := range allDeals {
|
||||
if d.State.SectorStartEpoch != -1 {
|
||||
out[d.Proposal.Provider]++
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var stateListActorsCmd = &cli.Command{
|
||||
Name: "list-actors",
|
||||
Usage: "list all actors in the network",
|
||||
@@ -935,7 +892,7 @@ var stateComputeStateCmd = &cli.Command{
|
||||
return c.Code, nil
|
||||
}
|
||||
|
||||
return computeStateHtml(ts, stout, getCode)
|
||||
return computeStateHtml(stout, getCode)
|
||||
}
|
||||
|
||||
fmt.Println("computed state cid: ", stout.Root)
|
||||
@@ -964,7 +921,7 @@ func codeStr(c cid.Cid) string {
|
||||
return string(cmh.Digest)
|
||||
}
|
||||
|
||||
func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||
func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||
fmt.Printf(`<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -990,10 +947,8 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>Tipset: <b>%s</b></div>
|
||||
<div>Height: %d</div>
|
||||
<div>State CID: <b>%s</b></div>
|
||||
<div>Calls</div>`, ts.Key(), ts.Height(), o.Root)
|
||||
<div>Calls</div>`, o.Root)
|
||||
|
||||
for _, ir := range o.Trace {
|
||||
toCode, err := getCode(ir.Msg.To)
|
||||
@@ -1082,15 +1037,12 @@ func printInternalExecutionsHtml(trace []*types.ExecutionResult, getCode func(ad
|
||||
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
|
||||
}
|
||||
|
||||
slow := im.Duration > 10*time.Millisecond
|
||||
veryslow := im.Duration > 50*time.Millisecond
|
||||
|
||||
fmt.Printf(`<div class="exec">
|
||||
<div><h4 class="call">%s:%s</h4></div>
|
||||
<div><b>%s</b> -> <b>%s</b> (%s FIL), M%d</div>
|
||||
%s
|
||||
<div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
|
||||
`, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, slow, veryslow, im.Duration, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret)
|
||||
<div><span class="exit%d">Exit: <b>%d</b></span>%s
|
||||
`, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret)
|
||||
if im.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, im.Error)
|
||||
}
|
||||
@@ -1161,39 +1113,14 @@ var stateWaitMsgCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := api.ChainGetMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message was executed in tipset: %s\n", mw.TipSet.Cids())
|
||||
fmt.Printf("Exit Code: %d\n", mw.Receipt.ExitCode)
|
||||
fmt.Printf("Gas Used: %d\n", mw.Receipt.GasUsed)
|
||||
fmt.Printf("Return: %x\n", mw.Receipt.Return)
|
||||
if err := printReceiptReturn(ctx, api, m, mw.Receipt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message was executed in tipset: %s", mw.TipSet.Cids())
|
||||
fmt.Printf("Exit Code: %d", mw.Receipt.ExitCode)
|
||||
fmt.Printf("Gas Used: %d", mw.Receipt.GasUsed)
|
||||
fmt.Printf("Return: %x", mw.Receipt.Return)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func printReceiptReturn(ctx context.Context, api api.FullNode, m *types.Message, r types.MessageReceipt) error {
|
||||
act, err := api.StateGetActor(ctx, m.To, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jret, err := jsonReturn(act.Code, m.Method, r.Return)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(jret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var stateSearchMsgCmd = &cli.Command{
|
||||
Name: "search-msg",
|
||||
Usage: "Search to see whether a message has appeared on chain",
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -67,11 +65,6 @@ var importBenchCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
bs := blockstore.NewBlockstore(bds)
|
||||
cbs, err := blockstore.CachedBlockstore(context.TODO(), bs, blockstore.DefaultCacheOpts())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bs = cbs
|
||||
ds := datastore.NewMapDatastore()
|
||||
cs := store.NewChainStore(bs, ds, vm.Syscalls(ffiwrapper.ProofVerifier))
|
||||
stm := stmgr.NewStateManager(cs)
|
||||
@@ -118,14 +111,7 @@ var importBenchCmd = &cli.Command{
|
||||
cur := tschain[i]
|
||||
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
|
||||
if cur.ParentState() != lastState {
|
||||
lastTrace := out[len(out)-1].Trace
|
||||
d, err := json.MarshalIndent(lastTrace, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("TRACE")
|
||||
fmt.Println(string(d))
|
||||
return xerrors.Errorf("tipset chain had state mismatch at height %d (%s != %s)", cur.Height(), cur.ParentState(), lastState)
|
||||
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
|
||||
}
|
||||
start := time.Now()
|
||||
st, trace, err := stm.ExecutionTrace(context.TODO(), cur)
|
||||
|
||||
+10
-5
@@ -527,11 +527,16 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, mid
|
||||
|
||||
if !skipc2 {
|
||||
svi := abi.SealVerifyInfo{
|
||||
SectorID: abi.SectorID{Miner: mid, Number: i},
|
||||
SealedCID: cids.Sealed,
|
||||
RegisteredProof: sb.SealProofType(),
|
||||
Proof: proof,
|
||||
DealIDs: nil,
|
||||
SectorID: abi.SectorID{Miner: mid, Number: i},
|
||||
OnChain: abi.OnChainSealVerifyInfo{
|
||||
SealedCID: cids.Sealed,
|
||||
InteractiveEpoch: seed.Epoch,
|
||||
RegisteredProof: sb.SealProofType(),
|
||||
Proof: proof,
|
||||
DealIDs: nil,
|
||||
SectorNumber: i,
|
||||
SealRandEpoch: 0,
|
||||
},
|
||||
Randomness: ticket,
|
||||
InteractiveRandomness: seed.Value,
|
||||
UnsealedCID: cids.Unsealed,
|
||||
|
||||
@@ -653,14 +653,6 @@ create temp table b (like blocks excluding constraints) on commit drop;
|
||||
eprof = bh.ElectionProof.VRFProof
|
||||
}
|
||||
|
||||
if bh.Ticket == nil {
|
||||
log.Warnf("got a block with nil ticket")
|
||||
|
||||
bh.Ticket = &types.Ticket{
|
||||
VRFProof: []byte{},
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := stmt2.Exec(
|
||||
bh.Cid().String(),
|
||||
bh.ParentWeight.String(),
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
</div>
|
||||
<div class="Index-node" id="formnd">
|
||||
<form id="f" action='/mkminer' method='POST'>
|
||||
<span>Enter owner/worker address:</span>
|
||||
<input type='text' name='address' style="width: 300px" placeholder="t3...">
|
||||
<span>Enter destination address:</span>
|
||||
<input type='text' name='address' style="width: 300px">
|
||||
<select name="sectorSize">
|
||||
<option selected value="34359738368">32GiB sectors</option>
|
||||
<option selected value="536870912">512MiB sectors</option>
|
||||
<option value="34359738368">32GiB sectors</option>
|
||||
<option value="68719476736">64GiB sectors</option>
|
||||
</select>
|
||||
<button type='submit'>Create Miner</button>
|
||||
@@ -27,9 +28,6 @@
|
||||
<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 class="Index-node">
|
||||
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://docs.lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
|
||||
@@ -8,16 +8,14 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
type CidWindow [][]cid.Cid
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
paramfetch "github.com/filecoin-project/go-paramfetch"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
|
||||
@@ -24,6 +22,8 @@ import (
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"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"
|
||||
"github.com/filecoin-project/lotus/lib/lotuslog"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
"github.com/filecoin-project/sector-storage"
|
||||
@@ -290,7 +290,7 @@ var runCmd = &cli.Command{
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
log.Warn("Shutting down...")
|
||||
log.Warn("Shutting down..")
|
||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||
log.Errorf("shutting down RPC server failed: %s", err)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
@@ -26,8 +26,6 @@ func main() {
|
||||
importCarCmd,
|
||||
commpToCidCmd,
|
||||
fetchParamCmd,
|
||||
proofsCmd,
|
||||
verifRegCmd,
|
||||
}
|
||||
|
||||
app := &cli.App{
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
var proofsCmd = &cli.Command{
|
||||
Name: "proofs",
|
||||
Subcommands: []*cli.Command{
|
||||
verifySealProofCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var verifySealProofCmd = &cli.Command{
|
||||
Name: "verify-seal",
|
||||
ArgsUsage: "<commr> <commd> <proof>",
|
||||
Description: "Verify a seal proof with manual inputs",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "ticket",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "proof-rand",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "miner",
|
||||
},
|
||||
&cli.Uint64Flag{
|
||||
Name: "sector-id",
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "proof-type",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 3 {
|
||||
return fmt.Errorf("must specify commR, commD, and proof to verify")
|
||||
}
|
||||
|
||||
commr, err := cid.Decode(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commd, err := cid.Decode(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proof, err := hex.DecodeString(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode hex proof input: %w", err)
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString(cctx.String("miner"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ticket, err := hex.DecodeString(cctx.String("ticket"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proofRand, err := hex.DecodeString(cctx.String("proof-rand"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
snum := abi.SectorNumber(cctx.Uint64("sector-id"))
|
||||
|
||||
ok, err := ffi.VerifySeal(abi.SealVerifyInfo{
|
||||
SectorID: abi.SectorID{
|
||||
Miner: abi.ActorID(mid),
|
||||
Number: snum,
|
||||
},
|
||||
SealedCID: commr,
|
||||
RegisteredProof: abi.RegisteredProof(cctx.Int64("proof-type")),
|
||||
Proof: proof,
|
||||
DealIDs: nil,
|
||||
Randomness: abi.SealRandomness(ticket),
|
||||
InteractiveRandomness: abi.InteractiveSealRandomness(proofRand),
|
||||
UnsealedCID: commd,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid proof")
|
||||
}
|
||||
|
||||
fmt.Println("proof valid!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apibstore"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
var verifRegCmd = &cli.Command{
|
||||
Name: "verifreg",
|
||||
Usage: "Interact with the verified registry actor",
|
||||
Flags: []cli.Flag{},
|
||||
Subcommands: []*cli.Command{
|
||||
verifRegAddVerifierCmd,
|
||||
verifRegVerifyClientCmd,
|
||||
verifRegListVerifiersCmd,
|
||||
verifRegListClientsCmd,
|
||||
verifRegCheckClientCmd,
|
||||
verifRegCheckVerifierCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegAddVerifierCmd = &cli.Command{
|
||||
Name: "add-verifier",
|
||||
Usage: "make a given account a verifier",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
fromk, err := address.NewFromString("t3qfoulel6fy6gn3hjmbhpdpf6fs5aqjb5fkurhtwvgssizq4jey5nw4ptq5up6h7jk7frdvvobv52qzmgjinq")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cctx.Args().Len() != 2 {
|
||||
return fmt.Errorf("must specify two arguments: address and allowance")
|
||||
}
|
||||
|
||||
target, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allowance, err := types.BigFromString(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&verifreg.AddVerifierParams{Address: target, Allowance: allowance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
msg := &types.Message{
|
||||
To: builtin.VerifiedRegistryActorAddr,
|
||||
From: fromk,
|
||||
Method: builtin.MethodsVerifiedRegistry.AddVerifier,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: 300000,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
|
||||
|
||||
mwait, err := api.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mwait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("failed to add verifier: %d", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegVerifyClientCmd = &cli.Command{
|
||||
Name: "verify-client",
|
||||
Usage: "make a given account a verified client",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "specify your verifier address to send the message from",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
froms := cctx.String("from")
|
||||
if froms == "" {
|
||||
return fmt.Errorf("must specify from address with --from")
|
||||
}
|
||||
|
||||
fromk, err := address.NewFromString(froms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cctx.Args().Len() != 2 {
|
||||
return fmt.Errorf("must specify two arguments: address and allowance")
|
||||
}
|
||||
|
||||
target, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allowance, err := types.BigFromString(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&verifreg.AddVerifiedClientParams{Address: target, Allowance: allowance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
msg := &types.Message{
|
||||
To: builtin.VerifiedRegistryActorAddr,
|
||||
From: fromk,
|
||||
Method: builtin.MethodsVerifiedRegistry.AddVerifiedClient,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: 300000,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
|
||||
|
||||
mwait, err := api.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mwait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("failed to add verified client: %d", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegListVerifiersCmd = &cli.Command{
|
||||
Name: "list-verifiers",
|
||||
Usage: "list all verifiers",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := apibstore.NewAPIBlockstore(api)
|
||||
cst := cbor.NewCborStore(apibs)
|
||||
|
||||
var st verifreg.State
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vh, err := hamt.LoadNode(ctx, cst, st.Verifiers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := vh.ForEach(ctx, func(k string, val interface{}) error {
|
||||
addr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dcap verifreg.DataCap
|
||||
|
||||
if err := dcap.UnmarshalCBOR(bytes.NewReader(val.(*cbg.Deferred).Raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %s\n", addr, dcap)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegListClientsCmd = &cli.Command{
|
||||
Name: "list-clients",
|
||||
Usage: "list all verified clients",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := apibstore.NewAPIBlockstore(api)
|
||||
cst := cbor.NewCborStore(apibs)
|
||||
|
||||
var st verifreg.State
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vh, err := hamt.LoadNode(ctx, cst, st.VerifiedClients)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := vh.ForEach(ctx, func(k string, val interface{}) error {
|
||||
addr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dcap verifreg.DataCap
|
||||
|
||||
if err := dcap.UnmarshalCBOR(bytes.NewReader(val.(*cbg.Deferred).Raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %s\n", addr, dcap)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegCheckClientCmd = &cli.Command{
|
||||
Name: "check-client",
|
||||
Usage: "check verified client remaining bytes",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify client address to check")
|
||||
}
|
||||
|
||||
caddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := apibstore.NewAPIBlockstore(api)
|
||||
cst := cbor.NewCborStore(apibs)
|
||||
|
||||
var st verifreg.State
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vh, err := hamt.LoadNode(ctx, cst, st.VerifiedClients)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dcap verifreg.DataCap
|
||||
if err := vh.Find(ctx, string(caddr.Bytes()), &dcap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(dcap)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegCheckVerifierCmd = &cli.Command{
|
||||
Name: "check-verifier",
|
||||
Usage: "check verifiers remaining bytes",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify verifier address to check")
|
||||
}
|
||||
|
||||
vaddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := apibstore.NewAPIBlockstore(api)
|
||||
cst := cbor.NewCborStore(apibs)
|
||||
|
||||
var st verifreg.State
|
||||
if err := cst.Get(ctx, act.Head, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vh, err := hamt.LoadNode(ctx, cst, st.Verifiers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dcap verifreg.DataCap
|
||||
if err := vh.Find(ctx, string(vaddr.Bytes()), &dcap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(dcap)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -98,23 +98,14 @@ var infoCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
nfaults, err := faults.Count()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("counting faults: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Sset), types.NewInt(uint64(mi.SectorSize)))))
|
||||
if nfaults == 0 {
|
||||
if len(faults) == 0 {
|
||||
fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset), types.NewInt(uint64(mi.SectorSize)))))
|
||||
} else {
|
||||
var faultyPercentage float64
|
||||
if secCounts.Sset != 0 {
|
||||
faultyPercentage = float64(10000*nfaults/secCounts.Sset) / 100.
|
||||
}
|
||||
fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n",
|
||||
types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset), types.NewInt(uint64(mi.SectorSize)))),
|
||||
types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))),
|
||||
faultyPercentage)
|
||||
types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset-uint64(len(faults))), types.NewInt(uint64(mi.SectorSize)))),
|
||||
types.SizeStr(types.BigMul(types.NewInt(uint64(len(faults))), types.NewInt(uint64(mi.SectorSize)))),
|
||||
float64(10000*uint64(len(faults))/secCounts.Pset)/100.)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
@@ -79,7 +79,7 @@ var initCmd = &cli.Command{
|
||||
&cli.StringFlag{
|
||||
Name: "sector-size",
|
||||
Usage: "specify sector size to use",
|
||||
Value: units.BytesSize(float64(build.DefaultSectorSize())),
|
||||
Value: fmt.Sprint(build.DefaultSectorSize()),
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "pre-sealed-sectors",
|
||||
@@ -362,7 +362,7 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, metadata string,
|
||||
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
size := binary.PutUvarint(buf, uint64(maxSectorID+1))
|
||||
return mds.Put(datastore.NewKey(modules.StorageCounterDSPrefix), buf[:size])
|
||||
return mds.Put(datastore.NewKey("/storage/nextid"), buf[:size])
|
||||
}
|
||||
|
||||
func findMarketDealID(ctx context.Context, api lapi.FullNode, deal market.DealProposal) (abi.DealID, error) {
|
||||
@@ -603,10 +603,7 @@ func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID,
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
ssize, err := units.RAMInBytes(cctx.String("sector-size"))
|
||||
if err != nil {
|
||||
return address.Undef, fmt.Errorf("failed to parse sector size: %w", err)
|
||||
}
|
||||
ssize := cctx.Uint64("sector-size")
|
||||
|
||||
worker := owner
|
||||
if cctx.String("worker") != "" {
|
||||
|
||||
@@ -27,7 +27,6 @@ func main() {
|
||||
initCmd,
|
||||
rewardsCmd,
|
||||
runCmd,
|
||||
stopCmd,
|
||||
sectorsCmd,
|
||||
storageCmd,
|
||||
setPriceCmd,
|
||||
|
||||
@@ -3,8 +3,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
@@ -12,6 +10,7 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"time"
|
||||
)
|
||||
|
||||
var provingCmd = &cli.Command{
|
||||
@@ -103,11 +102,6 @@ var provingInfoCmd = &cli.Command{
|
||||
provenSectors += c
|
||||
}
|
||||
|
||||
var faultPerc float64
|
||||
if provenSectors > 0 {
|
||||
faultPerc = float64(faults*10000/provenSectors) / 100
|
||||
}
|
||||
|
||||
fmt.Printf("Current Epoch: %d\n", cd.CurrentEpoch)
|
||||
fmt.Printf("Chain Period: %d\n", cd.CurrentEpoch/miner.WPoStProvingPeriod)
|
||||
fmt.Printf("Chain Period Start: %s\n", epochTime(cd.CurrentEpoch, (cd.CurrentEpoch/miner.WPoStProvingPeriod)*miner.WPoStProvingPeriod))
|
||||
@@ -117,7 +111,7 @@ var provingInfoCmd = &cli.Command{
|
||||
fmt.Printf("Proving Period Start: %s\n", epochTime(cd.CurrentEpoch, cd.PeriodStart))
|
||||
fmt.Printf("Next Period Start: %s\n\n", epochTime(cd.CurrentEpoch, cd.PeriodStart+miner.WPoStProvingPeriod))
|
||||
|
||||
fmt.Printf("Faults: %d (%.2f%%)\n", faults, faultPerc)
|
||||
fmt.Printf("Faults: %d (%.2f%%)\n", faults, float64(faults*10000/provenSectors)/100)
|
||||
fmt.Printf("Recovering: %d\n", recoveries)
|
||||
fmt.Printf("New Sectors: %d\n\n", newSectors)
|
||||
|
||||
|
||||
@@ -14,13 +14,12 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"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"
|
||||
"github.com/filecoin-project/lotus/lib/ulimit"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
@@ -100,12 +99,9 @@ var runCmd = &cli.Command{
|
||||
return xerrors.Errorf("repo at '%s' is not initialized, run 'lotus-storage-miner init' to set it up", storageRepoPath)
|
||||
}
|
||||
|
||||
shutdownChan := make(chan struct{})
|
||||
|
||||
var minerapi api.StorageMiner
|
||||
stop, err := node.New(ctx,
|
||||
node.StorageMiner(&minerapi),
|
||||
node.Override(new(dtypes.ShutdownChan), shutdownChan),
|
||||
node.Online(),
|
||||
node.Repo(r),
|
||||
|
||||
@@ -159,12 +155,8 @@ var runCmd = &cli.Command{
|
||||
|
||||
sigChan := make(chan os.Signal, 2)
|
||||
go func() {
|
||||
select {
|
||||
case <-sigChan:
|
||||
case <-shutdownChan:
|
||||
}
|
||||
|
||||
log.Warn("Shutting down...")
|
||||
<-sigChan
|
||||
log.Warn("Shutting down..")
|
||||
if err := stop(context.TODO()); err != nil {
|
||||
log.Errorf("graceful shutting down failed: %s", err)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "net/http/pprof"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
)
|
||||
|
||||
var stopCmd = &cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "Stop a running lotus storage miner",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
err = api.Shutdown(lcli.ReqContext(cctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
+1
-44
@@ -29,12 +29,10 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/peermgr"
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/modules/testing"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
@@ -45,26 +43,6 @@ const (
|
||||
preTemplateFlag = "genesis-template"
|
||||
)
|
||||
|
||||
var daemonStopCmd = &cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "Stop a running lotus daemon",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
err = api.Shutdown(lcli.ReqContext(cctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// DaemonCmd is the `go-lotus daemon` command
|
||||
var DaemonCmd = &cli.Command{
|
||||
Name: "daemon",
|
||||
@@ -108,10 +86,6 @@ var DaemonCmd = &cli.Command{
|
||||
Name: "pprof",
|
||||
Usage: "specify name of file for writing cpu profile to",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "profile",
|
||||
Usage: "specify type of node",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if prof := cctx.String("pprof"); prof != "" {
|
||||
@@ -126,16 +100,6 @@ var DaemonCmd = &cli.Command{
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
var isBootstrapper dtypes.Bootstrapper
|
||||
switch profile := cctx.String("profile"); profile {
|
||||
case "bootstrapper":
|
||||
isBootstrapper = true
|
||||
case "":
|
||||
// do nothing
|
||||
default:
|
||||
return fmt.Errorf("unrecognized profile type: %q", profile)
|
||||
}
|
||||
|
||||
ctx, _ := tag.New(context.Background(), tag.Insert(metrics.Version, build.BuildVersion), tag.Insert(metrics.Commit, build.CurrentCommit))
|
||||
{
|
||||
dir, err := homedir.Expand(cctx.String("repo"))
|
||||
@@ -191,15 +155,11 @@ var DaemonCmd = &cli.Command{
|
||||
genesis = node.Override(new(modules.Genesis), testing.MakeGenesis(cctx.String(makeGenFlag), cctx.String(preTemplateFlag)))
|
||||
}
|
||||
|
||||
shutdownChan := make(chan struct{})
|
||||
|
||||
var api api.FullNode
|
||||
|
||||
stop, err := node.New(ctx,
|
||||
node.FullAPI(&api),
|
||||
|
||||
node.Override(new(dtypes.Bootstrapper), isBootstrapper),
|
||||
node.Override(new(dtypes.ShutdownChan), shutdownChan),
|
||||
node.Online(),
|
||||
node.Repo(r),
|
||||
|
||||
@@ -245,10 +205,7 @@ var DaemonCmd = &cli.Command{
|
||||
}
|
||||
|
||||
// TODO: properly parse api endpoint (or make it a URL)
|
||||
return serveRPC(api, stop, endpoint, shutdownChan)
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
daemonStopCmd,
|
||||
return serveRPC(api, stop, endpoint)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+13
-19
@@ -9,6 +9,14 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/lib/auth"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
@@ -16,19 +24,11 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/prometheus"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
var log = logging.Logger("main")
|
||||
|
||||
func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shutdownCh <-chan struct{}) error {
|
||||
func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr) error {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", apistruct.PermissionedFullAPI(a))
|
||||
|
||||
@@ -62,23 +62,17 @@ func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shut
|
||||
|
||||
srv := &http.Server{Handler: http.DefaultServeMux}
|
||||
|
||||
sigCh := make(chan os.Signal, 2)
|
||||
sigChan := make(chan os.Signal, 2)
|
||||
go func() {
|
||||
select {
|
||||
case <-sigCh:
|
||||
case <-shutdownCh:
|
||||
}
|
||||
|
||||
log.Warn("Shutting down...")
|
||||
<-sigChan
|
||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||
log.Errorf("shutting down RPC server failed: %s", err)
|
||||
}
|
||||
if err := stop(context.TODO()); err != nil {
|
||||
log.Errorf("graceful shutting down failed: %s", err)
|
||||
}
|
||||
log.Warn("Graceful shutdown successful")
|
||||
}()
|
||||
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
|
||||
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
return srv.Serve(manet.NetListener(lst))
|
||||
}
|
||||
@@ -89,7 +83,7 @@ func handleImport(a *impl.FullNodeAPI) func(w http.ResponseWriter, r *http.Reque
|
||||
w.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
if !auth.HasPerm(r.Context(), nil, apistruct.PermWrite) {
|
||||
if !apistruct.HasPerm(r.Context(), apistruct.PermWrite) {
|
||||
w.WriteHeader(401)
|
||||
json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing write permission"})
|
||||
return
|
||||
|
||||
@@ -56,12 +56,6 @@
|
||||
"github": "en/join-testnet.md",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"title": "Use Lotus with systemd",
|
||||
"slug": "en+install-system-services",
|
||||
"github": "en/install-system-services.md",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"title": "Setup Troubleshooting",
|
||||
"slug": "en+setup-troubleshooting",
|
||||
@@ -143,7 +137,7 @@
|
||||
"value": null,
|
||||
"posts": [
|
||||
{
|
||||
"title": "Remote API Support",
|
||||
"title": "API Scripting Support",
|
||||
"slug": "en+api-scripting-support",
|
||||
"github": "en/api-scripting-support.md",
|
||||
"value": null
|
||||
@@ -169,6 +163,13 @@
|
||||
"value": null,
|
||||
"posts": []
|
||||
},
|
||||
{
|
||||
"title": "Pond UI",
|
||||
"slug": "en+dev-tools-pond-ui",
|
||||
"github": "en/dev-tools-pond-ui.md",
|
||||
"value": null,
|
||||
"posts": []
|
||||
},
|
||||
{
|
||||
"title": "Jaeger Tracing",
|
||||
"slug": "en+dev-tools-jaeger-tracing",
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
# Remote API Support
|
||||
# API Scripting Support
|
||||
|
||||
You may want to delegate the work **Lotus Storage Miner** or **Lotus Node** performs to other machines.
|
||||
Here is how to setup the necessary authorization and environment variables.
|
||||
You may want to delegate the work **Lotus Storage Miner** or **Lotus Node** perform to other machines. Here is how to setup the necessary authorization and environment variables.
|
||||
|
||||
## Generate a JWT
|
||||
|
||||
To generate a JWT for your environment variables, use this command:
|
||||
|
||||
```sh
|
||||
lotus auth create-token --perm admin
|
||||
lotus-storage-miner auth create-token --perm admin
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
Environmental variables are variables that are defined for the current shell and are inherited by any child shells or processes. Environmental variables are used to pass information into processes that are spawned from the shell.
|
||||
|
||||
Using the [JWT you generated](https://lotu.sh/en+api#how-do-i-generate-a-token-18865), you can assign it and the **multiaddr** to the appropriate environment variable.
|
||||
Using the JWT you generated, you can assign it and the **multiaddr** to the appropriate environment variable.
|
||||
|
||||
```sh
|
||||
# Lotus Node
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Here is an early overview of how to make API calls.
|
||||
|
||||
Implementation details for the **JSON-RPC** package are [here](https://github.com/filecoin-project/go-jsonrpc).
|
||||
Implementation details for the **JSON-RPC** package are [here](https://github.com/filecoin-project/lotus/tree/master/lib/jsonrpc).
|
||||
|
||||
## Overview: How do you modify the config.toml to change the API endpoint?
|
||||
|
||||
@@ -77,7 +77,7 @@ lotus-storage-miner auth create-token --perm admin
|
||||
|
||||
## What authorization level should I use?
|
||||
|
||||
When viewing [api/apistruct/struct.go](https://github.com/filecoin-project/lotus/blob/master/api/apistruct/struct.go), you will encounter these types:
|
||||
When viewing [api/struct.go](https://github.com/filecoin-project/lotus/blob/master/api/struct.go), you will encounter these types:
|
||||
|
||||
- `read` - Read node state, no private data.
|
||||
- `write` - Write to local store / chain, and `read` permissions.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Developer Tools
|
||||
# Development Tools
|
||||
|
||||
> Running a local network can be a great way to understand how Lotus works and test your setup.
|
||||
> This page is a work in progress
|
||||
|
||||
@@ -73,7 +73,7 @@ Information on how to send a `cURL` request to the JSON-RPC API can be found
|
||||
### What are the requests I can send over the JSON-RPC API?
|
||||
|
||||
Please have a look at the
|
||||
[source code](https://github.com/filecoin-project/lotus/blob/master/api/api_full.go)
|
||||
[source code](https://github.com/filecoin-project/lotus/blob/master/api/api_common.go)
|
||||
for a list of methods supported by the JSON-RPC API.
|
||||
## The Test Network
|
||||
|
||||
@@ -88,6 +88,12 @@ community for testing purposes.
|
||||
Nothing at all! Real-world incentives may be provided in a future phase of Testnet, but this is
|
||||
yet to be confirmed.
|
||||
|
||||
### Will there be future phases of Testnet?
|
||||
|
||||
Yes, there will be at least one more phase of Testnet. We plan on introducing interoperable
|
||||
[go-filecoin nodes](https://github.com/filecoin-project/go-filecoin#filecoin-go-filecoin)
|
||||
in a future phase.
|
||||
|
||||
### How can I see the status of Testnet?
|
||||
|
||||
The [dashboard](https://stats.testnet.filecoin.io/) displays the status of the network as
|
||||
@@ -130,9 +136,4 @@ You can do so by changing the storage path variable for the second miner, e.g.,
|
||||
|
||||
### How do I setup my own local devnet?
|
||||
|
||||
Follow the instructions found [here](https://lotu.sh/en+setup-local-dev-net).
|
||||
|
||||
### Are there any other implementations of Filecoin?
|
||||
|
||||
Yes! Check out the [go-filecoin](https://github.com/filecoin-project/go-filecoin#filecoin-go-filecoin)
|
||||
implementation, which is fully interoperable with Lotus!
|
||||
Follow the instructions found [here](https://lotu.sh/en+setup-local-dev-net).
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Lotus is an implementation of the **Filecoin Distributed Storage Network**. You can run the Lotus software client to join the **Filecoin Testnet**.
|
||||
|
||||
For more details about Filecoin, check out the [Filecoin Docs](https://docs.filecoin.io) and [Filecoin Spec](https://filecoin-project.github.io/specs/).
|
||||
For more details about Filecoin, check out the [Filecoin Docs](https://docs.filecoin.io) and [Filecoin Spec](https://github.com/filecoin-project/specs).
|
||||
|
||||
## What can I learn here?
|
||||
|
||||
@@ -11,7 +11,7 @@ For more details about Filecoin, check out the [Filecoin Docs](https://docs.file
|
||||
- [Storing](https://docs.lotu.sh/en+storing-data) or [retrieving](https://docs.lotu.sh/en+retrieving-data) data.
|
||||
- Mining Filecoin using the **Lotus Storage Miner** in your [CLI](https://docs.lotu.sh/en+mining).
|
||||
|
||||
## How is Lotus designed?
|
||||
## What makes Lotus different?
|
||||
|
||||
Lotus is architected modularly to keep clean API boundaries while using the same process. Installing Lotus will include two separate programs:
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Use Lotus with systemd
|
||||
|
||||
Lotus is capable of running as a systemd service daemon. You can find installable service files for systemd in the [lotus repo scripts directory](https://github.com/filecoin-project/lotus/tree/master/scripts) as files with `.service` extension. In order to install these service files, you can copy these `.service` files to the default systemd service path.
|
||||
|
||||
## Installing via `make`
|
||||
|
||||
NOTE: Before using lotus and lotus-miner as systemd services, don't forget to `sudo make install` to ensure the binaries are accessible by the root user.
|
||||
|
||||
If your host uses the default systemd service path, it can be installed with `sudo make install-services`:
|
||||
|
||||
```sh
|
||||
$ sudo make install-services
|
||||
```
|
||||
|
||||
## Interacting with service logs
|
||||
|
||||
Logs from the services can be reviewed using `journalctl`.
|
||||
|
||||
### Follow logs from a specific service unit
|
||||
|
||||
```sh
|
||||
$ sudo journalctl -u lotus-daemon -f
|
||||
```
|
||||
|
||||
### View logs in reverse order
|
||||
|
||||
```sh
|
||||
$ sudo journalctl -u lotus-miner -r
|
||||
```
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
Anyone can set up a **Lotus Node** and connect to the **Lotus Testnet**. This is the best way to explore the current CLI and the **Filecoin Decentralized Storage Market**.
|
||||
|
||||
If you have installed older versions, you may need to clear existing chain data, stored wallets and miners if you run into any errors. You can use this command:
|
||||
|
||||
```sh
|
||||
rm -rf ~/.lotus ~/.lotusstorage
|
||||
```
|
||||
|
||||
## Note: Using the Lotus Node from China
|
||||
|
||||
If you are trying to use `lotus` from China. You should set this **environment variable** on your machine:
|
||||
@@ -55,7 +61,7 @@ Here is an example of the response:
|
||||
t1aswwvjsae63tcrniz6x5ykvsuotlgkvlulnqpsi
|
||||
```
|
||||
|
||||
- Visit the [faucet](https://faucet.testnet.filecoin.io) to add funds.
|
||||
- Visit the [faucet](https://lotus-faucet.kittyhawk.wtf/funds.html) to add funds.
|
||||
- Paste the address you created.
|
||||
- Press the send button.
|
||||
|
||||
@@ -71,7 +77,7 @@ You will not see any attoFIL in your wallet if your **chain** is not fully synce
|
||||
|
||||
## Send FIL to another wallet
|
||||
|
||||
To send FIL to another wallet from your default account, use this command:
|
||||
To send FIL to another wallet, use this command:
|
||||
|
||||
```
|
||||
lotus send <target> <amount>
|
||||
|
||||
@@ -14,7 +14,7 @@ Download the 2048 byte parameters:
|
||||
Pre-seal some sectors:
|
||||
|
||||
```sh
|
||||
./lotus-seed pre-seal --sector-size 2KiB --num-sectors 2
|
||||
./lotus-seed pre-seal --sector-size 2048 --num-sectors 2
|
||||
```
|
||||
|
||||
Create the genesis block and start up the first node:
|
||||
@@ -34,7 +34,7 @@ Then, in another console, import the genesis miner key:
|
||||
Set up the genesis miner:
|
||||
|
||||
```sh
|
||||
./lotus-storage-miner init --genesis-miner --actor=t01000 --sector-size=2KiB --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync
|
||||
./lotus-storage-miner init --genesis-miner --actor=t01000 --sector-size=2048 --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync
|
||||
```
|
||||
|
||||
Now, finally, start up the miner:
|
||||
|
||||
@@ -12,6 +12,8 @@ lotus-storage-miner set-price <price>
|
||||
This command will set up your miner to accept deal proposals that meet the input price.
|
||||
The price is inputted in FIL per GiB per epoch, and the default is 0.0000000005.
|
||||
|
||||
<!-- TODO: Add info about setting min piece size, max piece size, duration -->
|
||||
|
||||
## Ensure you can be discovered
|
||||
|
||||
Clients need to be able to find you in order to make storage deals with you.
|
||||
|
||||
@@ -51,7 +51,7 @@ If you suspect that your GPU is not being used, first make sure it is properly c
|
||||
First, to watch GPU utilization run `nvtop` in one terminal, then in a separate terminal, run:
|
||||
|
||||
```sh
|
||||
lotus-bench --sector-size=2KiB
|
||||
lotus-bench --sector-size=2048
|
||||
```
|
||||
|
||||
This process uses a fair amount of GPU, and generally takes ~4 minutes to complete. If you do not see any activity in nvtop from lotus during the entire process, it is likely something is misconfigured with your GPU.
|
||||
|
||||
@@ -28,7 +28,7 @@ lotus wallet new bls
|
||||
|
||||
With your wallet address:
|
||||
|
||||
- Visit the [faucet](https://faucet.testnet.filecoin.io)
|
||||
- Visit the [faucet](https://lotus-faucet.kittyhawk.wtf/miner.html)
|
||||
- Click "Create Miner"
|
||||
- DO NOT REFRESH THE PAGE. THIS OPERATION CAN TAKE SOME TIME.
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# Why does Filecoin mining work best on AMD?
|
||||
Currently, Filecoin's Proof of Replication (PoRep) prefers to be run on AMD
|
||||
processors. More accurately, it runs much much slower on Intel CPUs (it runs
|
||||
competitively fast on some ARM processors, like the ones in newer Samsung
|
||||
phones, but they lack the RAM to seal the larger sector sizes). The main reason
|
||||
that we see this benefit on AMD processors is due to their implementation of
|
||||
the SHA hardware instructions. Now, why do we use the SHA instruction?
|
||||
|
||||
## PoRep security assumptions
|
||||
Our research team has two different models for the security of Proofs of
|
||||
Replication. These are the Latency Assumption, and the Cost Assumption. These
|
||||
assumptions are arguments for why an attacker cannot pull off a 'regeneration
|
||||
attack'. That is, the attacker cannot seal and commit random data (generated by
|
||||
a function), delete it, and then reseal it on the fly to respond to PoSt
|
||||
challenges, without actually storing the data for that time period.
|
||||
|
||||
### Cost Assumptions
|
||||
The cost assumption states that the real money cost (hardware, electricity,
|
||||
etc) of generating a sector is higher than the real money cost of simply
|
||||
storing it on disks. NSE is a new PoRep our research team is working on that is
|
||||
based on the cost assumption, and is thus able to be very parallelizable (In
|
||||
comparison to schemes based on a latency assumption, as will be explained
|
||||
next). However, cost assumptions vary greatly with available and hypothetical
|
||||
hardware. For example, someone making an ASIC for NSE could break the cost
|
||||
assumption by lowering the cost of sealing too much. This is one of our main
|
||||
hesitations around shipping NSE.
|
||||
|
||||
### Latency Assumptions
|
||||
A Proof of Replication that is secure under a latency assumption is secure
|
||||
because an attacker cannot regenerate the data in time. We use this assumption
|
||||
for SDR, where we assume that an attacker cannot regenerate enough of a sector
|
||||
fast enough to respond to a PoSt. The way we achieve this is through the use
|
||||
of depth-robust graphs. Without going into too much detail, depth-robust
|
||||
graphs guarantee a minimum number of serial operations to compute an encoding
|
||||
based on the graph. Each edge in the graph represents an operation we need to
|
||||
perform. We thus have a guarantee that someone has to perform some operation
|
||||
N times in a row in order to compute the encoding. That means that the
|
||||
computation of the encoding must take at least as long as N times the fastest
|
||||
someone can do that operation.
|
||||
|
||||
Now, to make this secure, we need to choose an operation that can't be made
|
||||
much faster. There are many potential candidates here, depending on what
|
||||
hardware you want to require. We opted not to require ASICs in order to mine
|
||||
Filecoin, so that limits our choices severely. We have to look at what
|
||||
operations CPUs are really good at. One candidate was AES encryption, which
|
||||
also has hardware instructions. However, the difference between the performance
|
||||
of CPU AES instructions, and the hypothetical 'best' performance you get was
|
||||
still too great. This gap is generally called 'Amax', an attacker’s maximum
|
||||
advantage. The higher the Amax of an algorithm we choose, the more expensive
|
||||
the overall process has to become in order to bound how fast the attacker could
|
||||
do it.
|
||||
As we were doing our research, we noticed that AMD shipped their new processors
|
||||
with a builtin SHA function, and we looked into how fast someone could possibly
|
||||
compute a SHA hash. We found that AMD’s implementation is only around 3 times
|
||||
slower than anyone could reasonably do (given estimates by the hardware
|
||||
engineers at [Supranational](https://www.supranational.net/) ). This is
|
||||
incredibly impressive for something you can get in consumer hardware. With
|
||||
this, we were able to make SDR sealing reasonably performant for people with
|
||||
off-the-shelf hardware.
|
||||
|
||||
## Super Optimized CPUs
|
||||
|
||||
Given all of the above, with a latency assumption that we're basing our proofs
|
||||
on right now, you need a processor that can do iterated SHA hashes really fast.
|
||||
As mentioned earlier, this isn’t just AMD processors, but many ARM processors
|
||||
also have support for this. Hopefully, new Intel processors also follow suit.
|
||||
But for now, Filecoin works best on AMD processors.
|
||||
|
||||
|
||||
|
||||
@@ -29,4 +29,15 @@ ERROR hello hello/hello.go:81 other peer has different genesis!
|
||||
- repo is already locked
|
||||
```
|
||||
|
||||
- You already have another lotus daemon running.
|
||||
- You already have another lotus deamon running.
|
||||
|
||||
## Warning: get message get failed
|
||||
|
||||
Some errors will occur that do not prevent Lotus from working:
|
||||
|
||||
```sh
|
||||
ERROR chainstore store/store.go:564 get message get failed: <Data CID>: blockstore: block not found
|
||||
|
||||
```
|
||||
|
||||
- Someone is requesting a **Data CID** from you that you don't have.
|
||||
|
||||
@@ -55,7 +55,7 @@ Check the status of a deal:
|
||||
lotus client list-deals
|
||||
```
|
||||
|
||||
- The `duration`, which represents how long the miner will keep your file hosted, is represented in blocks. Each block represents 25 seconds.
|
||||
- The `duration`, which represents how long the miner will keep your file hosted, is represented in blocks. Each block represents 45 seconds.
|
||||
|
||||
Upon success, this command will return a **Deal CID**.
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
Lotus supports making deals with data stored in IPFS, without having to re-import it into lotus.
|
||||
|
||||
To enable this integration, you need to have an IPFS daemon running in the background.
|
||||
Then, open up `~/.lotus/config.toml` (or if you manually set `LOTUS_PATH`, look under that directory)
|
||||
and look for the Client field, and set `UseIpfs` to `true`.
|
||||
To enable this integration, open up `~/.lotus/config.toml` (Or if you manually set `LOTUS_PATH`, look under that directory) and look for the Client field, and set `UseIpfs` to `true`.
|
||||
|
||||
```toml
|
||||
[Client]
|
||||
|
||||
@@ -8,4 +8,14 @@ git pull origin master
|
||||
|
||||
# clean and remake the binaries
|
||||
make clean && make build
|
||||
```
|
||||
```
|
||||
|
||||
Sometimes when you run Lotus after a pull, certain commands such as `lotus daemon` may break.
|
||||
|
||||
Here is a command that will delete your chain data, stored wallets and any miners you have set up:
|
||||
|
||||
```sh
|
||||
rm -rf ~/.lotus ~/.lotusstorage
|
||||
```
|
||||
|
||||
This command usually resolves any issues with running `lotus` commands but it is not always required for updates. We will share information about when resetting your chain data and miners is required for an update in the future.
|
||||
|
||||
Vendored
+1
-1
Submodule extern/filecoin-ffi updated: 6109b6ad2f...e297d672ba
Vendored
+1
-1
Submodule extern/serialization-vectors updated: 0cef69d481...1e778d5bd7
@@ -8,13 +8,13 @@ require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/GeertJohan/go.rice v1.0.0
|
||||
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.0.0
|
||||
github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/drand/drand v0.8.2-0.20200518165838-d61135e6e2c8
|
||||
github.com/drand/drand v0.8.1
|
||||
github.com/fatih/color v1.8.0
|
||||
github.com/filecoin-project/chain-validation v0.0.6-0.20200526171800-c56c1882dc99
|
||||
github.com/filecoin-project/filecoin-ffi v0.26.1-0.20200508175440-05b30afeb00d
|
||||
github.com/filecoin-project/chain-validation v0.0.6-0.20200512234642-6304037e1db6
|
||||
github.com/filecoin-project/filecoin-ffi v0.0.0-20200427223233-a0014b17f124
|
||||
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
|
||||
github.com/filecoin-project/go-amt-ipld/v2 v2.0.1-0.20200424220931-6263827e49f2
|
||||
github.com/filecoin-project/go-bitfield v0.0.1
|
||||
@@ -22,18 +22,16 @@ require (
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
|
||||
github.com/filecoin-project/go-data-transfer v0.3.0
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5
|
||||
github.com/filecoin-project/go-fil-markets v0.2.7
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
|
||||
github.com/filecoin-project/go-fil-markets v0.2.3
|
||||
github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6
|
||||
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200505180321-973f8949ea8e
|
||||
github.com/filecoin-project/go-statestore v0.1.0
|
||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
||||
github.com/filecoin-project/sector-storage v0.0.0-20200522011946-a59ca7536a95
|
||||
github.com/filecoin-project/specs-actors v0.5.4-0.20200521014528-0df536f7e461
|
||||
github.com/filecoin-project/sector-storage v0.0.0-20200509005126-ebc27d314ba4
|
||||
github.com/filecoin-project/specs-actors v0.5.2
|
||||
github.com/filecoin-project/specs-storage v0.0.0-20200417134612-61b2d91a6102
|
||||
github.com/filecoin-project/storage-fsm v0.0.0-20200522010518-83fd743db8bc
|
||||
github.com/filecoin-project/storage-fsm v0.0.0-20200427182014-01487d5ad3c8
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
@@ -61,43 +59,42 @@ require (
|
||||
github.com/ipfs/go-ipld-cbor v0.0.5-0.20200428170625-a0bd04d3cbdf
|
||||
github.com/ipfs/go-ipld-format v0.2.0
|
||||
github.com/ipfs/go-log v1.0.4
|
||||
github.com/ipfs/go-log/v2 v2.0.8
|
||||
github.com/ipfs/go-log/v2 v2.0.5
|
||||
github.com/ipfs/go-merkledag v0.3.1
|
||||
github.com/ipfs/go-path v0.0.7
|
||||
github.com/ipfs/go-unixfs v0.2.4
|
||||
github.com/ipfs/interface-go-ipfs-core v0.2.3
|
||||
github.com/ipld/go-car v0.1.1-0.20200430185908-8ff2e52a4c88
|
||||
github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/libp2p/go-eventbus v0.2.1
|
||||
github.com/libp2p/go-libp2p v0.9.4
|
||||
github.com/libp2p/go-libp2p-connmgr v0.2.4
|
||||
github.com/libp2p/go-libp2p-core v0.5.7
|
||||
github.com/libp2p/go-eventbus v0.1.0
|
||||
github.com/libp2p/go-libp2p v0.8.1
|
||||
github.com/libp2p/go-libp2p-circuit v0.2.1
|
||||
github.com/libp2p/go-libp2p-connmgr v0.1.1
|
||||
github.com/libp2p/go-libp2p-core v0.5.3
|
||||
github.com/libp2p/go-libp2p-discovery v0.4.0
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.8.1
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.7.6
|
||||
github.com/libp2p/go-libp2p-mplex v0.2.3
|
||||
github.com/libp2p/go-libp2p-peer v0.2.0
|
||||
github.com/libp2p/go-libp2p-peerstore v0.2.4
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.2
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.5.0
|
||||
github.com/libp2p/go-libp2p-peerstore v0.2.3
|
||||
github.com/libp2p/go-libp2p-pubsub v0.2.7-0.20200505181014-5bbe37191afb
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.1.1
|
||||
github.com/libp2p/go-libp2p-record v0.1.2
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.2.1
|
||||
github.com/libp2p/go-libp2p-secio v0.2.2
|
||||
github.com/libp2p/go-libp2p-swarm v0.2.6
|
||||
github.com/libp2p/go-libp2p-swarm v0.2.3
|
||||
github.com/libp2p/go-libp2p-tls v0.1.3
|
||||
github.com/libp2p/go-libp2p-yamux v0.2.8
|
||||
github.com/libp2p/go-maddr-filter v0.1.0
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/libp2p/go-libp2p-yamux v0.2.7
|
||||
github.com/libp2p/go-maddr-filter v0.0.5
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/multiformats/go-base32 v0.0.3
|
||||
github.com/multiformats/go-multiaddr v0.2.2
|
||||
github.com/multiformats/go-multiaddr v0.2.1
|
||||
github.com/multiformats/go-multiaddr-dns v0.2.0
|
||||
github.com/multiformats/go-multiaddr-net v0.1.5
|
||||
github.com/multiformats/go-multihash v0.0.13
|
||||
github.com/opentracing/opentracing-go v1.1.0
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200504204219-64967432584d
|
||||
@@ -107,16 +104,17 @@ require (
|
||||
go.uber.org/dig v1.8.0 // indirect
|
||||
go.uber.org/fx v1.9.0
|
||||
go.uber.org/multierr v1.5.0
|
||||
go.uber.org/zap v1.15.0
|
||||
go4.org v0.0.0-20190313082347-94abd6928b1d // indirect
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
|
||||
go.uber.org/zap v1.14.1
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
|
||||
golang.org/x/sys v0.0.0-20200427175716-29b57079015a
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
|
||||
gotest.tools v2.2.0+incompatible
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect
|
||||
)
|
||||
|
||||
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
|
||||
|
||||
replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi
|
||||
|
||||
replace github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
)
|
||||
|
||||
var log = logging.Logger("auth")
|
||||
|
||||
type Handler struct {
|
||||
Verify func(ctx context.Context, token string) ([]api.Permission, error)
|
||||
Next http.HandlerFunc
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
token := r.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
token = r.FormValue("token")
|
||||
if token != "" {
|
||||
token = "Bearer " + token
|
||||
}
|
||||
}
|
||||
|
||||
if token != "" {
|
||||
if !strings.HasPrefix(token, "Bearer ") {
|
||||
log.Warn("missing Bearer prefix in auth header")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
|
||||
allow, err := h.Verify(ctx, token)
|
||||
if err != nil {
|
||||
log.Warnf("JWT Verification failed: %s", err)
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = apistruct.WithPerm(ctx, allow)
|
||||
}
|
||||
|
||||
h.Next(w, r.WithContext(ctx))
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/multiformats/go-multihash"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
@@ -36,18 +35,6 @@ func NewIpfsBstore(ctx context.Context) (*IpfsBstore, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewRemoteIpfsBstore(ctx context.Context, maddr multiaddr.Multiaddr) (*IpfsBstore, error) {
|
||||
api, err := httpapi.NewApi(maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting remote ipfs api: %w", err)
|
||||
}
|
||||
|
||||
return &IpfsBstore{
|
||||
ctx: ctx,
|
||||
api: api,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *IpfsBstore) DeleteBlock(cid cid.Cid) error {
|
||||
return xerrors.Errorf("not supported")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
methodRetryFrequency = time.Second * 3
|
||||
)
|
||||
|
||||
var (
|
||||
errorType = reflect.TypeOf(new(error)).Elem()
|
||||
contextType = reflect.TypeOf(new(context.Context)).Elem()
|
||||
|
||||
log = logging.Logger("rpc")
|
||||
)
|
||||
|
||||
// ErrClient is an error which occurred on the client side the library
|
||||
type ErrClient struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *ErrClient) Error() string {
|
||||
return fmt.Sprintf("RPC client error: %s", e.err)
|
||||
}
|
||||
|
||||
// Unwrap unwraps the actual error
|
||||
func (e *ErrClient) Unwrap(err error) error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type clientResponse struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
Error *respError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type makeChanSink func() (context.Context, func([]byte, bool))
|
||||
|
||||
type clientRequest struct {
|
||||
req request
|
||||
ready chan clientResponse
|
||||
|
||||
// retCh provides a context and sink for handling incoming channel messages
|
||||
retCh makeChanSink
|
||||
}
|
||||
|
||||
// ClientCloser is used to close Client from further use
|
||||
type ClientCloser func()
|
||||
|
||||
// NewClient creates new jsonrpc 2.0 client
|
||||
//
|
||||
// handler must be pointer to a struct with function fields
|
||||
// Returned value closes the client connection
|
||||
// TODO: Example
|
||||
func NewClient(addr string, namespace string, handler interface{}, requestHeader http.Header) (ClientCloser, error) {
|
||||
return NewMergeClient(addr, namespace, []interface{}{handler}, requestHeader)
|
||||
}
|
||||
|
||||
type client struct {
|
||||
namespace string
|
||||
|
||||
requests chan clientRequest
|
||||
exiting <-chan struct{}
|
||||
idCtr int64
|
||||
}
|
||||
|
||||
// NewMergeClient is like NewClient, but allows to specify multiple structs
|
||||
// to be filled in the same namespace, using one connection
|
||||
func NewMergeClient(addr string, namespace string, outs []interface{}, requestHeader http.Header, opts ...Option) (ClientCloser, error) {
|
||||
var config Config
|
||||
for _, o := range opts {
|
||||
o(&config)
|
||||
}
|
||||
|
||||
connFactory := func() (*websocket.Conn, error) {
|
||||
conn, _, err := websocket.DefaultDialer.Dial(addr, requestHeader)
|
||||
return conn, err
|
||||
}
|
||||
|
||||
if config.proxyConnFactory != nil {
|
||||
connFactory = config.proxyConnFactory(connFactory)
|
||||
}
|
||||
|
||||
conn, err := connFactory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := client{
|
||||
namespace: namespace,
|
||||
}
|
||||
|
||||
stop := make(chan struct{})
|
||||
exiting := make(chan struct{})
|
||||
c.requests = make(chan clientRequest)
|
||||
c.exiting = exiting
|
||||
|
||||
handlers := map[string]rpcHandler{}
|
||||
go (&wsConn{
|
||||
conn: conn,
|
||||
connFactory: connFactory,
|
||||
reconnectInterval: config.ReconnectInterval,
|
||||
handler: handlers,
|
||||
requests: c.requests,
|
||||
stop: stop,
|
||||
exiting: exiting,
|
||||
}).handleWsConn(context.TODO())
|
||||
|
||||
for _, handler := range outs {
|
||||
htyp := reflect.TypeOf(handler)
|
||||
if htyp.Kind() != reflect.Ptr {
|
||||
return nil, xerrors.New("expected handler to be a pointer")
|
||||
}
|
||||
typ := htyp.Elem()
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return nil, xerrors.New("handler should be a struct")
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(handler)
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
fn, err := c.makeRpcFunc(typ.Field(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val.Elem().Field(i).Set(fn)
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
close(stop)
|
||||
<-exiting
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *client) makeOutChan(ctx context.Context, ftyp reflect.Type, valOut int) (func() reflect.Value, makeChanSink) {
|
||||
retVal := reflect.Zero(ftyp.Out(valOut))
|
||||
|
||||
chCtor := func() (context.Context, func([]byte, bool)) {
|
||||
// unpack chan type to make sure it's reflect.BothDir
|
||||
ctyp := reflect.ChanOf(reflect.BothDir, ftyp.Out(valOut).Elem())
|
||||
ch := reflect.MakeChan(ctyp, 0) // todo: buffer?
|
||||
chCtx, chCancel := context.WithCancel(ctx)
|
||||
retVal = ch.Convert(ftyp.Out(valOut))
|
||||
|
||||
buf := (&list.List{}).Init()
|
||||
var bufLk sync.Mutex
|
||||
|
||||
return ctx, func(result []byte, ok bool) {
|
||||
if !ok {
|
||||
chCancel()
|
||||
// remote channel closed, close ours too
|
||||
ch.Close()
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.New(ftyp.Out(valOut).Elem())
|
||||
if err := json.Unmarshal(result, val.Interface()); err != nil {
|
||||
log.Errorf("error unmarshaling chan response: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
bufLk.Lock()
|
||||
if ctx.Err() != nil {
|
||||
log.Errorf("got rpc message with cancelled context: %s", ctx.Err())
|
||||
bufLk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
buf.PushBack(val)
|
||||
|
||||
if buf.Len() > 1 {
|
||||
if buf.Len() > 10 {
|
||||
log.Warnw("rpc output message buffer", "n", buf.Len())
|
||||
} else {
|
||||
log.Infow("rpc output message buffer", "n", buf.Len())
|
||||
}
|
||||
bufLk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for buf.Len() > 0 {
|
||||
front := buf.Front()
|
||||
bufLk.Unlock()
|
||||
|
||||
cases := []reflect.SelectCase{
|
||||
{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: reflect.ValueOf(chCtx.Done()),
|
||||
},
|
||||
{
|
||||
Dir: reflect.SelectSend,
|
||||
Chan: ch,
|
||||
Send: front.Value.(reflect.Value).Elem(),
|
||||
},
|
||||
}
|
||||
|
||||
chosen, _, _ := reflect.Select(cases)
|
||||
bufLk.Lock()
|
||||
|
||||
switch chosen {
|
||||
case 0:
|
||||
buf.Init()
|
||||
case 1:
|
||||
buf.Remove(front)
|
||||
}
|
||||
}
|
||||
|
||||
bufLk.Unlock()
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return func() reflect.Value { return retVal }, chCtor
|
||||
}
|
||||
|
||||
func (c *client) sendRequest(ctx context.Context, req request, chCtor makeChanSink) (clientResponse, error) {
|
||||
rchan := make(chan clientResponse, 1)
|
||||
creq := clientRequest{
|
||||
req: req,
|
||||
ready: rchan,
|
||||
|
||||
retCh: chCtor,
|
||||
}
|
||||
select {
|
||||
case c.requests <- creq:
|
||||
case <-c.exiting:
|
||||
return clientResponse{}, fmt.Errorf("websocket routine exiting")
|
||||
}
|
||||
|
||||
var ctxDone <-chan struct{}
|
||||
var resp clientResponse
|
||||
|
||||
if ctx != nil {
|
||||
ctxDone = ctx.Done()
|
||||
}
|
||||
|
||||
// wait for response, handle context cancellation
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case resp = <-rchan:
|
||||
break loop
|
||||
case <-ctxDone: // send cancel request
|
||||
ctxDone = nil
|
||||
|
||||
cancelReq := clientRequest{
|
||||
req: request{
|
||||
Jsonrpc: "2.0",
|
||||
Method: wsCancel,
|
||||
Params: []param{{v: reflect.ValueOf(*req.ID)}},
|
||||
},
|
||||
}
|
||||
select {
|
||||
case c.requests <- cancelReq:
|
||||
case <-c.exiting:
|
||||
log.Warn("failed to send request cancellation, websocket routing exited")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type rpcFunc struct {
|
||||
client *client
|
||||
|
||||
ftyp reflect.Type
|
||||
name string
|
||||
|
||||
nout int
|
||||
valOut int
|
||||
errOut int
|
||||
|
||||
hasCtx int
|
||||
retCh bool
|
||||
|
||||
retry bool
|
||||
}
|
||||
|
||||
func (fn *rpcFunc) processResponse(resp clientResponse, rval reflect.Value) []reflect.Value {
|
||||
out := make([]reflect.Value, fn.nout)
|
||||
|
||||
if fn.valOut != -1 {
|
||||
out[fn.valOut] = rval
|
||||
}
|
||||
if fn.errOut != -1 {
|
||||
out[fn.errOut] = reflect.New(errorType).Elem()
|
||||
if resp.Error != nil {
|
||||
out[fn.errOut].Set(reflect.ValueOf(resp.Error))
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (fn *rpcFunc) processError(err error) []reflect.Value {
|
||||
out := make([]reflect.Value, fn.nout)
|
||||
|
||||
if fn.valOut != -1 {
|
||||
out[fn.valOut] = reflect.New(fn.ftyp.Out(fn.valOut)).Elem()
|
||||
}
|
||||
if fn.errOut != -1 {
|
||||
out[fn.errOut] = reflect.New(errorType).Elem()
|
||||
out[fn.errOut].Set(reflect.ValueOf(&ErrClient{err}))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (fn *rpcFunc) handleRpcCall(args []reflect.Value) (results []reflect.Value) {
|
||||
id := atomic.AddInt64(&fn.client.idCtr, 1)
|
||||
params := make([]param, len(args)-fn.hasCtx)
|
||||
for i, arg := range args[fn.hasCtx:] {
|
||||
params[i] = param{
|
||||
v: arg,
|
||||
}
|
||||
}
|
||||
|
||||
var ctx context.Context
|
||||
var span *trace.Span
|
||||
if fn.hasCtx == 1 {
|
||||
ctx = args[0].Interface().(context.Context)
|
||||
ctx, span = trace.StartSpan(ctx, "api.call")
|
||||
defer span.End()
|
||||
}
|
||||
|
||||
retVal := func() reflect.Value { return reflect.Value{} }
|
||||
|
||||
// if the function returns a channel, we need to provide a sink for the
|
||||
// messages
|
||||
var chCtor makeChanSink
|
||||
if fn.retCh {
|
||||
retVal, chCtor = fn.client.makeOutChan(ctx, fn.ftyp, fn.valOut)
|
||||
}
|
||||
|
||||
req := request{
|
||||
Jsonrpc: "2.0",
|
||||
ID: &id,
|
||||
Method: fn.client.namespace + "." + fn.name,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
if span != nil {
|
||||
span.AddAttributes(trace.StringAttribute("method", req.Method))
|
||||
|
||||
eSC := base64.StdEncoding.EncodeToString(
|
||||
propagation.Binary(span.SpanContext()))
|
||||
req.Meta = map[string]string{
|
||||
"SpanContext": eSC,
|
||||
}
|
||||
}
|
||||
|
||||
var resp clientResponse
|
||||
var err error
|
||||
// keep retrying if got a forced closed websocket conn and calling method
|
||||
// has retry annotation
|
||||
for {
|
||||
resp, err = fn.client.sendRequest(ctx, req, chCtor)
|
||||
if err != nil {
|
||||
return fn.processError(fmt.Errorf("sendRequest failed: %w", err))
|
||||
}
|
||||
|
||||
if resp.ID != *req.ID {
|
||||
return fn.processError(xerrors.New("request and response id didn't match"))
|
||||
}
|
||||
|
||||
if fn.valOut != -1 && !fn.retCh {
|
||||
val := reflect.New(fn.ftyp.Out(fn.valOut))
|
||||
|
||||
if resp.Result != nil {
|
||||
log.Debugw("rpc result", "type", fn.ftyp.Out(fn.valOut))
|
||||
if err := json.Unmarshal(resp.Result, val.Interface()); err != nil {
|
||||
log.Warnw("unmarshaling failed", "message", string(resp.Result))
|
||||
return fn.processError(xerrors.Errorf("unmarshaling result: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
retVal = func() reflect.Value { return val.Elem() }
|
||||
}
|
||||
retry := resp.Error != nil && resp.Error.Code == 2 && fn.retry
|
||||
if !retry {
|
||||
break
|
||||
}
|
||||
time.Sleep(methodRetryFrequency)
|
||||
}
|
||||
|
||||
return fn.processResponse(resp, retVal())
|
||||
}
|
||||
|
||||
func (c *client) makeRpcFunc(f reflect.StructField) (reflect.Value, error) {
|
||||
ftyp := f.Type
|
||||
if ftyp.Kind() != reflect.Func {
|
||||
return reflect.Value{}, xerrors.New("handler field not a func")
|
||||
}
|
||||
|
||||
fun := &rpcFunc{
|
||||
client: c,
|
||||
ftyp: ftyp,
|
||||
name: f.Name,
|
||||
retry: f.Tag.Get("retry") == "true",
|
||||
}
|
||||
fun.valOut, fun.errOut, fun.nout = processFuncOut(ftyp)
|
||||
|
||||
if ftyp.NumIn() > 0 && ftyp.In(0) == contextType {
|
||||
fun.hasCtx = 1
|
||||
}
|
||||
fun.retCh = fun.valOut != -1 && ftyp.Out(fun.valOut).Kind() == reflect.Chan
|
||||
|
||||
return reflect.MakeFunc(ftyp, fun.handleRpcCall), nil
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type rpcHandler struct {
|
||||
paramReceivers []reflect.Type
|
||||
nParams int
|
||||
|
||||
receiver reflect.Value
|
||||
handlerFunc reflect.Value
|
||||
|
||||
hasCtx int
|
||||
|
||||
errOut int
|
||||
valOut int
|
||||
}
|
||||
|
||||
type handlers map[string]rpcHandler
|
||||
|
||||
// Request / response
|
||||
|
||||
type request struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Params []param `json:"params"`
|
||||
Meta map[string]string `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
type respError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *respError) Error() string {
|
||||
if e.Code >= -32768 && e.Code <= -32000 {
|
||||
return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message)
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
ID int64 `json:"id"`
|
||||
Error *respError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Register
|
||||
|
||||
func (h handlers) register(namespace string, r interface{}) {
|
||||
val := reflect.ValueOf(r)
|
||||
//TODO: expect ptr
|
||||
|
||||
for i := 0; i < val.NumMethod(); i++ {
|
||||
method := val.Type().Method(i)
|
||||
|
||||
funcType := method.Func.Type()
|
||||
hasCtx := 0
|
||||
if funcType.NumIn() >= 2 && funcType.In(1) == contextType {
|
||||
hasCtx = 1
|
||||
}
|
||||
|
||||
ins := funcType.NumIn() - 1 - hasCtx
|
||||
recvs := make([]reflect.Type, ins)
|
||||
for i := 0; i < ins; i++ {
|
||||
recvs[i] = method.Type.In(i + 1 + hasCtx)
|
||||
}
|
||||
|
||||
valOut, errOut, _ := processFuncOut(funcType)
|
||||
|
||||
h[namespace+"."+method.Name] = rpcHandler{
|
||||
paramReceivers: recvs,
|
||||
nParams: ins,
|
||||
|
||||
handlerFunc: method.Func,
|
||||
receiver: val,
|
||||
|
||||
hasCtx: hasCtx,
|
||||
|
||||
errOut: errOut,
|
||||
valOut: valOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle
|
||||
|
||||
type rpcErrFunc func(w func(func(io.Writer)), req *request, code int, err error)
|
||||
type chanOut func(reflect.Value, int64) error
|
||||
|
||||
func (h handlers) handleReader(ctx context.Context, r io.Reader, w io.Writer, rpcError rpcErrFunc) {
|
||||
wf := func(cb func(io.Writer)) {
|
||||
cb(w)
|
||||
}
|
||||
|
||||
var req request
|
||||
if err := json.NewDecoder(r).Decode(&req); err != nil {
|
||||
rpcError(wf, &req, rpcParseError, xerrors.Errorf("unmarshaling request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
h.handle(ctx, req, wf, rpcError, func(bool) {}, nil)
|
||||
}
|
||||
|
||||
func doCall(methodName string, f reflect.Value, params []reflect.Value) (out []reflect.Value, err error) {
|
||||
defer func() {
|
||||
if i := recover(); i != nil {
|
||||
err = xerrors.Errorf("panic in rpc method '%s': %s", methodName, i)
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
out = f.Call(params)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (handlers) getSpan(ctx context.Context, req request) (context.Context, *trace.Span) {
|
||||
if req.Meta == nil {
|
||||
return ctx, nil
|
||||
}
|
||||
if eSC, ok := req.Meta["SpanContext"]; ok {
|
||||
bSC := make([]byte, base64.StdEncoding.DecodedLen(len(eSC)))
|
||||
_, err := base64.StdEncoding.Decode(bSC, []byte(eSC))
|
||||
if err != nil {
|
||||
log.Errorf("SpanContext: decode", "error", err)
|
||||
return ctx, nil
|
||||
}
|
||||
sc, ok := propagation.FromBinary(bSC)
|
||||
if !ok {
|
||||
log.Errorf("SpanContext: could not create span", "data", bSC)
|
||||
return ctx, nil
|
||||
}
|
||||
ctx, span := trace.StartSpanWithRemoteParent(ctx, "api.handle", sc)
|
||||
span.AddAttributes(trace.StringAttribute("method", req.Method))
|
||||
return ctx, span
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (h handlers) handle(ctx context.Context, req request, w func(func(io.Writer)), rpcError rpcErrFunc, done func(keepCtx bool), chOut chanOut) {
|
||||
// Not sure if we need to sanitize the incoming req.Method or not.
|
||||
ctx, span := h.getSpan(ctx, req)
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.RPCMethod, req.Method))
|
||||
defer span.End()
|
||||
|
||||
handler, ok := h[req.Method]
|
||||
if !ok {
|
||||
rpcError(w, &req, rpcMethodNotFound, fmt.Errorf("method '%s' not found", req.Method))
|
||||
stats.Record(ctx, metrics.RPCInvalidMethod.M(1))
|
||||
done(false)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Params) != handler.nParams {
|
||||
rpcError(w, &req, rpcInvalidParams, fmt.Errorf("wrong param count"))
|
||||
stats.Record(ctx, metrics.RPCRequestError.M(1))
|
||||
done(false)
|
||||
return
|
||||
}
|
||||
|
||||
outCh := handler.valOut != -1 && handler.handlerFunc.Type().Out(handler.valOut).Kind() == reflect.Chan
|
||||
defer done(outCh)
|
||||
|
||||
if chOut == nil && outCh {
|
||||
rpcError(w, &req, rpcMethodNotFound, fmt.Errorf("method '%s' not supported in this mode (no out channel support)", req.Method))
|
||||
stats.Record(ctx, metrics.RPCRequestError.M(1))
|
||||
return
|
||||
}
|
||||
|
||||
callParams := make([]reflect.Value, 1+handler.hasCtx+handler.nParams)
|
||||
callParams[0] = handler.receiver
|
||||
if handler.hasCtx == 1 {
|
||||
callParams[1] = reflect.ValueOf(ctx)
|
||||
}
|
||||
|
||||
for i := 0; i < handler.nParams; i++ {
|
||||
rp := reflect.New(handler.paramReceivers[i])
|
||||
if err := json.NewDecoder(bytes.NewReader(req.Params[i].data)).Decode(rp.Interface()); err != nil {
|
||||
rpcError(w, &req, rpcParseError, xerrors.Errorf("unmarshaling params for '%s' (param: %T): %w", req.Method, rp.Interface(), err))
|
||||
stats.Record(ctx, metrics.RPCRequestError.M(1))
|
||||
return
|
||||
}
|
||||
|
||||
callParams[i+1+handler.hasCtx] = reflect.ValueOf(rp.Elem().Interface())
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
callResult, err := doCall(req.Method, handler.handlerFunc, callParams)
|
||||
if err != nil {
|
||||
rpcError(w, &req, 0, xerrors.Errorf("fatal error calling '%s': %w", req.Method, err))
|
||||
stats.Record(ctx, metrics.RPCRequestError.M(1))
|
||||
return
|
||||
}
|
||||
if req.ID == nil {
|
||||
return // notification
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
resp := response{
|
||||
Jsonrpc: "2.0",
|
||||
ID: *req.ID,
|
||||
}
|
||||
|
||||
if handler.errOut != -1 {
|
||||
err := callResult[handler.errOut].Interface()
|
||||
if err != nil {
|
||||
log.Warnf("error in RPC call to '%s': %+v", req.Method, err)
|
||||
stats.Record(ctx, metrics.RPCResponseError.M(1))
|
||||
resp.Error = &respError{
|
||||
Code: 1,
|
||||
Message: err.(error).Error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var kind reflect.Kind
|
||||
var res interface{}
|
||||
var nonZero bool
|
||||
if handler.valOut != -1 {
|
||||
res = callResult[handler.valOut].Interface()
|
||||
kind = callResult[handler.valOut].Kind()
|
||||
nonZero = !callResult[handler.valOut].IsZero()
|
||||
}
|
||||
|
||||
if res != nil && kind == reflect.Chan {
|
||||
// Channel responses are sent from channel control goroutine.
|
||||
// Sending responses here could cause deadlocks on writeLk, or allow
|
||||
// sending channel messages before this rpc call returns
|
||||
|
||||
//noinspection GoNilness // already checked above
|
||||
err = chOut(callResult[handler.valOut], *req.ID)
|
||||
if err == nil {
|
||||
return // channel goroutine handles responding
|
||||
}
|
||||
|
||||
log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
|
||||
stats.Record(ctx, metrics.RPCResponseError.M(1))
|
||||
resp.Error = &respError{
|
||||
Code: 1,
|
||||
Message: err.(error).Error(),
|
||||
}
|
||||
} else if resp.Error == nil {
|
||||
// check error as JSON-RPC spec prohibits error and value at the same time
|
||||
resp.Result = res
|
||||
}
|
||||
if resp.Error != nil && nonZero {
|
||||
log.Errorw("error and res returned", "request", req, "r.err", resp.Error, "res", res)
|
||||
}
|
||||
|
||||
w(func(w io.Writer) {
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Error(err)
|
||||
stats.Record(ctx, metrics.RPCResponseError.M(1))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ReconnectInterval time.Duration
|
||||
|
||||
proxyConnFactory func(func() (*websocket.Conn, error)) func() (*websocket.Conn, error) // for testing
|
||||
}
|
||||
|
||||
var defaultConfig = Config{
|
||||
ReconnectInterval: time.Second * 5,
|
||||
}
|
||||
|
||||
type Option func(c *Config)
|
||||
|
||||
func WithReconnectInterval(d time.Duration) func(c *Config) {
|
||||
return func(c *Config) {
|
||||
c.ReconnectInterval = d
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type SimpleServerHandler struct {
|
||||
n int
|
||||
}
|
||||
|
||||
type TestType struct {
|
||||
S string
|
||||
I int
|
||||
}
|
||||
|
||||
type TestOut struct {
|
||||
TestType
|
||||
Ok bool
|
||||
}
|
||||
|
||||
func (h *SimpleServerHandler) Add(in int) error {
|
||||
if in == -3546 {
|
||||
return errors.New("test")
|
||||
}
|
||||
|
||||
h.n += in
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SimpleServerHandler) AddGet(in int) int {
|
||||
h.n += in
|
||||
return h.n
|
||||
}
|
||||
|
||||
func (h *SimpleServerHandler) StringMatch(t TestType, i2 int64) (out TestOut, err error) {
|
||||
if strconv.FormatInt(i2, 10) == t.S {
|
||||
out.Ok = true
|
||||
}
|
||||
if i2 != int64(t.I) {
|
||||
return TestOut{}, errors.New(":(")
|
||||
}
|
||||
out.I = t.I
|
||||
out.S = t.S
|
||||
return
|
||||
}
|
||||
|
||||
func TestRPC(t *testing.T) {
|
||||
// setup server
|
||||
|
||||
serverHandler := &SimpleServerHandler{}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("SimpleServerHandler", serverHandler)
|
||||
|
||||
// httptest stuff
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
// setup client
|
||||
|
||||
var client struct {
|
||||
Add func(int) error
|
||||
AddGet func(int) int
|
||||
StringMatch func(t TestType, i2 int64) (out TestOut, err error)
|
||||
}
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
defer closer()
|
||||
|
||||
// Add(int) error
|
||||
|
||||
require.NoError(t, client.Add(2))
|
||||
require.Equal(t, 2, serverHandler.n)
|
||||
|
||||
err = client.Add(-3546)
|
||||
require.EqualError(t, err, "test")
|
||||
|
||||
// AddGet(int) int
|
||||
|
||||
n := client.AddGet(3)
|
||||
require.Equal(t, 5, n)
|
||||
require.Equal(t, 5, serverHandler.n)
|
||||
|
||||
// StringMatch
|
||||
|
||||
o, err := client.StringMatch(TestType{S: "0"}, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "0", o.S)
|
||||
require.Equal(t, 0, o.I)
|
||||
|
||||
_, err = client.StringMatch(TestType{S: "5"}, 5)
|
||||
require.EqualError(t, err, ":(")
|
||||
|
||||
o, err = client.StringMatch(TestType{S: "8", I: 8}, 8)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "8", o.S)
|
||||
require.Equal(t, 8, o.I)
|
||||
|
||||
// Invalid client handlers
|
||||
|
||||
var noret struct {
|
||||
Add func(int)
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noret, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// this one should actually work
|
||||
noret.Add(4)
|
||||
require.Equal(t, 9, serverHandler.n)
|
||||
closer()
|
||||
|
||||
var noparam struct {
|
||||
Add func()
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noparam, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// shouldn't panic
|
||||
noparam.Add()
|
||||
closer()
|
||||
|
||||
var erronly struct {
|
||||
AddGet func() (int, error)
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &erronly, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = erronly.AddGet()
|
||||
if err == nil || err.Error() != "RPC error (-32602): wrong param count" {
|
||||
t.Error("wrong error:", err)
|
||||
}
|
||||
closer()
|
||||
|
||||
var wrongtype struct {
|
||||
Add func(string) error
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &wrongtype, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = wrongtype.Add("not an int")
|
||||
if err == nil || !strings.Contains(err.Error(), "RPC error (-32700):") || !strings.Contains(err.Error(), "json: cannot unmarshal string into Go value of type int") {
|
||||
t.Error("wrong error:", err)
|
||||
}
|
||||
closer()
|
||||
|
||||
var notfound struct {
|
||||
NotThere func(string) error
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", ¬found, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = notfound.NotThere("hello?")
|
||||
if err == nil || err.Error() != "RPC error (-32601): method 'SimpleServerHandler.NotThere' not found" {
|
||||
t.Error("wrong error:", err)
|
||||
}
|
||||
closer()
|
||||
}
|
||||
|
||||
type CtxHandler struct {
|
||||
lk sync.Mutex
|
||||
|
||||
cancelled bool
|
||||
i int
|
||||
}
|
||||
|
||||
func (h *CtxHandler) Test(ctx context.Context) {
|
||||
h.lk.Lock()
|
||||
defer h.lk.Unlock()
|
||||
timeout := time.After(300 * time.Millisecond)
|
||||
h.i++
|
||||
|
||||
select {
|
||||
case <-timeout:
|
||||
case <-ctx.Done():
|
||||
h.cancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestCtx(t *testing.T) {
|
||||
// setup server
|
||||
|
||||
serverHandler := &CtxHandler{}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("CtxHandler", serverHandler)
|
||||
|
||||
// httptest stuff
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
|
||||
// setup client
|
||||
|
||||
var client struct {
|
||||
Test func(ctx context.Context)
|
||||
}
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
client.Test(ctx)
|
||||
serverHandler.lk.Lock()
|
||||
|
||||
if !serverHandler.cancelled {
|
||||
t.Error("expected cancellation on the server side")
|
||||
}
|
||||
|
||||
serverHandler.cancelled = false
|
||||
|
||||
serverHandler.lk.Unlock()
|
||||
closer()
|
||||
|
||||
var noCtxClient struct {
|
||||
Test func()
|
||||
}
|
||||
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &noCtxClient, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noCtxClient.Test()
|
||||
|
||||
serverHandler.lk.Lock()
|
||||
|
||||
if serverHandler.cancelled || serverHandler.i != 2 {
|
||||
t.Error("wrong serverHandler state")
|
||||
}
|
||||
|
||||
serverHandler.lk.Unlock()
|
||||
closer()
|
||||
}
|
||||
|
||||
type UnUnmarshalable int
|
||||
|
||||
func (*UnUnmarshalable) UnmarshalJSON([]byte) error {
|
||||
return errors.New("nope")
|
||||
}
|
||||
|
||||
type UnUnmarshalableHandler struct{}
|
||||
|
||||
func (*UnUnmarshalableHandler) GetUnUnmarshalableStuff() (UnUnmarshalable, error) {
|
||||
return UnUnmarshalable(5), nil
|
||||
}
|
||||
|
||||
func TestUnmarshalableResult(t *testing.T) {
|
||||
var client struct {
|
||||
GetUnUnmarshalableStuff func() (UnUnmarshalable, error)
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("Handler", &UnUnmarshalableHandler{})
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "Handler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
defer closer()
|
||||
|
||||
_, err = client.GetUnUnmarshalableStuff()
|
||||
require.EqualError(t, err, "RPC client error: unmarshaling result: nope")
|
||||
}
|
||||
|
||||
type ChanHandler struct {
|
||||
wait chan struct{}
|
||||
ctxdone <-chan struct{}
|
||||
}
|
||||
|
||||
func (h *ChanHandler) Sub(ctx context.Context, i int, eq int) (<-chan int, error) {
|
||||
out := make(chan int)
|
||||
h.ctxdone = ctx.Done()
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
var n int
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Println("ctxdone1")
|
||||
return
|
||||
case <-h.wait:
|
||||
}
|
||||
|
||||
n += i
|
||||
|
||||
if n == eq {
|
||||
fmt.Println("eq")
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Println("ctxdone2")
|
||||
return
|
||||
case out <- n:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func TestChan(t *testing.T) {
|
||||
var client struct {
|
||||
Sub func(context.Context, int, int) (<-chan int, error)
|
||||
}
|
||||
|
||||
serverHandler := &ChanHandler{
|
||||
wait: make(chan struct{}, 5),
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("ChanHandler", serverHandler)
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer closer()
|
||||
|
||||
serverHandler.wait <- struct{}{}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// sub
|
||||
|
||||
sub, err := client.Sub(ctx, 2, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// recv one
|
||||
|
||||
require.Equal(t, 2, <-sub)
|
||||
|
||||
// recv many (order)
|
||||
|
||||
serverHandler.wait <- struct{}{}
|
||||
serverHandler.wait <- struct{}{}
|
||||
serverHandler.wait <- struct{}{}
|
||||
|
||||
require.Equal(t, 4, <-sub)
|
||||
require.Equal(t, 6, <-sub)
|
||||
require.Equal(t, 8, <-sub)
|
||||
|
||||
// close (through ctx)
|
||||
cancel()
|
||||
|
||||
_, ok := <-sub
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
// sub (again)
|
||||
|
||||
serverHandler.wait <- struct{}{}
|
||||
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
sub, err = client.Sub(ctx, 3, 6)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 3, <-sub)
|
||||
|
||||
// close (remote)
|
||||
serverHandler.wait <- struct{}{}
|
||||
_, ok = <-sub
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
|
||||
func TestChanServerClose(t *testing.T) {
|
||||
var client struct {
|
||||
Sub func(context.Context, int, int) (<-chan int, error)
|
||||
}
|
||||
|
||||
serverHandler := &ChanHandler{
|
||||
wait: make(chan struct{}, 5),
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("ChanHandler", serverHandler)
|
||||
|
||||
tctx, tcancel := context.WithCancel(context.Background())
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
testServ.Config.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
||||
return tctx
|
||||
}
|
||||
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer closer()
|
||||
|
||||
serverHandler.wait <- struct{}{}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// sub
|
||||
|
||||
sub, err := client.Sub(ctx, 2, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// recv one
|
||||
|
||||
require.Equal(t, 2, <-sub)
|
||||
|
||||
// make sure we're blocked
|
||||
|
||||
select {
|
||||
case <-time.After(200 * time.Millisecond):
|
||||
case <-sub:
|
||||
t.Fatal("didn't expect to get anything from sub")
|
||||
}
|
||||
|
||||
// close server
|
||||
|
||||
tcancel()
|
||||
testServ.Close()
|
||||
|
||||
_, ok := <-sub
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
|
||||
func TestServerChanLockClose(t *testing.T) {
|
||||
var client struct {
|
||||
Sub func(context.Context, int, int) (<-chan int, error)
|
||||
}
|
||||
|
||||
serverHandler := &ChanHandler{
|
||||
wait: make(chan struct{}),
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("ChanHandler", serverHandler)
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
|
||||
var closeConn func() error
|
||||
|
||||
_, err := NewMergeClient("ws://"+testServ.Listener.Addr().String(),
|
||||
"ChanHandler",
|
||||
[]interface{}{&client}, nil,
|
||||
func(c *Config) {
|
||||
c.proxyConnFactory = func(f func() (*websocket.Conn, error)) func() (*websocket.Conn, error) {
|
||||
return func() (*websocket.Conn, error) {
|
||||
c, err := f()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
closeConn = c.UnderlyingConn().Close
|
||||
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// sub
|
||||
|
||||
sub, err := client.Sub(ctx, 2, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// recv one
|
||||
|
||||
go func() {
|
||||
serverHandler.wait <- struct{}{}
|
||||
}()
|
||||
require.Equal(t, 2, <-sub)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
serverHandler.wait <- struct{}{}
|
||||
}
|
||||
|
||||
if err := closeConn(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-serverHandler.ctxdone
|
||||
}
|
||||
|
||||
func TestControlChanDeadlock(t *testing.T) {
|
||||
for r := 0; r < 20; r++ {
|
||||
testControlChanDeadlock(t)
|
||||
}
|
||||
}
|
||||
|
||||
func testControlChanDeadlock(t *testing.T) {
|
||||
var client struct {
|
||||
Sub func(context.Context, int, int) (<-chan int, error)
|
||||
}
|
||||
|
||||
n := 5000
|
||||
|
||||
serverHandler := &ChanHandler{
|
||||
wait: make(chan struct{}, n),
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
rpcServer.Register("ChanHandler", serverHandler)
|
||||
|
||||
testServ := httptest.NewServer(rpcServer)
|
||||
defer testServ.Close()
|
||||
|
||||
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer closer()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
serverHandler.wait <- struct{}{}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
sub, err := client.Sub(ctx, 1, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < n; i++ {
|
||||
require.Equal(t, i+1, <-sub)
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = client.Sub(ctx, 2, -1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
rpcParseError = -32700
|
||||
rpcMethodNotFound = -32601
|
||||
rpcInvalidParams = -32602
|
||||
)
|
||||
|
||||
// RPCServer provides a jsonrpc 2.0 http server handler
|
||||
type RPCServer struct {
|
||||
methods handlers
|
||||
}
|
||||
|
||||
// NewServer creates new RPCServer instance
|
||||
func NewServer() *RPCServer {
|
||||
return &RPCServer{
|
||||
methods: map[string]rpcHandler{},
|
||||
}
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func (s *RPCServer) handleWS(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: allow setting
|
||||
// (note that we still are mostly covered by jwt tokens)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
if r.Header.Get("Sec-WebSocket-Protocol") != "" {
|
||||
w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol"))
|
||||
}
|
||||
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
(&wsConn{
|
||||
conn: c,
|
||||
handler: s.methods,
|
||||
exiting: make(chan struct{}),
|
||||
}).handleWsConn(ctx)
|
||||
|
||||
if err := c.Close(); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: return errors to clients per spec
|
||||
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
h := strings.ToLower(r.Header.Get("Connection"))
|
||||
if strings.Contains(h, "upgrade") {
|
||||
s.handleWS(ctx, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
s.methods.handleReader(ctx, r.Body, w, rpcError)
|
||||
}
|
||||
|
||||
func rpcError(wf func(func(io.Writer)), req *request, code int, err error) {
|
||||
log.Errorf("RPC Error: %s", err)
|
||||
wf(func(w io.Writer) {
|
||||
if hw, ok := w.(http.ResponseWriter); ok {
|
||||
hw.WriteHeader(500)
|
||||
}
|
||||
|
||||
log.Warnf("rpc error: %s", err)
|
||||
|
||||
if req.ID == nil { // notification
|
||||
return
|
||||
}
|
||||
|
||||
resp := response{
|
||||
Jsonrpc: "2.0",
|
||||
ID: *req.ID,
|
||||
Error: &respError{
|
||||
Code: code,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(resp)
|
||||
if err != nil {
|
||||
log.Warnf("failed to write rpc error: %s", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Register registers new RPC handler
|
||||
//
|
||||
// Handler is any value with methods defined
|
||||
func (s *RPCServer) Register(namespace string, handler interface{}) {
|
||||
s.methods.register(namespace, handler)
|
||||
}
|
||||
|
||||
var _ error = &respError{}
|
||||
@@ -0,0 +1,55 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type param struct {
|
||||
data []byte // from unmarshal
|
||||
|
||||
v reflect.Value // to marshal
|
||||
}
|
||||
|
||||
func (p *param) UnmarshalJSON(raw []byte) error {
|
||||
p.data = make([]byte, len(raw))
|
||||
copy(p.data, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *param) MarshalJSON() ([]byte, error) {
|
||||
if p.v.Kind() == reflect.Invalid {
|
||||
return p.data, nil
|
||||
}
|
||||
|
||||
return json.Marshal(p.v.Interface())
|
||||
}
|
||||
|
||||
// processFuncOut finds value and error Outs in function
|
||||
func processFuncOut(funcType reflect.Type) (valOut int, errOut int, n int) {
|
||||
errOut = -1 // -1 if not found
|
||||
valOut = -1
|
||||
n = funcType.NumOut()
|
||||
|
||||
switch n {
|
||||
case 0:
|
||||
case 1:
|
||||
if funcType.Out(0) == errorType {
|
||||
errOut = 0
|
||||
} else {
|
||||
valOut = 0
|
||||
}
|
||||
case 2:
|
||||
valOut = 0
|
||||
errOut = 1
|
||||
if funcType.Out(1) != errorType {
|
||||
panic("expected error as second return value")
|
||||
}
|
||||
default:
|
||||
errstr := fmt.Sprintf("too many return values: %s", funcType)
|
||||
panic(errstr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const wsCancel = "xrpc.cancel"
|
||||
const chValue = "xrpc.ch.val"
|
||||
const chClose = "xrpc.ch.close"
|
||||
|
||||
type frame struct {
|
||||
// common
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
Meta map[string]string `json:"meta,omitempty"`
|
||||
|
||||
// request
|
||||
Method string `json:"method,omitempty"`
|
||||
Params []param `json:"params,omitempty"`
|
||||
|
||||
// response
|
||||
Result json.RawMessage `json:"result,omitempty"`
|
||||
Error *respError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type outChanReg struct {
|
||||
reqID int64
|
||||
|
||||
chID uint64
|
||||
ch reflect.Value
|
||||
}
|
||||
|
||||
type wsConn struct {
|
||||
// outside params
|
||||
conn *websocket.Conn
|
||||
connFactory func() (*websocket.Conn, error)
|
||||
reconnectInterval time.Duration
|
||||
handler handlers
|
||||
requests <-chan clientRequest
|
||||
stop <-chan struct{}
|
||||
exiting chan struct{}
|
||||
|
||||
// incoming messages
|
||||
incoming chan io.Reader
|
||||
incomingErr error
|
||||
|
||||
// outgoing messages
|
||||
writeLk sync.Mutex
|
||||
|
||||
// ////
|
||||
// Client related
|
||||
|
||||
// inflight are requests we've sent to the remote
|
||||
inflight map[int64]clientRequest
|
||||
|
||||
// chanHandlers is a map of client-side channel handlers
|
||||
chanHandlers map[uint64]func(m []byte, ok bool)
|
||||
|
||||
// ////
|
||||
// Server related
|
||||
|
||||
// handling are the calls we handle
|
||||
handling map[int64]context.CancelFunc
|
||||
handlingLk sync.Mutex
|
||||
|
||||
spawnOutChanHandlerOnce sync.Once
|
||||
|
||||
// chanCtr is a counter used for identifying output channels on the server side
|
||||
chanCtr uint64
|
||||
|
||||
registerCh chan outChanReg
|
||||
}
|
||||
|
||||
// //
|
||||
// WebSocket Message utils //
|
||||
// //
|
||||
|
||||
// nextMessage wait for one message and puts it to the incoming channel
|
||||
func (c *wsConn) nextMessage() {
|
||||
msgType, r, err := c.conn.NextReader()
|
||||
if err != nil {
|
||||
c.incomingErr = err
|
||||
close(c.incoming)
|
||||
return
|
||||
}
|
||||
if msgType != websocket.BinaryMessage && msgType != websocket.TextMessage {
|
||||
c.incomingErr = errors.New("unsupported message type")
|
||||
close(c.incoming)
|
||||
return
|
||||
}
|
||||
c.incoming <- r
|
||||
}
|
||||
|
||||
// nextWriter waits for writeLk and invokes the cb callback with WS message
|
||||
// writer when the lock is acquired
|
||||
func (c *wsConn) nextWriter(cb func(io.Writer)) {
|
||||
c.writeLk.Lock()
|
||||
defer c.writeLk.Unlock()
|
||||
|
||||
wcl, err := c.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
log.Error("handle me:", err)
|
||||
return
|
||||
}
|
||||
|
||||
cb(wcl)
|
||||
|
||||
if err := wcl.Close(); err != nil {
|
||||
log.Error("handle me:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wsConn) sendRequest(req request) error {
|
||||
c.writeLk.Lock()
|
||||
defer c.writeLk.Unlock()
|
||||
if err := c.conn.WriteJSON(req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// //
|
||||
// Output channels //
|
||||
// //
|
||||
|
||||
// handleOutChans handles channel communication on the server side
|
||||
// (forwards channel messages to client)
|
||||
func (c *wsConn) handleOutChans() {
|
||||
regV := reflect.ValueOf(c.registerCh)
|
||||
exitV := reflect.ValueOf(c.exiting)
|
||||
|
||||
cases := []reflect.SelectCase{
|
||||
{ // registration chan always 0
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: regV,
|
||||
},
|
||||
{ // exit chan always 1
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: exitV,
|
||||
},
|
||||
}
|
||||
internal := len(cases)
|
||||
var caseToID []uint64
|
||||
|
||||
for {
|
||||
chosen, val, ok := reflect.Select(cases)
|
||||
|
||||
switch chosen {
|
||||
case 0: // registration channel
|
||||
if !ok {
|
||||
// control channel closed - signals closed connection
|
||||
// This shouldn't happen, instead the exiting channel should get closed
|
||||
log.Warn("control channel closed")
|
||||
return
|
||||
}
|
||||
|
||||
registration := val.Interface().(outChanReg)
|
||||
|
||||
caseToID = append(caseToID, registration.chID)
|
||||
cases = append(cases, reflect.SelectCase{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: registration.ch,
|
||||
})
|
||||
|
||||
c.nextWriter(func(w io.Writer) {
|
||||
resp := &response{
|
||||
Jsonrpc: "2.0",
|
||||
ID: registration.reqID,
|
||||
Result: registration.chID,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
continue
|
||||
case 1: // exiting channel
|
||||
if !ok {
|
||||
// exiting channel closed - signals closed connection
|
||||
//
|
||||
// We're not closing any channels as we're on receiving end.
|
||||
// Also, context cancellation below should take care of any running
|
||||
// requests
|
||||
return
|
||||
}
|
||||
log.Warn("exiting channel received a message")
|
||||
continue
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// Output channel closed, cleanup, and tell remote that this happened
|
||||
|
||||
n := len(cases) - 1
|
||||
if n > 0 {
|
||||
cases[chosen] = cases[n]
|
||||
caseToID[chosen-internal] = caseToID[n-internal]
|
||||
}
|
||||
|
||||
id := caseToID[chosen-internal]
|
||||
cases = cases[:n]
|
||||
caseToID = caseToID[:n-internal]
|
||||
|
||||
if err := c.sendRequest(request{
|
||||
Jsonrpc: "2.0",
|
||||
ID: nil, // notification
|
||||
Method: chClose,
|
||||
Params: []param{{v: reflect.ValueOf(id)}},
|
||||
}); err != nil {
|
||||
log.Warnf("closed out channel sendRequest failed: %s", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// forward message
|
||||
if err := c.sendRequest(request{
|
||||
Jsonrpc: "2.0",
|
||||
ID: nil, // notification
|
||||
Method: chValue,
|
||||
Params: []param{{v: reflect.ValueOf(caseToID[chosen-internal])}, {v: val}},
|
||||
}); err != nil {
|
||||
log.Warnf("sendRequest failed: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleChanOut registers output channel for forwarding to client
|
||||
func (c *wsConn) handleChanOut(ch reflect.Value, req int64) error {
|
||||
c.spawnOutChanHandlerOnce.Do(func() {
|
||||
go c.handleOutChans()
|
||||
})
|
||||
id := atomic.AddUint64(&c.chanCtr, 1)
|
||||
|
||||
select {
|
||||
case c.registerCh <- outChanReg{
|
||||
reqID: req,
|
||||
|
||||
chID: id,
|
||||
ch: ch,
|
||||
}:
|
||||
return nil
|
||||
case <-c.exiting:
|
||||
return xerrors.New("connection closing")
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
// Context.Done propagation //
|
||||
// //
|
||||
|
||||
// handleCtxAsync handles context lifetimes for client
|
||||
// TODO: this should be aware of events going through chanHandlers, and quit
|
||||
// when the related channel is closed.
|
||||
// This should also probably be a single goroutine,
|
||||
// Note that not doing this should be fine for now as long as we are using
|
||||
// contexts correctly (cancelling when async functions are no longer is use)
|
||||
func (c *wsConn) handleCtxAsync(actx context.Context, id int64) {
|
||||
<-actx.Done()
|
||||
|
||||
c.sendRequest(request{
|
||||
Jsonrpc: "2.0",
|
||||
Method: wsCancel,
|
||||
Params: []param{{v: reflect.ValueOf(id)}},
|
||||
})
|
||||
}
|
||||
|
||||
// cancelCtx is a built-in rpc which handles context cancellation over rpc
|
||||
func (c *wsConn) cancelCtx(req frame) {
|
||||
if req.ID != nil {
|
||||
log.Warnf("%s call with ID set, won't respond", wsCancel)
|
||||
}
|
||||
|
||||
var id int64
|
||||
if err := json.Unmarshal(req.Params[0].data, &id); err != nil {
|
||||
log.Error("handle me:", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.handlingLk.Lock()
|
||||
defer c.handlingLk.Unlock()
|
||||
|
||||
cf, ok := c.handling[id]
|
||||
if ok {
|
||||
cf()
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
// Main Handling logic //
|
||||
// //
|
||||
|
||||
func (c *wsConn) handleChanMessage(frame frame) {
|
||||
var chid uint64
|
||||
if err := json.Unmarshal(frame.Params[0].data, &chid); err != nil {
|
||||
log.Error("failed to unmarshal channel id in xrpc.ch.val: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
hnd, ok := c.chanHandlers[chid]
|
||||
if !ok {
|
||||
log.Errorf("xrpc.ch.val: handler %d not found", chid)
|
||||
return
|
||||
}
|
||||
|
||||
hnd(frame.Params[1].data, true)
|
||||
}
|
||||
|
||||
func (c *wsConn) handleChanClose(frame frame) {
|
||||
var chid uint64
|
||||
if err := json.Unmarshal(frame.Params[0].data, &chid); err != nil {
|
||||
log.Error("failed to unmarshal channel id in xrpc.ch.val: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
hnd, ok := c.chanHandlers[chid]
|
||||
if !ok {
|
||||
log.Errorf("xrpc.ch.val: handler %d not found", chid)
|
||||
return
|
||||
}
|
||||
|
||||
delete(c.chanHandlers, chid)
|
||||
|
||||
hnd(nil, false)
|
||||
}
|
||||
|
||||
func (c *wsConn) handleResponse(frame frame) {
|
||||
req, ok := c.inflight[*frame.ID]
|
||||
if !ok {
|
||||
log.Error("client got unknown ID in response")
|
||||
return
|
||||
}
|
||||
|
||||
if req.retCh != nil && frame.Result != nil {
|
||||
// output is channel
|
||||
var chid uint64
|
||||
if err := json.Unmarshal(frame.Result, &chid); err != nil {
|
||||
log.Errorf("failed to unmarshal channel id response: %s, data '%s'", err, string(frame.Result))
|
||||
return
|
||||
}
|
||||
|
||||
var chanCtx context.Context
|
||||
chanCtx, c.chanHandlers[chid] = req.retCh()
|
||||
go c.handleCtxAsync(chanCtx, *frame.ID)
|
||||
}
|
||||
|
||||
req.ready <- clientResponse{
|
||||
Jsonrpc: frame.Jsonrpc,
|
||||
Result: frame.Result,
|
||||
ID: *frame.ID,
|
||||
Error: frame.Error,
|
||||
}
|
||||
delete(c.inflight, *frame.ID)
|
||||
}
|
||||
|
||||
func (c *wsConn) handleCall(ctx context.Context, frame frame) {
|
||||
req := request{
|
||||
Jsonrpc: frame.Jsonrpc,
|
||||
ID: frame.ID,
|
||||
Meta: frame.Meta,
|
||||
Method: frame.Method,
|
||||
Params: frame.Params,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
nextWriter := func(cb func(io.Writer)) {
|
||||
cb(ioutil.Discard)
|
||||
}
|
||||
done := func(keepCtx bool) {
|
||||
if !keepCtx {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
if frame.ID != nil {
|
||||
nextWriter = c.nextWriter
|
||||
|
||||
c.handlingLk.Lock()
|
||||
c.handling[*frame.ID] = cancel
|
||||
c.handlingLk.Unlock()
|
||||
|
||||
done = func(keepctx bool) {
|
||||
c.handlingLk.Lock()
|
||||
defer c.handlingLk.Unlock()
|
||||
|
||||
if !keepctx {
|
||||
cancel()
|
||||
delete(c.handling, *frame.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go c.handler.handle(ctx, req, nextWriter, rpcError, done, c.handleChanOut)
|
||||
}
|
||||
|
||||
// handleFrame handles all incoming messages (calls and responses)
|
||||
func (c *wsConn) handleFrame(ctx context.Context, frame frame) {
|
||||
// Get message type by method name:
|
||||
// "" - response
|
||||
// "xrpc.*" - builtin
|
||||
// anything else - incoming remote call
|
||||
switch frame.Method {
|
||||
case "": // Response to our call
|
||||
c.handleResponse(frame)
|
||||
case wsCancel:
|
||||
c.cancelCtx(frame)
|
||||
case chValue:
|
||||
c.handleChanMessage(frame)
|
||||
case chClose:
|
||||
c.handleChanClose(frame)
|
||||
default: // Remote call
|
||||
c.handleCall(ctx, frame)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wsConn) closeInFlight() {
|
||||
for id, req := range c.inflight {
|
||||
req.ready <- clientResponse{
|
||||
Jsonrpc: "2.0",
|
||||
ID: id,
|
||||
Error: &respError{
|
||||
Message: "handler: websocket connection closed",
|
||||
Code: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
c.handlingLk.Lock()
|
||||
for _, cancel := range c.handling {
|
||||
cancel()
|
||||
}
|
||||
c.handlingLk.Unlock()
|
||||
|
||||
c.inflight = map[int64]clientRequest{}
|
||||
c.handling = map[int64]context.CancelFunc{}
|
||||
}
|
||||
|
||||
func (c *wsConn) closeChans() {
|
||||
for chid := range c.chanHandlers {
|
||||
hnd := c.chanHandlers[chid]
|
||||
delete(c.chanHandlers, chid)
|
||||
hnd(nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wsConn) handleWsConn(ctx context.Context) {
|
||||
c.incoming = make(chan io.Reader)
|
||||
c.inflight = map[int64]clientRequest{}
|
||||
c.handling = map[int64]context.CancelFunc{}
|
||||
c.chanHandlers = map[uint64]func(m []byte, ok bool){}
|
||||
|
||||
c.registerCh = make(chan outChanReg)
|
||||
defer close(c.exiting)
|
||||
|
||||
// ////
|
||||
|
||||
// on close, make sure to return from all pending calls, and cancel context
|
||||
// on all calls we handle
|
||||
defer c.closeInFlight()
|
||||
|
||||
// wait for the first message
|
||||
go c.nextMessage()
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-c.incoming:
|
||||
if !ok {
|
||||
if c.incomingErr != nil {
|
||||
if !websocket.IsCloseError(c.incomingErr, websocket.CloseNormalClosure) {
|
||||
log.Debugw("websocket error", "error", c.incomingErr)
|
||||
// connection dropped unexpectedly, do our best to recover it
|
||||
c.closeInFlight()
|
||||
c.closeChans()
|
||||
c.incoming = make(chan io.Reader) // listen again for responses
|
||||
go func() {
|
||||
if c.connFactory == nil { // likely the server side, don't try to reconnect
|
||||
return
|
||||
}
|
||||
|
||||
var conn *websocket.Conn
|
||||
for conn == nil {
|
||||
time.Sleep(c.reconnectInterval)
|
||||
var err error
|
||||
if conn, err = c.connFactory(); err != nil {
|
||||
log.Debugw("websocket connection retry failed", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.writeLk.Lock()
|
||||
c.conn = conn
|
||||
c.incomingErr = nil
|
||||
c.writeLk.Unlock()
|
||||
|
||||
go c.nextMessage()
|
||||
}()
|
||||
continue
|
||||
}
|
||||
}
|
||||
return // remote closed
|
||||
}
|
||||
|
||||
// debug util - dump all messages to stderr
|
||||
// r = io.TeeReader(r, os.Stderr)
|
||||
|
||||
var frame frame
|
||||
if err := json.NewDecoder(r).Decode(&frame); err != nil {
|
||||
log.Error("handle me:", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.handleFrame(ctx, frame)
|
||||
go c.nextMessage()
|
||||
case req := <-c.requests:
|
||||
c.writeLk.Lock()
|
||||
if req.req.ID != nil {
|
||||
if c.incomingErr != nil { // No conn?, immediate fail
|
||||
req.ready <- clientResponse{
|
||||
Jsonrpc: "2.0",
|
||||
ID: *req.req.ID,
|
||||
Error: &respError{
|
||||
Message: "handler: websocket connection closed",
|
||||
Code: 2,
|
||||
},
|
||||
}
|
||||
c.writeLk.Unlock()
|
||||
break
|
||||
}
|
||||
c.inflight[*req.req.ID] = req
|
||||
}
|
||||
c.writeLk.Unlock()
|
||||
if err := c.sendRequest(req.req); err != nil {
|
||||
log.Errorf("sendReqest failed (Handle me): %s", err)
|
||||
}
|
||||
case <-c.stop:
|
||||
c.writeLk.Lock()
|
||||
cmsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
|
||||
if err := c.conn.WriteMessage(websocket.CloseMessage, cmsg); err != nil {
|
||||
log.Warn("failed to write close message: ", err)
|
||||
}
|
||||
if err := c.conn.Close(); err != nil {
|
||||
log.Warnw("websocket close error", "error", err)
|
||||
}
|
||||
c.writeLk.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-10
@@ -72,10 +72,6 @@ func CheckBlockSignature(blk *types.BlockHeader, ctx context.Context, worker add
|
||||
_, span := trace.StartSpan(ctx, "checkBlockSignature")
|
||||
defer span.End()
|
||||
|
||||
if blk.IsValidated() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if blk.BlockSig == nil {
|
||||
return xerrors.New("block signature not present")
|
||||
}
|
||||
@@ -85,12 +81,8 @@ func CheckBlockSignature(blk *types.BlockHeader, ctx context.Context, worker add
|
||||
return xerrors.Errorf("failed to get block signing bytes: %w", err)
|
||||
}
|
||||
|
||||
err = Verify(blk.BlockSig, worker, sigb)
|
||||
if err == nil {
|
||||
blk.SetValidated()
|
||||
}
|
||||
|
||||
return err
|
||||
_ = sigb
|
||||
return Verify(blk.BlockSig, worker, sigb)
|
||||
}
|
||||
|
||||
// SigShim is used for introducing signature functions
|
||||
|
||||
+1
-2
@@ -9,8 +9,7 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@ import (
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
const listenAddr = "127.0.0.1:2222"
|
||||
|
||||
+26
-4
@@ -4,14 +4,13 @@ import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
rpcmetrics "github.com/filecoin-project/go-jsonrpc/metrics"
|
||||
)
|
||||
|
||||
// Global Tags
|
||||
var (
|
||||
Version, _ = tag.NewKey("version")
|
||||
Commit, _ = tag.NewKey("commit")
|
||||
RPCMethod, _ = tag.NewKey("method")
|
||||
PeerID, _ = tag.NewKey("peer_id")
|
||||
FailureType, _ = tag.NewKey("failure_type")
|
||||
MessageFrom, _ = tag.NewKey("message_from")
|
||||
@@ -32,6 +31,9 @@ var (
|
||||
BlockValidationFailure = stats.Int64("block/failure", "Counter for block validation failures", stats.UnitDimensionless)
|
||||
BlockValidationSuccess = stats.Int64("block/success", "Counter for block validation successes", stats.UnitDimensionless)
|
||||
PeerCount = stats.Int64("peer/count", "Current number of FIL peers", stats.UnitDimensionless)
|
||||
RPCInvalidMethod = stats.Int64("rpc/invalid_method", "Total number of invalid RPC methods called", stats.UnitDimensionless)
|
||||
RPCRequestError = stats.Int64("rpc/request_error", "Total number of request errors handled", stats.UnitDimensionless)
|
||||
RPCResponseError = stats.Int64("rpc/response_error", "Total number of responses errors handled", stats.UnitDimensionless)
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -80,10 +82,26 @@ var (
|
||||
Measure: PeerCount,
|
||||
Aggregation: view.LastValue(),
|
||||
}
|
||||
// All RPC related metrics should at the very least tag the RPCMethod
|
||||
RPCInvalidMethodView = &view.View{
|
||||
Measure: RPCInvalidMethod,
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{RPCMethod},
|
||||
}
|
||||
RPCRequestErrorView = &view.View{
|
||||
Measure: RPCRequestError,
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{RPCMethod},
|
||||
}
|
||||
RPCResponseErrorView = &view.View{
|
||||
Measure: RPCResponseError,
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{RPCMethod},
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultViews is an array of OpenCensus views for metric gathering purposes
|
||||
var DefaultViews = append([]*view.View{
|
||||
var DefaultViews = []*view.View{
|
||||
InfoView,
|
||||
ChainNodeHeightView,
|
||||
ChainNodeWorkerHeightView,
|
||||
@@ -93,4 +111,8 @@ var DefaultViews = append([]*view.View{
|
||||
MessageReceivedView,
|
||||
MessageValidationFailureView,
|
||||
MessageValidationSuccessView,
|
||||
PeerCountView}, rpcmetrics.DefaultViews...)
|
||||
PeerCountView,
|
||||
RPCInvalidMethodView,
|
||||
RPCRequestErrorView,
|
||||
RPCResponseErrorView,
|
||||
}
|
||||
|
||||
+6
-104
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
@@ -19,7 +18,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -190,11 +188,7 @@ func (m *Miner) mine(ctx context.Context) {
|
||||
log.Errorf("failed to submit newly mined block: %s", err)
|
||||
}
|
||||
} else {
|
||||
// Wait until the next epoch, plus the propagation delay, so a new tipset
|
||||
// has enough time to form.
|
||||
//
|
||||
// See: https://github.com/filecoin-project/lotus/issues/1845
|
||||
nextRound := time.Unix(int64(base.TipSet.MinTimestamp()+uint64(build.BlockDelay*base.NullRounds))+int64(build.PropagationDelay), 0)
|
||||
nextRound := time.Unix(int64(base.TipSet.MinTimestamp()+uint64(build.BlockDelay*base.NullRounds)), 0)
|
||||
|
||||
select {
|
||||
case <-time.After(time.Until(nextRound)):
|
||||
@@ -244,12 +238,12 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error)
|
||||
}
|
||||
|
||||
func (m *Miner) hasPower(ctx context.Context, addr address.Address, ts *types.TipSet) (bool, error) {
|
||||
mpower, err := m.api.StateMinerPower(ctx, addr, ts.Key())
|
||||
power, err := m.api.StateMinerPower(ctx, addr, ts.Key())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return mpower.MinerPower.QualityAdjPower.GreaterThanEqual(power.ConsensusMinerMinPower), nil
|
||||
return !power.MinerPower.QualityAdjPower.Equals(types.NewInt(0)), nil
|
||||
}
|
||||
|
||||
func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, error) {
|
||||
@@ -267,8 +261,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tMBI := time.Now()
|
||||
|
||||
beaconPrev := mbi.PrevBeaconEntry
|
||||
|
||||
bvals, err := beacon.BeaconEntriesForBlock(ctx, m.beacon, round, beaconPrev)
|
||||
@@ -276,8 +268,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, xerrors.Errorf("get beacon entries failed: %w", err)
|
||||
}
|
||||
|
||||
tDrand := time.Now()
|
||||
|
||||
hasPower, err := m.hasPower(ctx, m.address, base.TipSet)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("checking if miner is slashed: %w", err)
|
||||
@@ -288,8 +278,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tPowercheck := time.Now()
|
||||
|
||||
log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(time.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds)
|
||||
|
||||
rbase := beaconPrev
|
||||
@@ -312,8 +300,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tTicket := time.Now()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.address.MarshalCBOR(buf); err != nil {
|
||||
return nil, xerrors.Errorf("failed to marshal miner address: %w", err)
|
||||
@@ -326,8 +312,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
|
||||
prand := abi.PoStRandomness(rand)
|
||||
|
||||
tSeed := time.Now()
|
||||
|
||||
postProof, err := m.epp.ComputeProof(ctx, mbi.Sectors, prand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute winning post proof: %w", err)
|
||||
@@ -339,27 +323,16 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, xerrors.Errorf("failed to get pending messages: %w", err)
|
||||
}
|
||||
|
||||
tPending := time.Now()
|
||||
|
||||
// TODO: winning post proof
|
||||
b, err := m.createBlock(base, m.address, ticket, winner, bvals, postProof, pending)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to create block: %w", err)
|
||||
}
|
||||
|
||||
tCreateBlock := time.Now()
|
||||
dur := tCreateBlock.Sub(start)
|
||||
dur := time.Since(start)
|
||||
log.Infow("mined new block", "cid", b.Cid(), "height", b.Header.Height, "took", dur)
|
||||
if dur > time.Second*build.BlockDelay {
|
||||
log.Warn("CAUTION: block production took longer than the block delay. Your computer may not be fast enough to keep up")
|
||||
|
||||
log.Warnw("tMinerBaseInfo ", "duration", tMBI.Sub(start))
|
||||
log.Warnw("tDrand ", "duration", tDrand.Sub(tMBI))
|
||||
log.Warnw("tPowercheck ", "duration", tPowercheck.Sub(tDrand))
|
||||
log.Warnw("tTicket ", "duration", tTicket.Sub(tPowercheck))
|
||||
log.Warnw("tSeed ", "duration", tSeed.Sub(tTicket))
|
||||
log.Warnw("tPending ", "duration", tPending.Sub(tSeed))
|
||||
log.Warnw("tCreateBlock ", "duration", tCreateBlock.Sub(tPending))
|
||||
}
|
||||
|
||||
return b, nil
|
||||
@@ -429,34 +402,6 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
|
||||
})
|
||||
}
|
||||
|
||||
type actCacheEntry struct {
|
||||
act *types.Actor
|
||||
err error
|
||||
}
|
||||
|
||||
type cachedActorLookup struct {
|
||||
tsk types.TipSetKey
|
||||
cache map[address.Address]actCacheEntry
|
||||
fallback ActorLookup
|
||||
}
|
||||
|
||||
func (c *cachedActorLookup) StateGetActor(ctx context.Context, a address.Address, tsk types.TipSetKey) (*types.Actor, error) {
|
||||
if c.tsk == tsk {
|
||||
e, has := c.cache[a]
|
||||
if has {
|
||||
return e.act, e.err
|
||||
}
|
||||
}
|
||||
|
||||
e, err := c.fallback(ctx, a, tsk)
|
||||
if c.tsk == tsk {
|
||||
c.cache[a] = actCacheEntry{
|
||||
act: e, err: err,
|
||||
}
|
||||
}
|
||||
return e, err
|
||||
}
|
||||
|
||||
type ActorLookup func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error)
|
||||
|
||||
func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
|
||||
@@ -469,34 +414,12 @@ func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
|
||||
}
|
||||
|
||||
func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
|
||||
al = (&cachedActorLookup{
|
||||
tsk: ts.Key(),
|
||||
cache: map[address.Address]actCacheEntry{},
|
||||
fallback: al,
|
||||
}).StateGetActor
|
||||
|
||||
out := make([]*types.SignedMessage, 0, build.BlockMessageLimit)
|
||||
inclNonces := make(map[address.Address]uint64)
|
||||
inclBalances := make(map[address.Address]types.BigInt)
|
||||
inclCount := make(map[address.Address]int)
|
||||
|
||||
tooLowFundMsgs := 0
|
||||
tooHighNonceMsgs := 0
|
||||
|
||||
start := time.Now()
|
||||
vmValid := time.Duration(0)
|
||||
getbal := time.Duration(0)
|
||||
|
||||
for _, msg := range msgs {
|
||||
vmstart := time.Now()
|
||||
|
||||
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(msg.ChainLength()) // TODO: really should be doing just msg.ChainLength() but the sync side of this code doesnt seem to have access to that
|
||||
if err := msg.VMMessage().ValidForBlockInclusion(minGas); err != nil {
|
||||
log.Warnf("invalid message in message pool: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
vmValid += time.Since(vmstart)
|
||||
|
||||
// TODO: this should be in some more general 'validate message' call
|
||||
if msg.Message.GasLimit > build.BlockGasLimit {
|
||||
@@ -511,7 +434,6 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
||||
|
||||
from := msg.Message.From
|
||||
|
||||
getBalStart := time.Now()
|
||||
if _, ok := inclNonces[from]; !ok {
|
||||
act, err := al(ctx, from, ts.Key())
|
||||
if err != nil {
|
||||
@@ -522,16 +444,14 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
||||
inclNonces[from] = act.Nonce
|
||||
inclBalances[from] = act.Balance
|
||||
}
|
||||
getbal += time.Since(getBalStart)
|
||||
|
||||
if inclBalances[from].LessThan(msg.Message.RequiredFunds()) {
|
||||
tooLowFundMsgs++
|
||||
// todo: drop from mpool
|
||||
log.Warnf("message in mempool does not have enough funds: %s", msg.Cid())
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Message.Nonce > inclNonces[from] {
|
||||
tooHighNonceMsgs++
|
||||
log.Debugf("message in mempool has too high of a nonce (%d > %d, from %s, inclcount %d) %s (%d pending for orig)", msg.Message.Nonce, inclNonces[from], from, inclCount[from], msg.Cid(), countFrom(msgs, from))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -549,23 +469,5 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if tooLowFundMsgs > 0 {
|
||||
log.Warnf("%d messages in mempool does not have enough funds", tooLowFundMsgs)
|
||||
}
|
||||
|
||||
if tooHighNonceMsgs > 0 {
|
||||
log.Warnf("%d messages in mempool had too high nonce", tooLowFundMsgs)
|
||||
}
|
||||
|
||||
sm := time.Now()
|
||||
if sm.Sub(start) > time.Second {
|
||||
log.Warnw("SelectMessages took a long time",
|
||||
"duration", sm.Sub(start),
|
||||
"vmvalidate", vmValid,
|
||||
"getbalance", getbal,
|
||||
"msgs", len(msgs))
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ func TestMessageFiltering(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
outmsgs, err := SelectMessages(ctx, af, &types.TipSet{}, wrapMsgs(msgs))
|
||||
outmsgs, err := SelectMessages(ctx, af, nil, wrapMsgs(msgs))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user