-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SR2 miner selector & safer default StorageConfig config #614
Changes from all commits
eef8da8
82bc2fa
076a67d
108f871
4846cff
615e2ff
0a15ab7
dc26aea
a3324b9
756c59f
0571c62
d12e5c1
bb2b732
59dc34f
167f09c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,17 +31,17 @@ var ( | |
// recover its last configured default StorageConfig from the datastore. | ||
zeroConfig = ffs.StorageConfig{ | ||
Hot: ffs.HotConfig{ | ||
Enabled: true, | ||
Enabled: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decided with @asutula to avoid disk usage explosion. |
||
Ipfs: ffs.IpfsConfig{ | ||
AddTimeout: 30, | ||
AddTimeout: 300, // 5 min | ||
}, | ||
}, | ||
Cold: ffs.ColdConfig{ | ||
Enabled: true, | ||
Filecoin: ffs.FilConfig{ | ||
RepFactor: 5, | ||
TrustedMiners: []string{"t016303", "t016304", "t016305", "t016306", "t016309"}, | ||
DealMinDuration: util.MinDealDuration * 2, | ||
DealMinDuration: util.MinDealDuration, | ||
FastRetrieval: true, | ||
DealStartOffset: 72 * 60 * 60 / util.EpochDurationSeconds, // 72hs | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package sr2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Summary of the selector:
|
||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"math/rand" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/filecoin-project/go-address" | ||
"github.com/filecoin-project/lotus/api/apistruct" | ||
"github.com/filecoin-project/lotus/chain/types" | ||
logger "github.com/ipfs/go-log/v2" | ||
"github.com/textileio/powergate/ffs" | ||
askindex "github.com/textileio/powergate/index/ask/runner" | ||
"github.com/textileio/powergate/lotus" | ||
) | ||
|
||
var ( | ||
log = logger.Logger("sr2-miner-selector") | ||
) | ||
|
||
// MinerSelector chooses miner under SR2 strategy. | ||
type MinerSelector struct { | ||
url string | ||
ai *askindex.Runner | ||
cb lotus.ClientBuilder | ||
} | ||
|
||
var _ ffs.MinerSelector = (*MinerSelector)(nil) | ||
|
||
type minersBuckets struct { | ||
Buckets []bucket | ||
} | ||
|
||
type bucket struct { | ||
Amount int | ||
MinerAddresses []string | ||
} | ||
|
||
// New returns a new SR2 miner selector. | ||
func New(url string, ai *askindex.Runner, cb lotus.ClientBuilder) (*MinerSelector, error) { | ||
ms := &MinerSelector{url: url, ai: ai, cb: cb} | ||
|
||
_, err := ms.getMiners() | ||
if err != nil { | ||
return nil, fmt.Errorf("verifying sr2 url content: %s", err) | ||
} | ||
|
||
return ms, nil | ||
} | ||
|
||
// GetMiners returns miners from SR2. | ||
func (ms *MinerSelector) GetMiners(n int, f ffs.MinerSelectorFilter) ([]ffs.MinerProposal, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Important fact. This MinerSelector will ignore So this is a very aggressive miner selector kind of forcing what was decided in SR2. No custom StorageConfig will change this fact, unless the user runs with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't remember, but I suppose the replication factor provided in the storage config is passed into a miner selector via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, is ignored. Now will be controled in this remote file now that |
||
asks := ms.ai.Get() | ||
mb, err := ms.getMiners() | ||
if err != nil { | ||
return nil, fmt.Errorf("getting miners from url: %s", err) | ||
} | ||
|
||
c, cls, err := ms.cb() | ||
if err != nil { | ||
return nil, fmt.Errorf("creating lotus client: %s", err) | ||
} | ||
defer cls() | ||
|
||
var selected []string | ||
for _, bucket := range mb.Buckets { | ||
rand.Seed(time.Now().UnixNano()) | ||
miners := bucket.MinerAddresses | ||
rand.Shuffle(len(miners), func(i, j int) { miners[i], miners[j] = miners[j], miners[i] }) | ||
|
||
// Stay safe. | ||
if bucket.Amount < 0 { | ||
bucket.Amount = 0 | ||
} | ||
if bucket.Amount > len(miners) { | ||
bucket.Amount = len(miners) | ||
} | ||
selected = append(selected, miners[:bucket.Amount]...) | ||
} | ||
|
||
if len(selected) == 0 { | ||
return nil, fmt.Errorf("no SR2 miners are available") | ||
} | ||
|
||
res := make([]ffs.MinerProposal, 0, len(selected)) | ||
for _, miner := range selected { | ||
sa, ok := asks.Storage[miner] | ||
if !ok { | ||
sask, err := getMinerQueryAsk(c, miner) | ||
if err != nil { | ||
log.Warnf("miner %s not in ask cache and query-ask errored: %s", miner, err) | ||
continue | ||
} | ||
|
||
log.Infof("miner %s not in ask-cache, direct query-ask price: %d", miner, sask) | ||
sa.Price = sask | ||
} | ||
res = append(res, ffs.MinerProposal{ | ||
Addr: miner, | ||
EpochPrice: sa.Price, | ||
}) | ||
} | ||
|
||
return res, nil | ||
} | ||
|
||
func (ms *MinerSelector) getMiners() (minersBuckets, error) { | ||
r, err := http.DefaultClient.Get(ms.url) | ||
if err != nil { | ||
return minersBuckets{}, fmt.Errorf("getting miners list from url: %s", err) | ||
} | ||
defer func() { | ||
if err := r.Body.Close(); err != nil { | ||
log.Warnf("closing request body from sr2 file: %s", err) | ||
} | ||
}() | ||
content, err := ioutil.ReadAll(r.Body) | ||
if err != nil { | ||
return minersBuckets{}, fmt.Errorf("reading body: %s", err) | ||
} | ||
var res minersBuckets | ||
if err := json.Unmarshal(content, &res); err != nil { | ||
return minersBuckets{}, fmt.Errorf("unmarshaling url contents: %s", err) | ||
} | ||
return res, nil | ||
} | ||
|
||
func getMinerQueryAsk(c *apistruct.FullNodeStruct, addrStr string) (uint64, error) { | ||
addr, err := address.NewFromString(addrStr) | ||
if err != nil { | ||
return 0, fmt.Errorf("miner address is invalid: %s", err) | ||
} | ||
ctx, cls := context.WithTimeout(context.Background(), time.Second*15) | ||
defer cls() | ||
mi, err := c.StateMinerInfo(ctx, addr, types.EmptyTSK) | ||
if err != nil { | ||
return 0, fmt.Errorf("getting miner %s info: %s", addr, err) | ||
} | ||
|
||
sask, err := c.ClientQueryAsk(ctx, *mi.PeerId, addr) | ||
if err != nil { | ||
return 0, fmt.Errorf("query asking: %s", err) | ||
} | ||
return sask.Ask.Price.Uint64(), nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MinerSelector
configuration depending on flags/envs.