Merge branch 'release/v1.20.0' into jen/resolveconflict

This commit is contained in:
Jennifer Wang committed 2023-01-13 21:50:57 -05:00
commit 427fbdb8a9
17 files changed
+149 -243

No files matched your search

+3 -9
View File
@@ -393,11 +393,9 @@ jobs:
- run: go install golang.org/x/tools/cmd/goimports
- run: go install github.com/hannahhoward/cbor-gen-for
- run: make gen
- run: git --no-pager diff
- run: git --no-pager diff --quiet
- run: git --no-pager diff && git --no-pager diff --quiet
- run: make docsgen-cli
- run: git --no-pager diff
- run: git --no-pager diff --quiet
- run: git --no-pager diff && git --no-pager diff --quiet
docs-check:
executor: golang
@@ -412,11 +410,7 @@ jobs:
- run: zcat build/openrpc/full.json.gz | jq > ../post-openrpc-full
- run: zcat build/openrpc/miner.json.gz | jq > ../post-openrpc-miner
- run: zcat build/openrpc/worker.json.gz | jq > ../post-openrpc-worker
- run: git --no-pager diff
- run: diff ../pre-openrpc-full ../post-openrpc-full
- run: diff ../pre-openrpc-miner ../post-openrpc-miner
- run: diff ../pre-openrpc-worker ../post-openrpc-worker
- run: git --no-pager diff --quiet
- run: diff ../pre-openrpc-full ../post-openrpc-full && diff ../pre-openrpc-miner ../post-openrpc-miner && diff ../pre-openrpc-worker ../post-openrpc-worker && git --no-pager diff && git --no-pager diff --quiet
lint: &lint
description: |
+3 -9
View File
@@ -393,11 +393,9 @@ jobs:
- run: go install golang.org/x/tools/cmd/goimports
- run: go install github.com/hannahhoward/cbor-gen-for
- run: make gen
- run: git --no-pager diff
- run: git --no-pager diff --quiet
- run: git --no-pager diff && git --no-pager diff --quiet
- run: make docsgen-cli
- run: git --no-pager diff
- run: git --no-pager diff --quiet
- run: git --no-pager diff && git --no-pager diff --quiet
docs-check:
executor: golang
@@ -412,11 +410,7 @@ jobs:
- run: zcat build/openrpc/full.json.gz | jq > ../post-openrpc-full
- run: zcat build/openrpc/miner.json.gz | jq > ../post-openrpc-miner
- run: zcat build/openrpc/worker.json.gz | jq > ../post-openrpc-worker
- run: git --no-pager diff
- run: diff ../pre-openrpc-full ../post-openrpc-full
- run: diff ../pre-openrpc-miner ../post-openrpc-miner
- run: diff ../pre-openrpc-worker ../post-openrpc-worker
- run: git --no-pager diff --quiet
- run: diff ../pre-openrpc-full ../post-openrpc-full && diff ../pre-openrpc-miner ../post-openrpc-miner && diff ../pre-openrpc-worker ../post-openrpc-worker && git --no-pager diff && git --no-pager diff --quiet
lint: &lint
description: |
+3
View File
@@ -430,6 +430,9 @@ func (c *client) sendRequestToPeer(ctx context.Context, peer peer.ID, req *Reque
}
_ = stream.SetWriteDeadline(time.Time{}) // clear deadline // FIXME: Needs
// its own API (https://github.com/libp2p/go-libp2p/core/issues/162).
if err := stream.CloseWrite(); err != nil {
log.Warnw("CloseWrite err", "error", err)
}
// Read response.
var res Response
-10
View File
@@ -226,16 +226,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
Data: make([]byte, 65),
},
}
default:
// XXX: Hack to make sending from f099 (and others) "just work".
// REMOVE ME.
msgApply = &types.SignedMessage{
Message: *msg,
Signature: crypto.Signature{
Type: crypto.SigTypeSecp256k1,
Data: make([]byte, 65),
},
}
}
ret, err = vmi.ApplyMessage(ctx, msgApply)
+3 -3
View File
@@ -206,14 +206,14 @@ func (x *FvmExtern) VerifyConsensusFault(ctx context.Context, a, b, extra []byte
// check blocks are properly signed by their respective miner
// note we do not need to check extra's: it is a parent to block b
// which itself is signed, so it was willingly included by the miner
gasA, sigErr := x.VerifyBlockSig(ctx, &blockA)
gasA, sigErr := x.verifyBlockSig(ctx, &blockA)
totalGas += gasA
if sigErr != nil {
log.Info("invalid consensus fault: cannot verify first block sig: %w", sigErr)
return ret, totalGas
}
gas2, sigErr := x.VerifyBlockSig(ctx, &blockB)
gas2, sigErr := x.verifyBlockSig(ctx, &blockB)
totalGas += gas2
if sigErr != nil {
log.Info("invalid consensus fault: cannot verify second block sig: %w", sigErr)
@@ -226,7 +226,7 @@ func (x *FvmExtern) VerifyConsensusFault(ctx context.Context, a, b, extra []byte
return ret, totalGas
}
func (x *FvmExtern) VerifyBlockSig(ctx context.Context, blk *types.BlockHeader) (int64, error) {
func (x *FvmExtern) verifyBlockSig(ctx context.Context, blk *types.BlockHeader) (int64, error) {
waddr, gasUsed, err := x.workerKeyAtLookback(ctx, blk.Miner, blk.Height)
if err != nil {
return gasUsed, err
+1 -1
View File
@@ -468,7 +468,7 @@ var sealBenchCmd = &cli.Command{
}
bo.EnvVar = make(map[string]string)
for _, envKey := range []string{"BELLMAN_NO_GPU", "FIL_PROOFS_MAXIMIZE_CACHING", "FIL_PROOFS_USE_GPU_COLUMN_BUILDER",
for _, envKey := range []string{"BELLMAN_NO_GPU", "FIL_PROOFS_USE_GPU_COLUMN_BUILDER",
"FIL_PROOFS_USE_GPU_TREE_BUILDER", "FIL_PROOFS_USE_MULTICORE_SDR", "BELLMAN_CUSTOM_GPU"} {
envValue, found := os.LookupEnv(envKey)
if found {
+15 -1
View File
@@ -813,6 +813,10 @@ var sectorsRenewCmd = &cli.Command{
Usage: "use up to this amount of FIL for one message. pass this flag to avoid message congestion.",
Value: "0",
},
&cli.Int64Flag{
Name: "max-sectors",
Usage: "the maximum number of sectors contained in each message message",
},
&cli.BoolFlag{
Name: "really-do-it",
Usage: "pass this flag to really renew sectors, otherwise will only print out json representation of parameters",
@@ -1020,10 +1024,20 @@ var sectorsRenewCmd = &cli.Command{
for l, exts := range extensions {
for newExp, numbers := range exts {
scount += len(numbers)
addrSectors, err := policy.GetAddressedSectorsMax(nv)
var addrSectors int
sectorsMax, err := policy.GetAddressedSectorsMax(nv)
if err != nil {
return err
}
if cctx.Int("max-sectors") == 0 {
addrSectors = sectorsMax
} else {
addrSectors = cctx.Int("max-sectors")
if addrSectors > sectorsMax {
return xerrors.Errorf("the specified max-sectors exceeds the maximum limit")
}
}
declMax, err := policy.GetDeclarationsMax(nv)
if err != nil {
return err
-1
View File
@@ -51,7 +51,6 @@ func main() {
exportChainCmd,
exportCarCmd,
consensusCmd,
storageStatsCmd,
syncCmd,
stateTreePruneCmd,
datastoreCmd,
-199
View File
@@ -1,199 +0,0 @@
package main
import (
"encoding/json"
corebig "math/big"
"os"
"strconv"
"github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
filbig "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
)
// How many epochs back to look at for dealstats
var defaultEpochLookback = abi.ChainEpoch(10)
type networkTotalsOutput struct {
Epoch int64 `json:"epoch"`
Endpoint string `json:"endpoint"`
Payload networkTotals `json:"payload"`
}
type providerMeta struct {
nonidentifiable bool
}
// force formatting as decimal to aid human readers
type humanFloat float64
func (f humanFloat) MarshalJSON() ([]byte, error) {
// 'f' uses decimal digits without exponents.
// The bit size of 32 ensures we don't use too many decimal places.
return []byte(strconv.FormatFloat(float64(f), 'f', -1, 32)), nil
}
type Totals struct {
TotalDeals int `json:"total_num_deals"`
TotalBytes int64 `json:"total_stored_data_size"`
PrivateTotalDeals int `json:"private_total_num_deals"`
PrivateTotalBytes int64 `json:"private_total_stored_data_size"`
CapacityCarryingData humanFloat `json:"capacity_fraction_carrying_data"`
}
type networkTotals struct {
QaNetworkPower filbig.Int `json:"total_qa_power"`
RawNetworkPower filbig.Int `json:"total_raw_capacity"`
UniqueCids int `json:"total_unique_cids"`
UniqueBytes int64 `json:"total_unique_data_size"`
UniqueClients int `json:"total_unique_clients"`
UniqueProviders int `json:"total_unique_providers"`
UniquePrivateProviders int `json:"total_unique_private_providers"`
Totals
FilPlus Totals `json:"filecoin_plus_subset"`
pieces map[cid.Cid]struct{}
clients map[address.Address]struct{}
providers map[address.Address]providerMeta
}
var storageStatsCmd = &cli.Command{
Name: "storage-stats",
Usage: "Translates current lotus state into a json summary suitable for driving https://storage.filecoin.io/",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "tipset",
Usage: "Comma separated array of cids, or @height",
},
},
Action: func(cctx *cli.Context) error {
ctx := lcli.ReqContext(cctx)
api, apiCloser, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer apiCloser()
var ts *types.TipSet
if cctx.String("tipset") == "" {
ts, err = api.ChainHead(ctx)
if err != nil {
return err
}
ts, err = api.ChainGetTipSetByHeight(ctx, ts.Height()-defaultEpochLookback, ts.Key())
if err != nil {
return err
}
} else {
ts, err = lcli.ParseTipSetRef(ctx, api, cctx.String("tipset"))
if err != nil {
return err
}
}
power, err := api.StateMinerPower(ctx, address.Address{}, ts.Key())
if err != nil {
return err
}
netTotals := networkTotals{
QaNetworkPower: power.TotalPower.QualityAdjPower,
RawNetworkPower: power.TotalPower.RawBytePower,
pieces: make(map[cid.Cid]struct{}),
clients: make(map[address.Address]struct{}),
providers: make(map[address.Address]providerMeta),
}
deals, err := api.StateMarketDeals(ctx, ts.Key())
if err != nil {
return err
}
for _, dealInfo := range deals {
// Only count deals that have properly started, not past/future ones
// https://github.com/filecoin-project/specs-actors/blob/v0.9.9/actors/builtin/market/deal.go#L81-L85
// Bail on 0 as well in case SectorStartEpoch is uninitialized due to some bug
//
// Additionally if the SlashEpoch is set this means the underlying sector is
// terminated for whatever reason ( not just slashed ), and the deal record
// will soon be removed from the state entirely
if dealInfo.State.SectorStartEpoch <= 0 ||
dealInfo.State.SectorStartEpoch > ts.Height() ||
dealInfo.State.SlashEpoch > -1 {
continue
}
netTotals.clients[dealInfo.Proposal.Client] = struct{}{}
if _, seen := netTotals.providers[dealInfo.Proposal.Provider]; !seen {
pm := providerMeta{}
mi, err := api.StateMinerInfo(ctx, dealInfo.Proposal.Provider, ts.Key())
if err != nil {
return err
}
if mi.PeerId == nil || *mi.PeerId == "" {
log.Infof("private provider %s", dealInfo.Proposal.Provider)
pm.nonidentifiable = true
netTotals.UniquePrivateProviders++
}
netTotals.providers[dealInfo.Proposal.Provider] = pm
netTotals.UniqueProviders++
}
if _, seen := netTotals.pieces[dealInfo.Proposal.PieceCID]; !seen {
netTotals.pieces[dealInfo.Proposal.PieceCID] = struct{}{}
netTotals.UniqueBytes += int64(dealInfo.Proposal.PieceSize)
netTotals.UniqueCids++
}
netTotals.TotalBytes += int64(dealInfo.Proposal.PieceSize)
netTotals.TotalDeals++
if netTotals.providers[dealInfo.Proposal.Provider].nonidentifiable {
netTotals.PrivateTotalBytes += int64(dealInfo.Proposal.PieceSize)
netTotals.PrivateTotalDeals++
}
if dealInfo.Proposal.VerifiedDeal {
netTotals.FilPlus.TotalBytes += int64(dealInfo.Proposal.PieceSize)
netTotals.FilPlus.TotalDeals++
if netTotals.providers[dealInfo.Proposal.Provider].nonidentifiable {
netTotals.FilPlus.PrivateTotalBytes += int64(dealInfo.Proposal.PieceSize)
netTotals.FilPlus.PrivateTotalDeals++
}
}
}
netTotals.UniqueClients = len(netTotals.clients)
ccd, _ := new(corebig.Rat).SetFrac(
corebig.NewInt(netTotals.TotalBytes),
netTotals.RawNetworkPower.Int,
).Float64()
netTotals.CapacityCarryingData = humanFloat(ccd)
ccdfp, _ := new(corebig.Rat).SetFrac(
corebig.NewInt(netTotals.FilPlus.TotalBytes),
netTotals.RawNetworkPower.Int,
).Float64()
netTotals.FilPlus.CapacityCarryingData = humanFloat(ccdfp)
return json.NewEncoder(os.Stdout).Encode(
networkTotalsOutput{
Epoch: int64(ts.Height()),
Endpoint: "NETWORK_WIDE_TOTALS",
Payload: netTotals,
},
)
},
}
+1
View File
@@ -1897,6 +1897,7 @@ OPTIONS:
--extension value try to extend selected sectors by this number of epochs, defaults to 540 days (default: 1555200)
--from value only consider sectors whose current expiration epoch is in the range of [from, to], <from> defaults to: now + 120 (1 hour) (default: 0)
--max-fee value use up to this amount of FIL for one message. pass this flag to avoid message congestion. (default: "0")
--max-sectors value the maximum number of sectors contained in each message message (default: 0)
--new-expiration value try to extend selected sectors to this epoch, ignoring extension (default: 0)
--only-cc only extend CC sectors (useful for making sector ready for snap upgrade) (default: false)
--really-do-it pass this flag to really renew sectors, otherwise will only print out json representation of parameters (default: false)
-1
View File
@@ -259,7 +259,6 @@ func ConfigFullNode(c interface{}) Option {
// Actor event filtering support
Override(new(events.EventAPI), From(new(modules.EventAPI))),
// in lite-mode Eth event api is provided by gateway
ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.ActorEvent))),
)
+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
+109
View File
@@ -0,0 +1,109 @@
package full
import (
"context"
"errors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
var ErrImplementMe = errors.New("Not implemented yet")
type EthModuleDummy struct{}
func (e *EthModuleDummy) EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrImplementMe
}
func (e *EthModuleDummy) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrImplementMe
}
func (e *EthModuleDummy) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrImplementMe
}
func (e *EthModuleDummy) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrImplementMe
}
func (e *EthModuleDummy) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
}
func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) {
return ethtypes.EthFeeHistory{}, ErrImplementMe
}
func (e *EthModuleDummy) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) NetVersion(ctx context.Context) (string, error) {
return "", ErrImplementMe
}
func (e *EthModuleDummy) NetListening(ctx context.Context) (bool, error) {
return false, ErrImplementMe
}
func (e *EthModuleDummy) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
}
func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
}
func (e *EthModuleDummy) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
}
func (e *EthModuleDummy) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
}
func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) {
return ethtypes.EthHash{}, ErrImplementMe
}
-1
View File
@@ -107,7 +107,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 -1
View File
@@ -36,7 +36,7 @@ parts:
- libhwloc15
- ocl-icd-libopencl1
override-build: |
LDFLAGS="-z noexecstack" make lotus lotus-miner lotus-worker
LDFLAGS="" make lotus lotus-miner lotus-worker
cp lotus lotus-miner lotus-worker $SNAPCRAFT_PART_INSTALL
cp scripts/snap-lotus-entrypoint.sh $SNAPCRAFT_PART_INSTALL
+5 -6
View File
@@ -21,16 +21,15 @@ Please report your issues with regards to sector-storage at the [lotus issue tra
Manages is the top-level piece of the storage system gluing all the other pieces
together. It also implements scheduling logic.
### `package stores`
### `package paths`
This package implements the sector storage subsystem. Fundamentally the storage
is divided into `path`s, each path has it's UUID, and stores a set of sector
'files'. There are currently 3 types of sector files - `unsealed`, `sealed`,
and `cache`.
'files'. There are currently 5 types of sector files - `unsealed`, `sealed`, `cache`, `update` and `update-cache`.
Paths can be shared between nodes by sharing the underlying filesystem.
### `stores.Local`
### `paths.Local`
The Local store implements SectorProvider for paths mounted in the local
filesystem. Paths can be shared between nodes, and support shared filesystems
@@ -38,12 +37,12 @@ such as NFS.
stores.Local implements all native filesystem-related operations
### `stores.Remote`
### `paths.Remote`
The Remote store extends Local store, handles fetching sector files into a local
store if needed, and handles removing sectors from non-local stores.
### `stores.Index`
### `paths.Index`
The Index is a singleton holding metadata about storage paths, and a mapping of
sector files to paths
+2 -1
View File
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than diagrams.net -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="851px" height="1001px" viewBox="-0.5 -0.5 851 1001" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-04-06T17:42:42.123Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;ilAX9XQorx-cbKmfSJum&quot; version=&quot;12.9.8&quot; type=&quot;browser&quot;&gt;&lt;diagram id=&quot;6lDjA8ebbJXz-MvvKowY&quot; name=&quot;Page-1&quot;&gt;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 truncated
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="851px" height="1001px" viewBox="-0.5 -0.5 851 1001" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-12-15T09:46:16.454Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36&quot; etag=&quot;ppr1dJrvkcN22-tXQQDf&quot; version=&quot;20.4.0&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;6lDjA8ebbJXz-MvvKowY&quot; name=&quot;Page-1&quot;&gt;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 truncated

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 74 KiB