diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a4db7c424a..bbbcc2455e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ ## Improvements -# v1.25.1-rc1 / 2023-12-05 +# v1.25.2-rc1 / 2023-12-09 -This is the first release candidate of the upcoming optional release of Lotus v1.25.1. This feature release includes numerous improvements and enhancements for node operators, ETH RPC-providers and storage providers. +This is the first release candidate of the upcoming optional release of Lotus v1.25.2, which was bumped from the previous v1.25.1-rc1 with the hot fix (see v1.24.1 & v1.25.1 for more details).. + +This feature release includes numerous improvements and enhancements for node operators, ETH RPC-providers and storage providers. ## ☢️ Upgrade Warnings ☢️ @@ -175,6 +177,17 @@ For more information on how to use SupraSeal PC2 with your `lotus-worker`, as we | Alejandro | 1 | +1/-1 | 1 | | lazavikmaria | 1 | +1/-0 | 1 | +# v1.25.1 / 2023-12-09 + +This is a **highly recommended PATCH RELEASE.** The patch release fixes the issue were node operators trying to catch up sync were unable to sync large message blocks/epochs due to an increased number of messages on the network. + +This patch release allows for up to 10k messages per block. Additionally, it introduces a limit on the amount of data that can be read at once, ensuring the system can handle worst-case scenarios. + +## Improvements +- fix: exchange: allow up to 10k messages per block ([filecoin-project/lotus#11506](https://github.com/filecoin-project/lotus/pull/11506)) + +>>>>>>> releases + # v 1.25.0 / 2023-11-22 This is a highly recommended feature release of Lotus. This optional release supports the Filecoin network version 21 upgrade, codenamed Watermelon 🍉, in addition to the numerous improvements and enhancements for node operators, ETH RPC-providers and storage providers. diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 1395acd3e35..7d5fd71f844 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 12b5766113c..d68b6ecc61f 100644 Binary files a/build/openrpc/gateway.json.gz and b/build/openrpc/gateway.json.gz differ diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index d73a9c61cb4..984346445d9 100644 Binary files a/build/openrpc/miner.json.gz and b/build/openrpc/miner.json.gz differ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 02bfa68d558..a304593f5a3 100644 Binary files a/build/openrpc/worker.json.gz and b/build/openrpc/worker.json.gz differ diff --git a/build/version.go b/build/version.go index 360c4a1363e..e3249808a18 100644 --- a/build/version.go +++ b/build/version.go @@ -37,7 +37,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.25.1-rc1" +const BuildVersion = "1.25.2-rc1" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/chain/exchange/cbor_gen.go b/chain/exchange/cbor_gen.go index e66b6d798c4..71c75869dba 100644 --- a/chain/exchange/cbor_gen.go +++ b/chain/exchange/cbor_gen.go @@ -306,9 +306,9 @@ func (t *Response) UnmarshalCBOR(r io.Reader) (err error) { return nil } -var lengthBufCompactedMessages = []byte{132} +var lengthBufCompactedMessagesCBOR = []byte{132} -func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { +func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -316,12 +316,12 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { cw := cbg.NewCborWriter(w) - if _, err := cw.Write(lengthBufCompactedMessages); err != nil { + if _, err := cw.Write(lengthBufCompactedMessagesCBOR); err != nil { return err } // t.Bls ([]*types.Message) (slice) - if len(t.Bls) > cbg.MaxLength { + if len(t.Bls) > 150000 { return xerrors.Errorf("Slice value in field t.Bls was too long") } @@ -334,7 +334,7 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { } } - // t.BlsIncludes ([][]uint64) (slice) + // t.BlsIncludes ([]exchange.messageIndices) (slice) if len(t.BlsIncludes) > cbg.MaxLength { return xerrors.Errorf("Slice value in field t.BlsIncludes was too long") } @@ -343,24 +343,13 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.BlsIncludes { - if len(v) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field v was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(v))); err != nil { + if err := v.MarshalCBOR(cw); err != nil { return err } - for _, v := range v { - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { - return err - } - - } } // t.Secpk ([]*types.SignedMessage) (slice) - if len(t.Secpk) > cbg.MaxLength { + if len(t.Secpk) > 150000 { return xerrors.Errorf("Slice value in field t.Secpk was too long") } @@ -373,7 +362,7 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { } } - // t.SecpkIncludes ([][]uint64) (slice) + // t.SecpkIncludes ([]exchange.messageIndices) (slice) if len(t.SecpkIncludes) > cbg.MaxLength { return xerrors.Errorf("Slice value in field t.SecpkIncludes was too long") } @@ -382,26 +371,15 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.SecpkIncludes { - if len(v) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field v was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(v))); err != nil { + if err := v.MarshalCBOR(cw); err != nil { return err } - for _, v := range v { - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { - return err - } - - } } return nil } -func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { - *t = CompactedMessages{} +func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { + *t = CompactedMessagesCBOR{} cr := cbg.NewCborReader(r) @@ -430,7 +408,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 150000 { return fmt.Errorf("t.Bls: array too large (%d)", extra) } @@ -471,7 +449,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } } - // t.BlsIncludes ([][]uint64) (slice) + // t.BlsIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() if err != nil { @@ -487,7 +465,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.BlsIncludes = make([][]uint64, extra) + t.BlsIncludes = make([]messageIndices, extra) } for i := 0; i < int(extra); i++ { @@ -499,47 +477,13 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { _ = extra _ = err - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > cbg.MaxLength { - return fmt.Errorf("t.BlsIncludes[i]: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.BlsIncludes[i] = make([]uint64, extra) - } - - for j := 0; j < int(extra); j++ { - { - var maj byte - var extra uint64 - var err error - _ = maj - _ = extra - _ = err - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.BlsIncludes[i][j] = uint64(extra) + { - } + if err := t.BlsIncludes[i].UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.BlsIncludes[i]: %w", err) } - } + } } } @@ -550,7 +494,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 150000 { return fmt.Errorf("t.Secpk: array too large (%d)", extra) } @@ -591,7 +535,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } } - // t.SecpkIncludes ([][]uint64) (slice) + // t.SecpkIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() if err != nil { @@ -607,7 +551,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.SecpkIncludes = make([][]uint64, extra) + t.SecpkIncludes = make([]messageIndices, extra) } for i := 0; i < int(extra); i++ { @@ -619,47 +563,13 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { _ = extra _ = err - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > cbg.MaxLength { - return fmt.Errorf("t.SecpkIncludes[i]: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.SecpkIncludes[i] = make([]uint64, extra) - } - - for j := 0; j < int(extra); j++ { - { - var maj byte - var extra uint64 - var err error - _ = maj - _ = extra - _ = err - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.SecpkIncludes[i][j] = uint64(extra) + { - } + if err := t.SecpkIncludes[i].UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.SecpkIncludes[i]: %w", err) } - } + } } } diff --git a/chain/exchange/client.go b/chain/exchange/client.go index 120b554a11f..769c375ca93 100644 --- a/chain/exchange/client.go +++ b/chain/exchange/client.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "fmt" + "io" "math/rand" "time" @@ -23,6 +24,10 @@ import ( "github.com/filecoin-project/lotus/lib/peermgr" ) +// Set the max exchange message size to 120MiB. Purely based on gas numbers, we can include ~8MiB of +// messages per block, so I've set this to 120MiB to be _very_ safe. +const maxExchangeMessageSize = (15 * 8) << 20 + // client implements exchange.Client, using the libp2p ChainExchange protocol // as the fetching mechanism. type client struct { @@ -434,10 +439,11 @@ func (c *client) sendRequestToPeer(ctx context.Context, peer peer.ID, req *Reque log.Warnw("CloseWrite err", "error", err) } - // Read response. + // Read response, limiting the size of the response to maxExchangeMessageSize as we allow a + // lot of messages (10k+) but they'll mostly be quite small. var res Response err = cborutil.ReadCborRPC( - bufio.NewReader(incrt.New(stream, ReadResMinSpeed, ReadResDeadline)), + bufio.NewReader(io.LimitReader(incrt.New(stream, ReadResMinSpeed, ReadResDeadline), maxExchangeMessageSize)), &res) if err != nil { c.peerTracker.logFailure(peer, build.Clock.Since(connectionStart), req.Length) diff --git a/chain/exchange/protocol.go b/chain/exchange/protocol.go index 5e12d31cc29..cd25f4a4350 100644 --- a/chain/exchange/protocol.go +++ b/chain/exchange/protocol.go @@ -154,6 +154,8 @@ type BSTipSet struct { // FIXME: The logic to decompress this structure should belong // // to itself, not to the consumer. +// +// NOTE: Max messages is: BlockMessageLimit (10k) * MaxTipsetSize (15) = 150k type CompactedMessages struct { Bls []*types.Message BlsIncludes [][]uint64 diff --git a/chain/exchange/protocol_encoding.go b/chain/exchange/protocol_encoding.go new file mode 100644 index 00000000000..040dd0d4070 --- /dev/null +++ b/chain/exchange/protocol_encoding.go @@ -0,0 +1,125 @@ +package exchange + +import ( + "fmt" + "io" + + cbg "github.com/whyrusleeping/cbor-gen" + xerrors "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/build" + types "github.com/filecoin-project/lotus/chain/types" +) + +// Type used for encoding/decoding compacted messages. This is a ustom type as we need custom limits. +// - Max messages is 150,000 as that's 15 times the max block size (in messages). It needs to be +// large enough to cover a full tipset full of full blocks. +type CompactedMessagesCBOR struct { + Bls []*types.Message `cborgen:"maxlen=150000"` + BlsIncludes []messageIndices + + Secpk []*types.SignedMessage `cborgen:"maxlen=150000"` + SecpkIncludes []messageIndices +} + +// Unmarshal into the "decoding" struct, then copy into the actual struct. +func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { + var c CompactedMessagesCBOR + if err := c.UnmarshalCBOR(r); err != nil { + return err + } + t.Bls = c.Bls + t.BlsIncludes = make([][]uint64, len(c.BlsIncludes)) + for i, v := range c.BlsIncludes { + t.BlsIncludes[i] = v.v + } + t.Secpk = c.Secpk + t.SecpkIncludes = make([][]uint64, len(c.SecpkIncludes)) + for i, v := range c.SecpkIncludes { + t.SecpkIncludes[i] = v.v + } + return nil +} + +// Copy into the encoding struct, then marshal. +func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + var c CompactedMessagesCBOR + c.Bls = t.Bls + c.BlsIncludes = make([]messageIndices, len(t.BlsIncludes)) + for i, v := range t.BlsIncludes { + c.BlsIncludes[i].v = v + } + c.Secpk = t.Secpk + c.SecpkIncludes = make([]messageIndices, len(t.SecpkIncludes)) + for i, v := range t.SecpkIncludes { + c.SecpkIncludes[i].v = v + } + return c.MarshalCBOR(w) +} + +// this needs to be a struct or cborgen will peak into it and ignore the Unmarshal/Marshal functions +type messageIndices struct { + v []uint64 +} + +func (t *messageIndices) UnmarshalCBOR(r io.Reader) (err error) { + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra > uint64(build.BlockMessageLimit) { + return fmt.Errorf("cbor input had wrong number of fields") + } + + if extra > 0 { + t.v = make([]uint64, extra) + } + + for i := 0; i < int(extra); i++ { + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.v[i] = extra + + } + return nil +} + +func (t *messageIndices) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if len(t.v) > build.BlockMessageLimit { + return xerrors.Errorf("Slice value in field v was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.v))); err != nil { + return err + } + for _, v := range t.v { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, v); err != nil { + return err + } + } + return nil +} diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 909c93e8e84..5519bbc6b53 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.25.1-rc1 + 1.25.2-rc1 COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index 7813cc71733..f6ae93fc5f1 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.25.1-rc1 + 1.25.2-rc1 COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index edc18b10a11..c752a360014 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.25.1-rc1 + 1.25.2-rc1 COMMANDS: daemon Start a lotus daemon process diff --git a/gen/main.go b/gen/main.go index 0cd3999c38a..942b3ac2c05 100644 --- a/gen/main.go +++ b/gen/main.go @@ -92,7 +92,7 @@ func main() { err = gen.WriteTupleEncodersToFile("./chain/exchange/cbor_gen.go", "exchange", exchange.Request{}, exchange.Response{}, - exchange.CompactedMessages{}, + exchange.CompactedMessagesCBOR{}, exchange.BSTipSet{}, ) if err != nil {