Merge branch 'master' into adlrocha/consistent-bcast

This commit is contained in:
Alfonso de la Rocha
2023-03-28 16:55:06 +02:00
664 changed files with 45134 additions and 10746 deletions
+7 -1
View File
@@ -24,12 +24,14 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/index"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/journal"
"github.com/filecoin-project/lotus/journal/alerting"
"github.com/filecoin-project/lotus/lib/lotuslog"
"github.com/filecoin-project/lotus/lib/peermgr"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/markets/storageadapter"
"github.com/filecoin-project/lotus/node/config"
@@ -66,9 +68,10 @@ var (
ConnectionManagerKey = special{9} // Libp2p option
AutoNATSvcKey = special{10} // Libp2p option
BandwidthReporterKey = special{11} // Libp2p option
ConnGaterKey = special{12} // libp2p option
ConnGaterKey = special{12} // Libp2p option
DAGStoreKey = special{13} // constructor returns multiple values
ResourceManagerKey = special{14} // Libp2p option
UserAgentKey = special{15} // Libp2p option
)
type invoke int
@@ -125,6 +128,8 @@ const (
SetApiEndpointKey
StoreEventsKey
_nInvokes // keep this last
)
@@ -386,6 +391,7 @@ func Test() Option {
Unset(new(*peermgr.PeerMgr)),
Override(new(beacon.Schedule), testing.RandomBeacon),
Override(new(*storageadapter.DealPublisher), storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{})),
Override(new(index.MsgIndex), modules.DummyMsgIndex),
)
}
+30 -1
View File
@@ -17,8 +17,10 @@ import (
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/consensus"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/exchange"
"github.com/filecoin-project/lotus/chain/gen/slashfilter"
"github.com/filecoin-project/lotus/chain/index"
"github.com/filecoin-project/lotus/chain/market"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/messagesigner"
@@ -75,7 +77,7 @@ var ChainNode = Options(
// Consensus: Chain storage/access
Override(new(chain.Genesis), chain.LoadGenesis),
Override(new(store.WeightFunc), filcns.Weight),
Override(new(stmgr.Executor), filcns.NewTipSetExecutor()),
Override(new(stmgr.Executor), consensus.NewTipSetExecutor(filcns.RewardFunc)),
Override(new(consensus.Consensus), filcns.NewFilecoinExpectedConsensus),
Override(new(*store.ChainStore), modules.ChainStore),
Override(new(*stmgr.StateManager), modules.StateManager),
@@ -151,6 +153,8 @@ var ChainNode = Options(
Override(new(full.MpoolModuleAPI), From(new(api.Gateway))),
Override(new(full.StateModuleAPI), From(new(api.Gateway))),
Override(new(stmgr.StateManagerAPI), rpcstmgr.NewRPCStateManager),
Override(new(full.EthModuleAPI), From(new(api.Gateway))),
Override(new(full.EthEventAPI), From(new(api.Gateway))),
),
// Full node API / service startup
@@ -216,6 +220,11 @@ func ConfigFullNode(c interface{}) Option {
Override(SetupFallbackBlockstoresKey, modules.InitFallbackBlockstores),
),
// If the Eth JSON-RPC is enabled, enable storing events at the ChainStore.
// This is the case even if real-time and historic filtering are disabled,
// as it enables us to serve logs in eth_getTransactionReceipt.
If(cfg.Fevm.EnableEthRPC, Override(StoreEventsKey, modules.EnableStoringEvents)),
Override(new(dtypes.ClientImportMgr), modules.ClientImportMgr),
Override(new(dtypes.ClientBlockstore), modules.ClientBlockstore),
@@ -241,6 +250,7 @@ func ConfigFullNode(c interface{}) Option {
Unset(new(*wallet.LocalWallet)),
Override(new(wallet.Default), wallet.NilDefault),
),
// Chain node cluster enabled
If(cfg.Cluster.ClusterModeEnabled,
Override(new(*gorpc.Client), modules.NewRPCClient),
@@ -251,6 +261,25 @@ func ConfigFullNode(c interface{}) Option {
Override(new(*modules.RPCHandler), modules.NewRPCHandler),
Override(GoRPCServer, modules.NewRPCServer),
),
// Actor event filtering support
Override(new(events.EventAPI), From(new(modules.EventAPI))),
// in lite-mode Eth api is provided by gateway
ApplyIf(isFullNode,
If(cfg.Fevm.EnableEthRPC,
Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)),
Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm)),
),
If(!cfg.Fevm.EnableEthRPC,
Override(new(full.EthModuleAPI), &full.EthModuleDummy{}),
Override(new(full.EthEventAPI), &full.EthModuleDummy{}),
),
),
// enable message index for full node when configured by the user, otherwise use dummy.
If(cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.MsgIndex)),
If(!cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.DummyMsgIndex)),
)
}
+1 -1
View File
@@ -4,6 +4,7 @@ import (
"errors"
"time"
provider "github.com/ipni/index-provider"
"go.uber.org/fx"
"golang.org/x/xerrors"
@@ -12,7 +13,6 @@ import (
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/storedask"
"github.com/filecoin-project/go-state-types/abi"
provider "github.com/filecoin-project/index-provider"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/v1api"
+26 -9
View File
@@ -70,11 +70,12 @@ func defCommon() Common {
DirectPeers: nil,
},
}
}
var DefaultDefaultMaxFee = types.MustParseFIL("0.07")
var DefaultSimultaneousTransfers = uint64(20)
var (
DefaultDefaultMaxFee = types.MustParseFIL("0.07")
DefaultSimultaneousTransfers = uint64(20)
)
// DefaultFullNode returns the default config
func DefaultFullNode() *FullNode {
@@ -88,16 +89,30 @@ func DefaultFullNode() *FullNode {
SimultaneousTransfersForRetrieval: DefaultSimultaneousTransfers,
},
Chainstore: Chainstore{
EnableSplitstore: false,
EnableSplitstore: true,
Splitstore: Splitstore{
ColdStoreType: "messages",
ColdStoreType: "discard",
HotStoreType: "badger",
MarkSetType: "badger",
HotStoreFullGCFrequency: 20,
HotStoreFullGCFrequency: 20,
HotStoreMaxSpaceThreshold: 150_000_000_000,
HotstoreMaxSpaceSafetyBuffer: 50_000_000_000,
},
},
Cluster: *DefaultUserRaftConfig(),
Fevm: FevmConfig{
EnableEthRPC: false,
EthTxHashMappingLifetimeDays: 0,
Events: Events{
DisableRealTimeFilterAPI: false,
DisableHistoricFilterAPI: false,
FilterTTL: Duration(time.Hour * 24),
MaxFilters: 100,
MaxFilterResults: 10000,
MaxFilterHeightRange: 2880, // conservative limit of one day
},
},
}
}
@@ -141,7 +156,7 @@ func DefaultStorageMiner() *StorageMiner {
},
Proving: ProvingConfig{
ParallelCheckLimit: 128,
ParallelCheckLimit: 32,
PartitionCheckTimeout: Duration(20 * time.Minute),
SingleCheckTimeout: Duration(10 * time.Minute),
},
@@ -255,8 +270,10 @@ func DefaultStorageMiner() *StorageMiner {
return cfg
}
var _ encoding.TextMarshaler = (*Duration)(nil)
var _ encoding.TextUnmarshaler = (*Duration)(nil)
var (
_ encoding.TextMarshaler = (*Duration)(nil)
_ encoding.TextUnmarshaler = (*Duration)(nil)
)
// Duration is a wrapper type for time.Duration
// for decoding and encoding from/to TOML
+124
View File
@@ -341,6 +341,59 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/#
Comment: ``,
},
},
"Events": []DocField{
{
Name: "DisableRealTimeFilterAPI",
Type: "bool",
Comment: `EnableEthRPC enables APIs that
DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted.
The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`,
},
{
Name: "DisableHistoricFilterAPI",
Type: "bool",
Comment: `DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events
that occurred in the past. HistoricFilterAPI maintains a queryable index of events.
The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`,
},
{
Name: "FilterTTL",
Type: "Duration",
Comment: `FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
this time become eligible for automatic deletion.`,
},
{
Name: "MaxFilters",
Type: "int",
Comment: `MaxFilters specifies the maximum number of filters that may exist at any one time.`,
},
{
Name: "MaxFilterResults",
Type: "int",
Comment: `MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.`,
},
{
Name: "MaxFilterHeightRange",
Type: "uint64",
Comment: `MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
the entire chain)`,
},
{
Name: "DatabasePath",
Type: "string",
Comment: `DatabasePath is the full path to a sqlite database that will be used to index actor events to
support the historic filter APIs. If the database does not exist it will be created. The directory containing
the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
relative to the CWD (current working directory).`,
},
},
"FeeConfig": []DocField{
{
Name: "DefaultMaxFee",
@@ -349,6 +402,28 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/#
Comment: ``,
},
},
"FevmConfig": []DocField{
{
Name: "EnableEthRPC",
Type: "bool",
Comment: `EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.`,
},
{
Name: "EthTxHashMappingLifetimeDays",
Type: "int",
Comment: `EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
Set to 0 to keep all mappings`,
},
{
Name: "Events",
Type: "Events",
Comment: ``,
},
},
"FullNode": []DocField{
{
Name: "Client",
@@ -380,6 +455,26 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/#
Comment: ``,
},
{
Name: "Fevm",
Type: "FevmConfig",
Comment: ``,
},
{
Name: "Index",
Type: "IndexConfig",
Comment: ``,
},
},
"IndexConfig": []DocField{
{
Name: "EnableMsgIndex",
Type: "bool",
Comment: `EnableMsgIndex enables indexing of messages on chain.`,
},
},
"IndexProviderConfig": []DocField{
{
@@ -1205,6 +1300,35 @@ the compaction boundary; default is 0.`,
A value of 0 disables, while a value 1 will do full GC in every compaction.
Default is 20 (about once a week).`,
},
{
Name: "HotStoreMaxSpaceTarget",
Type: "uint64",
Comment: `HotStoreMaxSpaceTarget sets a target max disk size for the hotstore. Splitstore GC
will run moving GC if disk utilization gets within a threshold (150 GB) of the target.
Splitstore GC will NOT run moving GC if the total size of the move would get
within 50 GB of the target, and instead will run a more aggressive online GC.
If both HotStoreFullGCFrequency and HotStoreMaxSpaceTarget are set then splitstore
GC will trigger moving GC if either configuration condition is met.
A reasonable minimum is 2x fully GCed hotstore size + 50 G buffer.
At this minimum size moving GC happens every time, any smaller and moving GC won't
be able to run. In spring 2023 this minimum is ~550 GB.`,
},
{
Name: "HotStoreMaxSpaceThreshold",
Type: "uint64",
Comment: `When HotStoreMaxSpaceTarget is set Moving GC will be triggered when total moving size
exceeds HotstoreMaxSpaceTarget - HotstoreMaxSpaceThreshold`,
},
{
Name: "HotstoreMaxSpaceSafetyBuffer",
Type: "uint64",
Comment: `Safety buffer to prevent moving GC from overflowing disk when HotStoreMaxSpaceTarget
is set. Moving GC will not occur when total moving size exceeds
HotstoreMaxSpaceTarget - HotstoreMaxSpaceSafetyBuffer`,
},
},
"StorageMiner": []DocField{
{
+122 -9
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"regexp"
@@ -17,17 +18,46 @@ import (
// FromFile loads config from a specified file overriding defaults specified in
// the def parameter. If file does not exist or is empty defaults are assumed.
func FromFile(path string, def interface{}) (interface{}, error) {
func FromFile(path string, opts ...LoadCfgOpt) (interface{}, error) {
var loadOpts cfgLoadOpts
var err error
for _, opt := range opts {
if err = opt(&loadOpts); err != nil {
return nil, xerrors.Errorf("failed to apply load cfg option: %w", err)
}
}
var def interface{}
if loadOpts.defaultCfg != nil {
def, err = loadOpts.defaultCfg()
if err != nil {
return nil, xerrors.Errorf("no config found")
}
}
// check for loadability
file, err := os.Open(path)
switch {
case os.IsNotExist(err):
if loadOpts.canFallbackOnDefault != nil {
if err := loadOpts.canFallbackOnDefault(); err != nil {
return nil, err
}
}
return def, nil
case err != nil:
return nil, err
}
defer file.Close() //nolint:errcheck // The file is RO
return FromReader(file, def)
defer file.Close() //nolint:errcheck,staticcheck // The file is RO
cfgBs, err := ioutil.ReadAll(file)
if err != nil {
return nil, xerrors.Errorf("failed to read config for validation checks %w", err)
}
buf := bytes.NewBuffer(cfgBs)
if loadOpts.validate != nil {
if err := loadOpts.validate(buf.String()); err != nil {
return nil, xerrors.Errorf("config failed validation: %w", err)
}
}
return FromReader(buf, def)
}
// FromReader loads config from a reader instance.
@@ -46,7 +76,88 @@ func FromReader(reader io.Reader, def interface{}) (interface{}, error) {
return cfg, nil
}
func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
type cfgLoadOpts struct {
defaultCfg func() (interface{}, error)
canFallbackOnDefault func() error
validate func(string) error
}
type LoadCfgOpt func(opts *cfgLoadOpts) error
func SetDefault(f func() (interface{}, error)) LoadCfgOpt {
return func(opts *cfgLoadOpts) error {
opts.defaultCfg = f
return nil
}
}
func SetCanFallbackOnDefault(f func() error) LoadCfgOpt {
return func(opts *cfgLoadOpts) error {
opts.canFallbackOnDefault = f
return nil
}
}
func SetValidate(f func(string) error) LoadCfgOpt {
return func(opts *cfgLoadOpts) error {
opts.validate = f
return nil
}
}
func NoDefaultForSplitstoreTransition() error {
return xerrors.Errorf("FullNode config not found and fallback to default disallowed while we transition to splitstore discard default. Use `lotus config default` to set this repo up with a default config. Be sure to set `EnableSplitstore` to `false` if you are running a full archive node")
}
// Match the EnableSplitstore field
func MatchEnableSplitstoreField(s string) bool {
enableSplitstoreRx := regexp.MustCompile(`(?m)^\s*EnableSplitstore\s*=`)
return enableSplitstoreRx.MatchString(s)
}
func ValidateSplitstoreSet(cfgRaw string) error {
if !MatchEnableSplitstoreField(cfgRaw) {
return xerrors.Errorf("Config does not contain explicit set of EnableSplitstore field, refusing to load. Please explicitly set EnableSplitstore. Set it to false if you are running a full archival node")
}
return nil
}
type cfgUpdateOpts struct {
comment bool
keepUncommented func(string) bool
}
// UpdateCfgOpt is a functional option for updating the config
type UpdateCfgOpt func(opts *cfgUpdateOpts) error
// KeepUncommented sets a function for matching default valeus that should remain uncommented during
// a config update that comments out default values.
func KeepUncommented(f func(string) bool) UpdateCfgOpt {
return func(opts *cfgUpdateOpts) error {
opts.keepUncommented = f
return nil
}
}
func Commented(commented bool) UpdateCfgOpt {
return func(opts *cfgUpdateOpts) error {
opts.comment = commented
return nil
}
}
func DefaultKeepUncommented() UpdateCfgOpt {
return KeepUncommented(MatchEnableSplitstoreField)
}
// ConfigUpdate takes in a config and a default config and optionally comments out default values
func ConfigUpdate(cfgCur, cfgDef interface{}, opts ...UpdateCfgOpt) ([]byte, error) {
var updateOpts cfgUpdateOpts
for _, opt := range opts {
if err := opt(&updateOpts); err != nil {
return nil, xerrors.Errorf("failed to apply update cfg option to ConfigUpdate's config: %w", err)
}
}
var nodeStr, defStr string
if cfgDef != nil {
buf := new(bytes.Buffer)
@@ -68,7 +179,7 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
nodeStr = buf.String()
}
if comment {
if updateOpts.comment {
// create a map of default lines, so we can comment those out later
defLines := strings.Split(defStr, "\n")
defaults := map[string]struct{}{}
@@ -130,8 +241,10 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
}
}
// if there is the same line in the default config, comment it out it output
if _, found := defaults[strings.TrimSpace(nodeLines[i])]; (cfgDef == nil || found) && len(line) > 0 {
// filter lines from options
optsFilter := updateOpts.keepUncommented != nil && updateOpts.keepUncommented(line)
// if there is the same line in the default config, comment it out in output
if _, found := defaults[strings.TrimSpace(nodeLines[i])]; (cfgDef == nil || found) && len(line) > 0 && !optsFilter {
line = pad + "#" + line[len(pad):]
}
outLines = append(outLines, line)
@@ -159,5 +272,5 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
}
func ConfigComment(t interface{}) ([]byte, error) {
return ConfigUpdate(t, nil, true)
return ConfigUpdate(t, nil, Commented(true), DefaultKeepUncommented())
}
+78 -3
View File
@@ -11,19 +11,21 @@ import (
"github.com/stretchr/testify/assert"
)
func fullNodeDefault() (interface{}, error) { return DefaultFullNode(), nil }
func TestDecodeNothing(t *testing.T) {
//stm: @NODE_CONFIG_LOAD_FILE_002
assert := assert.New(t)
{
cfg, err := FromFile(os.DevNull, DefaultFullNode())
cfg, err := FromFile(os.DevNull, SetDefault(fullNodeDefault))
assert.Nil(err, "error should be nil")
assert.Equal(DefaultFullNode(), cfg,
"config from empty file should be the same as default")
}
{
cfg, err := FromFile("./does-not-exist.toml", DefaultFullNode())
cfg, err := FromFile("./does-not-exist.toml", SetDefault(fullNodeDefault))
assert.Nil(err, "error should be nil")
assert.Equal(DefaultFullNode(), cfg,
"config from not exisiting file should be the same as default")
@@ -58,9 +60,82 @@ func TestParitalConfig(t *testing.T) {
assert.NoError(err, "closing tmp file should not error")
defer os.Remove(fname) //nolint:errcheck
cfg, err := FromFile(fname, DefaultFullNode())
cfg, err := FromFile(fname, SetDefault(fullNodeDefault))
assert.Nil(err, "error should be nil")
assert.Equal(expected, cfg,
"config from reader should contain changes")
}
}
func TestValidateSplitstoreSet(t *testing.T) {
cfgSet := `
EnableSplitstore = false
`
assert.NoError(t, ValidateSplitstoreSet(cfgSet))
cfgSloppySet := `
EnableSplitstore = true
`
assert.NoError(t, ValidateSplitstoreSet(cfgSloppySet))
// Missing altogether
cfgMissing := `
[Chainstore]
# type: bool
# env var: LOTUS_CHAINSTORE_ENABLESPLITSTORE
# oops its mising
[Chainstore.Splitstore]
ColdStoreType = "discard"
`
err := ValidateSplitstoreSet(cfgMissing)
assert.Error(t, err)
cfgCommentedOut := `
# EnableSplitstore = false
`
err = ValidateSplitstoreSet(cfgCommentedOut)
assert.Error(t, err)
}
// Default config keeps EnableSplitstore field uncommented
func TestKeepEnableSplitstoreUncommented(t *testing.T) {
cfgStr, err := ConfigComment(DefaultFullNode())
assert.NoError(t, err)
assert.True(t, MatchEnableSplitstoreField(string(cfgStr)))
cfgStrFromDef, err := ConfigUpdate(DefaultFullNode(), DefaultFullNode(), Commented(true), DefaultKeepUncommented())
assert.NoError(t, err)
assert.True(t, MatchEnableSplitstoreField(string(cfgStrFromDef)))
}
// Loading a config with commented EnableSplitstore fails when setting validator
func TestValidateConfigSetsEnableSplitstore(t *testing.T) {
cfgCommentedOutEnableSS, err := ConfigUpdate(DefaultFullNode(), DefaultFullNode(), Commented(true))
assert.NoError(t, err)
// assert that this config comments out EnableSplitstore
assert.False(t, MatchEnableSplitstoreField(string(cfgCommentedOutEnableSS)))
// write config with commented out EnableSplitstore to file
f, err := ioutil.TempFile("", "config.toml")
fname := f.Name()
assert.NoError(t, err)
defer func() {
err = f.Close()
assert.NoError(t, err)
os.Remove(fname) //nolint:errcheck
}()
_, err = f.WriteString(string(cfgCommentedOutEnableSS))
assert.NoError(t, err)
_, err = FromFile(fname, SetDefault(fullNodeDefault), SetValidate(ValidateSplitstoreSet))
assert.Error(t, err)
}
// Loading without a config file and a default fails if the default fallback is disabled
func TestFailToFallbackToDefault(t *testing.T) {
dir, err := ioutil.TempDir("", "dirWithNoFiles")
assert.NoError(t, err)
defer assert.NoError(t, os.RemoveAll(dir))
nonExistantFileName := dir + "/notarealfile"
_, err = FromFile(nonExistantFileName, SetDefault(fullNodeDefault), SetCanFallbackOnDefault(NoDefaultForSplitstoreTransition))
assert.Error(t, err)
}
+75 -1
View File
@@ -27,6 +27,8 @@ type FullNode struct {
Fees FeeConfig
Chainstore Chainstore
Cluster UserRaftConfig
Fevm FevmConfig
Index IndexConfig
}
// // Common
@@ -168,7 +170,6 @@ type DealmakingConfig struct {
}
type IndexProviderConfig struct {
// Enable set whether to enable indexing announcement to the network and expose endpoints that
// allow indexer nodes to process announcements. Enabled by default.
Enable bool
@@ -601,6 +602,25 @@ type Splitstore struct {
// A value of 0 disables, while a value 1 will do full GC in every compaction.
// Default is 20 (about once a week).
HotStoreFullGCFrequency uint64
// HotStoreMaxSpaceTarget sets a target max disk size for the hotstore. Splitstore GC
// will run moving GC if disk utilization gets within a threshold (150 GB) of the target.
// Splitstore GC will NOT run moving GC if the total size of the move would get
// within 50 GB of the target, and instead will run a more aggressive online GC.
// If both HotStoreFullGCFrequency and HotStoreMaxSpaceTarget are set then splitstore
// GC will trigger moving GC if either configuration condition is met.
// A reasonable minimum is 2x fully GCed hotstore size + 50 G buffer.
// At this minimum size moving GC happens every time, any smaller and moving GC won't
// be able to run. In spring 2023 this minimum is ~550 GB.
HotStoreMaxSpaceTarget uint64
// When HotStoreMaxSpaceTarget is set Moving GC will be triggered when total moving size
// exceeds HotstoreMaxSpaceTarget - HotstoreMaxSpaceThreshold
HotStoreMaxSpaceThreshold uint64
// Safety buffer to prevent moving GC from overflowing disk when HotStoreMaxSpaceTarget
// is set. Moving GC will not occur when total moving size exceeds
// HotstoreMaxSpaceTarget - HotstoreMaxSpaceSafetyBuffer
HotstoreMaxSpaceSafetyBuffer uint64
}
// // Full Node
@@ -658,3 +678,57 @@ type UserRaftConfig struct {
// Tracing enables propagation of contexts across binary boundaries.
Tracing bool
}
type FevmConfig struct {
// EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
// This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.
EnableEthRPC bool
// EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
// Set to 0 to keep all mappings
EthTxHashMappingLifetimeDays int
Events Events
}
type Events struct {
// EnableEthRPC enables APIs that
// DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted.
// The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
DisableRealTimeFilterAPI bool
// DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events
// that occurred in the past. HistoricFilterAPI maintains a queryable index of events.
// The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
DisableHistoricFilterAPI bool
// FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
// this time become eligible for automatic deletion.
FilterTTL Duration
// MaxFilters specifies the maximum number of filters that may exist at any one time.
MaxFilters int
// MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.
MaxFilterResults int
// MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
// the entire chain)
MaxFilterHeightRange uint64
// DatabasePath is the full path to a sqlite database that will be used to index actor events to
// support the historic filter APIs. If the database does not exist it will be created. The directory containing
// the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
// relative to the CWD (current working directory).
DatabasePath string
// Others, not implemented yet:
// Set a limit on the number of active websocket subscriptions (may be zero)
// Set a timeout for subscription clients
// Set upper bound on index size
}
type IndexConfig struct {
// EnableMsgIndex enables indexing of messages on chain.
EnableMsgIndex bool
}
+3 -3
View File
@@ -140,7 +140,7 @@ func (t *HelloMessage) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
return fmt.Errorf("int64 negative overflow")
}
extraI = -1 - extraI
default:
@@ -250,7 +250,7 @@ func (t *LatencyMessage) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
return fmt.Errorf("int64 negative overflow")
}
extraI = -1 - extraI
default:
@@ -275,7 +275,7 @@ func (t *LatencyMessage) UnmarshalCBOR(r io.Reader) (err error) {
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
return fmt.Errorf("int64 negative overflow")
}
extraI = -1 - extraI
default:
+3
View File
@@ -158,6 +158,9 @@ func (hs *Service) SayHello(ctx context.Context, pid peer.ID) error {
if err := cborutil.WriteCborRPC(s, hmsg); err != nil {
return xerrors.Errorf("writing rpc to peer: %w", err)
}
if err := s.CloseWrite(); err != nil {
log.Warnw("CloseWrite err", "error", err)
}
go func() {
defer s.Close() //nolint:errcheck
+4 -4
View File
@@ -17,8 +17,8 @@ import (
"github.com/ipfs/go-cid"
bstore "github.com/ipfs/go-ipfs-blockstore"
offline "github.com/ipfs/go-ipfs-exchange-offline"
files "github.com/ipfs/go-ipfs-files"
format "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-libipfs/files"
logging "github.com/ipfs/go-log/v2"
"github.com/ipfs/go-merkledag"
unixfile "github.com/ipfs/go-unixfs/file"
@@ -44,7 +44,7 @@ import (
"github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-commp-utils/writer"
datatransfer "github.com/filecoin-project/go-data-transfer"
datatransfer "github.com/filecoin-project/go-data-transfer/v2"
"github.com/filecoin-project/go-fil-markets/discovery"
rm "github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/storagemarket"
@@ -1302,8 +1302,8 @@ func (a *API) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Addre
return nil, err
}
for _, s := range ps {
if strings.HasPrefix(s, dealProtoPrefix) {
res.DealProtocols = append(res.DealProtocols, s)
if strings.HasPrefix(string(s), dealProtoPrefix) {
res.DealProtocols = append(res.DealProtocols, string(s))
}
}
sort.Strings(res.DealProtocols)
+4 -4
View File
@@ -16,7 +16,7 @@ import (
dssync "github.com/ipfs/go-datastore/sync"
blockstore "github.com/ipfs/go-ipfs-blockstore"
offline "github.com/ipfs/go-ipfs-exchange-offline"
files "github.com/ipfs/go-ipfs-files"
"github.com/ipfs/go-libipfs/files"
"github.com/ipfs/go-merkledag"
unixfile "github.com/ipfs/go-unixfs/file"
"github.com/ipld/go-car"
@@ -32,7 +32,7 @@ import (
var testdata embed.FS
func TestImportLocal(t *testing.T) {
//stm: @CLIENT_STORAGE_DEALS_IMPORT_LOCAL_001, @CLIENT_RETRIEVAL_FIND_001
// stm: @CLIENT_STORAGE_DEALS_IMPORT_LOCAL_001, @CLIENT_RETRIEVAL_FIND_001
ds := dssync.MutexWrap(datastore.NewMapDatastore())
dir := t.TempDir()
im := imports.NewManager(ds, dir)
@@ -46,7 +46,7 @@ func TestImportLocal(t *testing.T) {
b, err := testdata.ReadFile("testdata/payload.txt")
require.NoError(t, err)
//stm: @CLIENT_STORAGE_DEALS_LIST_IMPORTS_001
// stm: @CLIENT_STORAGE_DEALS_LIST_IMPORTS_001
root, err := a.ClientImportLocal(ctx, bytes.NewReader(b))
require.NoError(t, err)
require.NotEqual(t, cid.Undef, root)
@@ -59,7 +59,7 @@ func TestImportLocal(t *testing.T) {
require.Equal(t, root, *it.Root)
require.True(t, strings.HasPrefix(it.CARPath, dir))
//stm: @CLIENT_DATA_HAS_LOCAL_001
// stm: @CLIENT_DATA_HAS_LOCAL_001
local, err := a.ClientHasLocal(ctx, root)
require.NoError(t, err)
require.True(t, local)
+1
View File
@@ -35,6 +35,7 @@ type FullNodeAPI struct {
full.WalletAPI
full.SyncAPI
full.RaftAPI
full.EthAPI
DS dtypes.MetadataDS
NetworkName dtypes.NetworkName
+100 -16
View File
@@ -5,17 +5,22 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
offline "github.com/ipfs/go-ipfs-exchange-offline"
cbor "github.com/ipfs/go-ipld-cbor"
ipld "github.com/ipfs/go-ipld-format"
blocks "github.com/ipfs/go-libipfs/blocks"
logging "github.com/ipfs/go-log/v2"
"github.com/ipfs/go-merkledag"
mh "github.com/multiformats/go-multihash"
@@ -24,6 +29,7 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
amt4 "github.com/filecoin-project/go-amt-ipld/v4"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-actors/actors/util/adt"
@@ -36,6 +42,7 @@ import (
"github.com/filecoin-project/lotus/lib/oldpath"
"github.com/filecoin-project/lotus/lib/oldpath/oldresolver"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
)
var log = logging.Logger("fullnode")
@@ -87,6 +94,8 @@ type ChainAPI struct {
// BaseBlockstore is the underlying blockstore
BaseBlockstore dtypes.BaseBlockstore
Repo repo.LockedRepo
}
func (m *ChainModule) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, error) {
@@ -184,25 +193,14 @@ func (a *ChainAPI) ChainGetParentReceipts(ctx context.Context, bcid cid.Cid) ([]
return nil, nil
}
// TODO: need to get the number of messages better than this
pts, err := a.Chain.LoadTipSet(ctx, types.NewTipSetKey(b.Parents...))
receipts, err := a.Chain.ReadReceipts(ctx, b.ParentMessageReceipts)
if err != nil {
return nil, err
}
cm, err := a.Chain.MessagesForTipset(ctx, pts)
if err != nil {
return nil, err
}
var out []*types.MessageReceipt
for i := 0; i < len(cm); i++ {
r, err := a.Chain.GetParentReceipt(ctx, b, i)
if err != nil {
return nil, err
}
out = append(out, r)
out := make([]*types.MessageReceipt, len(receipts))
for i := range receipts {
out[i] = &receipts[i]
}
return out, nil
@@ -587,6 +585,54 @@ func (m *ChainModule) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.M
return cm.VMMessage(), nil
}
func (a ChainAPI) ChainExportRangeInternal(ctx context.Context, head, tail types.TipSetKey, cfg api.ChainExportConfig) error {
headTs, err := a.Chain.GetTipSetFromKey(ctx, head)
if err != nil {
return xerrors.Errorf("loading tipset %s: %w", head, err)
}
tailTs, err := a.Chain.GetTipSetFromKey(ctx, tail)
if err != nil {
return xerrors.Errorf("loading tipset %s: %w", tail, err)
}
if headTs.Height() < tailTs.Height() {
return xerrors.Errorf("Height of head-tipset (%d) must be greater or equal to the height of the tail-tipset (%d)", headTs.Height(), tailTs.Height())
}
fileName := filepath.Join(a.Repo.Path(), fmt.Sprintf("snapshot_%d_%d_%d.car", tailTs.Height(), headTs.Height(), time.Now().Unix()))
if err != nil {
return err
}
f, err := os.Create(fileName)
if err != nil {
return err
}
log.Infow("Exporting chain range", "path", fileName)
// buffer writes to the chain export file.
bw := bufio.NewWriterSize(f, cfg.WriteBufferSize)
defer func() {
if err := bw.Flush(); err != nil {
log.Errorw("failed to flush buffer", "error", err)
}
if err := f.Close(); err != nil {
log.Errorw("failed to close file", "error", err)
}
}()
if err := a.Chain.ExportRange(ctx,
bw,
headTs, tailTs,
cfg.IncludeMessages, cfg.IncludeReceipts, cfg.IncludeStateRoots,
cfg.NumWorkers,
); err != nil {
return fmt.Errorf("exporting chain range: %w", err)
}
return nil
}
func (a *ChainAPI) ChainExport(ctx context.Context, nroots abi.ChainEpoch, skipoldmsgs bool, tsk types.TipSetKey) (<-chan []byte, error) {
ts, err := a.Chain.GetTipSetFromKey(ctx, tsk)
if err != nil {
@@ -654,6 +700,33 @@ func (a *ChainAPI) ChainBlockstoreInfo(ctx context.Context) (map[string]interfac
return info.Info(), nil
}
// ChainGetEvents returns the events under an event AMT root CID.
//
// TODO (raulk) make copies of this logic elsewhere use this (e.g. itests, CLI, events filter).
func (a *ChainAPI) ChainGetEvents(ctx context.Context, root cid.Cid) ([]types.Event, error) {
store := cbor.NewCborStore(a.ExposedBlockstore)
evtArr, err := amt4.LoadAMT(ctx, store, root, amt4.UseTreeBitWidth(types.EventAMTBitwidth))
if err != nil {
return nil, xerrors.Errorf("load events amt: %w", err)
}
ret := make([]types.Event, 0, evtArr.Len())
var evt types.Event
err = evtArr.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error {
if u > math.MaxInt {
return xerrors.Errorf("too many events")
}
if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil {
return err
}
ret = append(ret, evt)
return nil
})
return ret, err
}
func (a *ChainAPI) ChainPrune(ctx context.Context, opts api.PruneOpts) error {
pruner, ok := a.BaseBlockstore.(interface {
PruneChain(opts api.PruneOpts) error
@@ -664,3 +737,14 @@ func (a *ChainAPI) ChainPrune(ctx context.Context, opts api.PruneOpts) error {
return pruner.PruneChain(opts)
}
func (a *ChainAPI) ChainHotGC(ctx context.Context, opts api.HotGCOpts) error {
pruner, ok := a.BaseBlockstore.(interface {
GCHotStore(api.HotGCOpts) error
})
if !ok {
return xerrors.Errorf("base blockstore does not support hot GC (%T)", a.BaseBlockstore)
}
return pruner.GCHotStore(opts)
}
+178
View File
@@ -0,0 +1,178 @@
package full
import (
"context"
"errors"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthRPC / LOTUS_FEVM_ENABLEETHRPC")
type EthModuleDummy struct{}
func (e *EthModuleDummy) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) {
return address.Undef, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*api.EthTxReceipt, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) {
return ethtypes.EthFeeHistory{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) NetVersion(ctx context.Context) (string, error) {
return "", ErrModuleDisabled
}
func (e *EthModuleDummy) NetListening(ctx context.Context) (bool, error) {
return false, ErrModuleDisabled
}
func (e *EthModuleDummy) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) {
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) {
return ethtypes.EthHash{}, ErrModuleDisabled
}
func (e *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) {
return "", ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewFilter(ctx context.Context, filter *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) {
return false, ErrModuleDisabled
}
func (e *EthModuleDummy) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return ethtypes.EthSubscriptionID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) {
return false, ErrModuleDisabled
}
var _ EthModuleAPI = &EthModuleDummy{}
var _ EthEventAPI = &EthModuleDummy{}
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
package full
import (
"testing"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
func TestEthLogFromEvent(t *testing.T) {
// basic empty
data, topics, ok := ethLogFromEvent(nil)
require.True(t, ok)
require.Nil(t, data)
require.Nil(t, topics)
// basic topic
data, topics, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "t1",
Codec: cid.Raw,
Value: make([]byte, 32),
}})
require.True(t, ok)
require.Nil(t, data)
require.Len(t, topics, 1)
require.Equal(t, topics[0], ethtypes.EthHash{})
// basic topic with data
data, topics, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "t1",
Codec: cid.Raw,
Value: make([]byte, 32),
}, {
Flags: 0,
Key: "d",
Codec: cid.Raw,
Value: []byte{0x0},
}})
require.True(t, ok)
require.Equal(t, data, []byte{0x0})
require.Len(t, topics, 1)
require.Equal(t, topics[0], ethtypes.EthHash{})
// skip topic
_, _, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "t2",
Codec: cid.Raw,
Value: make([]byte, 32),
}})
require.False(t, ok)
// duplicate topic
_, _, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "t1",
Codec: cid.Raw,
Value: make([]byte, 32),
}, {
Flags: 0,
Key: "t1",
Codec: cid.Raw,
Value: make([]byte, 32),
}})
require.False(t, ok)
// duplicate data
_, _, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "d",
Codec: cid.Raw,
Value: make([]byte, 32),
}, {
Flags: 0,
Key: "d",
Codec: cid.Raw,
Value: make([]byte, 32),
}})
require.False(t, ok)
// unknown key is fine
data, topics, ok = ethLogFromEvent([]types.EventEntry{{
Flags: 0,
Key: "t5",
Codec: cid.Raw,
Value: make([]byte, 32),
}, {
Flags: 0,
Key: "t1",
Codec: cid.Raw,
Value: make([]byte, 32),
}})
require.True(t, ok)
require.Nil(t, data)
require.Len(t, topics, 1)
require.Equal(t, topics[0], ethtypes.EthHash{})
}
func TestReward(t *testing.T) {
baseFee := big.NewInt(100)
testcases := []struct {
maxFeePerGas, maxPriorityFeePerGas big.Int
answer big.Int
}{
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(200)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(300), answer: big.NewInt(300)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(500), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(600), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(1000), answer: big.NewInt(500)},
{maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(-50)},
}
for _, tc := range testcases {
msg := &types.Message{GasFeeCap: tc.maxFeePerGas, GasPremium: tc.maxPriorityFeePerGas}
reward := msg.EffectiveGasPremium(baseFee)
require.Equal(t, 0, reward.Int.Cmp(tc.answer.Int), reward, tc.answer)
}
}
func TestRewardPercentiles(t *testing.T) {
testcases := []struct {
percentiles []float64
txGasRewards gasRewardSorter
answer []int64
}{
{
percentiles: []float64{25, 50, 75},
txGasRewards: []gasRewardTuple{},
answer: []int64{MinGasPremium, MinGasPremium, MinGasPremium},
},
{
percentiles: []float64{25, 50, 75, 100},
txGasRewards: []gasRewardTuple{
{gasUsed: int64(0), premium: big.NewInt(300)},
{gasUsed: int64(100), premium: big.NewInt(200)},
{gasUsed: int64(350), premium: big.NewInt(100)},
{gasUsed: int64(500), premium: big.NewInt(600)},
{gasUsed: int64(300), premium: big.NewInt(700)},
},
answer: []int64{200, 700, 700, 700},
},
}
for _, tc := range testcases {
rewards, totalGasUsed := calculateRewardsAndGasUsed(tc.percentiles, tc.txGasRewards)
var gasUsed int64
for _, tx := range tc.txGasRewards {
gasUsed += tx.gasUsed
}
ans := []ethtypes.EthBigInt{}
for _, bi := range tc.answer {
ans = append(ans, ethtypes.EthBigInt(big.NewInt(bi)))
}
require.Equal(t, totalGasUsed, gasUsed)
require.Equal(t, len(ans), len(tc.percentiles))
require.Equal(t, ans, rewards)
}
}
+83 -58
View File
@@ -4,9 +4,10 @@ import (
"context"
"math"
"math/rand"
"os"
"sort"
lru "github.com/hashicorp/golang-lru"
lru "github.com/hashicorp/golang-lru/v2"
"go.uber.org/fx"
"golang.org/x/xerrors"
@@ -61,7 +62,7 @@ type GasAPI struct {
func NewGasPriceCache() *GasPriceCache {
// 50 because we usually won't access more than 40
c, err := lru.New2Q(50)
c, err := lru.New2Q[types.TipSetKey, []GasMeta](50)
if err != nil {
// err only if parameter is bad
panic(err)
@@ -73,7 +74,7 @@ func NewGasPriceCache() *GasPriceCache {
}
type GasPriceCache struct {
c *lru.TwoQueueCache
c *lru.TwoQueueCache[types.TipSetKey, []GasMeta]
}
type GasMeta struct {
@@ -84,7 +85,7 @@ type GasMeta struct {
func (g *GasPriceCache) GetTSGasStats(ctx context.Context, cstore *store.ChainStore, ts *types.TipSet) ([]GasMeta, error) {
i, has := g.c.Get(ts.Key())
if has {
return i.([]GasMeta), nil
return i, nil
}
var prices []GasMeta
@@ -248,6 +249,58 @@ func (m *GasModule) GasEstimateGasLimit(ctx context.Context, msgIn *types.Messag
}
return gasEstimateGasLimit(ctx, m.Chain, m.Stmgr, m.Mpool, msgIn, ts)
}
// gasEstimateCallWithGas invokes a message "msgIn" on the earliest available tipset with pending
// messages in the message pool. The function returns the result of the message invocation, the
// pending messages, the tipset used for the invocation, and an error if occurred.
// The returned information can be used to make subsequent calls to CallWithGas with the same parameters.
func gasEstimateCallWithGas(
ctx context.Context,
cstore *store.ChainStore,
smgr *stmgr.StateManager,
mpool *messagepool.MessagePool,
msgIn *types.Message,
currTs *types.TipSet,
) (*api.InvocResult, []types.ChainMsg, *types.TipSet, error) {
msg := *msgIn
fromA, err := smgr.ResolveToDeterministicAddress(ctx, msgIn.From, currTs)
if err != nil {
return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting key address: %w", err)
}
pending, ts := mpool.PendingFor(ctx, fromA)
priorMsgs := make([]types.ChainMsg, 0, len(pending))
for _, m := range pending {
if m.Message.Nonce == msg.Nonce {
break
}
priorMsgs = append(priorMsgs, m)
}
applyTsMessages := true
if os.Getenv("LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS") == "1" {
applyTsMessages = false
}
// Try calling until we find a height with no migration.
var res *api.InvocResult
for {
res, err = smgr.CallWithGas(ctx, &msg, priorMsgs, ts, applyTsMessages)
if err != stmgr.ErrExpensiveFork {
break
}
ts, err = cstore.GetTipSetFromKey(ctx, ts.Parents())
if err != nil {
return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting parent tipset: %w", err)
}
}
if err != nil {
return nil, []types.ChainMsg{}, nil, xerrors.Errorf("CallWithGas failed: %w", err)
}
return res, priorMsgs, ts, nil
}
func gasEstimateGasLimit(
ctx context.Context,
cstore *store.ChainStore,
@@ -261,35 +314,11 @@ func gasEstimateGasLimit(
msg.GasFeeCap = big.Zero()
msg.GasPremium = big.Zero()
fromA, err := smgr.ResolveToKeyAddress(ctx, msgIn.From, currTs)
res, _, ts, err := gasEstimateCallWithGas(ctx, cstore, smgr, mpool, &msg, currTs)
if err != nil {
return -1, xerrors.Errorf("getting key address: %w", err)
return -1, xerrors.Errorf("gas estimation failed: %w", err)
}
pending, ts := mpool.PendingFor(ctx, fromA)
priorMsgs := make([]types.ChainMsg, 0, len(pending))
for _, m := range pending {
if m.Message.Nonce == msg.Nonce {
break
}
priorMsgs = append(priorMsgs, m)
}
// Try calling until we find a height with no migration.
var res *api.InvocResult
for {
res, err = smgr.CallWithGas(ctx, &msg, priorMsgs, ts)
if err != stmgr.ErrExpensiveFork {
break
}
ts, err = cstore.GetTipSetFromKey(ctx, ts.Parents())
if err != nil {
return -1, xerrors.Errorf("getting parent tipset: %w", err)
}
}
if err != nil {
return -1, xerrors.Errorf("CallWithGas failed: %w", err)
}
if res.MsgRct.ExitCode == exitcode.SysErrOutOfGas {
return -1, &api.ErrOutOfGas{}
}
@@ -300,12 +329,19 @@ func gasEstimateGasLimit(
ret := res.MsgRct.GasUsed
log.Infow("GasEstimateMessageGas CallWithGas Result", "GasUsed", ret, "ExitCode", res.MsgRct.ExitCode)
transitionalMulti := 1.0
// Overestimate gas around the upgrade
if ts.Height() <= build.UpgradeSkyrHeight && (build.UpgradeSkyrHeight-ts.Height() <= 20) {
transitionalMulti = 2.0
if ts.Height() <= build.UpgradeHyggeHeight && (build.UpgradeHyggeHeight-ts.Height() <= 20) {
func() {
// Bare transfers get about 3x more expensive: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0057.md#product-considerations
if msgIn.Method == builtin.MethodSend {
transitionalMulti = 3.0
return
}
st, err := smgr.ParentState(ts)
if err != nil {
return
@@ -317,42 +353,31 @@ func gasEstimateGasLimit(
if lbuiltin.IsStorageMinerActor(act.Code) {
switch msgIn.Method {
case 5:
transitionalMulti = 3.954
case 3:
transitionalMulti = 1.92
case 4:
transitionalMulti = 1.72
case 6:
transitionalMulti = 4.095
transitionalMulti = 1.06
case 7:
// skip, stay at 2.0
//transitionalMulti = 1.289
case 11:
transitionalMulti = 17.8758
transitionalMulti = 1.2
case 16:
transitionalMulti = 2.1704
case 25:
transitionalMulti = 3.1177
transitionalMulti = 1.19
case 18:
transitionalMulti = 1.73
case 23:
transitionalMulti = 1.73
case 26:
transitionalMulti = 2.3322
transitionalMulti = 1.15
case 27:
transitionalMulti = 1.18
default:
}
}
// skip storage market, 80th percentie for everything ~1.9, leave it at 2.0
}()
}
ret = (ret * int64(transitionalMulti*1024)) >> 10
// Special case for PaymentChannel collect, which is deleting actor
// We ignore errors in this special case since they CAN occur,
// and we just want to detect existing payment channel actors
st, err := smgr.ParentState(ts)
if err == nil {
act, err := st.GetActor(msg.To)
if err == nil && lbuiltin.IsPaymentChannelActor(act.Code) && msgIn.Method == builtin.MethodsPaych.Collect {
// add the refunded gas for DestroyActor back into the gas used
ret += 76e3
}
}
return ret, nil
}
+1 -1
View File
@@ -175,7 +175,7 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
}
}
fromA, err := a.Stmgr.ResolveToKeyAddress(ctx, msg.From, nil)
fromA, err := a.Stmgr.ResolveToDeterministicAddress(ctx, msg.From, nil)
if err != nil {
return nil, xerrors.Errorf("getting key address: %w", err)
}
+17 -9
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
@@ -507,7 +508,7 @@ func (m *StateModule) StateAccountKey(ctx context.Context, addr address.Address,
return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
return m.StateManager.ResolveToKeyAddress(ctx, addr, ts)
return m.StateManager.ResolveToDeterministicAddress(ctx, addr, ts)
}
func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) {
@@ -615,16 +616,22 @@ func (m *StateModule) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence
vmsg := cmsg.VMMessage()
t, err := stmgr.GetReturnType(ctx, m.StateManager, vmsg.To, vmsg.Method, ts)
if err != nil {
switch t, err := stmgr.GetReturnType(ctx, m.StateManager, vmsg.To, vmsg.Method, ts); {
case errors.Is(err, stmgr.ErrMetadataNotFound):
// This is not necessarily an error -- EVM methods (and in the future native actors) may
// return just bytes, and in the not so distant future we'll have native wasm actors
// that are by definition not in the registry.
// So in this case, log a debug message and retun the raw bytes.
log.Debugf("failed to get return type: %s", err)
returndec = recpt.Return
case err != nil:
return nil, xerrors.Errorf("failed to get return type: %w", err)
default:
if err := t.UnmarshalCBOR(bytes.NewReader(recpt.Return)); err != nil {
return nil, err
}
returndec = t
}
if err := t.UnmarshalCBOR(bytes.NewReader(recpt.Return)); err != nil {
return nil, err
}
returndec = t
}
return &api.MsgLookup{
@@ -1800,6 +1807,7 @@ func (a *StateAPI) StateGetNetworkParams(ctx context.Context) (*api.NetworkParam
UpgradeOhSnapHeight: build.UpgradeOhSnapHeight,
UpgradeSkyrHeight: build.UpgradeSkyrHeight,
UpgradeSharkHeight: build.UpgradeSharkHeight,
UpgradeHyggeHeight: build.UpgradeHyggeHeight,
},
}, nil
}
+8 -3
View File
@@ -11,6 +11,7 @@ import (
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/messagesigner"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
@@ -36,7 +37,7 @@ func (a *WalletAPI) WalletBalance(ctx context.Context, addr address.Address) (ty
}
func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) {
keyAddr, err := a.StateManagerAPI.ResolveToKeyAddress(ctx, k, nil)
keyAddr, err := a.StateManagerAPI.ResolveToDeterministicAddress(ctx, k, nil)
if err != nil {
return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr)
}
@@ -46,17 +47,21 @@ func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byt
}
func (a *WalletAPI) WalletSignMessage(ctx context.Context, k address.Address, msg *types.Message) (*types.SignedMessage, error) {
keyAddr, err := a.StateManagerAPI.ResolveToKeyAddress(ctx, k, nil)
keyAddr, err := a.StateManagerAPI.ResolveToDeterministicAddress(ctx, k, nil)
if err != nil {
return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr)
}
sb, err := messagesigner.SigningBytes(msg, keyAddr.Protocol())
if err != nil {
return nil, err
}
mb, err := msg.ToStorageBlock()
if err != nil {
return nil, xerrors.Errorf("serializing message: %w", err)
}
sig, err := a.Wallet.WalletSign(ctx, keyAddr, mb.Cid().Bytes(), api.MsgMeta{
sig, err := a.Wallet.WalletSign(ctx, keyAddr, sb, api.MsgMeta{
Type: api.MTChainMsg,
Extra: mb.RawData(),
})
+6 -2
View File
@@ -91,8 +91,12 @@ func (a *NetAPI) NetPeerInfo(_ context.Context, p peer.ID) (*api.ExtendedPeerInf
protocols, err := a.Host.Peerstore().GetProtocols(p)
if err == nil {
sort.Strings(protocols)
info.Protocols = protocols
protocolStrings := make([]string, 0, len(protocols))
for _, protocol := range protocols {
protocolStrings = append(protocolStrings, string(protocol))
}
sort.Strings(protocolStrings)
info.Protocols = protocolStrings
}
if cm := a.Host.ConnManager().GetTagInfo(p); cm != nil {
+4 -16
View File
@@ -25,8 +25,8 @@ import (
"github.com/filecoin-project/dagstore/shard"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
datatransfer "github.com/filecoin-project/go-data-transfer"
gst "github.com/filecoin-project/go-data-transfer/transport/graphsync"
datatransfer "github.com/filecoin-project/go-data-transfer/v2"
gst "github.com/filecoin-project/go-data-transfer/v2/transport/graphsync"
"github.com/filecoin-project/go-fil-markets/piecestore"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/storagemarket"
@@ -539,20 +539,8 @@ func (sm *StorageMinerAPI) MarketListDeals(ctx context.Context) ([]*api.MarketDe
return sm.listDeals(ctx)
}
func (sm *StorageMinerAPI) MarketListRetrievalDeals(ctx context.Context) ([]retrievalmarket.ProviderDealState, error) {
var out []retrievalmarket.ProviderDealState
deals := sm.RetrievalProvider.ListDeals()
for _, deal := range deals {
if deal.ChannelID != nil {
if deal.ChannelID.Initiator == "" || deal.ChannelID.Responder == "" {
deal.ChannelID = nil // don't try to push unparsable peer IDs over jsonrpc
}
}
out = append(out, deal)
}
return out, nil
func (sm *StorageMinerAPI) MarketListRetrievalDeals(ctx context.Context) ([]struct{}, error) {
return []struct{}{}, nil
}
func (sm *StorageMinerAPI) MarketGetDealUpdates(ctx context.Context) (<-chan storagemarket.MinerDeal, error) {
+153
View File
@@ -0,0 +1,153 @@
package modules
import (
"context"
"path/filepath"
"time"
"github.com/multiformats/go-varint"
"go.uber.org/fx"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/events/filter"
"github.com/filecoin-project/lotus/chain/messagepool"
"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/node/config"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
)
type EventAPI struct {
fx.In
full.ChainAPI
full.StateAPI
}
var _ events.EventAPI = &EventAPI{}
func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, error) {
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEvent, error) {
ctx := helpers.LifecycleCtx(mctx, lc)
ee := &full.EthEvent{
Chain: cs,
MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange),
SubscribtionCtx: ctx,
}
if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI {
// all event functionality is disabled
// the historic filter API relies on the real time one
return ee, nil
}
ee.SubManager = &full.EthSubscriptionManager{
Chain: cs,
StateAPI: stateapi,
ChainAPI: chainapi,
}
ee.FilterStore = filter.NewMemFilterStore(cfg.Events.MaxFilters)
// Start garbage collection for filters
lc.Append(fx.Hook{
OnStart: func(context.Context) error {
go ee.GC(ctx, time.Duration(cfg.Events.FilterTTL))
return nil
},
})
// Enable indexing of actor events
var eventIndex *filter.EventIndex
if !cfg.Events.DisableHistoricFilterAPI {
var dbPath string
if cfg.Events.DatabasePath == "" {
sqlitePath, err := r.SqlitePath()
if err != nil {
return nil, err
}
dbPath = filepath.Join(sqlitePath, "events.db")
} else {
dbPath = cfg.Events.DatabasePath
}
var err error
eventIndex, err = filter.NewEventIndex(dbPath)
if err != nil {
return nil, err
}
lc.Append(fx.Hook{
OnStop: func(context.Context) error {
return eventIndex.Close()
},
})
}
ee.EventFilterManager = &filter.EventFilterManager{
ChainStore: cs,
EventIndex: eventIndex, // will be nil unless EnableHistoricFilterAPI is true
AddressResolver: func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) {
// we only want to match using f4 addresses
idAddr, err := address.NewIDAddress(uint64(emitter))
if err != nil {
return address.Undef, false
}
actor, err := sm.LoadActor(ctx, idAddr, ts)
if err != nil || actor.Address == nil {
return address.Undef, false
}
// if robust address is not f4 then we won't match against it so bail early
if actor.Address.Protocol() != address.Delegated {
return address.Undef, false
}
// we have an f4 address, make sure it's assigned by the EAM
if namespace, _, err := varint.FromUvarint(actor.Address.Payload()); err != nil || namespace != builtintypes.EthereumAddressManagerActorID {
return address.Undef, false
}
return *actor.Address, true
},
MaxFilterResults: cfg.Events.MaxFilterResults,
}
ee.TipSetFilterManager = &filter.TipSetFilterManager{
MaxFilterResults: cfg.Events.MaxFilterResults,
}
ee.MemPoolFilterManager = &filter.MemPoolFilterManager{
MaxFilterResults: cfg.Events.MaxFilterResults,
}
const ChainHeadConfidence = 1
lc.Append(fx.Hook{
OnStart: func(context.Context) error {
ev, err := events.NewEventsWithConfidence(ctx, &evapi, ChainHeadConfidence)
if err != nil {
return err
}
// ignore returned tipsets
_ = ev.Observe(ee.EventFilterManager)
_ = ev.Observe(ee.TipSetFilterManager)
ch, err := mp.Updates(ctx)
if err != nil {
return err
}
go ee.MemPoolFilterManager.WaitForMpoolUpdates(ctx, ch)
return nil
},
})
return ee, nil
}
}
+8 -5
View File
@@ -82,11 +82,14 @@ func SplitBlockstore(cfg *config.Chainstore) func(lc fx.Lifecycle, r repo.Locked
}
cfg := &splitstore.Config{
MarkSetType: cfg.Splitstore.MarkSetType,
DiscardColdBlocks: cfg.Splitstore.ColdStoreType == "discard",
UniversalColdBlocks: cfg.Splitstore.ColdStoreType == "universal",
HotStoreMessageRetention: cfg.Splitstore.HotStoreMessageRetention,
HotStoreFullGCFrequency: cfg.Splitstore.HotStoreFullGCFrequency,
MarkSetType: cfg.Splitstore.MarkSetType,
DiscardColdBlocks: cfg.Splitstore.ColdStoreType == "discard",
UniversalColdBlocks: cfg.Splitstore.ColdStoreType == "universal",
HotStoreMessageRetention: cfg.Splitstore.HotStoreMessageRetention,
HotStoreFullGCFrequency: cfg.Splitstore.HotStoreFullGCFrequency,
HotstoreMaxSpaceTarget: cfg.Splitstore.HotStoreMaxSpaceTarget,
HotstoreMaxSpaceThreshold: cfg.Splitstore.HotStoreMaxSpaceThreshold,
HotstoreMaxSpaceSafetyBuffer: cfg.Splitstore.HotstoreMaxSpaceSafetyBuffer,
}
ss, err := splitstore.Open(path, ds, hot, cold, cfg)
if err != nil {
+9 -4
View File
@@ -4,9 +4,9 @@ import (
"context"
"time"
"github.com/ipfs/go-bitswap"
"github.com/ipfs/go-bitswap/network"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-libipfs/bitswap"
"github.com/ipfs/go-libipfs/bitswap/network"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/routing"
"go.uber.org/fx"
@@ -21,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/exchange"
"github.com/filecoin-project/lotus/chain/gen/slashfilter"
"github.com/filecoin-project/lotus/chain/index"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
@@ -69,7 +70,7 @@ func MessagePool(lc fx.Lifecycle, mctx helpers.MetricsCtx, us stmgr.UpgradeSched
return mp.Close()
},
})
protector.AddProtector(mp.ForEachPendingMessage)
protector.AddProtector(mp.TryForEachPendingMessage)
return mp, nil
}
@@ -123,7 +124,7 @@ func NetworkName(mctx helpers.MetricsCtx,
ctx := helpers.LifecycleCtx(mctx, lc)
sm, err := stmgr.NewStateManager(cs, tsexec, syscalls, us, nil)
sm, err := stmgr.NewStateManager(cs, tsexec, syscalls, us, nil, nil, index.DummyMsgIndex)
if err != nil {
return "", err
}
@@ -181,3 +182,7 @@ func NewSlashFilter(ds dtypes.MetadataDS) *slashfilter.SlashFilter {
func UpgradeSchedule() stmgr.UpgradeSchedule {
return filcns.DefaultUpgradeSchedule()
}
func EnableStoringEvents(cs *store.ChainStore) {
cs.StoreEvents(true)
}
+4 -14
View File
@@ -13,10 +13,10 @@ import (
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-data-transfer/channelmonitor"
dtimpl "github.com/filecoin-project/go-data-transfer/impl"
dtnet "github.com/filecoin-project/go-data-transfer/network"
dtgstransport "github.com/filecoin-project/go-data-transfer/transport/graphsync"
"github.com/filecoin-project/go-data-transfer/v2/channelmonitor"
dtimpl "github.com/filecoin-project/go-data-transfer/v2/impl"
dtnet "github.com/filecoin-project/go-data-transfer/v2/network"
dtgstransport "github.com/filecoin-project/go-data-transfer/v2/transport/graphsync"
"github.com/filecoin-project/go-fil-markets/discovery"
discoveryimpl "github.com/filecoin-project/go-fil-markets/discovery/impl"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
@@ -24,7 +24,6 @@ import (
rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network"
"github.com/filecoin-project/go-fil-markets/storagemarket"
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
smnet "github.com/filecoin-project/go-fil-markets/storagemarket/network"
"github.com/filecoin-project/go-state-types/abi"
@@ -95,15 +94,6 @@ func ClientBlockstore() dtypes.ClientBlockstore {
return blockstore.WrapIDStore(blockstore.FromDatastore(datastore.NewMapDatastore()))
}
// RegisterClientValidator is an initialization hook that registers the client
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterClientValidator(crv dtypes.ClientRequestValidator, dtm dtypes.ClientDataTransfer) {
if err := dtm.RegisterVoucherType(&requestvalidation.StorageDataTransferVoucher{}, (*requestvalidation.UnifiedRequestValidator)(crv)); err != nil {
panic(err)
}
}
// NewClientGraphsyncDataTransfer returns a data transfer manager that just
// uses the clients's Client DAG service for transfers
func NewClientGraphsyncDataTransfer(lc fx.Lifecycle, h host.Host, gs dtypes.Graphsync, ds dtypes.MetadataDS, r repo.LockedRepo) (dtypes.ClientDataTransfer, error) {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"github.com/ipfs/go-graphsync"
exchange "github.com/ipfs/go-ipfs-exchange-interface"
datatransfer "github.com/filecoin-project/go-data-transfer"
dtnet "github.com/filecoin-project/go-data-transfer/network"
datatransfer "github.com/filecoin-project/go-data-transfer/v2"
dtnet "github.com/filecoin-project/go-data-transfer/v2/network"
"github.com/filecoin-project/go-fil-markets/piecestore"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
"github.com/filecoin-project/go-statestore"
+93
View File
@@ -0,0 +1,93 @@
package modules
import (
"context"
"os"
"path/filepath"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/chain/ethhashlookup"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/node/config"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
)
func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI, full.MpoolAPI) (*full.EthModule, error) {
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI, mpoolapi full.MpoolAPI) (*full.EthModule, error) {
sqlitePath, err := r.SqlitePath()
if err != nil {
return nil, err
}
dbPath := filepath.Join(sqlitePath, "txhash.db")
// Check if the db exists, if not, we'll back-fill some entries
_, err = os.Stat(dbPath)
dbAlreadyExists := err == nil
transactionHashLookup, err := ethhashlookup.NewTransactionHashLookup(dbPath)
if err != nil {
return nil, err
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return transactionHashLookup.Close()
},
})
ethTxHashManager := full.EthTxHashManager{
StateAPI: stateapi,
TransactionHashLookup: transactionHashLookup,
}
if !dbAlreadyExists {
err = ethTxHashManager.PopulateExistingMappings(mctx, 0)
if err != nil {
return nil, err
}
}
const ChainHeadConfidence = 1
ctx := helpers.LifecycleCtx(mctx, lc)
lc.Append(fx.Hook{
OnStart: func(context.Context) error {
ev, err := events.NewEventsWithConfidence(ctx, &evapi, ChainHeadConfidence)
if err != nil {
return err
}
// Tipset listener
_ = ev.Observe(&ethTxHashManager)
ch, err := mp.Updates(ctx)
if err != nil {
return err
}
go full.WaitForMpoolUpdates(ctx, ch, &ethTxHashManager)
go full.EthTxHashGC(ctx, cfg.EthTxHashMappingLifetimeDays, &ethTxHashManager)
return nil
},
})
return &full.EthModule{
Chain: cs,
Mpool: mp,
StateManager: sm,
ChainAPI: chainapi,
MpoolAPI: mpoolapi,
StateAPI: stateapi,
EthTxHashManager: &ethTxHashManager,
}, nil
}
}
+7
View File
@@ -69,6 +69,13 @@ func Host(mctx helpers.MetricsCtx, lc fx.Lifecycle, params P2PHostIn) (RawHost,
return h, nil
}
func UserAgentOption(agent string) func() (opts Libp2pOpts, err error) {
return func() (opts Libp2pOpts, err error) {
opts.Opts = append(opts.Opts, libp2p.UserAgent(agent))
return
}
}
func MockHost(mn mocknet.Mocknet, id peer.ID, ps peerstore.Peerstore) (RawHost, error) {
return mn.AddPeerWithPeerstore(id, ps)
}
+32 -26
View File
@@ -7,6 +7,7 @@ import (
"math/bits"
"os"
"path/filepath"
"sync"
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p"
@@ -14,10 +15,9 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
"github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs"
rcmgrObs "github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs"
"github.com/prometheus/client_golang/prometheus"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"go.uber.org/fx"
@@ -25,6 +25,8 @@ import (
"github.com/filecoin-project/lotus/node/repo"
)
var rcmgrMetricsOnce sync.Once
func ResourceManager(connMgrHi uint) func(lc fx.Lifecycle, repo repo.LockedRepo) (network.ResourceManager, error) {
return func(lc fx.Lifecycle, repo repo.LockedRepo) (network.ResourceManager, error) {
isFullNode := repo.RepoType().Type() == "FullNode"
@@ -32,7 +34,7 @@ func ResourceManager(connMgrHi uint) func(lc fx.Lifecycle, repo repo.LockedRepo)
if (isFullNode && envvar == "0") || // only set NullResourceManager if envvar is explicitly "0"
(!isFullNode && envvar != "1") { // set NullResourceManager *unless* envvar is explicitly "1"
log.Info("libp2p resource manager is disabled")
return network.NullResourceManager, nil
return &network.NullResourceManager{}, nil
}
log.Info("libp2p resource manager is enabled")
@@ -52,37 +54,41 @@ func ResourceManager(connMgrHi uint) func(lc fx.Lifecycle, repo repo.LockedRepo)
// For every extra 1GB of memory we have available, increase our limit by 1GiB
defaultLimits.SystemLimitIncrease.Memory = 1 << 30
defaultLimitConfig := defaultLimits.AutoScale()
if defaultLimitConfig.System.Memory > 4<<30 {
changes := rcmgr.PartialLimitConfig{}
if defaultLimitConfig.ToPartialLimitConfig().System.Memory > 4<<30 {
// Cap our memory limit
defaultLimitConfig.System.Memory = 4 << 30
changes.System.Memory = 4 << 30
}
maxconns := int(connMgrHi)
if 2*maxconns > defaultLimitConfig.System.ConnsInbound {
if rcmgr.LimitVal(2*maxconns) > defaultLimitConfig.ToPartialLimitConfig().System.ConnsInbound {
// adjust conns to 2x to allow for two conns per peer (TCP+QUIC)
defaultLimitConfig.System.ConnsInbound = logScale(2 * maxconns)
defaultLimitConfig.System.ConnsOutbound = logScale(2 * maxconns)
defaultLimitConfig.System.Conns = logScale(4 * maxconns)
changes.System.ConnsInbound = rcmgr.LimitVal(logScale(2 * maxconns))
changes.System.ConnsOutbound = rcmgr.LimitVal(logScale(2 * maxconns))
changes.System.Conns = rcmgr.LimitVal(logScale(4 * maxconns))
defaultLimitConfig.System.StreamsInbound = logScale(16 * maxconns)
defaultLimitConfig.System.StreamsOutbound = logScale(64 * maxconns)
defaultLimitConfig.System.Streams = logScale(64 * maxconns)
changes.System.StreamsInbound = rcmgr.LimitVal(logScale(16 * maxconns))
changes.System.StreamsOutbound = rcmgr.LimitVal(logScale(64 * maxconns))
changes.System.Streams = rcmgr.LimitVal(logScale(64 * maxconns))
if 2*maxconns > defaultLimitConfig.System.FD {
defaultLimitConfig.System.FD = logScale(2 * maxconns)
if rcmgr.LimitVal(2*maxconns) > defaultLimitConfig.ToPartialLimitConfig().System.FD {
changes.System.FD = rcmgr.LimitVal(logScale(2 * maxconns))
}
defaultLimitConfig.ServiceDefault.StreamsInbound = logScale(8 * maxconns)
defaultLimitConfig.ServiceDefault.StreamsOutbound = logScale(32 * maxconns)
defaultLimitConfig.ServiceDefault.Streams = logScale(32 * maxconns)
changes.ServiceDefault.StreamsInbound = rcmgr.LimitVal(logScale(8 * maxconns))
changes.ServiceDefault.StreamsOutbound = rcmgr.LimitVal(logScale(32 * maxconns))
changes.ServiceDefault.Streams = rcmgr.LimitVal(logScale(32 * maxconns))
defaultLimitConfig.ProtocolDefault.StreamsInbound = logScale(8 * maxconns)
defaultLimitConfig.ProtocolDefault.StreamsOutbound = logScale(32 * maxconns)
defaultLimitConfig.ProtocolDefault.Streams = logScale(32 * maxconns)
changes.ProtocolDefault.StreamsInbound = rcmgr.LimitVal(logScale(8 * maxconns))
changes.ProtocolDefault.StreamsOutbound = rcmgr.LimitVal(logScale(32 * maxconns))
changes.ProtocolDefault.Streams = rcmgr.LimitVal(logScale(32 * maxconns))
log.Info("adjusted default resource manager limits")
}
changedLimitConfig := changes.Build(defaultLimitConfig)
// initialize
var limiter rcmgr.Limiter
var opts []rcmgr.Option
@@ -95,13 +101,13 @@ func ResourceManager(connMgrHi uint) func(lc fx.Lifecycle, repo repo.LockedRepo)
switch {
case err == nil:
defer limitsIn.Close() //nolint:errcheck
limiter, err = rcmgr.NewLimiterFromJSON(limitsIn, defaultLimitConfig)
limiter, err = rcmgr.NewLimiterFromJSON(limitsIn, changedLimitConfig)
if err != nil {
return nil, fmt.Errorf("error parsing limit file: %w", err)
}
case errors.Is(err, os.ErrNotExist):
limiter = rcmgr.NewFixedLimiter(defaultLimitConfig)
limiter = rcmgr.NewFixedLimiter(changedLimitConfig)
default:
return nil, err
@@ -111,10 +117,10 @@ func ResourceManager(connMgrHi uint) func(lc fx.Lifecycle, repo repo.LockedRepo)
if err != nil {
return nil, fmt.Errorf("error creating resource manager stats reporter: %w", err)
}
err = view.Register(obs.DefaultViews...)
if err != nil {
return nil, fmt.Errorf("error registering rcmgr metrics: %w", err)
}
rcmgrMetricsOnce.Do(func() {
rcmgrObs.MustRegisterWith(prometheus.DefaultRegisterer)
})
// Metrics
opts = append(opts, rcmgr.WithMetrics(rcmgrMetrics{}), rcmgr.WithTraceReporter(str))
+36
View File
@@ -0,0 +1,36 @@
package modules
import (
"context"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/chain/index"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
)
func MsgIndex(lc fx.Lifecycle, mctx helpers.MetricsCtx, cs *store.ChainStore, r repo.LockedRepo) (index.MsgIndex, error) {
basePath, err := r.SqlitePath()
if err != nil {
return nil, err
}
msgIndex, err := index.NewMsgIndex(helpers.LifecycleCtx(mctx, lc), basePath, cs)
if err != nil {
return nil, err
}
lc.Append(fx.Hook{
OnStop: func(_ context.Context) error {
return msgIndex.Close()
},
})
return msgIndex, nil
}
func DummyMsgIndex() index.MsgIndex {
return index.DummyMsgIndex
}
+2 -1
View File
@@ -12,6 +12,7 @@ import (
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
"go.uber.org/fx"
"golang.org/x/xerrors"
@@ -84,7 +85,7 @@ func RunHello(mctx helpers.MetricsCtx, lc fx.Lifecycle, h host.Host, svc *hello.
return nil
}
func protosContains(protos []string, search string) bool {
func protosContains(protos []protocol.ID, search protocol.ID) bool {
for _, p := range protos {
if p == search {
return true
+4 -2
View File
@@ -4,13 +4,15 @@ import (
"go.uber.org/fx"
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/index"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
func StateManager(lc fx.Lifecycle, cs *store.ChainStore, exec stmgr.Executor, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule, b beacon.Schedule) (*stmgr.StateManager, error) {
sm, err := stmgr.NewStateManager(cs, exec, sys, us, b)
func StateManager(lc fx.Lifecycle, cs *store.ChainStore, exec stmgr.Executor, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule, b beacon.Schedule, metadataDs dtypes.MetadataDS, msgIndex index.MsgIndex) (*stmgr.StateManager, error) {
sm, err := stmgr.NewStateManager(cs, exec, sys, us, b, metadataDs, msgIndex)
if err != nil {
return nil, err
}
+4 -4
View File
@@ -18,15 +18,16 @@ import (
graphsync "github.com/ipfs/go-graphsync/impl"
gsnet "github.com/ipfs/go-graphsync/network"
"github.com/ipfs/go-graphsync/storeutil"
provider "github.com/ipni/index-provider"
"github.com/libp2p/go-libp2p/core/host"
"go.uber.org/fx"
"go.uber.org/multierr"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
dtimpl "github.com/filecoin-project/go-data-transfer/impl"
dtnet "github.com/filecoin-project/go-data-transfer/network"
dtgstransport "github.com/filecoin-project/go-data-transfer/transport/graphsync"
dtimpl "github.com/filecoin-project/go-data-transfer/v2/impl"
dtnet "github.com/filecoin-project/go-data-transfer/v2/network"
dtgstransport "github.com/filecoin-project/go-data-transfer/v2/transport/graphsync"
piecefilestore "github.com/filecoin-project/go-fil-markets/filestore"
piecestoreimpl "github.com/filecoin-project/go-fil-markets/piecestore/impl"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
@@ -42,7 +43,6 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-statestore"
provider "github.com/filecoin-project/index-provider"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/v0api"
+8 -3
View File
@@ -5,14 +5,14 @@ import (
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
provider "github.com/ipni/index-provider"
"github.com/ipni/index-provider/engine"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/host"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
provider "github.com/filecoin-project/index-provider"
"github.com/filecoin-project/index-provider/engine"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/node/config"
@@ -41,10 +41,15 @@ func IndexProvider(cfg config.IndexProviderConfig) func(params IdxProv, marketHo
}
ipds := namespace.Wrap(args.Datastore, datastore.NewKey("/index-provider"))
addrs := marketHost.Addrs()
addrsString := make([]string, 0, len(addrs))
for _, addr := range addrs {
addrsString = append(addrsString, addr.String())
}
var opts = []engine.Option{
engine.WithDatastore(ipds),
engine.WithHost(marketHost),
engine.WithRetrievalAddrs(marketHost.Addrs()...),
engine.WithRetrievalAddrs(addrsString...),
engine.WithEntriesCacheCapacity(cfg.EntriesCacheCapacity),
engine.WithChainedEntries(cfg.EntriesChunkSize),
engine.WithTopicName(topicName),
+1 -1
View File
@@ -7,6 +7,7 @@ import (
"time"
"github.com/ipfs/go-datastore"
provider "github.com/ipni/index-provider"
"github.com/libp2p/go-libp2p"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/host"
@@ -14,7 +15,6 @@ import (
"go.uber.org/fx"
"github.com/filecoin-project/go-address"
provider "github.com/filecoin-project/index-provider"
"github.com/filecoin-project/lotus/node/config"
"github.com/filecoin-project/lotus/node/modules"
+4 -5
View File
@@ -31,7 +31,7 @@ const (
type LotusTraceEvent struct {
Type pubsub_pb.TraceEvent_Type `json:"type,omitempty"`
PeerID string `json:"peerID,omitempty"`
PeerID []byte `json:"peerID,omitempty"`
Timestamp *int64 `json:"timestamp,omitempty"`
PeerScore TraceEventPeerScore `json:"peerScore,omitempty"`
SourceAuth string `json:"sourceAuth,omitempty"`
@@ -46,7 +46,7 @@ type TopicScore struct {
}
type TraceEventPeerScore struct {
PeerID string `json:"peerID"`
PeerID []byte `json:"peerID"`
Score float64 `json:"score"`
AppSpecificScore float64 `json:"appSpecificScore"`
IPColocationFactor float64 `json:"ipColocationFactor"`
@@ -77,11 +77,11 @@ func (lt *lotusTracer) PeerScores(scores map[peer.ID]*pubsub.PeerScoreSnapshot)
evt := &LotusTraceEvent{
Type: *TraceEventPeerScores.Enum(),
PeerID: lt.pid.Pretty(),
Timestamp: &now,
PeerID: []byte(lt.pid),
SourceAuth: lt.sa,
PeerScore: TraceEventPeerScore{
PeerID: pid.Pretty(),
PeerID: []byte(pid),
Score: score.Score,
AppSpecificScore: score.AppSpecificScore,
IPColocationFactor: score.IPColocationFactor,
@@ -104,7 +104,6 @@ func (lt *lotusTracer) TraceLotusEvent(evt *LotusTraceEvent) {
log.Errorf("error while transporting peer scores: %s", err)
}
}
}
func (lt *lotusTracer) Trace(evt *pubsub_pb.TraceEvent) {
+2 -4
View File
@@ -30,16 +30,15 @@ func (ttt *testTracerTransport) Transport(evt TracerTransportEvent) error {
}
func TestTracer_PeerScores(t *testing.T) {
testTransport := NewTestTraceTransport(t, func(t *testing.T, evt TracerTransportEvent) {
require.Equal(t, peerIDA.Pretty(), evt.lotusTraceEvent.PeerID)
require.Equal(t, []byte(peerIDA), evt.lotusTraceEvent.PeerID)
require.Equal(t, "source-auth-token-test", evt.lotusTraceEvent.SourceAuth)
require.Equal(t, float64(32), evt.lotusTraceEvent.PeerScore.Score)
n := time.Now().UnixNano()
require.LessOrEqual(t, *evt.lotusTraceEvent.Timestamp, n)
require.Equal(t, peerIDA.Pretty(), evt.lotusTraceEvent.PeerScore.PeerID)
require.Equal(t, []byte(peerIDA), evt.lotusTraceEvent.PeerScore.PeerID)
require.Equal(t, 1, len(evt.lotusTraceEvent.PeerScore.Topics))
topic := evt.lotusTraceEvent.PeerScore.Topics[0]
@@ -85,7 +84,6 @@ func TestTracer_PubSubTrace(t *testing.T) {
PeerID: []byte(peerIDA),
Timestamp: &n,
})
}
func TestTracer_MultipleTransports(t *testing.T) {
+18 -1
View File
@@ -1,6 +1,11 @@
package repo
import badgerbs "github.com/filecoin-project/lotus/blockstore/badger"
import (
"os"
"strconv"
badgerbs "github.com/filecoin-project/lotus/blockstore/badger"
)
// BadgerBlockstoreOptions returns the badger options to apply for the provided
// domain.
@@ -42,5 +47,17 @@ func BadgerBlockstoreOptions(domain BlockstoreDomain, path string, readonly bool
opts.ReadOnly = readonly
// Envvar LOTUS_CHAIN_BADGERSTORE_COMPACTIONWORKERNUM
// Allows the number of compaction workers used by BadgerDB to be adjusted
// Unset - leaves the default number of compaction workers (4)
// "0" - disables compaction
// Positive integer - enables that number of compaction workers
if badgerNumCompactors, badgerNumCompactorsSet := os.LookupEnv("LOTUS_CHAIN_BADGERSTORE_COMPACTIONWORKERNUM"); badgerNumCompactorsSet {
if numWorkers, err := strconv.Atoi(badgerNumCompactors); err == nil && numWorkers >= 0 {
opts.NumCompactors = numWorkers
}
}
return opts, nil
}
+32 -14
View File
@@ -27,6 +27,7 @@ import (
"github.com/filecoin-project/lotus/node/config"
"github.com/filecoin-project/lotus/storage/sealer/fsutil"
"github.com/filecoin-project/lotus/storage/sealer/storiface"
"github.com/filecoin-project/lotus/system"
)
const (
@@ -37,6 +38,7 @@ const (
fsDatastore = "datastore"
fsLock = "repo.lock"
fsKeystore = "keystore"
fsSqlite = "sqlite"
)
func NewRepoTypeFromString(t string) RepoType {
@@ -411,6 +413,10 @@ type fsLockedRepo struct {
ssErr error
ssOnce sync.Once
sqlPath string
sqlErr error
sqlOnce sync.Once
storageLk sync.Mutex
configLk sync.Mutex
}
@@ -474,19 +480,8 @@ func (fsr *fsLockedRepo) Blockstore(ctx context.Context, domain BlockstoreDomain
return
}
//
// Tri-state environment variable LOTUS_CHAIN_BADGERSTORE_DISABLE_FSYNC
// - unset == the default (currently fsync enabled)
// - set with a false-y value == fsync enabled no matter what a future default is
// - set with any other value == fsync is disabled ignored defaults (recommended for day-to-day use)
//
if nosyncBs, nosyncBsSet := os.LookupEnv("LOTUS_CHAIN_BADGERSTORE_DISABLE_FSYNC"); nosyncBsSet {
nosyncBs = strings.ToLower(nosyncBs)
if nosyncBs == "" || nosyncBs == "0" || nosyncBs == "false" || nosyncBs == "no" {
opts.SyncWrites = true
} else {
opts.SyncWrites = false
}
if system.BadgerFsyncDisable {
opts.SyncWrites = false
}
bs, err := badgerbs.Open(opts)
@@ -515,6 +510,21 @@ func (fsr *fsLockedRepo) SplitstorePath() (string, error) {
return fsr.ssPath, fsr.ssErr
}
func (fsr *fsLockedRepo) SqlitePath() (string, error) {
fsr.sqlOnce.Do(func() {
path := fsr.join(fsSqlite)
if err := os.MkdirAll(path, 0755); err != nil {
fsr.sqlErr = err
return
}
fsr.sqlPath = path
})
return fsr.sqlPath, fsr.sqlErr
}
// join joins path elements with fsr.path
func (fsr *fsLockedRepo) join(paths ...string) string {
return filepath.Join(append([]string{fsr.path}, paths...)...)
@@ -535,7 +545,15 @@ func (fsr *fsLockedRepo) Config() (interface{}, error) {
}
func (fsr *fsLockedRepo) loadConfigFromDisk() (interface{}, error) {
return config.FromFile(fsr.configPath, fsr.repoType.Config())
var opts []config.LoadCfgOpt
if fsr.repoType == FullNode {
opts = append(opts, config.SetCanFallbackOnDefault(config.NoDefaultForSplitstoreTransition))
opts = append(opts, config.SetValidate(config.ValidateSplitstoreSet))
}
opts = append(opts, config.SetDefault(func() (interface{}, error) {
return fsr.repoType.Config(), nil
}))
return config.FromFile(fsr.configPath, opts...)
}
func (fsr *fsLockedRepo) SetConfig(c func(interface{})) error {
+3
View File
@@ -69,6 +69,9 @@ type LockedRepo interface {
// SplitstorePath returns the path for the SplitStore
SplitstorePath() (string, error)
// SqlitePath returns the path for the Sqlite database
SqlitePath() (string, error)
// Returns config in this repo
Config() (interface{}, error)
SetConfig(func(interface{})) error
+8
View File
@@ -277,6 +277,14 @@ func (lmem *lockedMemRepo) SplitstorePath() (string, error) {
return splitstorePath, nil
}
func (lmem *lockedMemRepo) SqlitePath() (string, error) {
sqlitePath := filepath.Join(lmem.Path(), "sqlite")
if err := os.MkdirAll(sqlitePath, 0755); err != nil {
return "", err
}
return sqlitePath, nil
}
func (lmem *lockedMemRepo) ListDatastores(ns string) ([]int64, error) {
return nil, nil
}
+5 -3
View File
@@ -75,10 +75,12 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
m := mux.NewRouter()
serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(api.RPCErrors))...)
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithReverseClient[api.EthSubscriberMethods]("Filecoin"), jsonrpc.WithServerErrors(api.RPCErrors))...)
rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
api.CreateEthRPCAliases(rpcServer)
var handler http.Handler = rpcServer
if permissioned {
handler = &auth.Handler{Verify: a.AuthVerify, Next: rpcServer.ServeHTTP}
@@ -92,8 +94,9 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
fnapi = api.PermissionedFullAPI(fnapi)
}
var v0 v0api.FullNode = &(struct{ v0api.FullNode }{&v0api.WrapperV1Full{FullNode: fnapi}})
serveRpc("/rpc/v1", fnapi)
serveRpc("/rpc/v0", &v0api.WrapperV1Full{FullNode: fnapi})
serveRpc("/rpc/v0", v0)
// Import handler
handleImportFunc := handleImport(a.(*impl.FullNodeAPI))
@@ -105,7 +108,6 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
Next: handleImportFunc,
}
m.Handle("/rest/v0/import", importAH)
exportAH := &auth.Handler{
Verify: a.AuthVerify,
Next: handleExportFunc,
+1
View File
@@ -51,6 +51,7 @@ func MonitorShutdown(triggerCh <-chan struct{}, handlers ...ShutdownHandler) <-c
close(out)
}()
signal.Reset(syscall.SIGTERM, syscall.SIGINT)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
return out
}