resolve merge conflicts with master

This commit is contained in:
Anton Evangelatov
2022-03-18 12:28:22 +01:00
194 changed files with 3519 additions and 980 deletions
+1
View File
@@ -1,3 +1,4 @@
//stm: #unit
package main
import (
+2
View File
@@ -1,3 +1,4 @@
//stm: #unit
package main
import (
@@ -8,6 +9,7 @@ import (
)
func TestRateLimit(t *testing.T) {
//stm: @CMD_LIMITER_GET_IP_LIMITER_001, @CMD_LIMITER_GET_WALLET_LIMITER_001
limiter := NewLimiter(LimiterConfig{
TotalRate: time.Second,
TotalBurst: 20,
+3
View File
@@ -1,3 +1,4 @@
//stm: #unit
package main
import (
@@ -9,6 +10,7 @@ import (
)
func TestAppendCIDsToWindow(t *testing.T) {
//stm: @CMD_HEALTH_APPEND_CIDS_001
assert := assert.New(t)
var window CidWindow
threshold := 3
@@ -27,6 +29,7 @@ func TestAppendCIDsToWindow(t *testing.T) {
}
func TestCheckWindow(t *testing.T) {
//stm: @CMD_HEALTH_APPEND_CIDS_001, @CMD_HEALTH_CHECK_WINDOW_001
assert := assert.New(t)
threshold := 3
+2
View File
@@ -1,3 +1,4 @@
//stm: #unit
package main
import (
@@ -23,6 +24,7 @@ import (
)
func TestWorkerKeyChange(t *testing.T) {
//stm: @OTHER_WORKER_KEY_CHANGE_001
if testing.Short() {
t.Skip("skipping test in short mode")
}
+2
View File
@@ -1,3 +1,4 @@
//stm: #integration
package main
import (
@@ -49,6 +50,7 @@ func TestMinerAllInfo(t *testing.T) {
t.Run("pre-info-all", run)
//stm: @CLIENT_DATA_IMPORT_001, @CLIENT_STORAGE_DEALS_GET_001
dh := kit.NewDealHarness(t, client, miner, miner)
deal, res, inPath := dh.MakeOnlineDeal(context.Background(), kit.MakeFullDealParams{Rseed: 6})
outPath := dh.PerformRetrieval(context.Background(), deal, res.Root, false)
+1
View File
@@ -466,6 +466,7 @@ var stateOrder = map[sealing.SectorState]stateMeta{}
var stateList = []stateMeta{
{col: 39, state: "Total"},
{col: color.FgGreen, state: sealing.Proving},
{col: color.FgGreen, state: sealing.Available},
{col: color.FgGreen, state: sealing.UpdateActivating},
{col: color.FgBlue, state: sealing.Empty},
+2 -57
View File
@@ -11,9 +11,6 @@ import (
"strings"
"time"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/docker/go-units"
"github.com/fatih/color"
cbor "github.com/ipfs/go-ipld-cbor"
@@ -56,7 +53,6 @@ var sectorsCmd = &cli.Command{
sectorsRemoveCmd,
sectorsSnapUpCmd,
sectorsSnapAbortCmd,
sectorsMarkForUpgradeCmd,
sectorsStartSealCmd,
sectorsSealDelayCmd,
sectorsCapacityCollateralCmd,
@@ -351,7 +347,7 @@ var sectorsListCmd = &cli.Command{
if cctx.Bool("unproven") {
for state := range sealing.ExistSectorStateList {
if state == sealing.Proving {
if state == sealing.Proving || state == sealing.Available {
continue
}
states = append(states, api.SectorState(state))
@@ -437,7 +433,7 @@ var sectorsListCmd = &cli.Command{
const verifiedPowerGainMul = 9
dw, vp := .0, .0
estimate := st.Expiration-st.Activation <= 0
estimate := (st.Expiration-st.Activation <= 0) || sealing.IsUpgradeState(sealing.SectorState(st.State))
if !estimate {
rdw := big.Add(st.DealWeight, st.VerifiedDealWeight)
dw = float64(big.Div(rdw, big.NewInt(int64(st.Expiration-st.Activation))).Uint64())
@@ -1568,57 +1564,6 @@ var sectorsSnapAbortCmd = &cli.Command{
},
}
var sectorsMarkForUpgradeCmd = &cli.Command{
Name: "mark-for-upgrade",
Usage: "Mark a committed capacity sector for replacement by a sector with deals",
ArgsUsage: "<sectorNum>",
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 1 {
return lcli.ShowHelp(cctx, xerrors.Errorf("must pass sector number"))
}
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
api, nCloser, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer nCloser()
ctx := lcli.ReqContext(cctx)
nv, err := api.StateNetworkVersion(ctx, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("failed to get network version: %w", err)
}
if nv >= network.Version15 {
return xerrors.Errorf("classic cc upgrades disabled v15 and beyond, use `snap-up`")
}
// disable mark for upgrade two days before the ntwk v15 upgrade
// TODO: remove the following block in v1.15.1
head, err := api.ChainHead(ctx)
if err != nil {
return xerrors.Errorf("failed to get chain head: %w", err)
}
twoDays := abi.ChainEpoch(2 * builtin.EpochsInDay)
if head.Height() > (build.UpgradeOhSnapHeight - twoDays) {
return xerrors.Errorf("OhSnap is coming soon, " +
"please use `snap-up` to upgrade your cc sectors after the network v15 upgrade!")
}
id, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
if err != nil {
return xerrors.Errorf("could not parse sector number: %w", err)
}
return nodeApi.SectorMarkForUpgrade(ctx, abi.SectorNumber(id), false)
},
}
var sectorsStartSealCmd = &cli.Command{
Name: "seal",
Usage: "Manually start sealing a sector (filling any unused space with junk)",
+7 -4
View File
@@ -598,7 +598,7 @@ var storageListSectorsCmd = &cli.Command{
ft storiface.SectorFileType
urls string
primary, seal, store bool
primary, copy, main, seal, store bool
state api.SectorState
}
@@ -626,8 +626,11 @@ var storageListSectorsCmd = &cli.Command{
urls: strings.Join(info.URLs, ";"),
primary: info.Primary,
seal: info.CanSeal,
store: info.CanStore,
copy: !info.Primary && len(si) > 1,
main: !info.Primary && len(si) == 1, // only copy, but not primary
seal: info.CanSeal,
store: info.CanStore,
state: st.State,
})
@@ -680,7 +683,7 @@ var storageListSectorsCmd = &cli.Command{
"Sector": e.id,
"Type": e.ft.String(),
"State": color.New(stateOrder[sealing.SectorState(e.state)].col).Sprint(e.state),
"Primary": maybeStr(e.seal, color.FgGreen, "primary"),
"Primary": maybeStr(e.primary, color.FgGreen, "primary") + maybeStr(e.copy, color.FgBlue, "copy") + maybeStr(e.main, color.FgRed, "main"),
"Path use": maybeStr(e.seal, color.FgMagenta, "seal ") + maybeStr(e.store, color.FgCyan, "store"),
"URLs": e.urls,
}
+342
View File
@@ -0,0 +1,342 @@
package main
import (
"bufio"
"encoding/binary"
"errors"
"fmt"
"hash"
"hash/crc32"
"io"
"os"
"strings"
"github.com/dgraph-io/badger/v2/y"
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-base32"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)
var datastoreVlog2CarCmd = &cli.Command{
Name: "vlog2car",
Usage: "convert badger blockstore .vlog to .car",
Flags: []cli.Flag{
&cli.PathFlag{
Name: "vlog",
Usage: "vlog file",
Required: true,
},
&cli.PathFlag{
Name: "car",
Usage: "out car file name (no .car)",
Required: true,
},
&cli.StringFlag{
Name: "key-prefix",
Usage: "datastore prefix",
Value: "/blocks/",
},
},
Action: func(cctx *cli.Context) error {
ctx := cctx.Context
maxSz := uint64(1 << 20)
carb := &rawCarb{
max: maxSz,
blocks: map[cid.Cid]block.Block{},
}
cars := 0
pref := cctx.String("key-prefix")
plen := len(pref)
{
// NOTE: Some bits of code in this code block come from https://github.com/dgraph-io/badger, which is licensed
// under Apache 2.0; See https://github.com/dgraph-io/badger/blob/master/LICENSE
vf, err := os.Open(cctx.Path("vlog"))
if err != nil {
return xerrors.Errorf("open vlog file: %w", err)
}
if _, err := vf.Seek(20, io.SeekStart); err != nil {
return xerrors.Errorf("seek past vlog start: %w", err)
}
reader := bufio.NewReader(vf)
read := &safeRead{
k: make([]byte, 10),
v: make([]byte, 10),
recordOffset: 20,
}
loop:
for {
e, err := read.Entry(reader)
switch {
case err == io.EOF:
break loop
case err == io.ErrUnexpectedEOF || err == errTruncate:
break loop
case err != nil:
return xerrors.Errorf("entry read error: %w", err)
case e == nil:
continue
}
if e.meta&0x40 > 0 {
e.Key = e.Key[:len(e.Key)-8]
} else if e.meta > 0 {
if e.meta&0x3f > 0 {
log.Infof("unk meta m:%x; k:%x, v:%60x", e.meta, e.Key, e.Value)
}
continue
}
{
if plen > 0 && !strings.HasPrefix(string(e.Key), pref) {
log.Infow("no blocks prefix", "key", string(e.Key))
continue
}
h, err := base32.RawStdEncoding.DecodeString(string(e.Key[plen:]))
if err != nil {
return xerrors.Errorf("decode b32 ds key %x: %w", e.Key, err)
}
c := cid.NewCidV1(cid.Raw, h)
b, err := block.NewBlockWithCid(e.Value, c)
if err != nil {
return xerrors.Errorf("readblk: %w", err)
}
err = carb.consume(c, b)
switch err {
case nil:
case errFullCar:
root, err := carb.finalize()
if err != nil {
return xerrors.Errorf("carb finalize: %w", err)
}
if err := carb.writeCar(ctx, fmt.Sprintf("%s%d.car", cctx.Path("car"), cars), root); err != nil {
return xerrors.Errorf("writeCar: %w", err)
}
cars++
carb = &rawCarb{
max: maxSz,
blocks: map[cid.Cid]block.Block{},
}
default:
return xerrors.Errorf("carb consume: %w", err)
}
}
}
if err := vf.Close(); err != nil {
return err
}
}
root, err := carb.finalize()
if err != nil {
return xerrors.Errorf("carb finalize: %w", err)
}
if err := carb.writeCar(ctx, fmt.Sprintf("%s%d.car", cctx.Path("car"), cars), root); err != nil {
return xerrors.Errorf("writeCar: %w", err)
}
return nil
},
}
// NOTE: Code below comes (with slight modifications) from https://github.com/dgraph-io/badger/blob/master/value.go
// Apache 2.0; See https://github.com/dgraph-io/badger/blob/master/LICENSE
var errTruncate = errors.New("do truncate")
// hashReader implements io.Reader, io.ByteReader interfaces. It also keeps track of the number
// bytes read. The hashReader writes to h (hash) what it reads from r.
type hashReader struct {
r io.Reader
h hash.Hash32
bytesRead int // Number of bytes read.
}
func newHashReader(r io.Reader) *hashReader {
hash := crc32.New(y.CastagnoliCrcTable)
return &hashReader{
r: r,
h: hash,
}
}
// Read reads len(p) bytes from the reader. Returns the number of bytes read, error on failure.
func (t *hashReader) Read(p []byte) (int, error) {
n, err := t.r.Read(p)
if err != nil {
return n, err
}
t.bytesRead += n
return t.h.Write(p[:n])
}
// ReadByte reads exactly one byte from the reader. Returns error on failure.
func (t *hashReader) ReadByte() (byte, error) {
b := make([]byte, 1)
_, err := t.Read(b)
return b[0], err
}
// Sum32 returns the sum32 of the underlying hash.
func (t *hashReader) Sum32() uint32 {
return t.h.Sum32()
}
type safeRead struct {
k []byte
v []byte
recordOffset uint32
}
// Entry provides Key, Value, UserMeta and ExpiresAt. This struct can be used by
// the user to set data.
type Entry struct {
Key []byte
Value []byte
UserMeta byte
ExpiresAt uint64 // time.Unix
meta byte
// Fields maintained internally.
offset uint32
hlen int // Length of the header.
}
// Entry reads an entry from the provided reader. It also validates the checksum for every entry
// read. Returns error on failure.
func (r *safeRead) Entry(reader io.Reader) (*Entry, error) {
tee := newHashReader(reader)
var h header
hlen, err := h.DecodeFrom(tee)
if err != nil {
return nil, err
}
if h.klen > uint32(1<<16) { // Key length must be below uint16.
return nil, errTruncate
}
kl := int(h.klen)
if cap(r.k) < kl {
r.k = make([]byte, 2*kl)
}
vl := int(h.vlen)
if cap(r.v) < vl {
r.v = make([]byte, 2*vl)
}
e := &Entry{}
e.offset = r.recordOffset
e.hlen = hlen
buf := make([]byte, h.klen+h.vlen)
if _, err := io.ReadFull(tee, buf[:]); err != nil {
if err == io.EOF {
err = errTruncate
}
return nil, err
}
e.Key = buf[:h.klen]
e.Value = buf[h.klen:]
var crcBuf [crc32.Size]byte
if _, err := io.ReadFull(reader, crcBuf[:]); err != nil {
if err == io.EOF {
err = errTruncate
}
return nil, err
}
crc := y.BytesToU32(crcBuf[:])
if crc != tee.Sum32() {
return nil, errTruncate
}
e.meta = h.meta
e.UserMeta = h.userMeta
e.ExpiresAt = h.expiresAt
return e, nil
}
// header is used in value log as a header before Entry.
type header struct {
klen uint32
vlen uint32
expiresAt uint64
meta byte
userMeta byte
}
// Encode encodes the header into []byte. The provided []byte should be atleast 5 bytes. The
// function will panic if out []byte isn't large enough to hold all the values.
// The encoded header looks like
// +------+----------+------------+--------------+-----------+
// | Meta | UserMeta | Key Length | Value Length | ExpiresAt |
// +------+----------+------------+--------------+-----------+
func (h header) Encode(out []byte) int {
out[0], out[1] = h.meta, h.userMeta
index := 2
index += binary.PutUvarint(out[index:], uint64(h.klen))
index += binary.PutUvarint(out[index:], uint64(h.vlen))
index += binary.PutUvarint(out[index:], h.expiresAt)
return index
}
// Decode decodes the given header from the provided byte slice.
// Returns the number of bytes read.
func (h *header) Decode(buf []byte) int {
h.meta, h.userMeta = buf[0], buf[1]
index := 2
klen, count := binary.Uvarint(buf[index:])
h.klen = uint32(klen)
index += count
vlen, count := binary.Uvarint(buf[index:])
h.vlen = uint32(vlen)
index += count
h.expiresAt, count = binary.Uvarint(buf[index:])
return index + count
}
// DecodeFrom reads the header from the hashReader.
// Returns the number of bytes read.
func (h *header) DecodeFrom(reader *hashReader) (int, error) {
var err error
h.meta, err = reader.ReadByte()
if err != nil {
return 0, err
}
h.userMeta, err = reader.ReadByte()
if err != nil {
return 0, err
}
klen, err := binary.ReadUvarint(reader)
if err != nil {
return 0, err
}
h.klen = uint32(klen)
vlen, err := binary.ReadUvarint(reader)
if err != nil {
return 0, err
}
h.vlen = uint32(vlen)
h.expiresAt, err = binary.ReadUvarint(reader)
if err != nil {
return 0, err
}
return reader.bytesRead, nil
}
+1
View File
@@ -32,6 +32,7 @@ var datastoreCmd = &cli.Command{
datastoreListCmd,
datastoreGetCmd,
datastoreRewriteCmd,
datastoreVlog2CarCmd,
},
}
+118
View File
@@ -0,0 +1,118 @@
package main
import (
"context"
"fmt"
"io"
"unicode/utf8"
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/urfave/cli/v2"
)
var dealLabelCmd = &cli.Command{
Name: "deal-label",
Usage: "Scrape state to report on how many deals have non UTF-8 labels",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Action: func(cctx *cli.Context) error {
ctx := context.TODO()
if !cctx.Args().Present() {
return fmt.Errorf("must pass state root")
}
sroot, err := cid.Decode(cctx.Args().First())
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}
fsrepo, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return err
}
lkrepo, err := fsrepo.Lock(repo.FullNode)
if err != nil {
return err
}
defer lkrepo.Close() //nolint:errcheck
bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore)
if err != nil {
return fmt.Errorf("failed to open blockstore: %w", err)
}
defer func() {
if c, ok := bs.(io.Closer); ok {
if err := c.Close(); err != nil {
log.Warnf("failed to close blockstore: %s", err)
}
}
}()
mds, err := lkrepo.Datastore(context.Background(), "/metadata")
if err != nil {
return err
}
cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil)
defer cs.Close() //nolint:errcheck
cst := cbor.NewCborStore(bs)
store := adt.WrapStore(ctx, cst)
tree, err := state.LoadStateTree(cst, sroot)
if err != nil {
return err
}
ma, err := tree.GetActor(market.Address)
if err != nil {
return err
}
ms, err := market.Load(store, ma)
if err != nil {
return err
}
ps, err := ms.Proposals()
if err != nil {
return err
}
var deals []abi.DealID
if err = ps.ForEach(func(id abi.DealID, dp market.DealProposal) error {
if !utf8.Valid([]byte(dp.Label)) {
deals = append(deals, id)
}
return nil
}); err != nil {
return err
}
fmt.Println("there are ", len(deals), " bad labels")
for _, d := range deals {
fmt.Print(d, " ")
}
return nil
},
}
+372 -2
View File
@@ -1,19 +1,38 @@
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/dgraph-io/badger/v2"
"github.com/dgraph-io/badger/v2/pb"
"github.com/dustin/go-humanize"
"github.com/filecoin-project/go-state-types/abi"
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
offline "github.com/ipfs/go-ipfs-exchange-offline"
"github.com/ipfs/go-merkledag"
"github.com/ipld/go-car"
"github.com/multiformats/go-base32"
mh "github.com/multiformats/go-multihash"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/cmd/lotus-shed/shedgen"
"github.com/filecoin-project/lotus/node/repo"
)
@@ -39,6 +58,9 @@ var exportChainCmd = &cli.Command{
Name: "skip-old-msgs",
},
},
Subcommands: []*cli.Command{
exportRawCmd,
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return lcli.ShowHelp(cctx, fmt.Errorf("must specify file name to write export to"))
@@ -130,3 +152,351 @@ var exportChainCmd = &cli.Command{
return nil
},
}
var exportRawCmd = &cli.Command{
Name: "raw",
Description: "Export raw blocks from repo (requires node to be offline)",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
&cli.StringFlag{
Name: "car-size",
Value: "50M",
},
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return lcli.ShowHelp(cctx, fmt.Errorf("must specify file name to write export to"))
}
ctx := context.TODO()
r, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return xerrors.Errorf("opening fs repo: %w", err)
}
exists, err := r.Exists()
if err != nil {
return err
}
if !exists {
return xerrors.Errorf("lotus repo doesn't exist")
}
lr, err := r.LockRO(repo.FullNode)
if err != nil {
return err
}
defer lr.Close() //nolint:errcheck
out := cctx.Args().First()
err = os.Mkdir(out, 0755)
if err != nil {
return xerrors.Errorf("creating output dir: %w", err)
}
maxSz, err := humanize.ParseBytes(cctx.String("car-size"))
if err != nil {
return xerrors.Errorf("parse --car-size: %w", err)
}
cars := 0
carb := &rawCarb{
max: maxSz,
blocks: map[cid.Cid]block.Block{},
}
{
consume := func(c cid.Cid, b block.Block) error {
err = carb.consume(c, b)
switch err {
case nil:
case errFullCar:
root, err := carb.finalize()
if err != nil {
return xerrors.Errorf("carb finalize: %w", err)
}
if err := carb.writeCar(ctx, filepath.Join(out, fmt.Sprintf("chain%d.car", cars)), root); err != nil {
return xerrors.Errorf("writeCar: %w", err)
}
cars++
if cars > 10 {
return xerrors.Errorf("enough")
}
carb = &rawCarb{
max: maxSz,
blocks: map[cid.Cid]block.Block{},
}
log.Infow("gc")
go runtime.GC()
default:
return xerrors.Errorf("carb consume: %w", err)
}
return nil
}
{
path := filepath.Join(lr.Path(), "datastore", "chain")
opts, err := repo.BadgerBlockstoreOptions(repo.UniversalBlockstore, path, false)
if err != nil {
return err
}
opts.Logger = &badgerLog{
SugaredLogger: log.Desugar().WithOptions(zap.AddCallerSkip(1)).Sugar(),
skip2: log.Desugar().WithOptions(zap.AddCallerSkip(2)).Sugar(),
}
log.Infow("open db")
db, err := badger.Open(opts.Options)
if err != nil {
return fmt.Errorf("failed to open badger blockstore: %w", err)
}
defer db.Close() // nolint:errcheck
log.Infow("new stream")
var wlk sync.Mutex
str := db.NewStream()
str.NumGo = 16
str.LogPrefix = "bstream"
str.Send = func(list *pb.KVList) (err error) {
defer func() {
if err != nil {
log.Errorw("send error", "err", err)
}
}()
for _, kv := range list.Kv {
if kv.Key == nil || kv.Value == nil {
continue
}
if !strings.HasPrefix(string(kv.Key), "/blocks/") {
log.Infow("no blocks prefix", "key", string(kv.Key))
continue
}
h, err := base32.RawStdEncoding.DecodeString(string(kv.Key[len("/blocks/"):]))
if err != nil {
return xerrors.Errorf("decode b32 ds key %x: %w", kv.Key, err)
}
c := cid.NewCidV1(cid.Raw, h)
b, err := block.NewBlockWithCid(kv.Value, c)
if err != nil {
return xerrors.Errorf("readblk: %w", err)
}
wlk.Lock()
err = consume(c, b)
wlk.Unlock()
if err != nil {
return xerrors.Errorf("consume stream block: %w", err)
}
}
return nil
}
if err := str.Orchestrate(ctx); err != nil {
return xerrors.Errorf("orchestrate stream: %w", err)
}
}
}
log.Infow("write last")
root, err := carb.finalize()
if err != nil {
return xerrors.Errorf("carb finalize: %w", err)
}
if err := carb.writeCar(ctx, filepath.Join(out, fmt.Sprintf("chain%d.car", cars)), root); err != nil {
return xerrors.Errorf("writeCar: %w", err)
}
return nil
},
}
var errFullCar = errors.New("full")
const maxlinks = 16
type rawCarb struct {
blockstore.Blockstore
max, cur uint64
nodes []*shedgen.CarbNode
blocks map[cid.Cid]block.Block
}
func (rc *rawCarb) Has(ctx context.Context, c cid.Cid) (bool, error) {
_, has := rc.blocks[c]
return has, nil
}
func (rc *rawCarb) Get(ctx context.Context, c cid.Cid) (block.Block, error) {
b, has := rc.blocks[c]
if !has {
return nil, blockstore.ErrNotFound
}
return b, nil
}
func (rc *rawCarb) GetSize(ctx context.Context, c cid.Cid) (int, error) {
b, has := rc.blocks[c]
if !has {
return 0, blockstore.ErrNotFound
}
return len(b.RawData()), nil
}
func (rc *rawCarb) checkNodes(maxl int) error {
if len(rc.nodes) == 0 {
log.Infow("add level", "l", 0)
rc.nodes = append(rc.nodes, new(shedgen.CarbNode))
}
for i := 0; i < len(rc.nodes); i++ {
if len(rc.nodes[i].Sub) <= maxl {
break
}
if len(rc.nodes) <= i+1 {
log.Infow("add level", "l", i+1)
rc.nodes = append(rc.nodes, new(shedgen.CarbNode))
}
var bb bytes.Buffer
if err := rc.nodes[i].MarshalCBOR(&bb); err != nil {
return err
}
c, err := cid.Prefix{
Version: 1,
Codec: cid.DagCBOR,
MhType: mh.SHA2_256,
MhLength: -1,
}.Sum(bb.Bytes())
if err != nil {
return xerrors.Errorf("gen cid: %w", err)
}
b, err := block.NewBlockWithCid(bb.Bytes(), c)
if err != nil {
return xerrors.Errorf("new block: %w", err)
}
if i > 1 {
log.Infow("compact", "from", i, "to", i+1, "sub", c.String())
}
rc.nodes[i+1].Sub = append(rc.nodes[i+1].Sub, c)
rc.blocks[c] = b
rc.nodes[i] = new(shedgen.CarbNode)
rc.cur += uint64(bb.Len())
}
return nil
}
func (rc *rawCarb) consume(c cid.Cid, b block.Block) error {
if err := rc.checkNodes(maxlinks); err != nil {
return err
}
if rc.cur+uint64(len(b.RawData())) > rc.max {
return errFullCar
}
rc.cur += uint64(len(b.RawData()))
b, err := block.NewBlockWithCid(b.RawData(), c)
if err != nil {
return xerrors.Errorf("create raw block: %w", err)
}
rc.blocks[c] = b
rc.nodes[0].Sub = append(rc.nodes[0].Sub, c)
return nil
}
func (rc *rawCarb) finalize() (cid.Cid, error) {
if len(rc.nodes) == 0 {
rc.nodes = append(rc.nodes, new(shedgen.CarbNode))
}
for i := 0; i < len(rc.nodes); i++ {
var bb bytes.Buffer
if err := rc.nodes[i].MarshalCBOR(&bb); err != nil {
return cid.Undef, err
}
c, err := cid.Prefix{
Version: 1,
Codec: cid.DagCBOR,
MhType: mh.SHA2_256,
MhLength: -1,
}.Sum(bb.Bytes())
if err != nil {
return cid.Undef, xerrors.Errorf("gen cid: %w", err)
}
b, err := block.NewBlockWithCid(bb.Bytes(), c)
if err != nil {
return cid.Undef, xerrors.Errorf("new block: %w", err)
}
log.Infow("fin", "level", i, "cid", c.String())
rc.blocks[c] = b
rc.nodes[i] = new(shedgen.CarbNode)
rc.cur += uint64(bb.Len())
if len(rc.nodes[i].Sub) <= 1 && i == len(rc.nodes)-1 {
return c, err
}
if len(rc.nodes) <= i+1 {
rc.nodes = append(rc.nodes, new(shedgen.CarbNode))
}
rc.nodes[i+1].Sub = append(rc.nodes[i+1].Sub, c)
}
return cid.Undef, xerrors.Errorf("failed to finalize")
}
func (rc *rawCarb) writeCar(ctx context.Context, path string, root cid.Cid) error {
f, err := os.Create(path)
if err != nil {
return xerrors.Errorf("create out car: %w", err)
}
bs := rc
ds := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs)))
log.Infow("write car", "path", path, "root", root.String(), "blocks", len(rc.blocks))
return car.WriteCar(ctx, ds, []cid.Cid{root}, f)
}
var _ blockstore.Blockstore = &rawCarb{}
type badgerLog struct {
*zap.SugaredLogger
skip2 *zap.SugaredLogger
}
func (b *badgerLog) Warningf(format string, args ...interface{}) {
b.skip2.Warnf(format, args...)
}
+104
View File
@@ -0,0 +1,104 @@
package main
import (
"bufio"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"github.com/chzyer/readline"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/itests/kit"
)
var itestdCmd = &cli.Command{
Name: "itestd",
Description: "Integration test debug env",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "listen",
Value: "127.0.0.1:5674",
},
},
Action: func(cctx *cli.Context) error {
var nodes []kit.ItestdNotif
m := http.NewServeMux()
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var notif kit.ItestdNotif
if err := json.NewDecoder(r.Body).Decode(&notif); err != nil {
fmt.Printf("!! Decode itest notif: %s\n", err)
return
}
fmt.Printf("%d @%s '%s=%s'\n", len(nodes), notif.TestName, notif.NodeType, notif.Api)
nodes = append(nodes, notif)
})
l, err := net.Listen("tcp", cctx.String("listen"))
if err != nil {
return xerrors.Errorf("net listen: %w", err)
}
s := &httptest.Server{
Listener: l,
Config: &http.Server{Handler: m},
}
s.Start()
fmt.Printf("ITest env:\n\nLOTUS_ITESTD=%s\n\nSay 'sh' to spawn a shell connected to test nodes\n--- waiting for clients\n", s.URL)
cs := readline.NewCancelableStdin(os.Stdin)
go func() {
<-cctx.Done()
cs.Close() // nolint:errcheck
}()
rl := bufio.NewReader(cs)
for {
cmd, _, err := rl.ReadLine()
if err != nil {
return xerrors.Errorf("readline: %w", err)
}
switch string(cmd) {
case "sh":
shell := "/bin/sh"
if os.Getenv("SHELL") != "" {
shell = os.Getenv("SHELL")
}
p := exec.Command(shell, "-i")
p.Env = append(p.Env, os.Environ()...)
lastNodes := map[string]string{}
for _, node := range nodes {
lastNodes[node.NodeType] = node.Api
}
if _, found := lastNodes["MARKETS_API_INFO"]; !found {
lastNodes["MARKETS_API_INFO"] = lastNodes["MINER_API_INFO"]
}
for typ, api := range lastNodes {
p.Env = append(p.Env, fmt.Sprintf("%s=%s", typ, api))
}
p.Stdout = os.Stdout
p.Stderr = os.Stderr
p.Stdin = os.Stdin
if err := p.Start(); err != nil {
return xerrors.Errorf("start shell: %w", err)
}
if err := p.Wait(); err != nil {
fmt.Printf("wait for shell: %s\n", err)
}
fmt.Println("\n--- shell quit")
default:
fmt.Println("!! Unknown command")
}
}
},
}
+2
View File
@@ -22,6 +22,7 @@ func main() {
bitFieldCmd,
cronWcCmd,
frozenMinersCmd,
dealLabelCmd,
keyinfoCmd,
jwtCmd,
noncefix,
@@ -69,6 +70,7 @@ func main() {
terminationsCmd,
migrationsCmd,
diffCmd,
itestdCmd,
}
app := &cli.App{
+300
View File
@@ -8,6 +8,8 @@ import (
"github.com/filecoin-project/go-state-types/abi"
miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner"
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
msig5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/multisig"
"github.com/filecoin-project/go-address"
@@ -29,6 +31,9 @@ var minerMultisigsCmd = &cli.Command{
mmApproveWithdrawBalance,
mmProposeChangeOwner,
mmApproveChangeOwner,
mmProposeChangeWorker,
mmConfirmChangeWorker,
mmProposeControlSet,
},
Flags: []cli.Flag{
&cli.StringFlag{
@@ -368,6 +373,301 @@ var mmApproveChangeOwner = &cli.Command{
},
}
var mmProposeChangeWorker = &cli.Command{
Name: "propose-change-worker",
Usage: "Propose an worker address change",
ArgsUsage: "[newWorker]",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must pass new worker address")
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
multisigAddr, sender, minerAddr, err := getInputs(cctx)
if err != nil {
return err
}
na, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK)
if err != nil {
return err
}
mi, err := api.StateMinerInfo(ctx, minerAddr, types.EmptyTSK)
if err != nil {
return err
}
if mi.NewWorker.Empty() {
if mi.Worker == newAddr {
return fmt.Errorf("worker address already set to %s", na)
}
} else {
if mi.NewWorker == newAddr {
fmt.Fprintf(cctx.App.Writer, "Worker key change to %s successfully proposed.\n", na)
fmt.Fprintf(cctx.App.Writer, "Call 'confirm-change-worker' at or after height %d to complete.\n", mi.WorkerChangeEpoch)
return fmt.Errorf("change to worker address %s already pending", na)
}
}
cwp := &miner2.ChangeWorkerAddressParams{
NewWorker: newAddr,
NewControlAddrs: mi.ControlAddresses,
}
fmt.Fprintf(cctx.App.Writer, "newAddr: %s\n", newAddr)
fmt.Fprintf(cctx.App.Writer, "NewControlAddrs: %s\n", mi.ControlAddresses)
sp, err := actors.SerializeParams(cwp)
if err != nil {
return xerrors.Errorf("serializing params: %w", err)
}
pcid, err := api.MsigPropose(ctx, multisigAddr, minerAddr, big.Zero(), sender, uint64(miner.Methods.ChangeWorkerAddress), sp)
if err != nil {
return xerrors.Errorf("proposing message: %w", err)
}
fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", pcid)
// wait for it to get mined into a block
wait, err := api.StateWaitMsg(ctx, pcid, build.MessageConfidence)
if err != nil {
return err
}
// check it executed successfully
if wait.Receipt.ExitCode != 0 {
fmt.Fprintln(cctx.App.Writer, "Propose worker change tx failed!")
return err
}
var retval msig5.ProposeReturn
if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
return fmt.Errorf("failed to unmarshal propose return value: %w", err)
}
fmt.Printf("Transaction ID: %d\n", retval.TxnID)
if retval.Applied {
fmt.Printf("Transaction was executed during propose\n")
fmt.Printf("Exit Code: %d\n", retval.Code)
fmt.Printf("Return Value: %x\n", retval.Ret)
}
return nil
},
}
var mmConfirmChangeWorker = &cli.Command{
Name: "confirm-change-worker",
Usage: "Confirm an worker address change",
ArgsUsage: "[newWorker]",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must pass new worker address")
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
multisigAddr, sender, minerAddr, err := getInputs(cctx)
if err != nil {
return err
}
na, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK)
if err != nil {
return err
}
mi, err := api.StateMinerInfo(ctx, minerAddr, types.EmptyTSK)
if err != nil {
return err
}
if mi.NewWorker.Empty() {
return xerrors.Errorf("no worker key change proposed")
} else if mi.NewWorker != newAddr {
return xerrors.Errorf("worker key %s does not match current worker key proposal %s", newAddr, mi.NewWorker)
}
if head, err := api.ChainHead(ctx); err != nil {
return xerrors.Errorf("failed to get the chain head: %w", err)
} else if head.Height() < mi.WorkerChangeEpoch {
return xerrors.Errorf("worker key change cannot be confirmed until %d, current height is %d", mi.WorkerChangeEpoch, head.Height())
}
pcid, err := api.MsigPropose(ctx, multisigAddr, minerAddr, big.Zero(), sender, uint64(miner.Methods.ConfirmUpdateWorkerKey), nil)
if err != nil {
return xerrors.Errorf("proposing message: %w", err)
}
fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", pcid)
// wait for it to get mined into a block
wait, err := api.StateWaitMsg(ctx, pcid, build.MessageConfidence)
if err != nil {
return err
}
// check it executed successfully
if wait.Receipt.ExitCode != 0 {
fmt.Fprintln(cctx.App.Writer, "Propose worker change tx failed!")
return err
}
var retval msig5.ProposeReturn
if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
return fmt.Errorf("failed to unmarshal propose return value: %w", err)
}
fmt.Printf("Transaction ID: %d\n", retval.TxnID)
if retval.Applied {
fmt.Printf("Transaction was executed during propose\n")
fmt.Printf("Exit Code: %d\n", retval.Code)
fmt.Printf("Return Value: %x\n", retval.Ret)
}
return nil
},
}
var mmProposeControlSet = &cli.Command{
Name: "propose-control-set",
Usage: "Set control address(-es)",
ArgsUsage: "[...address]",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must pass new owner address")
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
multisigAddr, sender, minerAddr, err := getInputs(cctx)
if err != nil {
return err
}
mi, err := api.StateMinerInfo(ctx, minerAddr, types.EmptyTSK)
if err != nil {
return err
}
del := map[address.Address]struct{}{}
existing := map[address.Address]struct{}{}
for _, controlAddress := range mi.ControlAddresses {
ka, err := api.StateAccountKey(ctx, controlAddress, types.EmptyTSK)
if err != nil {
return err
}
del[ka] = struct{}{}
existing[ka] = struct{}{}
}
var toSet []address.Address
for i, as := range cctx.Args().Slice() {
a, err := address.NewFromString(as)
if err != nil {
return xerrors.Errorf("parsing address %d: %w", i, err)
}
ka, err := api.StateAccountKey(ctx, a, types.EmptyTSK)
if err != nil {
return err
}
// make sure the address exists on chain
_, err = api.StateLookupID(ctx, ka, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("looking up %s: %w", ka, err)
}
delete(del, ka)
toSet = append(toSet, ka)
}
for a := range del {
fmt.Println("Remove", a)
}
for _, a := range toSet {
if _, exists := existing[a]; !exists {
fmt.Println("Add", a)
}
}
cwp := &miner2.ChangeWorkerAddressParams{
NewWorker: mi.Worker,
NewControlAddrs: toSet,
}
sp, err := actors.SerializeParams(cwp)
if err != nil {
return xerrors.Errorf("serializing params: %w", err)
}
pcid, err := api.MsigPropose(ctx, multisigAddr, minerAddr, big.Zero(), sender, uint64(miner.Methods.ChangeWorkerAddress), sp)
if err != nil {
return xerrors.Errorf("proposing message: %w", err)
}
fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", pcid)
// wait for it to get mined into a block
wait, err := api.StateWaitMsg(ctx, pcid, build.MessageConfidence)
if err != nil {
return err
}
// check it executed successfully
if wait.Receipt.ExitCode != 0 {
fmt.Fprintln(cctx.App.Writer, "Propose worker change tx failed!")
return err
}
var retval msig5.ProposeReturn
if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
return fmt.Errorf("failed to unmarshal propose return value: %w", err)
}
fmt.Printf("Transaction ID: %d\n", retval.TxnID)
if retval.Applied {
fmt.Printf("Transaction was executed during propose\n")
fmt.Printf("Exit Code: %d\n", retval.Code)
fmt.Printf("Return Value: %x\n", retval.Ret)
}
return nil
},
}
func getInputs(cctx *cli.Context) (address.Address, address.Address, address.Address, error) {
multisigAddr, err := address.NewFromString(cctx.String("multisig"))
if err != nil {
+128
View File
@@ -0,0 +1,128 @@
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
package shedgen
import (
"fmt"
"io"
"math"
"sort"
cid "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
var _ = xerrors.Errorf
var _ = cid.Undef
var _ = math.E
var _ = sort.Sort
func (t *CarbNode) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{161}); err != nil {
return err
}
scratch := make([]byte, 9)
// t.Sub ([]cid.Cid) (slice)
if len("Sub") > cbg.MaxLength {
return xerrors.Errorf("Value in field \"Sub\" was too long")
}
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Sub"))); err != nil {
return err
}
if _, err := io.WriteString(w, string("Sub")); err != nil {
return err
}
if len(t.Sub) > cbg.MaxLength {
return xerrors.Errorf("Slice value in field t.Sub was too long")
}
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Sub))); err != nil {
return err
}
for _, v := range t.Sub {
if err := cbg.WriteCidBuf(scratch, w, v); err != nil {
return xerrors.Errorf("failed writing cid field t.Sub: %w", err)
}
}
return nil
}
func (t *CarbNode) UnmarshalCBOR(r io.Reader) error {
*t = CarbNode{}
br := cbg.GetPeeker(r)
scratch := make([]byte, 8)
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if maj != cbg.MajMap {
return fmt.Errorf("cbor input should be of type map")
}
if extra > cbg.MaxLength {
return fmt.Errorf("CarbNode: map struct too large (%d)", extra)
}
var name string
n := extra
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadStringBuf(br, scratch)
if err != nil {
return err
}
name = string(sval)
}
switch name {
// t.Sub ([]cid.Cid) (slice)
case "Sub":
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if extra > cbg.MaxLength {
return fmt.Errorf("t.Sub: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Sub = make([]cid.Cid, extra)
}
for i := 0; i < int(extra); i++ {
c, err := cbg.ReadCid(br)
if err != nil {
return xerrors.Errorf("reading cid field t.Sub failed: %w", err)
}
t.Sub[i] = c
}
default:
// Field doesn't exist on this type, so ignore it
cbg.ScanForLinks(r, func(cid.Cid) {})
}
}
return nil
}
+7
View File
@@ -0,0 +1,7 @@
package shedgen
import "github.com/ipfs/go-cid"
type CarbNode struct {
Sub []cid.Cid
}
@@ -44,7 +44,7 @@ type BlockBuilder struct {
parentTs *types.TipSet
parentSt *state.StateTree
vm *vm.VM
vm *vm.LegacyVM
sm *stmgr.StateManager
gasTotal int64
@@ -73,9 +73,9 @@ func NewBlockBuilder(ctx context.Context, logger *zap.SugaredLogger, sm *stmgr.S
parentSt: parentSt,
}
// Then we construct a VM to execute messages for gas estimation.
// Then we construct a LegacyVM to execute messages for gas estimation.
//
// Most parts of this VM are "real" except:
// Most parts of this LegacyVM are "real" except:
// 1. We don't charge a fee.
// 2. The runtime has "fake" proof logic.
// 3. We don't actually save any of the results.
@@ -92,7 +92,7 @@ func NewBlockBuilder(ctx context.Context, logger *zap.SugaredLogger, sm *stmgr.S
BaseFee: abi.NewTokenAmount(0),
LookbackState: stmgr.LookbackStateGetterForTipset(sm, parentTs),
}
bb.vm, err = vm.NewVM(bb.ctx, vmopt)
bb.vm, err = vm.NewLegacyVM(bb.ctx, vmopt)
if err != nil {
return nil, err
}
@@ -190,12 +190,12 @@ func (bb *BlockBuilder) PushMessage(msg *types.Message) (*types.MessageReceipt,
return &ret.MessageReceipt, nil
}
// ActorStore returns the VM's current (pending) blockstore.
// ActorStore returns the LegacyVM's current (pending) blockstore.
func (bb *BlockBuilder) ActorStore() adt.Store {
return bb.vm.ActorStore(bb.ctx)
}
// StateTree returns the VM's current (pending) state-tree. This includes any changes made by
// StateTree returns the LegacyVM's current (pending) state-tree. This includes any changes made by
// successfully pushed messages.
//
// You probably want ParentStateTree
@@ -1,3 +1,4 @@
//stm: #unit
package stages
import (
@@ -13,6 +14,7 @@ import (
)
func TestCommitQueue(t *testing.T) {
//stm: @CMD_COMMIT_Q_ENQUEUE_COMMIT_001
var q commitQueue
addr1, err := address.NewIDAddress(1000)
require.NoError(t, err)
@@ -46,6 +48,7 @@ func TestCommitQueue(t *testing.T) {
SectorNumber: 6,
}))
//stm: @CMD_COMMIT_Q_ADVANCE_EPOCH_001, @CMD_COMMIT_Q_NEXT_MINER_001
epoch := abi.ChainEpoch(0)
q.advanceEpoch(epoch)
_, _, ok := q.nextMiner()
+2
View File
@@ -1,3 +1,4 @@
//stm: #unit
package main
import (
@@ -10,6 +11,7 @@ import (
)
func TestProtocolCodenames(t *testing.T) {
//stm: @OTHER_IMPLEMENTATION_EPOCH_CODENAMES_001
if height := abi.ChainEpoch(100); GetProtocolCodename(height) != "genesis" {
t.Fatal("expected genesis codename")
}