From 58978cbfe94926668e30295782cd0c57b7633449 Mon Sep 17 00:00:00 2001 From: yhrzpm Date: Mon, 26 Jun 2023 14:40:24 +0800 Subject: [PATCH 01/16] Release 2.3.2 (#326) * feat: drop escrow api and wallet api * fix: rename the btfs protocol to p2p (#311) * fix: rename the btfs protocol to p2p * test: rm escrow releated * Simple node (#314) * feat: deamon start * chore: del guaid starts and ends * chore: add isSimpleMode * feat: mod simple node info by config * chore: add simple node * chore: build * chore: * feat: add sync-simple-mode * mod: go mod --------- Co-authored-by: fish <920886811@163.com> * chore: drop dashboard url output * Pre 2.3.2 (#322) * feat: directory atomatic sharding * feat[gateway]: sync latest ipfs gateway (#318) * feat: add notices (#312) * feat: add new mux * feat[gateway]: sync ipfs gateway * test: wan lan dht test case * feat: upgrade conns inbound (#319) * fix[gateway]: ipns to btns (#320) * chore: del some simplemod cmd * feature[gateway]: using btfs dir-index.html (#321) Co-authored-by: Shannon Han * chore: replace 'TRON-US' deps to 'bittorrent' * chore: mannauly go fmt code --------- Co-authored-by: steve Co-authored-by: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Co-authored-by: fish <920886811@163.com> Co-authored-by: Hanzc <1297411677@qq.com> Co-authored-by: Shannon Han * fix: go-btfs-files tag * fix: lauchpad.net/gocheck deps issue * chore: add new bootstap node (#324) Co-authored-by: fish <920886811@163.com> * test: update test case (#325) Co-authored-by: shawn * test: update test case (#327) * test: update test case * test: add and fix some test cases --------- Co-authored-by: shawn * feat: update config connMgr default (#328) * feat: update version and dashboard address (#330) * feat: update version and dashboard address * fix: update comment --------- Co-authored-by: fish <920886811@163.com> Co-authored-by: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Co-authored-by: laocheng-cheng <73106671+laocheng-cheng@users.noreply.github.com> Co-authored-by: steve Co-authored-by: Hanzc <1297411677@qq.com> Co-authored-by: Shannon Han Co-authored-by: shawn --- .gitignore | 6 +- assets/assets.go | 6 +- assets/bindata.go | 14 +- autoupdate/main.go | 2 +- bigint/bigint.go | 2 +- bindata/bindata.go | 12 +- chain/config/config.go | 2 +- chain/utils.go | 6 +- cmd/btfs/autoupdate.go | 2 +- cmd/btfs/chain_config_test.go | 2 +- cmd/btfs/daemon.go | 271 +- cmd/btfs/init.go | 13 +- cmd/btfs/main.go | 2 +- cmd/ipfswatch/main.go | 6 +- commands/context.go | 6 +- core/commands/add.go | 6 +- core/commands/block.go | 6 +- core/commands/bootstrap.go | 2 +- core/commands/bttc/send_btt_to.go | 5 + core/commands/bttc/send_token_to.go | 5 + core/commands/bttc/send_wbtt_to.go | 5 + core/commands/bttc/swap_btt2wbtt.go | 5 + core/commands/bttc/swap_wbtt2btt.go | 5 + core/commands/cat.go | 8 +- core/commands/cheque/balance_btt.go | 6 + core/commands/cheque/balance_muti_tokens.go | 11 + core/commands/cheque/cash_list.go | 6 + core/commands/cheque/cash_status.go | 5 + core/commands/cheque/chaininfo.go | 6 + core/commands/cheque/cheque.go | 14 + core/commands/cheque/receive.go | 5 + core/commands/cheque/receive_history_list.go | 6 + core/commands/cheque/receive_history_peer.go | 6 + core/commands/cheque/receive_history_stats.go | 6 + .../cheque/receive_history_stats_all.go | 6 + core/commands/cheque/receive_list.go | 6 + core/commands/cheque/receive_list_all.go | 6 + core/commands/cheque/receive_total_count.go | 6 + .../commands/cheque/send-history-stats-all.go | 6 + core/commands/cheque/send-history-stats.go | 6 + core/commands/cheque/send.go | 6 + core/commands/cheque/send_history_list.go | 6 + core/commands/cheque/send_history_peer.go | 5 + core/commands/cheque/send_list.go | 6 + core/commands/cheque/send_list_all.go | 6 + core/commands/cheque/send_total_count.go | 6 + core/commands/cheque/stats-all.go | 6 + core/commands/cheque/stats.go | 8 +- core/commands/cmdenv/env.go | 6 +- core/commands/cmdenv/file.go | 10 +- core/commands/commands_test.go | 12 +- core/commands/config.go | 58 +- core/commands/dag/dag.go | 8 +- core/commands/dag/export.go | 2 +- core/commands/dht_test.go | 2 +- core/commands/dns.go | 9 +- core/commands/encryption_test.go | 4 +- core/commands/files.go | 8 +- core/commands/guard.go | 9 +- core/commands/id.go | 20 +- core/commands/keystore.go | 2 +- core/commands/ls.go | 10 +- core/commands/metadata.go | 4 +- core/commands/mount_nofuse.go | 2 +- core/commands/mount_unix.go | 2 +- core/commands/name/ipns.go | 4 +- core/commands/name/publish.go | 6 +- core/commands/network.go | 8 +- core/commands/object/diff.go | 2 +- core/commands/object/object.go | 4 +- core/commands/object/patch.go | 4 +- core/commands/pin.go | 6 +- core/commands/pubsub.go | 2 +- core/commands/refs.go | 4 +- core/commands/resolve.go | 6 +- core/commands/rm/rm.go | 6 +- core/commands/root.go | 1 - core/commands/settlements/list.go | 6 + core/commands/settlements/peer.go | 6 + core/commands/statuscontract.go | 28 +- core/commands/statusonline.go | 23 +- core/commands/storage/announce/announce.go | 1 - core/commands/storage/challenge/challenge.go | 13 +- .../storage/challenge/challenge_helper.go | 6 +- .../challenge/challenge_helper_test.go | 4 +- core/commands/storage/contracts/contracts.go | 26 +- core/commands/storage/helper/call.go | 4 +- core/commands/storage/helper/contracts.go | 2 +- core/commands/storage/helper/hosts.go | 4 +- core/commands/storage/helper/hosts_test.go | 6 +- core/commands/storage/helper/reed_solomon.go | 8 +- .../storage/helper/reed_solomon_test.go | 4 +- core/commands/storage/hosts/hosts.go | 13 +- core/commands/storage/info/info.go | 8 +- core/commands/storage/path/path.go | 4 +- core/commands/storage/stats/stats.go | 22 +- core/commands/storage/stats/stats_test.go | 2 +- core/commands/storage/upload/escrow/escrow.go | 8 +- core/commands/storage/upload/guard/guard.go | 12 +- .../storage/upload/helper/hosts_helper.go | 6 +- .../storage/upload/helper/upload_helper.go | 6 +- .../offline/offline_get_contract_batch.go | 6 + .../upload/offline/offline_get_unsigned.go | 6 + .../storage/upload/offline/offline_sign.go | 6 + .../offline/offline_sign_contract_batch.go | 6 + .../storage/upload/sessions/datastore.go | 2 +- .../storage/upload/sessions/host_shards.go | 4 +- .../upload/sessions/renter_sessions.go | 2 +- .../storage/upload/sessions/renter_shards.go | 6 +- .../storage/upload/upload/dc_repair_router.go | 19 +- .../storage/upload/upload/do_guard.go | 10 +- .../upload/upload/do_sign_guard_contracts.go | 8 +- .../storage/upload/upload/do_waitupload.go | 6 +- .../storage/upload/upload/host_manager.go | 6 +- .../upload/upload/host_manager_test.go | 4 +- .../upload/upload/receive_check_tokens.go | 6 + .../storage/upload/upload/recieve_cheque.go | 6 + .../storage/upload/upload/recieve_contract.go | 8 +- .../storage/upload/upload/recieve_init.go | 18 +- core/commands/storage/upload/upload/repair.go | 12 +- core/commands/storage/upload/upload/status.go | 12 +- core/commands/storage/upload/upload/upload.go | 6 + core/commands/swarm.go | 4 +- core/commands/tar.go | 2 +- core/commands/test.go | 2 +- core/commands/unixfs/ls.go | 4 +- core/commands/urlstore.go | 4 +- core/commands/vault/vault_address.go | 6 + core/commands/vault/vault_balance.go | 6 + core/commands/vault/vault_balance_all.go | 5 + core/commands/vault/vault_deposit.go | 5 + core/commands/vault/vault_upgrade.go | 6 + core/commands/vault/vault_withdraw.go | 5 + core/commands/vault/wbtt_balance.go | 6 + core/commands/wallet.go | 437 - core/core.go | 34 +- core/core_test.go | 2 +- core/coreapi/block.go | 6 +- core/coreapi/coreapi.go | 33 +- core/coreapi/dht.go | 6 +- core/coreapi/key.go | 6 +- core/coreapi/name.go | 12 +- core/coreapi/object.go | 8 +- core/coreapi/path.go | 33 +- core/coreapi/pin.go | 6 +- core/coreapi/pubsub.go | 4 +- core/coreapi/swarm.go | 2 +- core/coreapi/test/api_test.go | 6 +- core/coreapi/unixfs.go | 32 +- core/coreapi/unixfs_test.go | 2 +- core/corehttp/commands.go | 9 +- core/corehttp/gateway.go | 266 +- core/corehttp/gateway/README.md | 34 + core/corehttp/gateway/assets/README.md | 27 + core/corehttp/gateway/assets/assets.go | 203 + core/corehttp/gateway/assets/build.sh | 14 + core/corehttp/gateway/assets/dag-index.html | 67 + .../gateway/assets/directory-index.html | 97 + core/corehttp/gateway/assets/knownIcons.txt | 65 + .../gateway/assets/src/dag-index.html | 66 + .../gateway/assets/src/directory-index.html | 96 + core/corehttp/gateway/assets/src/icons.css | 807 + core/corehttp/gateway/assets/src/style.css | 213 + core/corehttp/gateway/assets/test/go.mod | 3 + core/corehttp/gateway/assets/test/main.go | 156 + core/corehttp/gateway/blocks_gateway.go | 455 + core/corehttp/gateway/dns.go | 92 + core/corehttp/gateway/errors.go | 191 + core/corehttp/gateway/errors_test.go | 65 + core/corehttp/gateway/gateway.go | 227 + core/corehttp/gateway/gateway_test.go | 545 + core/corehttp/gateway/handler.go | 773 + core/corehttp/gateway/handler_block.go | 55 + core/corehttp/gateway/handler_car.go | 95 + core/corehttp/gateway/handler_codec.go | 281 + core/corehttp/gateway/handler_defaults.go | 192 + core/corehttp/gateway/handler_ipns_record.go | 93 + core/corehttp/gateway/handler_tar.go | 98 + core/corehttp/gateway/handler_test.go | 231 + .../gateway/handler_unixfs__redirects.go | 294 + core/corehttp/gateway/handler_unixfs_dir.go | 224 + core/corehttp/gateway/handler_unixfs_file.go | 108 + core/corehttp/gateway/hostname.go | 594 + core/corehttp/gateway/hostname_test.go | 290 + core/corehttp/{ => gateway}/lazyseek.go | 2 +- core/corehttp/gateway/lazyseek_test.go | 98 + core/corehttp/gateway/metrics.go | 269 + core/corehttp/gateway/testdata/fixtures.car | Bin 0 -> 1688 bytes core/corehttp/gateway_handler.go | 974 - core/corehttp/gateway_indexPage.go | 108 - core/corehttp/gateway_reedsolomon.go | 57 - core/corehttp/gateway_test.go | 519 +- core/corehttp/hostname.go | 503 - core/corehttp/hostname_test.go | 198 - core/corehttp/lazyseek_test.go | 137 - core/corehttp/metrics.go | 72 +- core/corehttp/mutex_profile.go | 35 + core/corehttp/p2p_proxy.go | 9 +- core/corehttp/redirect.go | 17 +- core/corehttp/remote/call.go | 2 +- core/corehttp/remote/p2p_call.go | 2 +- core/corehttp/webui.go | 3 +- core/corerepo/gc.go | 2 +- core/coreunix/add.go | 22 +- core/coreunix/metadata.go | 18 +- core/coreunix/reed_solomon_add.go | 6 +- core/coreunix/test/add.go | 12 +- core/coreunix/test/add_test.go | 6 +- core/coreunix/test/metadata_test.go | 20 +- core/hub/settings.go | 6 +- core/hub/settings_test.go | 2 +- core/hub/sync.go | 4 +- core/mock/mock.go | 17 +- core/node/builder.go | 2 +- core/node/core.go | 32 +- core/node/dns.go | 2 +- core/node/groups.go | 9 +- core/node/helpers.go | 2 +- core/node/ipns.go | 18 +- core/node/libp2p/host.go | 2 +- core/node/libp2p/hostopt.go | 5 +- core/node/libp2p/libp2p.go | 2 +- core/node/libp2p/nat.go | 2 +- core/node/libp2p/rcmgr.go | 2 +- core/node/libp2p/rcmgr_defaults.go | 8 +- core/node/libp2p/relay.go | 2 +- core/node/libp2p/routing.go | 2 +- core/node/libp2p/routingopt.go | 2 +- core/node/libp2p/sec.go | 2 +- core/node/libp2p/smux.go | 2 +- core/node/libp2p/transport.go | 2 +- core/node/storage.go | 2 +- core/wallet/aes.go | 73 - core/wallet/helper.go | 16 - core/wallet/import.go | 75 - core/wallet/import_test.go | 50 - core/wallet/ledger.go | 53 - core/wallet/signature.go | 113 - core/wallet/speed.go | 80 - core/wallet/speed_darwin.go | 21 - core/wallet/speed_other.go | 14 - core/wallet/speed_test.go | 68 - core/wallet/speed_windows.go | 18 - core/wallet/transaction.go | 732 - core/wallet/tron.go | 372 - core/wallet/tron_test.go | 78 - core/wallet/wallet.go | 264 - docs/examples/go-ipfs-as-a-library/main.go | 8 +- fuse/ipns/common.go | 2 +- fuse/ipns/ipns_unix.go | 10 +- fuse/readonly/ipfs_test.go | 10 +- fuse/readonly/readonly_unix.go | 52 +- go.mod | 91 +- go.sum | 377 +- namesys/base.go | 2 +- namesys/dns.go | 17 +- namesys/dns_test.go | 5 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 6 +- namesys/namesys.go | 87 +- namesys/namesys_test.go | 17 +- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +- namesys/publisher_test.go | 2 +- namesys/republisher/repub.go | 4 +- namesys/republisher/repub_test.go | 15 +- namesys/resolve/pathresolver_test.go | 32 - namesys/resolve/resolve.go | 5 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 6 +- plugin/daemon.go | 2 +- plugin/loader/loader.go | 4 +- protos/contracts/contracts.pb.go | 6 +- protos/renter/renters.pb.go | 6 +- protos/session/session.pb.go | 6 +- protos/shard/shard.pb.go | 6 +- protos/wallet/wallet.pb.go | 8 +- repo/fsrepo/config_test.go | 2 +- repo/fsrepo/doc.go | 26 +- repo/fsrepo/fsrepo.go | 4 +- repo/fsrepo/fsrepo_test.go | 2 +- repo/fsrepo/migrations/migrations.go | 2 +- repo/mock.go | 2 +- repo/onlyone.go | 4 +- repo/pbstore.go | 2 +- repo/pbstore_test.go | 2 +- repo/repo.go | 2 +- reportstatus/reportstatus.go | 4 +- routing/delegated.go | 2 +- routing/delegated_test.go | 2 +- routing/error.go | 2 +- settlement/swap/swapprotocol/pb/swap.pb.go | 2 +- settlement/swap/swapprotocol/swapprotocol.go | 2 +- settlement/swap/vault/chequestore.go | 11 +- spin/analytics.go | 9 +- spin/analytics_online.go | 6 +- spin/analytics_online_daily.go | 6 +- tar/format.go | 6 +- test/bench/bench_cli_ipfs_add/main.go | 2 +- test/bench/offline_add/main.go | 2 +- .../graphsync-get/graphsync-get.go | 10 +- test/integration/addcat_test.go | 6 +- test/integration/bench_cat_test.go | 2 +- test/integration/rs_addcat_test.go | 4 +- test/integration/three_legged_cat_test.go | 2 +- test/integration/wan_lan_dht_test.go | 72 +- test_pkgs.txt | 2 +- tests_coverage.html | 38514 ++++++++++++++++ thirdparty/notifier/notifier.go | 90 +- utils/common.go | 22 + version.go | 2 +- 311 files changed, 47665 insertions(+), 6077 deletions(-) delete mode 100644 core/commands/wallet.go create mode 100644 core/corehttp/gateway/README.md create mode 100644 core/corehttp/gateway/assets/README.md create mode 100644 core/corehttp/gateway/assets/assets.go create mode 100755 core/corehttp/gateway/assets/build.sh create mode 100644 core/corehttp/gateway/assets/dag-index.html create mode 100644 core/corehttp/gateway/assets/directory-index.html create mode 100644 core/corehttp/gateway/assets/knownIcons.txt create mode 100644 core/corehttp/gateway/assets/src/dag-index.html create mode 100644 core/corehttp/gateway/assets/src/directory-index.html create mode 100644 core/corehttp/gateway/assets/src/icons.css create mode 100644 core/corehttp/gateway/assets/src/style.css create mode 100644 core/corehttp/gateway/assets/test/go.mod create mode 100644 core/corehttp/gateway/assets/test/main.go create mode 100644 core/corehttp/gateway/blocks_gateway.go create mode 100644 core/corehttp/gateway/dns.go create mode 100644 core/corehttp/gateway/errors.go create mode 100644 core/corehttp/gateway/errors_test.go create mode 100644 core/corehttp/gateway/gateway.go create mode 100644 core/corehttp/gateway/gateway_test.go create mode 100644 core/corehttp/gateway/handler.go create mode 100644 core/corehttp/gateway/handler_block.go create mode 100644 core/corehttp/gateway/handler_car.go create mode 100644 core/corehttp/gateway/handler_codec.go create mode 100644 core/corehttp/gateway/handler_defaults.go create mode 100644 core/corehttp/gateway/handler_ipns_record.go create mode 100644 core/corehttp/gateway/handler_tar.go create mode 100644 core/corehttp/gateway/handler_test.go create mode 100644 core/corehttp/gateway/handler_unixfs__redirects.go create mode 100644 core/corehttp/gateway/handler_unixfs_dir.go create mode 100644 core/corehttp/gateway/handler_unixfs_file.go create mode 100644 core/corehttp/gateway/hostname.go create mode 100644 core/corehttp/gateway/hostname_test.go rename core/corehttp/{ => gateway}/lazyseek.go (98%) create mode 100644 core/corehttp/gateway/lazyseek_test.go create mode 100644 core/corehttp/gateway/metrics.go create mode 100644 core/corehttp/gateway/testdata/fixtures.car delete mode 100644 core/corehttp/gateway_handler.go delete mode 100644 core/corehttp/gateway_indexPage.go delete mode 100644 core/corehttp/gateway_reedsolomon.go delete mode 100644 core/corehttp/hostname.go delete mode 100644 core/corehttp/hostname_test.go delete mode 100644 core/corehttp/lazyseek_test.go delete mode 100644 core/wallet/aes.go delete mode 100644 core/wallet/helper.go delete mode 100644 core/wallet/import.go delete mode 100644 core/wallet/import_test.go delete mode 100644 core/wallet/ledger.go delete mode 100644 core/wallet/signature.go delete mode 100644 core/wallet/speed.go delete mode 100644 core/wallet/speed_darwin.go delete mode 100644 core/wallet/speed_other.go delete mode 100644 core/wallet/speed_test.go delete mode 100644 core/wallet/speed_windows.go delete mode 100644 core/wallet/transaction.go delete mode 100644 core/wallet/tron.go delete mode 100644 core/wallet/tron_test.go delete mode 100644 core/wallet/wallet.go delete mode 100644 namesys/resolve/pathresolver_test.go create mode 100644 tests_coverage.html create mode 100644 utils/common.go diff --git a/.gitignore b/.gitignore index 126c00840..937692cce 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,8 @@ cmd/btfs/btfs cmd/btfs/btfs.upgrade cmd/btfs/ttt cmd/btfs/tt -cmd/btfs/t* \ No newline at end of file +cmd/btfs/t* +cmd/btfs/btfs.1.* +cmd/btfs/btfs.2.* +cmd/btfs/btfs.3.* +btfs.linux.* \ No newline at end of file diff --git a/assets/assets.go b/assets/assets.go index 667520544..e86626bc0 100644 --- a/assets/assets.go +++ b/assets/assets.go @@ -13,9 +13,9 @@ import ( "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/coreapi" - files "github.com/TRON-US/go-btfs-files" - options "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" + files "github.com/bittorrent/go-btfs-files" + options "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" ) diff --git a/assets/bindata.go b/assets/bindata.go index 3d1722b2e..d8eb87f4a 100644 --- a/assets/bindata.go +++ b/assets/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package assets generated by go-bindata.// sources: +// Package assets generated by go-bindata.// sources: // init-doc/about // init-doc/contact // init-doc/help @@ -333,11 +333,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error diff --git a/autoupdate/main.go b/autoupdate/main.go index dc404ad30..29b4f6208 100644 --- a/autoupdate/main.go +++ b/autoupdate/main.go @@ -11,7 +11,7 @@ import ( "github.com/bittorrent/go-btfs/logger" - "github.com/TRON-US/go-btfs-api" + "github.com/bittorrent/go-btfs-api" ) var log = logger.InitLogger("update.log").Sugar() diff --git a/bigint/bigint.go b/bigint/bigint.go index 3692d0cb9..5c334f2c5 100644 --- a/bigint/bigint.go +++ b/bigint/bigint.go @@ -38,7 +38,7 @@ func (i *BigInt) UnmarshalJSON(b []byte) error { return nil } -//Wrap wraps big.Int pointer into BigInt struct. +// Wrap wraps big.Int pointer into BigInt struct. func Wrap(i *big.Int) *BigInt { return &BigInt{Int: i} } diff --git a/bindata/bindata.go b/bindata/bindata.go index fcc5f7cc4..06bcc55d2 100644 --- a/bindata/bindata.go +++ b/bindata/bindata.go @@ -156,11 +156,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error diff --git a/chain/config/config.go b/chain/config/config.go index 996e44885..e9459d99f 100644 --- a/chain/config/config.go +++ b/chain/config/config.go @@ -2,7 +2,7 @@ package config import ( "errors" - cfg "github.com/TRON-US/go-btfs-config" + cfg "github.com/bittorrent/go-btfs-config" "github.com/ethereum/go-ethereum/common" ) diff --git a/chain/utils.go b/chain/utils.go index 571a769c7..1e3df1324 100644 --- a/chain/utils.go +++ b/chain/utils.go @@ -4,21 +4,21 @@ import ( "encoding/base64" "errors" "fmt" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "io/ioutil" "math/rand" "os" "time" cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs-common/crypto" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" oldcmds "github.com/bittorrent/go-btfs/commands" "github.com/bittorrent/go-btfs/core/commands/storage/path" "github.com/bittorrent/go-btfs/settlement/swap/vault" cpt "github.com/bittorrent/go-btfs/transaction/crypto" "github.com/bittorrent/go-btfs/transaction/storage" "github.com/ethereum/go-ethereum/common" - "github.com/tron-us/go-btfs-common/crypto" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" ) // after btfs init diff --git a/cmd/btfs/autoupdate.go b/cmd/btfs/autoupdate.go index bee13654d..1d36756d3 100644 --- a/cmd/btfs/autoupdate.go +++ b/cmd/btfs/autoupdate.go @@ -17,8 +17,8 @@ import ( "strings" "time" - "github.com/TRON-US/go-btfs-api" btfs_version "github.com/bittorrent/go-btfs" + "github.com/bittorrent/go-btfs-api" "github.com/mholt/archiver/v3" "github.com/pkg/errors" "gopkg.in/yaml.v2" diff --git a/cmd/btfs/chain_config_test.go b/cmd/btfs/chain_config_test.go index 61654149e..24511989b 100644 --- a/cmd/btfs/chain_config_test.go +++ b/cmd/btfs/chain_config_test.go @@ -6,8 +6,8 @@ import ( "gotest.tools/assert" - Cfg "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" + Cfg "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/chain" ) diff --git a/cmd/btfs/daemon.go b/cmd/btfs/daemon.go index 0210d0152..889e5d984 100644 --- a/cmd/btfs/daemon.go +++ b/cmd/btfs/daemon.go @@ -7,7 +7,6 @@ import ( "errors" _ "expvar" "fmt" - "github.com/bittorrent/go-btfs/chain/tokencfg" "io/ioutil" "math/rand" "net" @@ -22,12 +21,14 @@ import ( "sync" "time" + "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/guide" - config "github.com/TRON-US/go-btfs-config" - cserial "github.com/TRON-US/go-btfs-config/serialize" version "github.com/bittorrent/go-btfs" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + cserial "github.com/bittorrent/go-btfs-config/serialize" "github.com/bittorrent/go-btfs/bindata" "github.com/bittorrent/go-btfs/chain" cc "github.com/bittorrent/go-btfs/chain/config" @@ -53,6 +54,8 @@ import ( "github.com/bittorrent/go-btfs/transaction/storage" "github.com/ethereum/go-ethereum/common" + cp "github.com/bittorrent/go-btfs-common/crypto" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" multierror "github.com/hashicorp/go-multierror" util "github.com/ipfs/go-ipfs-util" mprome "github.com/ipfs/go-metrics-prometheus" @@ -62,8 +65,6 @@ import ( manet "github.com/multiformats/go-multiaddr/net" prometheus "github.com/prometheus/client_golang/prometheus" promauto "github.com/prometheus/client_golang/prometheus/promauto" - cp "github.com/tron-us/go-btfs-common/crypto" - nodepb "github.com/tron-us/go-btfs-common/protos/node" ) const ( @@ -251,9 +252,6 @@ func wrapDaemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environ } func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) (_err error) { - //swapprotocol.Req = req - //swapprotocol.Env = env - cctx := env.(*oldcmds.Context) _, b := os.LookupEnv(path.BtfsPathKey) if !b { @@ -321,7 +319,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment } if err = doInit(os.Stdout, cfg, false, utilmain.NBitsForKeypairDefault, profiles, conf, - keyTypeDefault, "", "", false); err != nil { + keyTypeDefault, "", "", false, false); err != nil { return err } @@ -397,17 +395,20 @@ If the user need to start multiple nodes on the same machine, the configuration fmt.Println("the address of Bttc format is: ", address0x) fmt.Println("the address of Tron format is: ", keys.Base58Address) - // guide server init - optionApiAddr, _ := req.Options[commands.ApiOption].(string) - guide.SetServerAddr(cfg.Addresses.API, optionApiAddr) - guide.SetInfo(&guide.Info{ - BtfsVersion: version.CurrentVersionNumber, - HostID: cfg.Identity.PeerID, - BttcAddress: address0x.String(), - PrivateKey: hex.EncodeToString(pkbytesOri[4:]), - }) - guide.StartServer() - defer guide.TryShutdownServer() + SimpleMode := cfg.SimpleMode + if SimpleMode == false { + // guide server init + optionApiAddr, _ := req.Options[commands.ApiOption].(string) + guide.SetServerAddr(cfg.Addresses.API, optionApiAddr) + guide.SetInfo(&guide.Info{ + BtfsVersion: version.CurrentVersionNumber, + HostID: cfg.Identity.PeerID, + BttcAddress: address0x.String(), + PrivateKey: hex.EncodeToString(pkbytesOri[4:]), + }) + guide.StartServer() + defer guide.TryShutdownServer() + } //chain init configRoot := cctx.ConfigRoot @@ -420,113 +421,107 @@ If the user need to start multiple nodes on the same machine, the configuration statestore.Close() }() - chainid, stored, err := getChainID(req, cfg, statestore) - if err != nil { - return err - } - chainCfg, err := chainconfig.InitChainConfig(cfg, stored, chainid) - if err != nil { - return err - } - - // upgrade factory to v2 if necessary - needUpdateFactory := false - needUpdateFactory, err = doIfNeedUpgradeFactoryToV2(chainid, chainCfg, statestore, repo, cfg, configRoot) - if err != nil { - fmt.Printf("upgrade vault contract failed, err=%s\n", err) - return err - } - if needUpdateFactory { // no error means upgrade preparation done, re-init the statestore - statestore, err = chain.InitStateStore(configRoot) + if SimpleMode == false { + chainid, stored, err := getChainID(req, cfg, statestore) if err != nil { - fmt.Println("init statestore err: ", err) return err } - err = chain.StoreChainIdIfNotExists(chainid, statestore) + chainCfg, err := chainconfig.InitChainConfig(cfg, stored, chainid) if err != nil { - fmt.Printf("save chainid failed, err: %s\n", err) - return + return err } - } - tokencfg.InitToken(chainid) - - //endpoint - chainInfo, err := chain.InitChain(context.Background(), statestore, singer, time.Duration(1000000000), - chainid, cfg.Identity.PeerID, chainCfg) - if err != nil { - return err - } + // upgrade factory to v2 if necessary + needUpdateFactory := false + needUpdateFactory, err = doIfNeedUpgradeFactoryToV2(chainid, chainCfg, statestore, repo, cfg, configRoot) + if err != nil { + fmt.Printf("upgrade vault contract failed, err=%s\n", err) + return err + } + if needUpdateFactory { // no error means upgrade preparation done, re-init the statestore + statestore, err = chain.InitStateStore(configRoot) + if err != nil { + fmt.Println("init statestore err: ", err) + return err + } + err = chain.StoreChainIdIfNotExists(chainid, statestore) + if err != nil { + fmt.Printf("save chainid failed, err: %s\n", err) + return + } + } - // Sync the with the given Ethereum backend: - isSynced, _, err := transaction.IsSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) - if err != nil { - return fmt.Errorf("is synced: %w", err) - } + tokencfg.InitToken(chainid) - if !isSynced { - log.Infof("waiting to sync with the Ethereum backend") + //endpoint + chainInfo, err := chain.InitChain(context.Background(), statestore, singer, time.Duration(1000000000), + chainid, cfg.Identity.PeerID, chainCfg) + if err != nil { + return err + } - err := transaction.WaitSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) + // Sync the with the given Ethereum backend: + isSynced, _, err := transaction.IsSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) if err != nil { - return fmt.Errorf("waiting backend sync: %w", err) + return fmt.Errorf("is synced: %w", err) } - } - deployGasPrice, found := req.Options[deploymentGasPrice].(string) - if !found { - deployGasPrice = chainInfo.Chainconfig.DeploymentGas - } + if !isSynced { + log.Infof("waiting to sync with the Ethereum backend") - /*settleinfo*/ - settleInfo, err := chain.InitSettlement(context.Background(), statestore, chainInfo, deployGasPrice, chainInfo.ChainID) - if err != nil { - fmt.Println("init settlement err: ", err) - if strings.Contains(err.Error(), "insufficient funds") { - fmt.Println("Please recharge BTT to your address to solve this error") + err := transaction.WaitSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) + if err != nil { + return fmt.Errorf("waiting backend sync: %w", err) + } } - if strings.Contains(err.Error(), "contract deployment failed") { - fmt.Println(`Solution1: It is recommended to check if the balance is sufficient. If the balance is low, it is recommended to top up.`) - fmt.Println(`Solution2: Suggest to redeploy.`) + + deployGasPrice, found := req.Options[deploymentGasPrice].(string) + if !found { + deployGasPrice = chainInfo.Chainconfig.DeploymentGas } - return err - } + /*settleinfo*/ + settleInfo, err := chain.InitSettlement(context.Background(), statestore, chainInfo, deployGasPrice, chainInfo.ChainID) + if err != nil { + fmt.Println("init settlement err: ", err) + if strings.Contains(err.Error(), "insufficient funds") { + fmt.Println("Please recharge BTT to your address to solve this error") + } + if strings.Contains(err.Error(), "contract deployment failed") { + fmt.Println(`Solution1: It is recommended to check if the balance is sufficient. If the balance is low, it is recommended to top up.`) + fmt.Println(`Solution2: Suggest to redeploy.`) + } - /*upgrade vault implementation*/ - oldImpl, newImpl, err := settleInfo.VaultService.UpgradeTo(context.Background(), chainInfo.Chainconfig.VaultLogicAddress) - if err != nil { - emsg := err.Error() - if strings.Contains(emsg, "already upgraded") { - fmt.Printf("vault implementation is updated: %s\n", chainInfo.Chainconfig.VaultLogicAddress) - err = nil - } else { - fmt.Println("upgrade vault implementation err: ", err) return err } - } else { - fmt.Printf("vault logic implementation upgrade from %s to %s\n", oldImpl, newImpl) - } - // init report status contract - //reportStatusServ := reportstatus.Init(chainInfo.TransactionService, cfg, chainCfg.StatusAddress) - //err = CheckExistLastOnlineReport(cfg, configRoot, chainid, reportStatusServ) - //if err != nil { - // fmt.Println("check report status, err: ", err) - // return err - //} + /*upgrade vault implementation*/ + oldImpl, newImpl, err := settleInfo.VaultService.UpgradeTo(context.Background(), chainInfo.Chainconfig.VaultLogicAddress) + if err != nil { + emsg := err.Error() + if strings.Contains(emsg, "already upgraded") { + fmt.Printf("vault implementation is updated: %s\n", chainInfo.Chainconfig.VaultLogicAddress) + err = nil + } else { + fmt.Println("upgrade vault implementation err: ", err) + return err + } + } else { + fmt.Printf("vault logic implementation upgrade from %s to %s\n", oldImpl, newImpl) + } - // init report online info - err = CheckExistLastOnlineReportV2(cfg, configRoot, chainid) - if err != nil { - fmt.Println("check report status, err: ", err) - return err - } + // init report online info + err = CheckExistLastOnlineReportV2(cfg, configRoot, chainid) + if err != nil { + fmt.Println("check report status, err: ", err) + return err + } - err = CheckHubDomainConfig(cfg, configRoot, chainid) - if err != nil { - fmt.Println("check report status, err: ", err) - return err + err = CheckHubDomainConfig(cfg, configRoot, chainid) + if err != nil { + fmt.Println("check report status, err: ", err) + return err + } } // init ip2location db @@ -648,11 +643,13 @@ If the user need to start multiple nodes on the same machine, the configuration } node.Process.AddChild(goprocess.WithTeardown(cctx.Plugins.Close)) - // if the guide server was started, shutdown it - guide.TryShutdownServer() + if SimpleMode == false { + // if the guide server was started, shutdown it + guide.TryShutdownServer() + } // construct api endpoint - every time - apiErrc, err := serveHTTPApi(req, cctx) + apiErrc, err := serveHTTPApi(req, cctx, SimpleMode) if err != nil { return err } @@ -716,19 +713,22 @@ If the user need to start multiple nodes on the same machine, the configuration functest(cfg.Services.OnlineServerDomain, cfg.Identity.PeerID, hValue) } - // set Analytics flag if specified - if dc, ok := req.Options[enableDataCollection]; ok == true { - node.Repo.SetConfigKey("Experimental.Analytics", dc) - } - // Spin jobs in the background - spin.RenterSessions(req, env) - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err + if SimpleMode == false { + // set Analytics flag if specified + if dc, ok := req.Options[enableDataCollection]; ok == true { + node.Repo.SetConfigKey("Experimental.Analytics", dc) + } + // Spin jobs in the background + spin.RenterSessions(req, env) + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + + spin.Analytics(api, cctx.ConfigRoot, node, version.CurrentVersionNumber, hValue) + spin.Hosts(node, env) + spin.Contracts(node, req, env, nodepb.ContractStat_HOST.String()) } - spin.Analytics(api, cctx.ConfigRoot, node, version.CurrentVersionNumber, hValue) - spin.Hosts(node, env) - spin.Contracts(node, req, env, nodepb.ContractStat_HOST.String()) // Give the user some immediate feedback when they hit C-c go func() { @@ -751,7 +751,7 @@ If the user need to start multiple nodes on the same machine, the configuration } // serveHTTPApi collects options, creates listener, prints status message and starts serving requests -func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { +func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context, SimpleMode bool) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { return nil, fmt.Errorf("serveHTTPApi: GetConfig() failed: %s", err) @@ -799,7 +799,9 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error // Browsers require TCP. switch listener.Addr().Network() { case "tcp", "tcp4", "tcp6": - fmt.Printf("Dashboard: http://%s/dashboard\n", listener.Addr()) + if SimpleMode == false { + fmt.Printf("Dashboard: http://%s/dashboard\n", listener.Addr()) + } } } @@ -808,9 +810,9 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error // only the webui objects are allowed. // if you know what you're doing, go ahead and pass --unrestricted-api. unrestricted, _ := req.Options[unrestrictedApiAccessKwd].(bool) - gatewayOpt := corehttp.GatewayOption(false, corehttp.WebUIPaths...) + gatewayOpt := corehttp.GatewayOption(corehttp.WebUIPaths...) if unrestricted { - gatewayOpt = corehttp.GatewayOption(true, "/btfs", "/btns") + gatewayOpt = corehttp.GatewayOption("/btfs", "/btns") } var opts = []corehttp.ServeOption{ @@ -824,7 +826,9 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error corehttp.VersionOption(), defaultMux("/debug/vars"), defaultMux("/debug/pprof/"), + defaultMux("/debug/stack"), corehttp.MutexFractionOption("/debug/pprof-mutex/"), + corehttp.BlockProfileRateOption("/debug/pprof-block/"), corehttp.MetricsScrapingOption("/debug/metrics/prometheus"), corehttp.LogOption(), } @@ -961,7 +965,9 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e if !writableOptionFound { writable = cfg.Gateway.Writable } - + if writable { + log.Errorf("Support for Gateway.Writable and --writable has been REMOVED. Please remove it from your config file or CLI.") + } listeners, err := sockets.TakeListeners("io.ipfs.gateway") if err != nil { return nil, fmt.Errorf("serveHTTPGateway: socket activation failed: %s", err) @@ -991,14 +997,8 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e listeners = append(listeners, gwLis) } - // we might have listened to /tcp/0 - let's see what we are listing on - gwType := "readonly" - if writable { - gwType = "writable" - } - for _, listener := range listeners { - fmt.Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr()) + fmt.Printf("Gateway server listening on %s\n", listener.Multiaddr()) } cmdctx := *cctx @@ -1007,7 +1007,8 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e var opts = []corehttp.ServeOption{ corehttp.MetricsCollectionOption("gateway"), corehttp.HostnameOption(), - corehttp.GatewayOption(writable, "/btfs", "/btns"), + // TODO: rm writable + corehttp.GatewayOption("/btfs", "/btns"), corehttp.VersionOption(), corehttp.CheckVersionOption(), corehttp.CommandsROOption(cmdctx), @@ -1025,7 +1026,9 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e if err != nil { return nil, fmt.Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) } - + if len(cfg.Gateway.PathPrefixes) > 0 { + log.Errorf("Support for custom Gateway.PathPrefixes was removed") + } errc := make(chan error) var wg sync.WaitGroup for _, lis := range listeners { diff --git a/cmd/btfs/init.go b/cmd/btfs/init.go index d6a4fa91c..1261778a2 100644 --- a/cmd/btfs/init.go +++ b/cmd/btfs/init.go @@ -21,9 +21,9 @@ import ( "github.com/bittorrent/go-btfs/namesys" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" ) const ( @@ -35,6 +35,7 @@ const ( importKeyOptionName = "import" rmOnUnpinOptionName = "rm-on-unpin" seedOptionName = "seed" + simpleMode = "simple-mode" /* passWordOptionName = "password" passwordFileoptionName = "password-file" @@ -70,6 +71,7 @@ environment variable: cmds.StringOption(importKeyOptionName, "i", "Import TRON private key to generate btfs PeerID."), cmds.BoolOption(rmOnUnpinOptionName, "r", "Remove unpinned files.").WithDefault(false), cmds.StringOption(seedOptionName, "s", "Import seed phrase"), + cmds.BoolOption(simpleMode, "sm", "init with simple mode or not."), /* cmds.StringOption(passWordOptionName, "", "password for decrypting keys."), cmds.StringOption(passwordFileoptionName, "", "path to a file that contains password for decrypting keys"), @@ -133,12 +135,13 @@ environment variable: importKey, _ := req.Options[importKeyOptionName].(string) keyType, _ := req.Options[keyTypeOptionName].(string) seedPhrase, _ := req.Options[seedOptionName].(string) + simpleModeIn, _ := req.Options[simpleMode].(bool) /* password, _ := req.Options[passWordOptionName].(string) passwordFile, _ := req.Options[passwordFileoptionName].(string) */ - return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profile, conf, keyType, importKey, seedPhrase, rmOnUnpin) + return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profile, conf, keyType, importKey, seedPhrase, rmOnUnpin, simpleModeIn) }, } @@ -147,7 +150,7 @@ Reinitializing would overwrite your keys. `) func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, confProfiles string, conf *config.Config, - keyType string, importKey string, mnemonic string, rmOnUnpin bool) error { + keyType string, importKey string, mnemonic string, rmOnUnpin bool, simpleModeIn bool) error { importKey, mnemonic, err := util.GenerateKey(importKey, keyType, mnemonic) if err != nil { @@ -194,6 +197,8 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con return err } + conf.SimpleMode = simpleModeIn + if err := fsrepo.Init(repoRoot, conf); err != nil { return err } diff --git a/cmd/btfs/main.go b/cmd/btfs/main.go index e6d5598e0..b2d31c2b5 100644 --- a/cmd/btfs/main.go +++ b/cmd/btfs/main.go @@ -21,10 +21,10 @@ import ( repo "github.com/bittorrent/go-btfs/repo" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs-cmds/cli" cmdhttp "github.com/bittorrent/go-btfs-cmds/http" + config "github.com/bittorrent/go-btfs-config" u "github.com/ipfs/go-ipfs-util" logging "github.com/ipfs/go-log" loggables "github.com/libp2p/go-libp2p-loggables" diff --git a/cmd/ipfswatch/main.go b/cmd/ipfswatch/main.go index 375fde3bb..825388a36 100644 --- a/cmd/ipfswatch/main.go +++ b/cmd/ipfswatch/main.go @@ -15,8 +15,8 @@ import ( corehttp "github.com/bittorrent/go-btfs/core/corehttp" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" fsnotify "github.com/fsnotify/fsnotify" process "github.com/jbenet/goprocess" homedir "github.com/mitchellh/go-homedir" @@ -92,7 +92,7 @@ func run(ipfsPath, watchPath string) error { if *http { addr := "/ip4/127.0.0.1/tcp/5001" var opts = []corehttp.ServeOption{ - corehttp.GatewayOption(true, "/btfs", "/btns"), + corehttp.GatewayOption("/btfs", "/btns"), corehttp.WebUIOption, corehttp.DashboardOption, corehttp.HostUIOption, diff --git a/commands/context.go b/commands/context.go index 0c53ff80c..c3a042f67 100644 --- a/commands/context.go +++ b/commands/context.go @@ -10,10 +10,10 @@ import ( coreapi "github.com/bittorrent/go-btfs/core/coreapi" loader "github.com/bittorrent/go-btfs/plugin/loader" - config "github.com/TRON-US/go-btfs-config" - coreiface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + coreiface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" logging "github.com/ipfs/go-log" ) diff --git a/core/commands/add.go b/core/commands/add.go index dbc370dcf..bd547b1da 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -10,10 +10,10 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" - files "github.com/TRON-US/go-btfs-files" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + files "github.com/bittorrent/go-btfs-files" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/options" mh "github.com/multiformats/go-multihash" pb "gopkg.in/cheggaaa/pb.v1" ) diff --git a/core/commands/block.go b/core/commands/block.go index 66a9122f9..cb7bf8153 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -9,10 +9,10 @@ import ( util "github.com/bittorrent/go-btfs/blocks/blockstoreutil" cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - files "github.com/TRON-US/go-btfs-files" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + files "github.com/bittorrent/go-btfs-files" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" mh "github.com/multiformats/go-multihash" ) diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 196843165..b835adbf2 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -10,8 +10,8 @@ import ( repo "github.com/bittorrent/go-btfs/repo" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" ) diff --git a/core/commands/bttc/send_btt_to.go b/core/commands/bttc/send_btt_to.go index 69fc25770..8ee79552a 100644 --- a/core/commands/bttc/send_btt_to.go +++ b/core/commands/bttc/send_btt_to.go @@ -28,6 +28,11 @@ var BttcSendBttToCmd = &cmds.Command{ RunTimeout: 5 * time.Minute, Type: &BttcSendBttToCmdRet{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = utils.CheckSimpleMode(env) + if err != nil { + return err + } + addressStr := req.Arguments[0] if !common.IsHexAddress(addressStr) { return fmt.Errorf("invalid bttc address %s", addressStr) diff --git a/core/commands/bttc/send_token_to.go b/core/commands/bttc/send_token_to.go index 486e027c4..7248c4e08 100644 --- a/core/commands/bttc/send_token_to.go +++ b/core/commands/bttc/send_token_to.go @@ -33,6 +33,11 @@ var BttcSendTokenToCmd = &cmds.Command{ RunTimeout: 5 * time.Minute, Type: &BttcSendTokenToCmdRet{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = utils.CheckSimpleMode(env) + if err != nil { + return err + } + addressStr := req.Arguments[0] if !common.IsHexAddress(addressStr) { return fmt.Errorf("invalid bttc address %s", addressStr) diff --git a/core/commands/bttc/send_wbtt_to.go b/core/commands/bttc/send_wbtt_to.go index db3c84660..278928122 100644 --- a/core/commands/bttc/send_wbtt_to.go +++ b/core/commands/bttc/send_wbtt_to.go @@ -28,6 +28,11 @@ var BttcSendWbttToCmd = &cmds.Command{ RunTimeout: 5 * time.Minute, Type: &BttcSendWbttToCmdRet{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = utils.CheckSimpleMode(env) + if err != nil { + return err + } + addressStr := req.Arguments[0] if !common.IsHexAddress(addressStr) { return fmt.Errorf("invalid bttc address %s", addressStr) diff --git a/core/commands/bttc/swap_btt2wbtt.go b/core/commands/bttc/swap_btt2wbtt.go index 70d17ac31..30070d933 100644 --- a/core/commands/bttc/swap_btt2wbtt.go +++ b/core/commands/bttc/swap_btt2wbtt.go @@ -26,6 +26,11 @@ var BttcBtt2WbttCmd = &cmds.Command{ RunTimeout: 5 * time.Minute, Type: &BttcBtt2WbttCmdRet{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = utils.CheckSimpleMode(env) + if err != nil { + return err + } + amountStr := utils.RemoveSpaceAndComma(req.Arguments[0]) amount, ok := new(big.Int).SetString(amountStr, 10) if !ok { diff --git a/core/commands/bttc/swap_wbtt2btt.go b/core/commands/bttc/swap_wbtt2btt.go index c06cfb0ec..8056d60ca 100644 --- a/core/commands/bttc/swap_wbtt2btt.go +++ b/core/commands/bttc/swap_wbtt2btt.go @@ -26,6 +26,11 @@ var BttcWbtt2BttCmd = &cmds.Command{ RunTimeout: 5 * time.Minute, Type: &BttcWbtt2BttCmdRet{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = utils.CheckSimpleMode(env) + if err != nil { + return err + } + amountStr := utils.RemoveSpaceAndComma(req.Arguments[0]) amount, ok := new(big.Int).SetString(amountStr, 10) if !ok { diff --git a/core/commands/cat.go b/core/commands/cat.go index 9eb58ca2e..0e74a1cc8 100644 --- a/core/commands/cat.go +++ b/core/commands/cat.go @@ -8,11 +8,11 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" - files "github.com/TRON-US/go-btfs-files" - iface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + files "github.com/bittorrent/go-btfs-files" + iface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" ) const ( diff --git a/core/commands/cheque/balance_btt.go b/core/commands/cheque/balance_btt.go index 358f1b01e..f1797fb1b 100644 --- a/core/commands/cheque/balance_btt.go +++ b/core/commands/cheque/balance_btt.go @@ -2,6 +2,7 @@ package cheque import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -25,6 +26,11 @@ var ChequeBttBalanceCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + addr := req.Arguments[0] balance, err := chain.SettleObject.VaultService.BTTBalanceOf(context.Background(), common.HexToAddress(addr), nil) if err != nil { diff --git a/core/commands/cheque/balance_muti_tokens.go b/core/commands/cheque/balance_muti_tokens.go index 6f52f71b6..7e7333b55 100644 --- a/core/commands/cheque/balance_muti_tokens.go +++ b/core/commands/cheque/balance_muti_tokens.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -23,6 +24,11 @@ var ChequeAllTokenBalanceCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + addr := req.Arguments[0] mp := make(map[string]*big.Int, 0) @@ -58,6 +64,11 @@ var ChequeTokenBalanceCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + addr := req.Arguments[0] tokenStr := req.Options[tokencfg.TokenTypeName].(string) diff --git a/core/commands/cheque/cash_list.go b/core/commands/cheque/cash_list.go index 001d6f64a..24bb294f8 100644 --- a/core/commands/cheque/cash_list.go +++ b/core/commands/cheque/cash_list.go @@ -3,6 +3,7 @@ package cheque import ( "encoding/json" "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "sort" @@ -37,6 +38,11 @@ var ChequeCashListCmd = &cmds.Command{ cmds.StringArg("limit", true, false, "page limit."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + from, err := strconv.Atoi(req.Arguments[0]) if err != nil { return fmt.Errorf("parse from:%v failed", req.Arguments[0]) diff --git a/core/commands/cheque/cash_status.go b/core/commands/cheque/cash_status.go index f7c359e3f..d09db2ef8 100644 --- a/core/commands/cheque/cash_status.go +++ b/core/commands/cheque/cash_status.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "math/big" "time" @@ -36,6 +37,10 @@ var ChequeCashStatusCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } // get the peer id peerID := req.Arguments[0] diff --git a/core/commands/cheque/chaininfo.go b/core/commands/cheque/chaininfo.go index 7f0e555a5..9ce5ee4d4 100644 --- a/core/commands/cheque/chaininfo.go +++ b/core/commands/cheque/chaininfo.go @@ -2,6 +2,7 @@ package cheque import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" cmds "github.com/bittorrent/go-btfs-cmds" @@ -20,6 +21,11 @@ var ChequeChainInfoCmd = &cmds.Command{ Tagline: "Show current chain info.", }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + walletImportPrvKey, err := chain.GetWalletImportPrvKey(env) if err != nil { return err diff --git a/core/commands/cheque/cheque.go b/core/commands/cheque/cheque.go index 453d84ba0..b9bf4783c 100644 --- a/core/commands/cheque/cheque.go +++ b/core/commands/cheque/cheque.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -114,6 +115,11 @@ var StorePriceCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + // token: parse token option tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Println("... use token = ", tokenStr) @@ -155,6 +161,10 @@ var StorePriceAllCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } mp := make(map[string]*PriceInfo, 0) for k, token := range tokencfg.MpTokenAddr { @@ -201,6 +211,10 @@ var CashChequeCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } // get the peer id peerID := req.Arguments[0] diff --git a/core/commands/cheque/receive.go b/core/commands/cheque/receive.go index 99e35c69a..279b2bfeb 100644 --- a/core/commands/cheque/receive.go +++ b/core/commands/cheque/receive.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" @@ -23,6 +24,10 @@ var ReceiveChequeCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } var record cheque peer_id := req.Arguments[0] diff --git a/core/commands/cheque/receive_history_list.go b/core/commands/cheque/receive_history_list.go index 30697e972..233625563 100644 --- a/core/commands/cheque/receive_history_list.go +++ b/core/commands/cheque/receive_history_list.go @@ -3,6 +3,7 @@ package cheque import ( "encoding/json" "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "sort" "strconv" @@ -25,6 +26,11 @@ var ChequeReceiveHistoryListCmd = &cmds.Command{ cmds.StringArg("limit", true, false, "page limit."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + from, err := strconv.Atoi(req.Arguments[0]) if err != nil { return fmt.Errorf("parse from:%v failed", req.Arguments[0]) diff --git a/core/commands/cheque/receive_history_peer.go b/core/commands/cheque/receive_history_peer.go index 1b6b85ffb..47c16364c 100644 --- a/core/commands/cheque/receive_history_peer.go +++ b/core/commands/cheque/receive_history_peer.go @@ -2,6 +2,7 @@ package cheque import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "time" @@ -17,6 +18,11 @@ var ChequeReceiveHistoryPeerCmd = &cmds.Command{ cmds.StringArg("peer-id", true, false, "The peer id of cheques received."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + var listRet ChequeRecords peer_id := req.Arguments[0] fmt.Println("ChequeReceiveHistoryPeerCmd peer_id = ", peer_id) diff --git a/core/commands/cheque/receive_history_stats.go b/core/commands/cheque/receive_history_stats.go index 7ecdf4c73..ffe186ff1 100644 --- a/core/commands/cheque/receive_history_stats.go +++ b/core/commands/cheque/receive_history_stats.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" @@ -26,6 +27,11 @@ var ChequeReceiveHistoryStatsCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] diff --git a/core/commands/cheque/receive_history_stats_all.go b/core/commands/cheque/receive_history_stats_all.go index ee3457b59..34e984d04 100644 --- a/core/commands/cheque/receive_history_stats_all.go +++ b/core/commands/cheque/receive_history_stats_all.go @@ -6,6 +6,7 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" ) @@ -17,6 +18,11 @@ var ChequeReceiveHistoryStatsAllCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + mp := make(map[string][]chequeReceivedHistoryStats, 0) for k, tokenAddr := range tokencfg.MpTokenAddr { diff --git a/core/commands/cheque/receive_list.go b/core/commands/cheque/receive_list.go index da089e311..c345cd69a 100644 --- a/core/commands/cheque/receive_list.go +++ b/core/commands/cheque/receive_list.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "strconv" @@ -26,6 +27,11 @@ var ListReceiveChequeCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + offset, err := strconv.Atoi(req.Arguments[0]) if err != nil { return fmt.Errorf("parse offset:%v failed", req.Arguments[0]) diff --git a/core/commands/cheque/receive_list_all.go b/core/commands/cheque/receive_list_all.go index 22654d786..4779e9537 100644 --- a/core/commands/cheque/receive_list_all.go +++ b/core/commands/cheque/receive_list_all.go @@ -3,6 +3,7 @@ package cheque import ( "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "sort" @@ -22,6 +23,11 @@ var ListReceiveChequeAllCmd = &cmds.Command{ cmds.StringArg("limit", true, false, "page limit."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + offset, err := strconv.Atoi(req.Arguments[0]) if err != nil { return fmt.Errorf("parse offset:%v failed", req.Arguments[0]) diff --git a/core/commands/cheque/receive_total_count.go b/core/commands/cheque/receive_total_count.go index d2ccd4a77..b555cf5c0 100644 --- a/core/commands/cheque/receive_total_count.go +++ b/core/commands/cheque/receive_total_count.go @@ -2,6 +2,7 @@ package cheque import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" cmds "github.com/bittorrent/go-btfs-cmds" @@ -18,6 +19,11 @@ var ReceiveChequesCountCmd = &cmds.Command{ }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + count, err := chain.SettleObject.SwapService.ReceivedChequeRecordsCount() if err != nil { return err diff --git a/core/commands/cheque/send-history-stats-all.go b/core/commands/cheque/send-history-stats-all.go index 146a46aaf..b2cd409f1 100644 --- a/core/commands/cheque/send-history-stats-all.go +++ b/core/commands/cheque/send-history-stats-all.go @@ -6,6 +6,7 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" ) @@ -14,6 +15,11 @@ var ChequeSendHistoryStatsAllCmd = &cmds.Command{ Tagline: "Display the received cheques from peer, of all tokens", }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + mp := make(map[string][]chequeSentHistoryStats, 0) for k, tokenAddr := range tokencfg.MpTokenAddr { // now only return 30days cheque sent stats diff --git a/core/commands/cheque/send-history-stats.go b/core/commands/cheque/send-history-stats.go index 29af32a78..6f88f0c46 100644 --- a/core/commands/cheque/send-history-stats.go +++ b/core/commands/cheque/send-history-stats.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" @@ -26,6 +27,11 @@ var ChequeSendHistoryStatsCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] diff --git a/core/commands/cheque/send.go b/core/commands/cheque/send.go index cff1242c7..8db26a59d 100644 --- a/core/commands/cheque/send.go +++ b/core/commands/cheque/send.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" cmds "github.com/bittorrent/go-btfs-cmds" @@ -21,6 +22,11 @@ var SendChequeCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + var record cheque peer_id := req.Arguments[0] fmt.Println("SendChequeCmd peer_id = ", peer_id) diff --git a/core/commands/cheque/send_history_list.go b/core/commands/cheque/send_history_list.go index 44701d8cf..1a5e7de7e 100644 --- a/core/commands/cheque/send_history_list.go +++ b/core/commands/cheque/send_history_list.go @@ -3,6 +3,7 @@ package cheque import ( "encoding/json" "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "sort" "strconv" @@ -21,6 +22,11 @@ var ChequeSendHistoryListCmd = &cmds.Command{ }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + from, err := strconv.Atoi(req.Arguments[0]) if err != nil { return fmt.Errorf("parse from:%v failed", req.Arguments[0]) diff --git a/core/commands/cheque/send_history_peer.go b/core/commands/cheque/send_history_peer.go index 84754ea7c..cf5da5d1d 100644 --- a/core/commands/cheque/send_history_peer.go +++ b/core/commands/cheque/send_history_peer.go @@ -2,6 +2,7 @@ package cheque import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "time" @@ -18,6 +19,10 @@ var ChequeSendHistoryPeerCmd = &cmds.Command{ }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } var listRet ChequeRecords peer_id := req.Arguments[0] diff --git a/core/commands/cheque/send_list.go b/core/commands/cheque/send_list.go index abfaccf77..4f2570ebf 100644 --- a/core/commands/cheque/send_list.go +++ b/core/commands/cheque/send_list.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" cmds "github.com/bittorrent/go-btfs-cmds" @@ -18,6 +19,11 @@ var ListSendChequesCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] diff --git a/core/commands/cheque/send_list_all.go b/core/commands/cheque/send_list_all.go index a213d0996..a70104cfe 100644 --- a/core/commands/cheque/send_list_all.go +++ b/core/commands/cheque/send_list_all.go @@ -5,6 +5,7 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" ) @@ -13,6 +14,11 @@ var ListSendChequesAllCmd = &cmds.Command{ Tagline: "List cheque(s) send to peers.", }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + listRet := ListChequeRet{} listRet.Cheques = make([]cheque, 0, 0) listRet.Len = 0 diff --git a/core/commands/cheque/send_total_count.go b/core/commands/cheque/send_total_count.go index b99398246..e7b4ba63b 100644 --- a/core/commands/cheque/send_total_count.go +++ b/core/commands/cheque/send_total_count.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" cmds "github.com/bittorrent/go-btfs-cmds" @@ -22,6 +23,11 @@ var SendChequesCountCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] diff --git a/core/commands/cheque/stats-all.go b/core/commands/cheque/stats-all.go index abf24c7a5..240865b7a 100644 --- a/core/commands/cheque/stats-all.go +++ b/core/commands/cheque/stats-all.go @@ -5,6 +5,7 @@ import ( "fmt" cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" ) @@ -17,6 +18,11 @@ var ChequeStatsAllCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + mp := make(map[string]*chequeStats, 0) for k, tokenAddr := range tokencfg.MpTokenAddr { cs := chequeStats{ diff --git a/core/commands/cheque/stats.go b/core/commands/cheque/stats.go index 642f3e45b..11a9d0e88 100644 --- a/core/commands/cheque/stats.go +++ b/core/commands/cheque/stats.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "github.com/ethereum/go-ethereum/common" "golang.org/x/net/context" "io" @@ -34,6 +35,11 @@ var ChequeStatsCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] @@ -49,7 +55,7 @@ var ChequeStatsCmd = &cmds.Command{ TotalReceivedDailyUncashed: big.NewInt(0), } - err := GetChequeStatsToken(&cs, token) + err = GetChequeStatsToken(&cs, token) if err != nil { } diff --git a/core/commands/cmdenv/env.go b/core/commands/cmdenv/env.go index ed53eef90..b4d31ad31 100644 --- a/core/commands/cmdenv/env.go +++ b/core/commands/cmdenv/env.go @@ -7,10 +7,10 @@ import ( "github.com/bittorrent/go-btfs/commands" "github.com/bittorrent/go-btfs/core" - config "github.com/TRON-US/go-btfs-config" - coreiface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + coreiface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" logging "github.com/ipfs/go-log" ) diff --git a/core/commands/cmdenv/file.go b/core/commands/cmdenv/file.go index d0445bd2b..738a6301e 100644 --- a/core/commands/cmdenv/file.go +++ b/core/commands/cmdenv/file.go @@ -9,15 +9,15 @@ import ( gopath "path" "strings" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/corerepo" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" "github.com/ipfs/go-cid" ) diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index c9008eb2c..1982d51b9 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -97,6 +97,7 @@ func TestCommands(t *testing.T) { //"/config/profile/apply", "/config/storage-host-enable", "/config/sync-chain-info", + "/config/sync-simple-mode", "/config/optin", "/config/optout", "/dag", @@ -324,17 +325,6 @@ func TestCommands(t *testing.T) { "/bttc/send-btt-to", "/bttc/send-wbtt-to", "/bttc/send-token-to", - //"/wallet/discovery", - "/wallet/balance", - //"/wallet/init", - "/wallet", - //"/wallet/transfer", - //"/wallet/import", - //"/wallet/generate_key", - "/wallet/deposit", - "/wallet/transactions", - //"/wallet/keys", - "/wallet/withdraw", "/statuscontract", "/statuscontract/total", "/statuscontract/reportlist", diff --git a/core/commands/config.go b/core/commands/config.go index cd30f4657..d731bb4d3 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -17,8 +17,8 @@ import ( "github.com/bittorrent/go-btfs/repo/fsrepo" "github.com/ethereum/go-ethereum/common" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" "github.com/elgris/jsondiff" ) @@ -70,6 +70,7 @@ Set the value of the 'Datastore.Path' key: //"profile": configProfileCmd, "storage-host-enable": storageHostEnableCmd, "sync-chain-info": SyncChainInfoCmd, + "sync-simple-mode": SyncSimpleModeCmd, "optin": optInCmd, "optout": optOutCmd, }, @@ -432,6 +433,40 @@ var SyncChainInfoCmd = &cmds.Command{ }, } +var SyncSimpleModeCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "simple mode is true or not.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("value", true, false, "simple mode is true or not."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + enable, err := strconv.ParseBool(req.Arguments[0]) + if err != nil { + return err + } + + cfgRoot, err := cmdenv.GetConfigRoot(env) + if err != nil { + return err + } + + err = SetSimpleMode(cfgRoot, enable) + if err != nil { + return err + } + + out := fmt.Sprintf("set simple mode = %v \n please restart the node to use it!\n", enable) + return cmds.EmitOnce(res, &out) + }, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *string) error { + _, err := w.Write([]byte(*out)) + return err + }), + }, +} + var configProfileCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Apply profiles to config.", @@ -805,6 +840,27 @@ func SetConfigStorageHostEnable(configRoot string, enable bool) error { return nil } +func SetSimpleMode(configRoot string, enable bool) error { + r, err := fsrepo.Open(configRoot) + if err != nil { + return err + } + defer r.Close() + + cfg, err := r.Config() + if err != nil { + return err + } + cfg.SimpleMode = enable + + err = r.SetConfig(cfg) + if err != nil { + return err + } + + return nil +} + func getConfig(r repo.Repo, key string) (*ConfigField, error) { value, err := r.GetConfigKey(key) if err != nil { diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go index e28dc2597..109ff66cc 100644 --- a/core/commands/dag/dag.go +++ b/core/commands/dag/dag.go @@ -9,15 +9,15 @@ import ( "strings" "time" - iface "github.com/TRON-US/interface-go-btfs-core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/e" "github.com/bittorrent/go-btfs/core/coredag" + iface "github.com/bittorrent/interface-go-btfs-core" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" ipld "github.com/ipfs/go-ipld-format" diff --git a/core/commands/dag/export.go b/core/commands/dag/export.go index 98c94b024..78c60c71b 100644 --- a/core/commands/dag/export.go +++ b/core/commands/dag/export.go @@ -8,8 +8,8 @@ import ( "os" "time" - iface "github.com/TRON-US/interface-go-btfs-core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" + iface "github.com/bittorrent/interface-go-btfs-core" "github.com/cheggaaa/pb" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" diff --git a/core/commands/dht_test.go b/core/commands/dht_test.go index 1dac2125c..47a1bbf12 100644 --- a/core/commands/dht_test.go +++ b/core/commands/dht_test.go @@ -5,7 +5,7 @@ import ( "github.com/bittorrent/go-btfs/namesys" - ipns "github.com/TRON-US/go-btns" + ipns "github.com/bittorrent/go-btns" "github.com/libp2p/go-libp2p/core/test" ) diff --git a/core/commands/dns.go b/core/commands/dns.go index 34146573b..08f8779d6 100644 --- a/core/commands/dns.go +++ b/core/commands/dns.go @@ -4,9 +4,10 @@ import ( "fmt" "io" - nsopts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" ncmd "github.com/bittorrent/go-btfs/core/commands/name" namesys "github.com/bittorrent/go-btfs/namesys" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" cmds "github.com/bittorrent/go-btfs-cmds" ) @@ -60,9 +61,13 @@ The resolver can recursively resolve: cmds.BoolOption(dnsRecursiveOptionName, "r", "Resolve until the result is not a DNS link.").WithDefault(true), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + node, err := cmdenv.GetNode(env) + if err != nil { + return err + } recursive, _ := req.Options[dnsRecursiveOptionName].(bool) name := req.Arguments[0] - resolver := namesys.NewDNSResolver() + resolver := namesys.NewDNSResolver(node.DNSResolver.LookupTXT) var routing []nsopts.ResolveOpt if !recursive { diff --git a/core/commands/encryption_test.go b/core/commands/encryption_test.go index c8342ffae..2ce78abcc 100644 --- a/core/commands/encryption_test.go +++ b/core/commands/encryption_test.go @@ -14,8 +14,8 @@ import ( "github.com/bittorrent/go-btfs/core/coreapi" coremock "github.com/bittorrent/go-btfs/core/mock" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/interface-go-btfs-core/options" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/interface-go-btfs-core/options" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/libp2p/go-testutil" diff --git a/core/commands/files.go b/core/commands/files.go index 66420e508..1b9768bf1 100644 --- a/core/commands/files.go +++ b/core/commands/files.go @@ -13,11 +13,11 @@ import ( "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" - "github.com/TRON-US/go-mfs" - ft "github.com/TRON-US/go-unixfs" - iface "github.com/TRON-US/interface-go-btfs-core" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-mfs" + ft "github.com/bittorrent/go-unixfs" + iface "github.com/bittorrent/interface-go-btfs-core" + path "github.com/bittorrent/interface-go-btfs-core/path" "github.com/dustin/go-humanize" bservice "github.com/ipfs/go-blockservice" cid "github.com/ipfs/go-cid" diff --git a/core/commands/guard.go b/core/commands/guard.go index 0ebba0833..1e32909f9 100644 --- a/core/commands/guard.go +++ b/core/commands/guard.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "strings" "time" @@ -13,7 +14,7 @@ import ( "github.com/bittorrent/go-btfs/core/hub" cmds "github.com/bittorrent/go-btfs-cmds" - cconfig "github.com/tron-us/go-btfs-common/config" + cconfig "github.com/bittorrent/go-btfs-common/config" "github.com/ipfs/go-cid" ) @@ -66,6 +67,12 @@ to the guard service.`, }, RunTimeout: 30 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + fmt.Println("CheckSimpleMode ", err) + // get config settings cfg, err := cmdenv.GetConfig(env) if err != nil { diff --git a/core/commands/id.go b/core/commands/id.go index 585d1e228..79975741d 100644 --- a/core/commands/id.go +++ b/core/commands/id.go @@ -18,12 +18,12 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" ke "github.com/bittorrent/go-btfs/core/commands/keyencode" + "github.com/bittorrent/go-btfs-common/crypto" kb "github.com/libp2p/go-libp2p-kbucket" ic "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" pstore "github.com/libp2p/go-libp2p/core/peerstore" - "github.com/tron-us/go-btfs-common/crypto" ) const offlineIdErrorMessage = `'btfs id' currently cannot query information on remote @@ -47,6 +47,7 @@ type IdOutput struct { BttcAddress string VaultAddress string ChainID int64 + SimpleMode bool } const ( @@ -229,6 +230,11 @@ func printPeer(keyEnc ke.KeyEncoder, ps pstore.Peerstore, p peer.ID, node *core. // printing self is special cased as we get values differently. func printSelf(keyEnc ke.KeyEncoder, node *core.IpfsNode, env cmds.Environment) (interface{}, error) { + conf, err := cmdenv.GetConfig(env) + if err != nil { + return nil, err + } + info := new(IdOutput) info.ID = keyEnc.FormatID(node.Identity) @@ -262,15 +268,19 @@ func printSelf(keyEnc ke.KeyEncoder, node *core.IpfsNode, env cmds.Environment) return nil, err } info.TronAddress = keys.Base58Address + info.SimpleMode = conf.SimpleMode if node.IsDaemon { info.DaemonProcessID = os.Getpid() - info.BttcAddress = chain.ChainObject.OverlayAddress.Hex() - info.VaultAddress = chain.SettleObject.VaultService.Address().Hex() + if !conf.SimpleMode { + info.BttcAddress = chain.ChainObject.OverlayAddress.Hex() + info.VaultAddress = chain.SettleObject.VaultService.Address().Hex() + + // show chain id only local peer and in daemon mode + info.ChainID = chain.ChainObject.ChainID + } - // show chain id only local peer and in daemon mode - info.ChainID = chain.ChainObject.ChainID } else { info.DaemonProcessID = -1 diff --git a/core/commands/keystore.go b/core/commands/keystore.go index 07d48e4a3..fc4bee79b 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -8,8 +8,8 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" ke "github.com/bittorrent/go-btfs/core/commands/keyencode" - options "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + options "github.com/bittorrent/interface-go-btfs-core/options" ) var KeyCmd = &cmds.Command{ diff --git a/core/commands/ls.go b/core/commands/ls.go index fc2ea3db8..7ad1c675d 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -9,12 +9,12 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - unixfs "github.com/TRON-US/go-unixfs" - unixfs_pb "github.com/TRON-US/go-unixfs/pb" - iface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + unixfs "github.com/bittorrent/go-unixfs" + unixfs_pb "github.com/bittorrent/go-unixfs/pb" + iface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" ) // LsLink contains printable data for a single ipld link in ls output diff --git a/core/commands/metadata.go b/core/commands/metadata.go index dc14db144..90aed3140 100644 --- a/core/commands/metadata.go +++ b/core/commands/metadata.go @@ -3,10 +3,10 @@ package commands import ( "errors" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/core/commands/cmdenv" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" ) type MetaResult struct { diff --git a/core/commands/mount_nofuse.go b/core/commands/mount_nofuse.go index 03d7806f0..d6505a8c3 100644 --- a/core/commands/mount_nofuse.go +++ b/core/commands/mount_nofuse.go @@ -16,7 +16,7 @@ for mounting. If you'd like to be able to mount, please use a version of btfs compiled with fuse. For the latest instructions, please check the project's repository: - http://github.com/TRON-US/go-btfs + http://github.com/bittorrent/go-btfs `, }, } diff --git a/core/commands/mount_unix.go b/core/commands/mount_unix.go index 9e238070a..ac90e7b5c 100644 --- a/core/commands/mount_unix.go +++ b/core/commands/mount_unix.go @@ -10,8 +10,8 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" nodeMount "github.com/bittorrent/go-btfs/fuse/node" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" ) const ( diff --git a/core/commands/name/ipns.go b/core/commands/name/ipns.go index 838485226..32f802b7c 100644 --- a/core/commands/name/ipns.go +++ b/core/commands/name/ipns.go @@ -10,9 +10,9 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" namesys "github.com/bittorrent/go-btfs/namesys" - options "github.com/TRON-US/interface-go-btfs-core/options" - nsopts "github.com/TRON-US/interface-go-btfs-core/options/namesys" cmds "github.com/bittorrent/go-btfs-cmds" + options "github.com/bittorrent/interface-go-btfs-core/options" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" logging "github.com/ipfs/go-log" path "github.com/ipfs/go-path" ) diff --git a/core/commands/name/publish.go b/core/commands/name/publish.go index fd1ff4b0f..3e3d69200 100644 --- a/core/commands/name/publish.go +++ b/core/commands/name/publish.go @@ -9,10 +9,10 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" ke "github.com/bittorrent/go-btfs/core/commands/keyencode" - iface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + iface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" peer "github.com/libp2p/go-libp2p/core/peer" ) diff --git a/core/commands/network.go b/core/commands/network.go index bf02fcc74..6372c844d 100644 --- a/core/commands/network.go +++ b/core/commands/network.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "time" @@ -28,8 +29,13 @@ var NetworkCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + timeoutCtx, _ := context.WithTimeout(context.Background(), CheckBackoffDuration*time.Duration(CheckMaxRetries)) - _, err := chain.ChainObject.Backend.BlockNumber(timeoutCtx) + _, err = chain.ChainObject.Backend.BlockNumber(timeoutCtx) if err != nil { chain.CodeBttc = chain.ConstCodeError chain.ErrBttc = err diff --git a/core/commands/object/diff.go b/core/commands/object/diff.go index 7e9d5354c..5aa65fe1b 100644 --- a/core/commands/object/diff.go +++ b/core/commands/object/diff.go @@ -6,8 +6,8 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + path "github.com/bittorrent/interface-go-btfs-core/path" "github.com/ipfs/go-merkledag/dagutils" ) diff --git a/core/commands/object/object.go b/core/commands/object/object.go index beac6b37a..524e490f0 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -11,9 +11,9 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" - "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" humanize "github.com/dustin/go-humanize" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil/cidenc" diff --git a/core/commands/object/patch.go b/core/commands/object/patch.go index c10f05221..f2eb0dc4e 100644 --- a/core/commands/object/patch.go +++ b/core/commands/object/patch.go @@ -6,9 +6,9 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" ) var ObjectPatchCmd = &cmds.Command{ diff --git a/core/commands/pin.go b/core/commands/pin.go index 83ae5173f..e5dc96bd7 100644 --- a/core/commands/pin.go +++ b/core/commands/pin.go @@ -12,10 +12,10 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" e "github.com/bittorrent/go-btfs/core/commands/e" - coreiface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + coreiface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" pin "github.com/ipfs/go-ipfs-pinner" bserv "github.com/ipfs/go-blockservice" diff --git a/core/commands/pubsub.go b/core/commands/pubsub.go index 7c89ab220..022c13147 100644 --- a/core/commands/pubsub.go +++ b/core/commands/pubsub.go @@ -10,8 +10,8 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - options "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + options "github.com/bittorrent/interface-go-btfs-core/options" ) var PubsubCmd = &cmds.Command{ diff --git a/core/commands/refs.go b/core/commands/refs.go index 07fe505fe..9f564297c 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -9,9 +9,9 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - iface "github.com/TRON-US/interface-go-btfs-core" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + iface "github.com/bittorrent/interface-go-btfs-core" + path "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" ipld "github.com/ipfs/go-ipld-format" diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 5551485ef..23ea98d02 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -11,10 +11,10 @@ import ( ncmd "github.com/bittorrent/go-btfs/core/commands/name" ns "github.com/bittorrent/go-btfs/namesys" - options "github.com/TRON-US/interface-go-btfs-core/options" - nsopts "github.com/TRON-US/interface-go-btfs-core/options/namesys" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + options "github.com/bittorrent/interface-go-btfs-core/options" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" + path "github.com/bittorrent/interface-go-btfs-core/path" cidenc "github.com/ipfs/go-cidutil/cidenc" ipfspath "github.com/ipfs/go-path" ) diff --git a/core/commands/rm/rm.go b/core/commands/rm/rm.go index 341076180..dd1af065b 100644 --- a/core/commands/rm/rm.go +++ b/core/commands/rm/rm.go @@ -7,10 +7,10 @@ import ( "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" ipld "github.com/ipfs/go-ipld-format" ) diff --git a/core/commands/root.go b/core/commands/root.go index 7e547fa7f..7a9c9ed21 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -177,7 +177,6 @@ var rootSubcommands = map[string]*cmds.Command{ "settlement": settlement.SettlementCmd, //"update": ExternalBinary(), "network": NetworkCmd, - "wallet": WalletCmd, "statuscontract": StatusContractCmd, "bittorrent": bittorrentCmd, } diff --git a/core/commands/settlements/list.go b/core/commands/settlements/list.go index 1d7ca0cdf..2d3ef8024 100644 --- a/core/commands/settlements/list.go +++ b/core/commands/settlements/list.go @@ -4,6 +4,7 @@ import ( "context" "errors" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "math/big" "time" @@ -34,6 +35,11 @@ var ListSettlementCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) diff --git a/core/commands/settlements/peer.go b/core/commands/settlements/peer.go index 1a7b2f578..a461006fb 100644 --- a/core/commands/settlements/peer.go +++ b/core/commands/settlements/peer.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "math/big" "time" @@ -24,6 +25,11 @@ var PeerSettlementCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + peerID := req.Arguments[0] peerexists := false diff --git a/core/commands/statuscontract.go b/core/commands/statuscontract.go index 6aac584c3..80ff9d53d 100644 --- a/core/commands/statuscontract.go +++ b/core/commands/statuscontract.go @@ -4,7 +4,8 @@ import ( "encoding/json" "errors" "fmt" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "strconv" @@ -52,6 +53,11 @@ var TotalCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err @@ -120,6 +126,11 @@ var ReportListCmd = &cmds.Command{ cmds.StringArg("limit", true, false, "page limit."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err @@ -187,6 +198,11 @@ var LastInfoCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + last, err := chain.GetLastOnline() if err != nil { return err @@ -230,6 +246,11 @@ var StatusConfigCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + rs, err := chain.GetReportStatus() if err != nil { return err @@ -257,6 +278,11 @@ var ReportOnlineServerCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + node, err := cmdenv.GetNode(env) if err != nil { return err diff --git a/core/commands/statusonline.go b/core/commands/statusonline.go index 5cba31934..4948bc344 100644 --- a/core/commands/statusonline.go +++ b/core/commands/statusonline.go @@ -4,10 +4,11 @@ import ( "encoding/json" "fmt" cmds "github.com/bittorrent/go-btfs-cmds" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/spin" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" + "github.com/bittorrent/go-btfs/utils" "io" "strconv" "time" @@ -20,6 +21,11 @@ var ReportOnlineDailyCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + node, err := cmdenv.GetNode(env) if err != nil { return err @@ -57,6 +63,11 @@ var ReportListDailyCmd = &cmds.Command{ cmds.StringArg("limit", true, false, "page limit."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err @@ -155,6 +166,11 @@ var TotalDailyCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err @@ -201,6 +217,11 @@ var ReportLastTimeDailyCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + last, err := chain.GetReportOnlineLastTimeDaily() if err != nil { return err diff --git a/core/commands/storage/announce/announce.go b/core/commands/storage/announce/announce.go index 116b74a10..2550a5e4b 100644 --- a/core/commands/storage/announce/announce.go +++ b/core/commands/storage/announce/announce.go @@ -2,7 +2,6 @@ package announce import ( "fmt" - "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/helper" diff --git a/core/commands/storage/challenge/challenge.go b/core/commands/storage/challenge/challenge.go index a1798627b..75dcb5726 100644 --- a/core/commands/storage/challenge/challenge.go +++ b/core/commands/storage/challenge/challenge.go @@ -2,6 +2,7 @@ package challenge import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "strconv" "time" @@ -9,7 +10,7 @@ import ( "github.com/bittorrent/go-btfs/core/corehttp/remote" cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-common/v2/json" + "github.com/bittorrent/go-common/v2/json" cidlib "github.com/ipfs/go-cid" ) @@ -41,6 +42,11 @@ still store a piece of file (usually a shard) as agreed in storage contract.`, }, StorageChallengeResponseCmd.Arguments...), // append pass-through arguments RunTimeout: 20 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err @@ -109,6 +115,11 @@ the challenge request back to the caller.`, }, RunTimeout: 1 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err diff --git a/core/commands/storage/challenge/challenge_helper.go b/core/commands/storage/challenge/challenge_helper.go index 21a4b06d4..03ade4ead 100644 --- a/core/commands/storage/challenge/challenge_helper.go +++ b/core/commands/storage/challenge/challenge_helper.go @@ -9,10 +9,10 @@ import ( "math/big" "sync" - coreiface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" core "github.com/bittorrent/go-btfs/core" + coreiface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" uuid "github.com/google/uuid" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" diff --git a/core/commands/storage/challenge/challenge_helper_test.go b/core/commands/storage/challenge/challenge_helper_test.go index 8a8c5638e..15945ea71 100644 --- a/core/commands/storage/challenge/challenge_helper_test.go +++ b/core/commands/storage/challenge/challenge_helper_test.go @@ -6,8 +6,8 @@ import ( unixtest "github.com/bittorrent/go-btfs/core/coreunix/test" - unixfs "github.com/TRON-US/go-unixfs" - path "github.com/TRON-US/interface-go-btfs-core/path" + unixfs "github.com/bittorrent/go-unixfs" + path "github.com/bittorrent/interface-go-btfs-core/path" ) func TestGenAndSolveChallenge(t *testing.T) { diff --git a/core/commands/storage/contracts/contracts.go b/core/commands/storage/contracts/contracts.go index 9303aa65a..b08a6398a 100644 --- a/core/commands/storage/contracts/contracts.go +++ b/core/commands/storage/contracts/contracts.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/bittorrent/go-btfs/utils" "sort" "strings" "time" @@ -18,12 +19,12 @@ import ( contractspb "github.com/bittorrent/go-btfs/protos/contracts" shardpb "github.com/bittorrent/go-btfs/protos/shard" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - nodepb "github.com/tron-us/go-btfs-common/protos/node" - "github.com/tron-us/go-btfs-common/utils/grpc" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + "github.com/bittorrent/go-btfs-common/utils/grpc" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" logging "github.com/ipfs/go-log" @@ -94,6 +95,11 @@ This command contracts stats based on role from network(hub) to local node data }, RunTimeout: 10 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err @@ -133,6 +139,11 @@ This command get contracts stats based on role from the local node data store.`, }, RunTimeout: 3 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cr, err := checkContractStatRole(req.Arguments[0]) if err != nil { return err @@ -209,6 +220,11 @@ This command get contracts list based on role from the local node data store.`, }, RunTimeout: 3 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err diff --git a/core/commands/storage/helper/call.go b/core/commands/storage/helper/call.go index 85728d701..806f7e830 100644 --- a/core/commands/storage/helper/call.go +++ b/core/commands/storage/helper/call.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - shell "github.com/TRON-US/go-btfs-api" - "github.com/TRON-US/go-btfs-config" + shell "github.com/bittorrent/go-btfs-api" + "github.com/bittorrent/go-btfs-config" ) func Call(ctx context.Context, cfg *config.Config, sub string) error { diff --git a/core/commands/storage/helper/contracts.go b/core/commands/storage/helper/contracts.go index 021acc9ef..63896e186 100644 --- a/core/commands/storage/helper/contracts.go +++ b/core/commands/storage/helper/contracts.go @@ -1,6 +1,6 @@ package helper -import guardpb "github.com/tron-us/go-btfs-common/protos/guard" +import guardpb "github.com/bittorrent/go-btfs-common/protos/guard" var ContractFilterMap = map[string]map[guardpb.Contract_ContractState]bool{ "active": { diff --git a/core/commands/storage/helper/hosts.go b/core/commands/storage/helper/hosts.go index e6c11ae75..9c93632f4 100644 --- a/core/commands/storage/helper/hosts.go +++ b/core/commands/storage/helper/hosts.go @@ -9,8 +9,8 @@ import ( "github.com/bittorrent/go-btfs/core/hub" "github.com/bittorrent/go-btfs/repo" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" "github.com/dustin/go-humanize" "github.com/gogo/protobuf/proto" diff --git a/core/commands/storage/helper/hosts_test.go b/core/commands/storage/helper/hosts_test.go index 83883f42f..11f985ebb 100644 --- a/core/commands/storage/helper/hosts_test.go +++ b/core/commands/storage/helper/hosts_test.go @@ -10,11 +10,11 @@ import ( unixtest "github.com/bittorrent/go-btfs/core/coreunix/test" "github.com/bittorrent/go-btfs/repo" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" - config "github.com/TRON-US/go-btfs-config" "github.com/alecthomas/units" + config "github.com/bittorrent/go-btfs-config" "github.com/gogo/protobuf/proto" ) diff --git a/core/commands/storage/helper/reed_solomon.go b/core/commands/storage/helper/reed_solomon.go index 6565840cd..ad2db0290 100644 --- a/core/commands/storage/helper/reed_solomon.go +++ b/core/commands/storage/helper/reed_solomon.go @@ -7,10 +7,10 @@ import ( "github.com/bittorrent/go-btfs/core" - chunker "github.com/TRON-US/go-btfs-chunker" - "github.com/TRON-US/go-unixfs" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + "github.com/bittorrent/go-unixfs" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" ) diff --git a/core/commands/storage/helper/reed_solomon_test.go b/core/commands/storage/helper/reed_solomon_test.go index 36806a899..76e1b54b4 100644 --- a/core/commands/storage/helper/reed_solomon_test.go +++ b/core/commands/storage/helper/reed_solomon_test.go @@ -8,8 +8,8 @@ import ( unixtest "github.com/bittorrent/go-btfs/core/coreunix/test" - uio "github.com/TRON-US/go-unixfs/io" - "github.com/TRON-US/interface-go-btfs-core/path" + uio "github.com/bittorrent/go-unixfs/io" + "github.com/bittorrent/interface-go-btfs-core/path" rs "github.com/klauspost/reedsolomon" ) diff --git a/core/commands/storage/hosts/hosts.go b/core/commands/storage/hosts/hosts.go index d90c55ad2..62a933a0f 100644 --- a/core/commands/storage/hosts/hosts.go +++ b/core/commands/storage/hosts/hosts.go @@ -3,6 +3,7 @@ package hosts import ( "context" "fmt" + "github.com/bittorrent/go-btfs/utils" "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" @@ -10,7 +11,7 @@ import ( "github.com/bittorrent/go-btfs/core/hub" cmds "github.com/bittorrent/go-btfs-cmds" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" logging "github.com/ipfs/go-log" ) @@ -46,6 +47,11 @@ Mode options include:` + hub.AllModeHelpText, cmds.StringOption(hostInfoModeOptionName, "m", "Hosts info showing mode. Default: mode set in config option Experimental.HostsSyncMode."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err @@ -91,6 +97,11 @@ Mode options include:` + hub.AllModeHelpText, cmds.StringOption(hostSyncModeOptionName, "m", "Hosts syncing mode. Default: mode set in config option Experimental.HostsSyncMode."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err diff --git a/core/commands/storage/info/info.go b/core/commands/storage/info/info.go index 794f85e0f..40a1e2a60 100644 --- a/core/commands/storage/info/info.go +++ b/core/commands/storage/info/info.go @@ -2,12 +2,13 @@ package info import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/helper" cmds "github.com/bittorrent/go-btfs-cmds" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" ) var StorageInfoCmd = &cmds.Command{ @@ -21,6 +22,11 @@ By default it shows local host node information.`, cmds.StringArg("peer-id", false, false, "Peer ID to show storage-related information. Default to self."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err diff --git a/core/commands/storage/path/path.go b/core/commands/storage/path/path.go index 60c681841..7aa1c9c0a 100644 --- a/core/commands/storage/path/path.go +++ b/core/commands/storage/path/path.go @@ -34,7 +34,9 @@ var ( srcProperties string ) -/* can be dir of `btfs` or path like `/private/var/folders/q0/lc8cmwd93gv50ygrsy3bwfyc0000gn/T`, +/* + can be dir of `btfs` or path like `/private/var/folders/q0/lc8cmwd93gv50ygrsy3bwfyc0000gn/T`, + depends on how `btfs` is called */ func init() { diff --git a/core/commands/storage/stats/stats.go b/core/commands/storage/stats/stats.go index 2e15f2ecc..a9687885f 100644 --- a/core/commands/storage/stats/stats.go +++ b/core/commands/storage/stats/stats.go @@ -3,6 +3,7 @@ package stats import ( "context" "errors" + "github.com/bittorrent/go-btfs/utils" "sort" "strconv" "strings" @@ -15,13 +16,13 @@ import ( "github.com/bittorrent/go-btfs/core/corerepo" "github.com/bittorrent/go-btfs/core/hub" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + config "github.com/bittorrent/go-btfs-config" + "github.com/bittorrent/protobuf/proto" ds "github.com/ipfs/go-datastore" "github.com/shirou/gopsutil/v3/disk" - "github.com/tron-us/protobuf/proto" ) const ( @@ -54,6 +55,11 @@ This command synchronize node stats from network(hub) to local node data store.` }, Arguments: []cmds.Argument{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err @@ -138,6 +144,11 @@ This command get node stats in the network from the local node data store.`, }, RunTimeout: 30 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + cfg, err := cmdenv.GetConfig(env) if err != nil { return err @@ -206,6 +217,11 @@ This command list node stats in the network from the local node data store.`, }, RunTimeout: 30 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + n, err := cmdenv.GetNode(env) if err != nil { return err diff --git a/core/commands/storage/stats/stats_test.go b/core/commands/storage/stats/stats_test.go index 18486dee5..6907cc37f 100644 --- a/core/commands/storage/stats/stats_test.go +++ b/core/commands/storage/stats/stats_test.go @@ -7,7 +7,7 @@ import ( unixtest "github.com/bittorrent/go-btfs/core/coreunix/test" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" "github.com/gogo/protobuf/proto" ) diff --git a/core/commands/storage/upload/escrow/escrow.go b/core/commands/storage/upload/escrow/escrow.go index 9eca6b099..f85df2bf0 100644 --- a/core/commands/storage/upload/escrow/escrow.go +++ b/core/commands/storage/upload/escrow/escrow.go @@ -5,10 +5,10 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/helper" - config "github.com/TRON-US/go-btfs-config" - "github.com/tron-us/go-btfs-common/crypto" - escrowpb "github.com/tron-us/go-btfs-common/protos/escrow" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/go-btfs-common/crypto" + escrowpb "github.com/bittorrent/go-btfs-common/protos/escrow" + config "github.com/bittorrent/go-btfs-config" + "github.com/bittorrent/protobuf/proto" ) func NewSignedContract(contract *escrowpb.EscrowContract) *escrowpb.SignedEscrowContract { diff --git a/core/commands/storage/upload/guard/guard.go b/core/commands/storage/upload/guard/guard.go index 4dd4b1ea5..797474ced 100644 --- a/core/commands/storage/upload/guard/guard.go +++ b/core/commands/storage/upload/guard/guard.go @@ -10,12 +10,12 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" renterpb "github.com/bittorrent/go-btfs/protos/renter" - config "github.com/TRON-US/go-btfs-config" - cc "github.com/tron-us/go-btfs-common/config" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - cgrpc "github.com/tron-us/go-btfs-common/utils/grpc" - "github.com/tron-us/protobuf/proto" + cc "github.com/bittorrent/go-btfs-common/config" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + cgrpc "github.com/bittorrent/go-btfs-common/utils/grpc" + config "github.com/bittorrent/go-btfs-config" + "github.com/bittorrent/protobuf/proto" cidlib "github.com/ipfs/go-cid" ) diff --git a/core/commands/storage/upload/helper/hosts_helper.go b/core/commands/storage/upload/helper/hosts_helper.go index bcb7cbade..0af3fc5b1 100644 --- a/core/commands/storage/upload/helper/hosts_helper.go +++ b/core/commands/storage/upload/helper/hosts_helper.go @@ -12,9 +12,9 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/helper" "github.com/bittorrent/go-btfs/core/corehttp/remote" - iface "github.com/TRON-US/interface-go-btfs-core" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + iface "github.com/bittorrent/interface-go-btfs-core" "github.com/libp2p/go-libp2p/core/peer" ) diff --git a/core/commands/storage/upload/helper/upload_helper.go b/core/commands/storage/upload/helper/upload_helper.go index 954e465ab..dfbbd8f47 100644 --- a/core/commands/storage/upload/helper/upload_helper.go +++ b/core/commands/storage/upload/helper/upload_helper.go @@ -13,10 +13,10 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/helper" - config "github.com/TRON-US/go-btfs-config" - iface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" + iface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/path" "github.com/alecthomas/units" "github.com/cenkalti/backoff/v4" diff --git a/core/commands/storage/upload/offline/offline_get_contract_batch.go b/core/commands/storage/upload/offline/offline_get_contract_batch.go index 6b92a8be8..eac5e367d 100644 --- a/core/commands/storage/upload/offline/offline_get_contract_batch.go +++ b/core/commands/storage/upload/offline/offline_get_contract_batch.go @@ -2,6 +2,7 @@ package offline import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "github.com/bittorrent/go-btfs/core/commands/storage/helper" uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -31,6 +32,11 @@ the contracts to the caller.`, cmds.StringArg("contracts-type", true, false, "get guard or escrow contracts"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ssId := req.Arguments[0] ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { diff --git a/core/commands/storage/upload/offline/offline_get_unsigned.go b/core/commands/storage/upload/offline/offline_get_unsigned.go index bcebcad72..ed13c3c82 100644 --- a/core/commands/storage/upload/offline/offline_get_unsigned.go +++ b/core/commands/storage/upload/offline/offline_get_unsigned.go @@ -2,6 +2,7 @@ package offline import ( "errors" + "github.com/bittorrent/go-btfs/utils" "github.com/bittorrent/go-btfs/core/commands/storage/helper" uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -25,6 +26,11 @@ This command obtains the upload signing input data for from the upload session cmds.StringArg("session-status", true, false, "current upload session status."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ssId := req.Arguments[0] ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { diff --git a/core/commands/storage/upload/offline/offline_sign.go b/core/commands/storage/upload/offline/offline_sign.go index 747e8f655..4a1dbb027 100644 --- a/core/commands/storage/upload/offline/offline_sign.go +++ b/core/commands/storage/upload/offline/offline_sign.go @@ -3,6 +3,7 @@ package offline import ( "errors" "fmt" + "github.com/bittorrent/go-btfs/utils" "github.com/bittorrent/go-btfs/core/commands/storage/helper" uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -30,6 +31,11 @@ to the upload session.`, cmds.StringArg("signed", true, false, "signed json data."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ssId := req.Arguments[0] ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { diff --git a/core/commands/storage/upload/offline/offline_sign_contract_batch.go b/core/commands/storage/upload/offline/offline_sign_contract_batch.go index 2735e3ba6..c596aeb6f 100644 --- a/core/commands/storage/upload/offline/offline_sign_contract_batch.go +++ b/core/commands/storage/upload/offline/offline_sign_contract_batch.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/bittorrent/go-btfs/utils" "strconv" "github.com/bittorrent/go-btfs/core/commands/storage/helper" @@ -31,6 +32,11 @@ This command reads all the unsigned contracts from the upload session cmds.StringArg("signed-data-items", true, false, "signed data items."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ssID := req.Arguments[0] ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { diff --git a/core/commands/storage/upload/sessions/datastore.go b/core/commands/storage/upload/sessions/datastore.go index 30bb81b39..34a6726c8 100644 --- a/core/commands/storage/upload/sessions/datastore.go +++ b/core/commands/storage/upload/sessions/datastore.go @@ -4,7 +4,7 @@ import ( "context" "strings" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/protobuf/proto" ds "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" diff --git a/core/commands/storage/upload/sessions/host_shards.go b/core/commands/storage/upload/sessions/host_shards.go index 9b49a96f0..99e201a79 100644 --- a/core/commands/storage/upload/sessions/host_shards.go +++ b/core/commands/storage/upload/sessions/host_shards.go @@ -9,8 +9,8 @@ import ( uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" shardpb "github.com/bittorrent/go-btfs/protos/shard" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/protobuf/proto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/protobuf/proto" "github.com/ipfs/go-datastore" "github.com/looplab/fsm" diff --git a/core/commands/storage/upload/sessions/renter_sessions.go b/core/commands/storage/upload/sessions/renter_sessions.go index 87259e9ae..edf717083 100644 --- a/core/commands/storage/upload/sessions/renter_sessions.go +++ b/core/commands/storage/upload/sessions/renter_sessions.go @@ -14,7 +14,7 @@ import ( renterpb "github.com/bittorrent/go-btfs/protos/renter" sessionpb "github.com/bittorrent/go-btfs/protos/session" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/protobuf/proto" "github.com/ipfs/go-datastore" "github.com/looplab/fsm" diff --git a/core/commands/storage/upload/sessions/renter_shards.go b/core/commands/storage/upload/sessions/renter_shards.go index 756a602fd..e1cb72976 100644 --- a/core/commands/storage/upload/sessions/renter_shards.go +++ b/core/commands/storage/upload/sessions/renter_shards.go @@ -12,9 +12,9 @@ import ( uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" shardpb "github.com/bittorrent/go-btfs/protos/shard" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - nodepb "github.com/tron-us/go-btfs-common/protos/node" - "github.com/tron-us/protobuf/proto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + "github.com/bittorrent/protobuf/proto" "github.com/ipfs/go-datastore" logging "github.com/ipfs/go-log" diff --git a/core/commands/storage/upload/upload/dc_repair_router.go b/core/commands/storage/upload/upload/dc_repair_router.go index 4e2a5012b..3200e1d72 100644 --- a/core/commands/storage/upload/upload/dc_repair_router.go +++ b/core/commands/storage/upload/upload/dc_repair_router.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/bittorrent/go-btfs/utils" "strconv" "strings" "sync" @@ -18,10 +19,10 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/utils/grpc" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/utils/grpc" + "github.com/bittorrent/protobuf/proto" "github.com/alecthomas/units" "github.com/cenkalti/backoff/v4" @@ -76,6 +77,11 @@ This command sends request to mining host to negotiate the repair works.`, }, HostRepairResponseCmd.Arguments...), // append pass-through arguments RunTimeout: 20 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + fileHash := req.Arguments[1] lostShardHashes := strings.Split(req.Arguments[2], ",") fileSize, err := strconv.ParseInt(req.Arguments[3], 10, 64) @@ -157,6 +163,11 @@ returns the repairer's signed contract to the invoker.`, }, RunTimeout: 1 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ctxParams, err := uh.ExtractContextParams(req, env) repairId := ctxParams.N.Identity.Pretty() fileHash := req.Arguments[0] diff --git a/core/commands/storage/upload/upload/do_guard.go b/core/commands/storage/upload/upload/do_guard.go index 836486d7f..819ffa8d7 100644 --- a/core/commands/storage/upload/upload/do_guard.go +++ b/core/commands/storage/upload/upload/do_guard.go @@ -10,11 +10,11 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" renterpb "github.com/bittorrent/go-btfs/protos/renter" - config "github.com/TRON-US/go-btfs-config" - "github.com/tron-us/go-btfs-common/crypto" - escrowpb "github.com/tron-us/go-btfs-common/protos/escrow" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - cgrpc "github.com/tron-us/go-btfs-common/utils/grpc" + "github.com/bittorrent/go-btfs-common/crypto" + escrowpb "github.com/bittorrent/go-btfs-common/protos/escrow" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + cgrpc "github.com/bittorrent/go-btfs-common/utils/grpc" + config "github.com/bittorrent/go-btfs-config" "github.com/gogo/protobuf/proto" cidlib "github.com/ipfs/go-cid" diff --git a/core/commands/storage/upload/upload/do_sign_guard_contracts.go b/core/commands/storage/upload/upload/do_sign_guard_contracts.go index 6721e10a1..c1eef5d5f 100644 --- a/core/commands/storage/upload/upload/do_sign_guard_contracts.go +++ b/core/commands/storage/upload/upload/do_sign_guard_contracts.go @@ -8,10 +8,10 @@ import ( uh "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" - config "github.com/TRON-US/go-btfs-config" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + config "github.com/bittorrent/go-btfs-config" + "github.com/bittorrent/protobuf/proto" "github.com/libp2p/go-libp2p/core/peer" ) diff --git a/core/commands/storage/upload/upload/do_waitupload.go b/core/commands/storage/upload/upload/do_waitupload.go index 381e7fd77..471a6b30c 100644 --- a/core/commands/storage/upload/upload/do_waitupload.go +++ b/core/commands/storage/upload/upload/do_waitupload.go @@ -12,9 +12,9 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" renterpb "github.com/bittorrent/go-btfs/protos/renter" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/utils/grpc" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/utils/grpc" "github.com/alecthomas/units" "github.com/cenkalti/backoff/v4" diff --git a/core/commands/storage/upload/upload/host_manager.go b/core/commands/storage/upload/upload/host_manager.go index 53e9becbf..8dda7ec29 100644 --- a/core/commands/storage/upload/upload/host_manager.go +++ b/core/commands/storage/upload/upload/host_manager.go @@ -3,9 +3,9 @@ package upload import ( "github.com/bittorrent/go-btfs/core/commands/storage/contracts" - config "github.com/TRON-US/go-btfs-config" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/protos/node" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/protos/node" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" ) diff --git a/core/commands/storage/upload/upload/host_manager_test.go b/core/commands/storage/upload/upload/host_manager_test.go index 2309ba885..0b790e185 100644 --- a/core/commands/storage/upload/upload/host_manager_test.go +++ b/core/commands/storage/upload/upload/host_manager_test.go @@ -6,8 +6,8 @@ import ( coremock "github.com/bittorrent/go-btfs/core/mock" - config "github.com/TRON-US/go-btfs-config" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" "github.com/stretchr/testify/assert" diff --git a/core/commands/storage/upload/upload/receive_check_tokens.go b/core/commands/storage/upload/upload/receive_check_tokens.go index 2aac1933c..77e48f09f 100644 --- a/core/commands/storage/upload/upload/receive_check_tokens.go +++ b/core/commands/storage/upload/upload/receive_check_tokens.go @@ -3,6 +3,7 @@ package upload import ( "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "time" cmds "github.com/bittorrent/go-btfs-cmds" @@ -17,6 +18,11 @@ var StorageUploadSupportTokensCmd = &cmds.Command{ Arguments: []cmds.Argument{}, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { return err diff --git a/core/commands/storage/upload/upload/recieve_cheque.go b/core/commands/storage/upload/upload/recieve_cheque.go index e6259d560..101906a45 100644 --- a/core/commands/storage/upload/upload/recieve_cheque.go +++ b/core/commands/storage/upload/upload/recieve_cheque.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "github.com/ethereum/go-ethereum/common" "math/big" "time" @@ -28,6 +29,11 @@ var StorageUploadChequeCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + fmt.Printf("receive cheque ...\n") ctxParams, err := uh.ExtractContextParams(req, env) diff --git a/core/commands/storage/upload/upload/recieve_contract.go b/core/commands/storage/upload/upload/recieve_contract.go index 8fc5f55a1..eb53f7195 100644 --- a/core/commands/storage/upload/upload/recieve_contract.go +++ b/core/commands/storage/upload/upload/recieve_contract.go @@ -2,6 +2,7 @@ package upload import ( "errors" + "github.com/bittorrent/go-btfs/utils" "strconv" "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -9,7 +10,7 @@ import ( "github.com/bittorrent/go-btfs/core/corehttp/remote" cmds "github.com/bittorrent/go-btfs-cmds" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" "github.com/gogo/protobuf/proto" ) @@ -26,6 +27,11 @@ var StorageUploadRecvContractCmd = &cmds.Command{ cmds.StringArg("guard-contract", true, false, "Signed Guard contract."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + contractId, err := doRecv(req, env) if contractId != "" { if ch, ok := ShardErrChanMap.Get(contractId); ok { diff --git a/core/commands/storage/upload/upload/recieve_init.go b/core/commands/storage/upload/upload/recieve_init.go index e71b28c30..30ffecb26 100644 --- a/core/commands/storage/upload/upload/recieve_init.go +++ b/core/commands/storage/upload/upload/recieve_init.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/bittorrent/go-btfs/utils" "math/big" "strconv" "sync" @@ -23,12 +24,12 @@ import ( "github.com/bittorrent/go-btfs/core/corehttp/remote" cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - "github.com/tron-us/go-btfs-common/ledger" - escrowpb "github.com/tron-us/go-btfs-common/protos/escrow" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/utils/grpc" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/go-btfs-common/crypto" + "github.com/bittorrent/go-btfs-common/ledger" + escrowpb "github.com/bittorrent/go-btfs-common/protos/escrow" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/utils/grpc" + "github.com/bittorrent/protobuf/proto" "github.com/alecthomas/units" "github.com/cenkalti/backoff/v4" @@ -58,6 +59,11 @@ the shard and replies back to client for the next challenge step.`, }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { return err diff --git a/core/commands/storage/upload/upload/repair.go b/core/commands/storage/upload/upload/repair.go index b8a1ed29c..8e74e9105 100644 --- a/core/commands/storage/upload/upload/repair.go +++ b/core/commands/storage/upload/upload/repair.go @@ -4,6 +4,7 @@ import ( "context" "errors" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "strings" "time" @@ -12,9 +13,9 @@ import ( "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/utils/grpc" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/utils/grpc" "github.com/libp2p/go-libp2p/core/peer" ) @@ -33,6 +34,11 @@ This command repairs the given shards of a file.`, }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + ctxParams, err := uh.ExtractContextParams(req, env) if err != nil { return err diff --git a/core/commands/storage/upload/upload/status.go b/core/commands/storage/upload/upload/status.go index df7ef8e0d..18c8d1711 100644 --- a/core/commands/storage/upload/upload/status.go +++ b/core/commands/storage/upload/upload/status.go @@ -3,15 +3,16 @@ package upload import ( "context" "fmt" + "github.com/bittorrent/go-btfs/utils" "time" "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - guardpb "github.com/tron-us/go-btfs-common/protos/guard" - "github.com/tron-us/go-btfs-common/utils/grpc" + "github.com/bittorrent/go-btfs-common/crypto" + guardpb "github.com/bittorrent/go-btfs-common/protos/guard" + "github.com/bittorrent/go-btfs-common/utils/grpc" "github.com/ipfs/go-datastore" ) @@ -26,6 +27,11 @@ This command print upload and payment status by the time queried.`, cmds.StringArg("session-id", true, false, "ID for the entire storage upload session.").EnableStdin(), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + status := &StatusRes{} // check and get session info from sessionMap ssId := req.Arguments[0] diff --git a/core/commands/storage/upload/upload/upload.go b/core/commands/storage/upload/upload/upload.go index e3ed80569..7a8c7b248 100644 --- a/core/commands/storage/upload/upload/upload.go +++ b/core/commands/storage/upload/upload/upload.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "strconv" "strings" "time" @@ -110,6 +111,11 @@ Use status command to check for completion: }, RunTimeout: 15 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + swapprotocol.Req = req swapprotocol.Env = env diff --git a/core/commands/swarm.go b/core/commands/swarm.go index bf7aba3fc..8cc1ab579 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -17,8 +17,8 @@ import ( repo "github.com/bittorrent/go-btfs/repo" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" + config "github.com/bittorrent/go-btfs-config" inet "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" @@ -150,7 +150,7 @@ var swarmPeersCmd = &cmds.Command{ Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ci *connInfos) error { for _, info := range ci.Peers { - fmt.Fprintf(w, "%s/%s/%s/%s/%s", info.Addr, "btfs", info.Peer, "country_short", info.CountryShort) + fmt.Fprintf(w, "%s/%s/%s/%s/%s", info.Addr, "p2p", info.Peer, "country_short", info.CountryShort) if info.Latency != "" { fmt.Fprintf(w, " %s", info.Latency) } diff --git a/core/commands/tar.go b/core/commands/tar.go index e6678d295..837a4a1e1 100644 --- a/core/commands/tar.go +++ b/core/commands/tar.go @@ -8,7 +8,7 @@ import ( "github.com/bittorrent/go-btfs/core/commands/cmdenv" tar "github.com/bittorrent/go-btfs/tar" - path "github.com/TRON-US/interface-go-btfs-core/path" + path "github.com/bittorrent/interface-go-btfs-core/path" dag "github.com/ipfs/go-merkledag" ) diff --git a/core/commands/test.go b/core/commands/test.go index f05e02fba..0b74828f9 100644 --- a/core/commands/test.go +++ b/core/commands/test.go @@ -11,6 +11,7 @@ import ( "time" cmds "github.com/bittorrent/go-btfs-cmds" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -18,7 +19,6 @@ import ( "github.com/bittorrent/go-btfs/core/hub" "github.com/bittorrent/go-btfs/settlement/swap/swapprotocol" "github.com/bittorrent/go-btfs/settlement/swap/swapprotocol/pb" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" "github.com/ethereum/go-ethereum/common" peerInfo "github.com/libp2p/go-libp2p/core/peer" diff --git a/core/commands/unixfs/ls.go b/core/commands/unixfs/ls.go index 16d5d890e..f8df8a5b7 100644 --- a/core/commands/unixfs/ls.go +++ b/core/commands/unixfs/ls.go @@ -8,9 +8,9 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" - unixfs "github.com/TRON-US/go-unixfs" - path "github.com/TRON-US/interface-go-btfs-core/path" cmds "github.com/bittorrent/go-btfs-cmds" + unixfs "github.com/bittorrent/go-unixfs" + path "github.com/bittorrent/interface-go-btfs-core/path" merkledag "github.com/ipfs/go-merkledag" ) diff --git a/core/commands/urlstore.go b/core/commands/urlstore.go index 0047d11c7..52aff129b 100644 --- a/core/commands/urlstore.go +++ b/core/commands/urlstore.go @@ -8,9 +8,9 @@ import ( cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" filestore "github.com/ipfs/go-filestore" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/interface-go-btfs-core/options" cmds "github.com/bittorrent/go-btfs-cmds" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/interface-go-btfs-core/options" ) var urlStoreCmd = &cmds.Command{ diff --git a/core/commands/vault/vault_address.go b/core/commands/vault/vault_address.go index d8a82f361..3f9664e32 100644 --- a/core/commands/vault/vault_address.go +++ b/core/commands/vault/vault_address.go @@ -2,6 +2,7 @@ package vault import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "time" @@ -19,6 +20,11 @@ var VaultAddrCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + addr := chain.SettleObject.VaultService.Address() return cmds.EmitOnce(res, &VaultAddrCmdRet{ diff --git a/core/commands/vault/vault_balance.go b/core/commands/vault/vault_balance.go index 2ddf75bc8..0f07448ed 100644 --- a/core/commands/vault/vault_balance.go +++ b/core/commands/vault/vault_balance.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -26,6 +27,11 @@ var VaultBalanceCmd = &cmds.Command{ cmds.StringOption(tokencfg.TokenTypeName, "tk", "file storage with token type,default WBTT, other TRX/USDD/USDT.").WithDefault("WBTT"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + tokenStr := req.Options[tokencfg.TokenTypeName].(string) //fmt.Printf("... token:%+v\n", tokenStr) token, bl := tokencfg.MpTokenAddr[tokenStr] diff --git a/core/commands/vault/vault_balance_all.go b/core/commands/vault/vault_balance_all.go index b66455084..4599cbcf4 100644 --- a/core/commands/vault/vault_balance_all.go +++ b/core/commands/vault/vault_balance_all.go @@ -3,6 +3,7 @@ package vault import ( "fmt" "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -18,6 +19,10 @@ var VaultBalanceAllCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } mp := make(map[string]*big.Int, 0) for k, tokenAddr := range tokencfg.MpTokenAddr { diff --git a/core/commands/vault/vault_deposit.go b/core/commands/vault/vault_deposit.go index 49aacc477..220293c41 100644 --- a/core/commands/vault/vault_deposit.go +++ b/core/commands/vault/vault_deposit.go @@ -30,6 +30,11 @@ var VaultDepositCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + argAmount := utils.RemoveSpaceAndComma(req.Arguments[0]) amount, ok := new(big.Int).SetString(argAmount, 10) if !ok { diff --git a/core/commands/vault/vault_upgrade.go b/core/commands/vault/vault_upgrade.go index ce6e15651..02679109a 100644 --- a/core/commands/vault/vault_upgrade.go +++ b/core/commands/vault/vault_upgrade.go @@ -5,6 +5,7 @@ import ( "fmt" cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/utils" "io" "time" ) @@ -37,6 +38,11 @@ upgrade your vault contract's version, and won't modify any data in your vault.` }), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + oldImpl, newImpl, err := chain.SettleObject.VaultService.UpgradeTo(context.Background(), chain.ChainObject.Chainconfig.VaultLogicAddress) upgraded := true description := "" diff --git a/core/commands/vault/vault_withdraw.go b/core/commands/vault/vault_withdraw.go index 386fa3f0e..093b64104 100644 --- a/core/commands/vault/vault_withdraw.go +++ b/core/commands/vault/vault_withdraw.go @@ -30,6 +30,11 @@ var VaultWithdrawCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + argAmount := utils.RemoveSpaceAndComma(req.Arguments[0]) amount, ok := new(big.Int).SetString(argAmount, 10) if !ok { diff --git a/core/commands/vault/wbtt_balance.go b/core/commands/vault/wbtt_balance.go index deaf0eb2d..a861a8274 100644 --- a/core/commands/vault/wbtt_balance.go +++ b/core/commands/vault/wbtt_balance.go @@ -2,6 +2,7 @@ package vault import ( "fmt" + "github.com/bittorrent/go-btfs/utils" "io" "math/big" "time" @@ -25,6 +26,11 @@ var VaultWbttBalanceCmd = &cmds.Command{ }, RunTimeout: 5 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + addr := req.Arguments[0] balance, err := chain.SettleObject.VaultService.WBTTBalanceOf(context.Background(), common.HexToAddress(addr)) if err != nil { diff --git a/core/commands/wallet.go b/core/commands/wallet.go deleted file mode 100644 index 047fd5d36..000000000 --- a/core/commands/wallet.go +++ /dev/null @@ -1,437 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "io" - "strconv" - "strings" - - "github.com/bittorrent/go-btfs/cmd/btfs/util" - "github.com/bittorrent/go-btfs/core" - "github.com/bittorrent/go-btfs/core/commands/cmdenv" - "github.com/bittorrent/go-btfs/core/commands/storage/path" - "github.com/bittorrent/go-btfs/core/wallet" - walletpb "github.com/bittorrent/go-btfs/protos/wallet" - - cmds "github.com/bittorrent/go-btfs-cmds" - "github.com/tron-us/go-btfs-common/crypto" - - "github.com/libp2p/go-libp2p/core/peer" -) - -var WalletCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet", - ShortDescription: `'btfs wallet' is a set of commands to interact with block chain and ledger.`, - LongDescription: `'btfs wallet' is a set of commands interact with block chain and ledger to deposit, -withdraw and query balance of token used in BTFS.`, - }, - - Subcommands: map[string]*cmds.Command{ - //"init": walletInitCmd, - "deposit": walletDepositCmd, - "withdraw": walletWithdrawCmd, - "balance": walletBalanceCmd, - //"keys": walletKeysCmd, - "transactions": walletTransactionsCmd, - //"import": walletImportCmd, - //"transfer": walletTransferCmd, - //"discovery": walletDiscoveryCmd, - //"generate_key": walletGenerateKeyCmd, - }, -} - -var walletInitCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "initialize BTFS wallet", - ShortDescription: "initialize BTFS wallet", - }, - Arguments: []cmds.Argument{ - cmds.StringArg("private_key", true, false, "private key"), - cmds.StringArg("encrypted_private_key", true, false, "encrypted private key"), - cmds.StringArg("encrypted_mnemonic", true, false, "encrypted mnemonic"), - }, - Options: []cmds.Option{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - cfg.Identity.PrivKey = req.Arguments[0] - cfg.Identity.Mnemonic = "" - cfg.Identity.EncryptedPrivKey = req.Arguments[1] - cfg.Identity.EncryptedMnemonic = req.Arguments[2] - ks, err := crypto.ToPrivKey(req.Arguments[0]) - if err != nil { - return err - } - id, err := peer.IDFromPrivateKey(ks) - if err != nil { - return err - } - cfg.Identity.PeerID = id.String() - cfg.UI.Wallet.Initialized = true - if err = n.Repo.SetConfig(cfg); err != nil { - return err - } - go path.DoRestart(false) - return nil - }, -} - -var walletGenerateKeyCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "Generate new private_key and Mnemonic", - ShortDescription: "Generate new private_key and Mnemonic", - }, - - Arguments: []cmds.Argument{}, - Options: []cmds.Option{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - k, m, err := util.GenerateKey("", "BIP39", "") - if err != nil { - return err - } - ks, err := crypto.FromPrivateKey(k) - if err != nil { - return err - } - k64, err := crypto.Hex64ToBase64(ks.HexPrivateKey) - if err != nil { - return err - } - return cmds.EmitOnce(res, Keys{ - PrivateKey: k64, - Mnemonic: m, - SkInBase64: k64, - SkInHex: ks.HexPrivateKey, - }) - }, - Type: Keys{}, -} - -const ( - asyncOptionName = "async" - passwordOptionName = "password" -) - -var walletDepositCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet deposit", - ShortDescription: "BTFS wallet deposit from block chain to ledger.", - Options: "unit is µBTT (=0.000001BTT)", - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("amount", true, false, "amount to deposit."), - }, - Options: []cmds.Option{ - cmds.BoolOption(asyncOptionName, "a", "Deposit asynchronously."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - async, _ := req.Options[asyncOptionName].(bool) - - amount, err := strconv.ParseInt(req.Arguments[0], 10, 64) - if err != nil { - return err - } - - runDaemon := false - - currentNode, err := cmdenv.GetNode(env) - if err != nil { - log.Error("Wrong while get current Node information", err) - return err - } - runDaemon = currentNode.IsDaemon - - err = wallet.WalletDeposit(req.Context, cfg, n, amount, runDaemon, async) - if err != nil { - if strings.Contains(err.Error(), "Please deposit at least") { - err = errors.New("Please deposit at least 10,000,000µBTT(=10BTT)") - } - return err - } - s := fmt.Sprintf("BTFS wallet deposit submitted. Please wait one minute for the transaction to confirm.") - if !runDaemon { - s = fmt.Sprintf("BTFS wallet deposit Done.") - } - return cmds.EmitOnce(res, &MessageOutput{s}) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *MessageOutput) error { - fmt.Fprint(w, out.Message) - return nil - }), - }, - Type: MessageOutput{}, -} - -var walletWithdrawCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet withdraw", - ShortDescription: "BTFS wallet withdraw from ledger to block chain.", - Options: "unit is µBTT (=0.000001BTT)", - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("amount", true, false, "amount to deposit."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - amount, err := strconv.ParseInt(req.Arguments[0], 10, 64) - if err != nil { - return err - } - - err = wallet.WalletWithdraw(req.Context, cfg, n, amount) - if err != nil { - if strings.Contains(err.Error(), "Please withdraw at least") { - err = errors.New("Please withdraw at least 1,000,000,000µBTT(=1000BTT)") - } - return err - } - - s := fmt.Sprintf("BTFS wallet withdraw submitted. Please wait one minute for the transaction to confirm.") - return cmds.EmitOnce(res, &MessageOutput{s}) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *MessageOutput) error { - fmt.Fprint(w, out.Message) - return nil - }), - }, - Type: MessageOutput{}, -} - -var walletBalanceCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet balance", - ShortDescription: "Query BTFS wallet balance in ledger and block chain.", - Options: "unit is µBTT (=0.000001BTT)", - }, - - Arguments: []cmds.Argument{}, - Options: []cmds.Option{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - - tronBalance, ledgerBalance, err := wallet.GetBalance(req.Context, cfg) - if err != nil { - log.Error("wallet get balance failed, ERR: ", err) - return err - } - s := fmt.Sprintf("BTFS wallet tron balance '%d', ledger balance '%d'\n", tronBalance, ledgerBalance) - log.Info(s) - - return cmds.EmitOnce(res, &BalanceResponse{ - BtfsWalletBalance: uint64(ledgerBalance), - BttWalletBalance: uint64(tronBalance), - }) - }, - Type: BalanceResponse{}, -} - -type BalanceResponse struct { - BtfsWalletBalance uint64 - BttWalletBalance uint64 -} - -var walletKeysCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet keys", - ShortDescription: "get keys of BTFS wallet", - }, - Arguments: []cmds.Argument{}, - Options: []cmds.Option{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - var keys *Keys - if !cfg.UI.Wallet.Initialized { - keys = &Keys{ - PrivateKey: cfg.Identity.PrivKey, - Mnemonic: cfg.Identity.Mnemonic, - } - } else { - keys = &Keys{ - PrivateKey: cfg.Identity.EncryptedPrivKey, - Mnemonic: cfg.Identity.EncryptedMnemonic, - } - } - return cmds.EmitOnce(res, keys) - }, - Type: Keys{}, -} - -type Keys struct { - PrivateKey string - Mnemonic string - SkInBase64 string - SkInHex string -} - -var walletTransactionsCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet transactions", - ShortDescription: "get transactions of BTFS wallet", - }, - Arguments: []cmds.Argument{}, - Options: []cmds.Option{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - txs, err := wallet.GetTransactions(n.Repo.Datastore(), n.Identity.Pretty()) - if err != nil { - return err - } - return cmds.EmitOnce(res, txs) - }, - Type: []*walletpb.TransactionV1{}, -} - -var walletTransferCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "Send to another BTT wallet.", - ShortDescription: "Send to another BTT wallet from current BTT wallet.", - }, - Arguments: []cmds.Argument{ - cmds.StringArg("to", true, false, "address of another BTFS wallet to transfer to"), - cmds.StringArg("amount", true, false, "amount of µBTT (=0.000001BTT) to transfer"), - cmds.StringArg("memo", false, false, "attached memo"), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - amount, err := strconv.ParseInt(req.Arguments[1], 10, 64) - if err != nil { - return err - } - memo := "" - if len(req.Arguments) == 3 { - memo = req.Arguments[2] - } - ret, err := wallet.TransferBTTWithMemo(req.Context, n, cfg, nil, "", req.Arguments[0], amount, memo) - if err != nil { - return err - } - msg := fmt.Sprintf("transaction %v sent", ret.TxId) - return cmds.EmitOnce(res, &TransferResult{ - Result: ret.Result, - Message: msg, - }) - }, - Type: &TransferResult{}, -} - -type TransferResult struct { - Result bool - Message string -} - -const privateKeyOptionName = "privateKey" -const mnemonicOptionName = "mnemonic" - -var walletImportCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "BTFS wallet import", - ShortDescription: "import BTFS wallet", - }, - Arguments: []cmds.Argument{}, - Options: []cmds.Option{ - cmds.StringOption(privateKeyOptionName, "p", "Private Key to import."), - cmds.StringOption(mnemonicOptionName, "m", "Mnemonic to import."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - - privKey, _ := req.Options[privateKeyOptionName].(string) - mnemonic, _ := req.Options[mnemonicOptionName].(string) - if privKey == "" && mnemonic == "" { - return errors.New("required private key or mnemonic") - } - if err = doSetKeys(n, privKey, mnemonic); err != nil { - return err - } - go path.DoRestart(false) - return nil - }, -} - -func doSetKeys(n *core.IpfsNode, privKey string, mnemonic string) error { - return wallet.SetKeys(n, privKey, mnemonic) -} - -var walletDiscoveryCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "Speed wallet discovery", - ShortDescription: "Speed wallet discovery", - }, - Arguments: []cmds.Argument{}, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - n, err := cmdenv.GetNode(env) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - if cfg.UI.Wallet.Initialized { - return errors.New("Already init, cannot discovery.") - } - key, err := wallet.DiscoverySpeedKey(req.Options[passwordOptionName].(string)) - if err != nil { - return err - } - return cmds.EmitOnce(res, DiscoveryResult{Key: key}) - }, -} - -type DiscoveryResult struct { - Key string -} diff --git a/core/core.go b/core/core.go index 12359a564..c26256236 100644 --- a/core/core.go +++ b/core/core.go @@ -5,7 +5,7 @@ Packages underneath core/ provide a (relatively) stable, low-level API to carry out most IPFS-related tasks. For more details on the other interfaces and how core/... fits into the bigger BTFS picture, see: - $ godoc github.com/TRON-US/go-btfs + $ godoc github.com/bittorrent/go-btfs */ package core @@ -24,9 +24,9 @@ import ( ipnsrp "github.com/bittorrent/go-btfs/namesys/republisher" "github.com/bittorrent/go-btfs/p2p" "github.com/bittorrent/go-btfs/repo" - - mfs "github.com/TRON-US/go-mfs" + mfs "github.com/bittorrent/go-mfs" bserv "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-fetcher" "github.com/ipfs/go-filestore" "github.com/ipfs/go-graphsync" bstore "github.com/ipfs/go-ipfs-blockstore" @@ -35,7 +35,6 @@ import ( provider "github.com/ipfs/go-ipfs-provider" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - resolver "github.com/ipfs/go-path/resolver" goprocess "github.com/jbenet/goprocess" ddht "github.com/libp2p/go-libp2p-kad-dht/dual" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -50,6 +49,7 @@ import ( discovery "github.com/libp2p/go-libp2p/p2p/discovery/mdns" p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic" ma "github.com/multiformats/go-multiaddr" + madns "github.com/multiformats/go-multiaddr-dns" ) var log = logging.Logger("core") @@ -69,18 +69,19 @@ type IpfsNode struct { PNetFingerprint libp2p.PNetFingerprint `optional:"true"` // fingerprint of private network // Services - Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances - Blockstore bstore.GCBlockstore // the block store (lower level) - Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore - BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping - GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc - Blocks bserv.BlockService // the block service, get/add blocks. - DAG ipld.DAGService // the merkle dag service, get/add objects. - Resolver *resolver.Resolver // the path resolution system - Reporter *metrics.BandwidthCounter `optional:"true"` - Discovery discovery.Service `optional:"true"` - FilesRoot *mfs.Root - RecordValidator record.Validator + Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances + Blockstore bstore.GCBlockstore // the block store (lower level) + Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore + BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping + GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc + Blocks bserv.BlockService // the block service, get/add blocks. + DAG ipld.DAGService // the merkle dag service, get/add objects. + IPLDFetcherFactory fetcher.Factory `name:"ipldFetcher"` // fetcher that paths over the IPLD data model + UnixFSFetcherFactory fetcher.Factory `name:"unixfsFetcher"` // fetcher that interprets UnixFS data + Reporter *metrics.BandwidthCounter `optional:"true"` + Discovery discovery.Service `optional:"true"` + FilesRoot *mfs.Root + RecordValidator record.Validator //Statestore storage.StateStorer // Online @@ -89,6 +90,7 @@ type IpfsNode struct { Filters *ma.Filters `optional:"true"` Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper Routing irouting.ProvideManyRouter `optional:"true"` // the routing system. recommend ipfs-dht + DNSResolver *madns.Resolver // the DNS resolver Exchange exchange.Interface // the block exchange + strategy (bitswap) Namesys namesys.NameSystem // the name system, resolves paths to hashes Provider provider.System // the value provider system diff --git a/core/core_test.go b/core/core_test.go index 6e65513fd..bb27480ea 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,7 +7,7 @@ import ( "github.com/bittorrent/go-btfs/repo" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" ) diff --git a/core/coreapi/block.go b/core/coreapi/block.go index 1b26f4cb7..065bbb483 100644 --- a/core/coreapi/block.go +++ b/core/coreapi/block.go @@ -9,9 +9,9 @@ import ( util "github.com/bittorrent/go-btfs/blocks/blockstoreutil" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" pin "github.com/ipfs/go-ipfs-pinner" diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index 16cfbfd12..20e0fafc5 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -23,9 +23,10 @@ import ( "github.com/bittorrent/go-btfs/namesys" "github.com/bittorrent/go-btfs/repo" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/options" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/options" bserv "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-fetcher" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" offlinexch "github.com/ipfs/go-ipfs-exchange-offline" @@ -54,13 +55,14 @@ type CoreAPI struct { baseBlocks blockstore.Blockstore pinning pin.Pinner - blocks bserv.BlockService - dag ipld.DAGService - - peerstore pstore.Peerstore - peerHost p2phost.Host - recordValidator record.Validator - exchange exchange.Interface + blocks bserv.BlockService + dag ipld.DAGService + ipldFetcherFactory fetcher.Factory + unixFSFetcherFactory fetcher.Factory + peerstore pstore.Peerstore + peerHost p2phost.Host + recordValidator record.Validator + exchange exchange.Interface namesys namesys.NameSystem routing routing.Routing @@ -165,8 +167,10 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e baseBlocks: n.BaseBlocks, pinning: n.Pinning, - blocks: n.Blocks, - dag: n.DAG, + blocks: n.Blocks, + dag: n.DAG, + ipldFetcherFactory: n.IPLDFetcherFactory, + unixFSFetcherFactory: n.UnixFSFetcherFactory, peerstore: n.Peerstore, peerHost: n.PeerHost, @@ -212,7 +216,12 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e } subApi.routing = offlineroute.NewOfflineRouter(subApi.repo.Datastore(), subApi.recordValidator) - subApi.namesys = namesys.NewNameSystem(subApi.routing, subApi.repo.Datastore(), cs) + subApi.namesys, err = namesys.NewNameSystem(subApi.routing, + namesys.WithDatastore(subApi.repo.Datastore()), + namesys.WithCache(cs)) + if err != nil { + return nil, fmt.Errorf("error constructing namesys: %w", err) + } subApi.provider = provider.NewOfflineProvider() subApi.peerstore = nil diff --git a/core/coreapi/dht.go b/core/coreapi/dht.go index 6c4466729..7c7f507be 100644 --- a/core/coreapi/dht.go +++ b/core/coreapi/dht.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" blockservice "github.com/ipfs/go-blockservice" cid "github.com/ipfs/go-cid" cidutil "github.com/ipfs/go-cidutil" diff --git a/core/coreapi/key.go b/core/coreapi/key.go index b282e0cbc..593de7892 100644 --- a/core/coreapi/key.go +++ b/core/coreapi/key.go @@ -7,9 +7,9 @@ import ( "fmt" "sort" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" ipfspath "github.com/ipfs/go-path" crypto "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" diff --git a/core/coreapi/name.go b/core/coreapi/name.go index 5306e87ac..5375d4d7c 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -9,9 +9,9 @@ import ( "github.com/bittorrent/go-btfs/keystore" "github.com/bittorrent/go-btfs/namesys" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" ipath "github.com/ipfs/go-path" ci "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" @@ -95,7 +95,11 @@ func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.Name var resolver namesys.Resolver = api.namesys if !options.Cache { - resolver = namesys.NewNameSystem(api.routing, api.repo.Datastore(), 0) + resolver, err = namesys.NewNameSystem(api.routing, + namesys.WithDatastore(api.repo.Datastore())) + if err != nil { + return nil, err + } } if !strings.HasPrefix(name, "/btns/") { diff --git a/core/coreapi/object.go b/core/coreapi/object.go index f9533d52d..12b79d57e 100644 --- a/core/coreapi/object.go +++ b/core/coreapi/object.go @@ -13,10 +13,10 @@ import ( "github.com/bittorrent/go-btfs/core/coreunix" - ft "github.com/TRON-US/go-unixfs" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - ipath "github.com/TRON-US/interface-go-btfs-core/path" + ft "github.com/bittorrent/go-unixfs" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + ipath "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" pin "github.com/ipfs/go-ipfs-pinner" ipld "github.com/ipfs/go-ipld-format" diff --git a/core/coreapi/path.go b/core/coreapi/path.go index 9fbb68872..e5eaa3283 100644 --- a/core/coreapi/path.go +++ b/core/coreapi/path.go @@ -6,14 +6,13 @@ import ( gopath "path" "github.com/bittorrent/go-btfs/namesys/resolve" - - uio "github.com/TRON-US/go-unixfs/io" - coreiface "github.com/TRON-US/interface-go-btfs-core" - path "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + path "github.com/bittorrent/interface-go-btfs-core/path" "github.com/ipfs/go-cid" + "github.com/ipfs/go-fetcher" ipld "github.com/ipfs/go-ipld-format" ipfspath "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" + ipfspathresolver "github.com/ipfs/go-path/resolver" ) // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the @@ -37,29 +36,27 @@ func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.Resolved if _, ok := p.(path.Resolved); ok { return p.(path.Resolved), nil } - - ipath, err := api.ResolveIpnsPath(ctx, p) - if err != nil { + ipath := ipfspath.Path(p.String()) + ipath, err := resolve.ResolveIPNS(ctx, api.namesys, ipath) + if err == resolve.ErrNoNamesys { + return nil, coreiface.ErrOffline + } else if err != nil { return nil, err } - var resolveOnce resolver.ResolveOnce - + var dataFetcher fetcher.Factory switch ipath.Segments()[0] { case "btfs": - resolveOnce = uio.ResolveUnixfsOnce + dataFetcher = api.unixFSFetcherFactory case "ipld": - resolveOnce = resolver.ResolveSingle + dataFetcher = api.ipldFetcherFactory default: return nil, fmt.Errorf("unsupported path namespace: %s", p.Namespace()) } - r := &resolver.Resolver{ - DAG: api.dag, - ResolveOnce: resolveOnce, - } + r := ipfspathresolver.NewBasicResolver(dataFetcher) - node, rest, err := r.ResolveToLastNode(ctx, *ipath) + node, rest, err := r.ResolveToLastNode(ctx, ipath) if err != nil { return nil, err } @@ -69,7 +66,7 @@ func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.Resolved return nil, err } - return path.NewResolvedPath(*ipath, node, root, gopath.Join(rest...)), nil + return path.NewResolvedPath(ipath, node, root, gopath.Join(rest...)), nil } // ResolveIpnsPath resolves only the IPNS path `p` using the Unixfs resolver and returns the diff --git a/core/coreapi/pin.go b/core/coreapi/pin.go index 0f7aa46f2..bc1abf423 100644 --- a/core/coreapi/pin.go +++ b/core/coreapi/pin.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" pin "github.com/ipfs/go-ipfs-pinner" bserv "github.com/ipfs/go-blockservice" diff --git a/core/coreapi/pubsub.go b/core/coreapi/pubsub.go index b07b058a6..1a7b3004d 100644 --- a/core/coreapi/pubsub.go +++ b/core/coreapi/pubsub.go @@ -4,8 +4,8 @@ import ( "context" "errors" - coreiface "github.com/TRON-US/interface-go-btfs-core" - caopts "github.com/TRON-US/interface-go-btfs-core/options" + coreiface "github.com/bittorrent/interface-go-btfs-core" + caopts "github.com/bittorrent/interface-go-btfs-core/options" pubsub "github.com/libp2p/go-libp2p-pubsub" peer "github.com/libp2p/go-libp2p/core/peer" diff --git a/core/coreapi/swarm.go b/core/coreapi/swarm.go index 1b65194f4..6891f71f6 100644 --- a/core/coreapi/swarm.go +++ b/core/coreapi/swarm.go @@ -5,7 +5,7 @@ import ( "sort" "time" - coreiface "github.com/TRON-US/interface-go-btfs-core" + coreiface "github.com/bittorrent/interface-go-btfs-core" inet "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" pstore "github.com/libp2p/go-libp2p/core/peerstore" diff --git a/core/coreapi/test/api_test.go b/core/coreapi/test/api_test.go index c723ce637..762fe20b9 100644 --- a/core/coreapi/test/api_test.go +++ b/core/coreapi/test/api_test.go @@ -16,9 +16,9 @@ import ( "github.com/bittorrent/go-btfs/keystore" "github.com/bittorrent/go-btfs/repo" - config "github.com/TRON-US/go-btfs-config" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/tests" + config "github.com/bittorrent/go-btfs-config" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/tests" "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-filestore" diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go index 3601d5aed..e7a9d4a3e 100644 --- a/core/coreapi/unixfs.go +++ b/core/coreapi/unixfs.go @@ -16,18 +16,18 @@ import ( "github.com/bittorrent/go-btfs/core/coreunix" ci "github.com/libp2p/go-libp2p/core/crypto" - chunker "github.com/TRON-US/go-btfs-chunker" - files "github.com/TRON-US/go-btfs-files" - ecies "github.com/TRON-US/go-eccrypto" - mfs "github.com/TRON-US/go-mfs" - ft "github.com/TRON-US/go-unixfs" - unixfile "github.com/TRON-US/go-unixfs/file" - "github.com/TRON-US/go-unixfs/importer/helpers" - uio "github.com/TRON-US/go-unixfs/io" - ftutil "github.com/TRON-US/go-unixfs/util" - coreiface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + files "github.com/bittorrent/go-btfs-files" + ecies "github.com/bittorrent/go-eccrypto" + mfs "github.com/bittorrent/go-mfs" + ft "github.com/bittorrent/go-unixfs" + unixfile "github.com/bittorrent/go-unixfs/file" + "github.com/bittorrent/go-unixfs/importer/helpers" + uio "github.com/bittorrent/go-unixfs/io" + ftutil "github.com/bittorrent/go-unixfs/util" + coreiface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" blockservice "github.com/ipfs/go-blockservice" cid "github.com/ipfs/go-cid" @@ -630,13 +630,13 @@ func (api *UnixfsAPI) RemoveMetadata(ctx context.Context, p path.Path, m string, } func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, settings *options.UnixfsLsSettings) coreiface.DirEntry { + if linkres.Err != nil { + return coreiface.DirEntry{Err: linkres.Err} + } + lnk := coreiface.DirEntry{ Name: linkres.Link.Name, Cid: linkres.Link.Cid, - Err: linkres.Err, - } - if lnk.Err != nil { - return lnk } switch lnk.Cid.Type() { diff --git a/core/coreapi/unixfs_test.go b/core/coreapi/unixfs_test.go index eb165f286..b974fd6b1 100644 --- a/core/coreapi/unixfs_test.go +++ b/core/coreapi/unixfs_test.go @@ -3,7 +3,7 @@ package coreapi import ( "testing" - "github.com/TRON-US/interface-go-btfs-core/path" + "github.com/bittorrent/interface-go-btfs-core/path" "github.com/stretchr/testify/assert" ) diff --git a/core/corehttp/commands.go b/core/corehttp/commands.go index e565b9dc9..7ecffea94 100644 --- a/core/corehttp/commands.go +++ b/core/corehttp/commands.go @@ -3,22 +3,24 @@ package corehttp import ( "errors" "fmt" - "github.com/TRON-US/go-btfs-api" "net" "net/http" "os" "strconv" "strings" + shell "github.com/bittorrent/go-btfs-api" + version "github.com/bittorrent/go-btfs" oldcmds "github.com/bittorrent/go-btfs/commands" "github.com/bittorrent/go-btfs/core" corecommands "github.com/bittorrent/go-btfs/core/commands" - config "github.com/TRON-US/go-btfs-config" cmds "github.com/bittorrent/go-btfs-cmds" cmdsHttp "github.com/bittorrent/go-btfs-cmds/http" + config "github.com/bittorrent/go-btfs-config" path "github.com/ipfs/go-path" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) var ( @@ -47,6 +49,8 @@ var redirectPaths = []string{ var defaultLocalhostOrigins = []string{ "http://127.0.0.1:", "https://127.0.0.1:", + "http://[::1]:", + "https://[::1]:", "http://localhost:", "https://localhost:", } @@ -148,6 +152,7 @@ func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool) patchCORSVars(cfg, l.Addr()) cmdHandler := cmdsHttp.NewHandler(&cctx, command, cfg) + cmdHandler = otelhttp.NewHandler(cmdHandler, "corehttp.cmdsHandler") mux.Handle(APIPath+"/", cmdHandler) for _, rp := range redirectPaths { mux.Handle(rp+"/", cmdHandler) diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index 3532b439b..36329cfbf 100644 --- a/core/corehttp/gateway.go +++ b/core/corehttp/gateway.go @@ -1,106 +1,87 @@ package corehttp import ( + "context" + "errors" "fmt" + "io" "net" "net/http" - "sort" - "github.com/Workiva/go-datastructures/cache" version "github.com/bittorrent/go-btfs" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" core "github.com/bittorrent/go-btfs/core" - coreapi "github.com/bittorrent/go-btfs/core/coreapi" - - options "github.com/TRON-US/interface-go-btfs-core/options" + "github.com/bittorrent/go-btfs/core/corehttp/gateway" + "github.com/bittorrent/go-btfs/core/node" + namesys "github.com/bittorrent/go-btfs/namesys" + iface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-cid" + offline "github.com/ipfs/go-ipfs-exchange-offline" + offlineroute "github.com/ipfs/go-ipfs-routing/offline" + "github.com/libp2p/go-libp2p/core/routing" id "github.com/libp2p/go-libp2p/p2p/protocol/identify" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -type GatewayConfig struct { - Headers map[string][]string - Writable bool - PathPrefixes []string -} - -// A helper function to clean up a set of headers: -// 1. Canonicalizes. -// 2. Deduplicates. -// 3. Sorts. -func cleanHeaderSet(headers []string) []string { - // Deduplicate and canonicalize. - m := make(map[string]struct{}, len(headers)) - for _, h := range headers { - m[http.CanonicalHeaderKey(h)] = struct{}{} - } - result := make([]string, 0, len(m)) - for k := range m { - result = append(result, k) - } - - // Sort - sort.Strings(result) - return result -} - -func GatewayOption(writable bool, paths ...string) ServeOption { +func GatewayOption(paths ...string) ServeOption { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { cfg, err := n.Repo.Config() if err != nil { return nil, err } - api, err := coreapi.NewCoreAPI(n, options.Api.FetchBlocks(!cfg.Gateway.NoFetch)) - if err != nil { - return nil, err - } - headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders)) for h, v := range cfg.Gateway.HTTPHeaders { headers[http.CanonicalHeaderKey(h)] = v } - // Hard-coded headers. - const ACAHeadersName = "Access-Control-Allow-Headers" - const ACEHeadersName = "Access-Control-Expose-Headers" - const ACAOriginName = "Access-Control-Allow-Origin" - const ACAMethodsName = "Access-Control-Allow-Methods" + gateway.AddAccessControlHeaders(headers) - if _, ok := headers[ACAOriginName]; !ok { - // Default to *all* - headers[ACAOriginName] = []string{"*"} + gwConfig := gateway.Config{ + Headers: headers, } - if _, ok := headers[ACAMethodsName]; !ok { - // Default to GET - headers[ACAMethodsName] = []string{http.MethodGet} + + gwAPI, err := newGatewayBackend(n) + if err != nil { + return nil, err } - headers[ACAHeadersName] = cleanHeaderSet( - append([]string{ - "Content-Type", - "User-Agent", - "Range", - "X-Requested-With", - }, headers[ACAHeadersName]...)) - - headers[ACEHeadersName] = cleanHeaderSet( - append([]string{ - "Content-Range", - "X-Chunked-Output", - "X-Stream-Output", - }, headers[ACEHeadersName]...)) - - gateway := newGatewayHandler(GatewayConfig{ - Headers: headers, - Writable: writable, - PathPrefixes: cfg.Gateway.PathPrefixes, - }, api, cache.New(GatewayReedSolomonDirectoryCacheCapacity)) + gw := gateway.NewHandler(gwConfig, gwAPI) + gw = otelhttp.NewHandler(gw, "Gateway") + + // By default, our HTTP handler is the gateway handler. + handler := gw.ServeHTTP for _, p := range paths { - mux.Handle(p+"/", gateway) + mux.HandleFunc(p+"/", handler) } + return mux, nil } } +func HostnameOption() ServeOption { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + gwAPI, err := newGatewayBackend(n) + if err != nil { + return nil, err + } + + publicGateways := convertPublicGateways(cfg.Gateway.PublicGateways) + childMux := http.NewServeMux() + mux.HandleFunc("/", gateway.WithHostname(childMux, gwAPI, publicGateways, cfg.Gateway.NoDNSLink).ServeHTTP) + return childMux, nil + } +} + func VersionOption() ServeOption { return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { @@ -111,3 +92,150 @@ func VersionOption() ServeOption { return mux, nil } } + +func newGatewayBackend(n *core.IpfsNode) (gateway.IPFSBackend, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + bserv := n.Blocks + var vsRouting routing.ValueStore = n.Routing + nsys := n.Namesys + if cfg.Gateway.NoFetch { + bserv = blockservice.New(bserv.Blockstore(), offline.Exchange(bserv.Blockstore())) + + cs := cfg.Ipns.ResolveCacheSize + if cs == 0 { + cs = node.DefaultIpnsCacheSize + } + if cs < 0 { + return nil, fmt.Errorf("cannot specify negative resolve cache size") + } + + vsRouting = offlineroute.NewOfflineRouter(n.Repo.Datastore(), n.RecordValidator) + nsys, err = namesys.NewNameSystem(vsRouting, + namesys.WithDatastore(n.Repo.Datastore()), + namesys.WithDNSResolver(n.DNSResolver), + namesys.WithCache(cs)) + if err != nil { + return nil, fmt.Errorf("error constructing namesys: %w", err) + } + } + + gw, err := gateway.NewBlocksGateway(bserv, gateway.WithValueStore(vsRouting), gateway.WithNameSystem(nsys)) + if err != nil { + return nil, err + } + return &offlineGatewayErrWrapper{gwimpl: gw}, nil +} + +type offlineGatewayErrWrapper struct { + gwimpl gateway.IPFSBackend +} + +func offlineErrWrap(err error) error { + if errors.Is(err, iface.ErrOffline) { + return fmt.Errorf("%s : %w", err.Error(), gateway.ErrServiceUnavailable) + } + return err +} + +func (o *offlineGatewayErrWrapper) Get(ctx context.Context, path gateway.ImmutablePath, ranges ...gateway.ByteRange) (gateway.ContentPathMetadata, *gateway.GetResponse, error) { + md, n, err := o.gwimpl.Get(ctx, path, ranges...) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) GetAll(ctx context.Context, path gateway.ImmutablePath) (gateway.ContentPathMetadata, files.Node, error) { + md, n, err := o.gwimpl.GetAll(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) GetBlock(ctx context.Context, path gateway.ImmutablePath) (gateway.ContentPathMetadata, files.File, error) { + md, n, err := o.gwimpl.GetBlock(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) Head(ctx context.Context, path gateway.ImmutablePath) (gateway.ContentPathMetadata, files.Node, error) { + md, n, err := o.gwimpl.Head(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) ResolvePath(ctx context.Context, path gateway.ImmutablePath) (gateway.ContentPathMetadata, error) { + md, err := o.gwimpl.ResolvePath(ctx, path) + err = offlineErrWrap(err) + return md, err +} + +func (o *offlineGatewayErrWrapper) GetCAR(ctx context.Context, path gateway.ImmutablePath) (gateway.ContentPathMetadata, io.ReadCloser, <-chan error, error) { + md, data, errCh, err := o.gwimpl.GetCAR(ctx, path) + err = offlineErrWrap(err) + return md, data, errCh, err +} + +func (o *offlineGatewayErrWrapper) IsCached(ctx context.Context, path path.Path) bool { + return o.gwimpl.IsCached(ctx, path) +} + +func (o *offlineGatewayErrWrapper) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + rec, err := o.gwimpl.GetIPNSRecord(ctx, c) + err = offlineErrWrap(err) + return rec, err +} + +func (o *offlineGatewayErrWrapper) ResolveMutable(ctx context.Context, path path.Path) (gateway.ImmutablePath, error) { + imPath, err := o.gwimpl.ResolveMutable(ctx, path) + err = offlineErrWrap(err) + return imPath, err +} + +func (o *offlineGatewayErrWrapper) GetDNSLinkRecord(ctx context.Context, s string) (path.Path, error) { + p, err := o.gwimpl.GetDNSLinkRecord(ctx, s) + err = offlineErrWrap(err) + return p, err +} + +var _ gateway.IPFSBackend = (*offlineGatewayErrWrapper)(nil) + +var defaultPaths = []string{"/btfs/", "/btns/", "/api/", "/p2p/", "/version"} + +var subdomainGatewaySpec = &gateway.Specification{ + Paths: defaultPaths, + UseSubdomains: true, +} + +var defaultKnownGateways = map[string]*gateway.Specification{ + "localhost": subdomainGatewaySpec, +} + +func convertPublicGateways(publicGateways map[string]*config.GatewaySpec) map[string]*gateway.Specification { + gws := map[string]*gateway.Specification{} + + // First, implicit defaults such as subdomain gateway on localhost + for hostname, gw := range defaultKnownGateways { + gws[hostname] = gw + } + + // Then apply values from Gateway.PublicGateways, if present in the config + for hostname, gw := range publicGateways { + if gw == nil { + // Remove any implicit defaults, if present. This is useful when one + // wants to disable subdomain gateway on localhost etc. + delete(gws, hostname) + continue + } + + gws[hostname] = &gateway.Specification{ + Paths: gw.Paths, + NoDNSLink: gw.NoDNSLink, + UseSubdomains: gw.UseSubdomains, + InlineDNSLink: gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink), + } + } + + return gws +} diff --git a/core/corehttp/gateway/README.md b/core/corehttp/gateway/README.md new file mode 100644 index 000000000..0aacd9069 --- /dev/null +++ b/core/corehttp/gateway/README.md @@ -0,0 +1,34 @@ +# IPFS Gateway + +> A reference implementation of HTTP Gateway Specifications. + +## Documentation + +* Go Documentation: https://pkg.go.dev/github.com/ipfs/boxo/gateway +* Gateway Specification: https://github.com/ipfs/specs/tree/main/http-gateways#readme +* Types of HTTP Gateways: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#http-gateways +## Example + +```go +// Initialize your headers and apply the default headers. +headers := map[string][]string{} +gateway.AddAccessControlHeaders(headers) + +conf := gateway.Config{ + Headers: headers, +} + +// Initialize a NodeAPI interface for both an online and offline versions. +// The offline version should not make any network request for missing content. +ipfs := ... + +// Create http mux and setup path gateway handler. +mux := http.NewServeMux() +gwHandler := gateway.NewHandler(conf, ipfs) +mux.Handle("/ipfs/", gwHandler) +mux.Handle("/ipns/", gwHandler) + +// Start the server on :8080 and voilá! You have a basic IPFS gateway running +// in http://localhost:8080. +_ = http.ListenAndServe(":8080", mux) +``` diff --git a/core/corehttp/gateway/assets/README.md b/core/corehttp/gateway/assets/README.md new file mode 100644 index 000000000..25d1a35e8 --- /dev/null +++ b/core/corehttp/gateway/assets/README.md @@ -0,0 +1,27 @@ +# Required Assets for the Gateway + +> DAG and Directory HTML for HTTP gateway + +## Updating + +When making updates to the templates, please note the following: + +1. Make your changes to the (human-friendly) source documents in the `src` directory. +2. Before testing or releasing, go to `assets/` and run `go generate .`. + +## Testing + +1. Make sure you have [Go](https://golang.org/dl/) installed +2. Start the test server, which lives in its own directory: + +```bash +> cd test +> go run . +``` + +This will listen on [`localhost:3000`](http://localhost:3000/) and reload the template every time you refresh the page. Here you have two pages: + +- [`localhost:3000/dag`](http://localhost:3000/dag) for the DAG template preview; and +- [`localhost:3000/directory`](http://localhost:3000/directory) for the Directory template preview. + +If you get a "no such file or directory" error upon trying `go run .`, make sure you ran `go generate .` to generate the minified artifact that the test is looking for. diff --git a/core/corehttp/gateway/assets/assets.go b/core/corehttp/gateway/assets/assets.go new file mode 100644 index 000000000..41b4ffc00 --- /dev/null +++ b/core/corehttp/gateway/assets/assets.go @@ -0,0 +1,203 @@ +//go:generate ./build.sh +package assets + +import ( + "embed" + "io" + "io/fs" + "net" + "strconv" + + "html/template" + "net/url" + "path" + "strings" + + "github.com/cespare/xxhash" + + ipfspath "github.com/ipfs/go-path" +) + +//go:embed dag-index.html directory-index.html knownIcons.txt +var asset embed.FS + +// AssetHash a non-cryptographic hash of all embedded assets +var AssetHash string + +var ( + DirectoryTemplate *template.Template + DagTemplate *template.Template +) + +func init() { + initAssetsHash() + initTemplates() +} + +func initAssetsHash() { + sum := xxhash.New() + err := fs.WalkDir(asset, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + file, err := asset.Open(path) + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(sum, file) + return err + }) + if err != nil { + panic("error creating asset sum: " + err.Error()) + } + + AssetHash = strconv.FormatUint(sum.Sum64(), 32) +} + +func initTemplates() { + knownIconsBytes, err := asset.ReadFile("knownIcons.txt") + if err != nil { + panic(err) + } + knownIcons := make(map[string]struct{}) + for _, ext := range strings.Split(strings.TrimSuffix(string(knownIconsBytes), "\n"), "\n") { + knownIcons[ext] = struct{}{} + } + + // helper to guess the type/icon for it by the extension name + iconFromExt := func(name string) string { + ext := path.Ext(name) + _, ok := knownIcons[ext] + if !ok { + // default blank icon + return "ipfs-_blank" + } + return "ipfs-" + ext[1:] // slice of the first dot + } + + // custom template-escaping function to escape a full path, including '#' and '?' + urlEscape := func(rawUrl string) string { + pathURL := url.URL{Path: rawUrl} + return pathURL.String() + } + + // Directory listing template + dirIndexBytes, err := asset.ReadFile("directory-index.html") + if err != nil { + panic(err) + } + + DirectoryTemplate = template.Must(template.New("dir").Funcs(template.FuncMap{ + "iconFromExt": iconFromExt, + "urlEscape": urlEscape, + }).Parse(string(dirIndexBytes))) + + // DAG Index template + dagIndexBytes, err := asset.ReadFile("dag-index.html") + if err != nil { + panic(err) + } + + DagTemplate = template.Must(template.New("dir").Parse(string(dagIndexBytes))) +} + +type DagTemplateData struct { + Path string + CID string + CodecName string + CodecHex string +} + +type DirectoryTemplateData struct { + GatewayURL string + DNSLink bool + Listing []DirectoryItem + Size string + Path string + Breadcrumbs []Breadcrumb + BackLink string + Hash string +} + +type DirectoryItem struct { + Size string + Name string + Path string + Hash string + ShortHash string +} + +type Breadcrumb struct { + Name string + Path string +} + +func Breadcrumbs(urlPath string, dnslinkOrigin bool) []Breadcrumb { + var ret []Breadcrumb + + p, err := ipfspath.ParsePath(urlPath) + if err != nil { + // No assets.Breadcrumbs, fallback to bare Path in template + return ret + } + segs := p.Segments() + contentRoot := segs[1] + for i, seg := range segs { + if i == 0 { + ret = append(ret, Breadcrumb{Name: seg}) + } else { + ret = append(ret, Breadcrumb{ + Name: seg, + Path: "/" + strings.Join(segs[0:i+1], "/"), + }) + } + } + + // Drop the /btns/ prefix from assets.Breadcrumb Paths when directory + // listing on a DNSLink website (loaded due to Host header in HTTP + // request). Necessary because the hostname most likely won't have a + // public gateway mounted. + if dnslinkOrigin { + prefix := "/btns/" + contentRoot + for i, crumb := range ret { + if strings.HasPrefix(crumb.Path, prefix) { + ret[i].Path = strings.Replace(crumb.Path, prefix, "", 1) + } + } + // Make contentRoot assets.Breadcrumb link to the website root + ret[1].Path = "/" + } + + return ret +} + +func ShortHash(hash string) string { + if len(hash) <= 8 { + return hash + } + return (hash[0:4] + "\u2026" + hash[len(hash)-4:]) +} + +// helper to detect DNSLink website context +// (when hostname from gwURL is matching /btns/ in path) +func HasDNSLinkOrigin(gwURL string, path string) bool { + if gwURL != "" { + fqdn := stripPort(strings.TrimPrefix(gwURL, "//")) + return strings.HasPrefix(path, "/btns/"+fqdn) + } + return false +} + +func stripPort(hostname string) string { + host, _, err := net.SplitHostPort(hostname) + if err == nil { + return host + } + return hostname +} diff --git a/core/corehttp/gateway/assets/build.sh b/core/corehttp/gateway/assets/build.sh new file mode 100755 index 000000000..531bbfc02 --- /dev/null +++ b/core/corehttp/gateway/assets/build.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -euo pipefail + +function build() { + rm -f $1 + sed '/ ./base-html.html + (echo "") > ./minified-wrapped-style.html + sed '/<\/title>/ r ./minified-wrapped-style.html' ./base-html.html > ./$1 + rm ./base-html.html && rm ./minified-wrapped-style.html +} + +build "directory-index.html" +build "dag-index.html" diff --git a/core/corehttp/gateway/assets/dag-index.html b/core/corehttp/gateway/assets/dag-index.html new file mode 100644 index 000000000..e314a75c7 --- /dev/null +++ b/core/corehttp/gateway/assets/dag-index.html @@ -0,0 +1,67 @@ + +{{ $root := . }} + + + + + + + + + + + + + + + + + +{{ .Path }} + + + + +
+
+

CID: {{.CID}}
+ Codec: {{.CodecName}} ({{.CodecHex}})

+
+
+ + + + + + + +
+

Preview as JSON
(application/json)

+
+

Or download as: +

+

+
+
+
+ + diff --git a/core/corehttp/gateway/assets/directory-index.html b/core/corehttp/gateway/assets/directory-index.html new file mode 100644 index 000000000..8dce50795 --- /dev/null +++ b/core/corehttp/gateway/assets/directory-index.html @@ -0,0 +1,97 @@ + +{{ $root := . }} + + + + + + + + + + + + + + + + + +{{ .Path }} + + + + +
+
+
+ + Index of + {{ range .Breadcrumbs -}} + /{{ if .Path }}{{ .Name }}{{ else }}{{ .Name }}{{ end }} + {{- else }} + {{ .Path }} + {{ end }} + + {{ if .Hash }} +
+ {{ .Hash }} +
+ {{ end }} +
+ {{ if .Size }} +
+  {{ .Size }} +
+ {{ end }} +
+
+ + + + + + + + {{ range .Listing }} + + + + + + + {{ end }} +
+
 
+
+ .. +
+
 
+
+ {{ .Name }} + + {{ if .Hash }} + + {{ .ShortHash }} + + {{ end }} + {{ .Size }}
+
+
+ + \ No newline at end of file diff --git a/core/corehttp/gateway/assets/knownIcons.txt b/core/corehttp/gateway/assets/knownIcons.txt new file mode 100644 index 000000000..c110530ea --- /dev/null +++ b/core/corehttp/gateway/assets/knownIcons.txt @@ -0,0 +1,65 @@ +.aac +.aiff +.ai +.avi +.bmp +.c +.cpp +.css +.dat +.dmg +.doc +.dotx +.dwg +.dxf +.eps +.exe +.flv +.gif +.h +.hpp +.html +.ics +.iso +.java +.jpg +.jpeg +.js +.key +.less +.mid +.mkv +.mov +.mp3 +.mp4 +.mpg +.odf +.ods +.odt +.otp +.ots +.ott +.pdf +.php +.png +.ppt +.psd +.py +.qt +.rar +.rb +.rtf +.sass +.scss +.sql +.tga +.tgz +.tiff +.txt +.wav +.wmv +.xls +.xlsx +.xml +.yml +.zip diff --git a/core/corehttp/gateway/assets/src/dag-index.html b/core/corehttp/gateway/assets/src/dag-index.html new file mode 100644 index 000000000..7a42ef6be --- /dev/null +++ b/core/corehttp/gateway/assets/src/dag-index.html @@ -0,0 +1,66 @@ + +{{ $root := . }} + + + + + + + + + + + + + + + + + + + +{{ .Path }} + + + +
+
+

CID: {{.CID}}
+ Codec: {{.CodecName}} ({{.CodecHex}})

+
+
+ + + + + + + +
+

Preview as JSON
(application/json)

+
+

Or download as: +

+

+
+
+
+ + diff --git a/core/corehttp/gateway/assets/src/directory-index.html b/core/corehttp/gateway/assets/src/directory-index.html new file mode 100644 index 000000000..ce1c1a9f8 --- /dev/null +++ b/core/corehttp/gateway/assets/src/directory-index.html @@ -0,0 +1,96 @@ + +{{ $root := . }} + + + + + + + + + + + + + + + + + + + +{{ .Path }} + + + +
+
+
+ + Index of + {{ range .Breadcrumbs -}} + /{{ if .Path }}{{ .Name }}{{ else }}{{ .Name }}{{ end }} + {{- else }} + {{ .Path }} + {{ end }} + + {{ if .Hash }} +
+ {{ .Hash }} +
+ {{ end }} +
+ {{ if .Size }} +
+  {{ .Size }} +
+ {{ end }} +
+
+ + + + + + + + {{ range .Listing }} + + + + + + + {{ end }} +
+
 
+
+ .. +
+
 
+
+ {{ .Name }} + + {{ if .Hash }} + + {{ .ShortHash }} + + {{ end }} + {{ .Size }}
+
+
+ + \ No newline at end of file diff --git a/core/corehttp/gateway/assets/src/icons.css b/core/corehttp/gateway/assets/src/icons.css new file mode 100644 index 000000000..a2f360c5a --- /dev/null +++ b/core/corehttp/gateway/assets/src/icons.css @@ -0,0 +1,807 @@ +/* Source - fileicons.org */ + +.ipfs-_blank { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNqEUj1LxEAQnd1MVA4lyIEWx6UIKEGUExGsbC3tLfwJ/hT/g7VlCnubqxXBwg/Q4hQP/LhKL5nZuBsvuGfW5MGyuzM7jzdvVuR5DgYnZ+f99ai7Vt5t9K9unu4HLweI3qWYxI6PDosdy0fhcntxO44CcOBzPA7mfEyuHwf7ntQk4jcnywOxIlfxOCNYaLVgb6cXbkTdhJXq2SIlNMC0xIqhHczDbi8OVzpLSUa0WebRfmigLHqj1EcPZnwf7gbDIrYVRyEinurj6jTBHyI7pqVrFQqEbt6TEmZ9v1NRAJNC1xTYxIQh/MmRUlmFQE3qWOW1nqB2TWk1/3tgJV0waVvkFIEeZbHq4ElyKzAmEXOx6gnEVJuWBzmkRJBRPYGZBDsVaOlpSgVJE2yVaAe/0kx/3azBRO0VsbMFZE3CDSZKweZfYIVg+DZ6v7h9GDVOwZPw/PoxKu/fAgwALbDAXf7DdQkAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-_page { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpsUztv01AYPfdhOy/XTZ80VV1VoCqlA2zQqUgwMEErWBALv4GJDfEDmOEHsFTqVCTExAiiSI2QEKJKESVFFBWo04TESRzfy2c7LY/kLtf2d8+555zvM9NaI1ora5svby9OnbUEBxgDlIKiWjXQeLy19/X17sEtcPY2rtHS96/Hu0RvXXLz+cUzM87zShsI29DpHCYt4E6Box4IZzTnbDx7V74GjhOSfwgE0H2638K9h08A3iHGVbjTw7g6YmAyw/BgecHNGGJjvfQhIfmfIFDAXJpjuugi7djIFVI4P0plctgJQ0xnFe5eOO02OwEp2VkhSCnC8WOCdqgwnzFx4/IyppwRVN+XYXsecqZA1pB48ekAnw9/4GZx3L04N/GoTwEjX4cNH5vlPfjtAIYp8cWrQutxrC5Mod3VsXVTMFSqtaE+gl9dhaUxE2tXZiF7nYiiatJ3v5s8R/1yOCNLOuwjkELiTbmC9dJHpIaGASsDkoFQGJQwHWMcHWJYOmUj1OjvQotuytt5nHMLEGkCyx6QU384jwkUAd2sxJbS/QShZtg/8rHzzQOzSaFhxQrA6YgQMQHojCUlgnCAAvKFBoXXaHfArSCZDE0gyWJgFIKmvUFKO4MUNIk2a4+hODtDUVuJ/J732AKS6ZtImdTyAQQB3bZN8l9t75IFh0JMUdVKsohsUPqRgnka0tYgggYpCHkKGTsHI5NOMojB4iTICCepvX53AIEfQta1iUCmoTiBmdEri2RgddKFhuJoqb/af/yw/d3zTNM6UkaOfis62aUgddAbnz+rXuPY+Vnzjt9/CzAAbmLjCrfBiRgAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-aac { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNp0Uk1PE0EYftruVlvAUkhVEPoBcsEoLRJBY01MPHjCs3cvogcT/4qJJN5NvHhoohcOnPw4YEGIkCh+oLGBKm3Z7nZ3dme2vjOhTcjiJJvZzPvOM8/HG2q325Dr3kLp7Y1ibpIxjs4KhQBZfvV6s7K5Vb0bjeof5ZlcGysP1a51mifODybvzE8mzCbrAoTDIThMoGXZiZ4YSiurf+Z1XeuCqJ7Oj+sK3jQcNAmg8xkGQ71mYejcAB49vpmeuzJccl0+dUj6KIAvfHCPg3N+uAv4vg9BOxcCmfEzuP/genpmeqhEMgude10Jwm+DuUIyUdTlqu2byoMfX/dRermBeExHsTiWNi3+lMpzRwDki8zxCIATmzbevfmClukiP5NFhJgwkjeRTeLShdOoVJqnAgwkgCAZ6+UdLC9twjQZ8pdzioFkZBHY3q6B3l4dJEEEPOCeD4cYVH7Xsf15F+FImC775INAJBJSkVoWo0QY9YqgiR4ZZzRaGBkdwK3bFxGLRZUfB3Rm2x4x9CGtsUxH9QYkKICDFuLxKAozGZwdTqBRs2FbLlXbiPdECMCHadj/AaDXZNFqedCIvnRcS4UpRo7+hC5zUmw8Ope9wUFinvpmZ7NKt2RTmB4hKZo6n8qP4Oq1HBkKlVYAQBrUlziB0XQSif4YmQhksgNIJk9iaLhPaV9b/Um+uJSCdzyDbGZQRSkvjo+n4JNxubGUSsCj+ZCpODYjkGMAND2k7exUsfhkCd+29yguB88Wl7FW/o6tT7/gcXqAgGv7hhx1LWBireHVn79YP6ChQ3njb/eFlfWqGqT3H3ZlGIhGI2i2UO/U/wkwAAmoalcxlNA1AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ai { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk5JREFUeNpsU01vElEUPTPzZqBAQaSFQiJYUmlKYhoTF41L3Tbu/Q/+AvsX3Bp/gPsuWLrqyqQ7TUxMtAvF1tYGoXwNw7wv7zwYgtKX3Lw379575p5z77O01ohW+/DVh8zj7aYKhflGdG9ZsGwLNydffgVfr19YHvsEa+Zu/nxndob5StQK+dyzvZzyw/gKlmMj7IygFM+xvNcanp4/t5dAomXHBy2UUBOO2MAl/B9/cPb6PULuoHx0WM0e3GvpUOxD3wZAJWutZqYUYmqpSg5OMgH3YQObL59W0/ullpryR3HegkKEqiWBSGV4R3vQ7sIhScTZFTpHx3A215B5sluVY/WWMg7+ATB/lcLsKpTonHzD+OMFEuTz8ikkt9Kwt9YJZB38cpBdoQAZJdLvCGByfoPB6Xdk90pYy6Xg3c/DaWwArg09DaG5lCsUFN0pckZAojdC8m4auBqaALuSgez7VB1RtDSUWOQvUaBLFUzJBMJ2DwmPgd1Jwm0WoSgJfjDvrTKxtwAIyEkAOQ5hU//Zdg5uowDlUNMnwZLW0sSuUuACYhwQRwFvJxupCjEYUUccOkoaKmdOlZnY1TkgAcXAhxhOwLsDsHoN3u4O5JTDfVCH6I9nfjId3gIgSUATFJk/hVevGtOMwS0XwQ3AzB/FrlKg8Q27I2javVoZrFgwD4qVipAEyMlnaFArzaj/D0DiMXlJAFQyK2r8fnMMRZp4lQ1MaSL5tU/1kqAkMCh2tYI+7+kh70cjPbr4bEZ51jZr8TJnB9PJXpz3V4ABAPOQVJn2Q60GAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-aiff { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAohJREFUeNpkU9tqE1EUXZmZpE3aTBLbJFPTtFURtSCthr7UCyKKFJ/9An3og6Ag/oXfoUj7og9asCBYKT6UIPHaWtpq7NU2aZK5z5wZ9xxMpMwZDuewz9prr32ZiO/7CNaDx3OLt6fOjBqGg/aKRCIInp8+KzfKH7fudnVF58nE16el+/yU2mBFSWZKpWJKVc0OgUBo02K4NDmU6o75Mx+Wdu9IUXFeiOA/pn1xHeYaugVDdzpbp91qGlAKGTx8dC19/Wpxhjnsxj/RRwk85hGJC9d1O6fneWAuoztDYSSLe9OT6SuXB2ccx73Z9uukwDwfls1g0xZIY/Ad/Gnyt/XVfbyYrSDRE8PExHB6/8B6QuaxIwRBFMt0iIAiMx+LCys8jfGJEUik2WpZOD2SQf9oDtVqQwopCAiY66FS/om3b75CVS2MlU7AJ2WiJBCZjZ2dJuRkDJZFwFAR7UCBja3fNfxY2YEoCtRCj9em3Tpds6FpJseGCBxS0GgYGBzqw62p84gnYnAI2CSbSbPhEpFAaE2zODaUAlWWwDoS5DheGqbWpVE/0CmqCY9qkEyINBceb2uADRNQ8bSWAVVzIFKomCQim+0luS4yKYlsHlRyZo7EsSEC23K5vAsXh/H92zZkuRvxeBS5nEx2yp2KqhxPoV5TYS/8CtdApylM9sZQKKSQzyeRTseRV2QoAzIYY8jme5DN9fI0dQoUIjANGydP9VM7PZw9p/AiBpNYrdbw/t0yTJqRtdU9UrfJCUMpSJIgbWzsYe51BcViHzLHeqCRqhZ1YX1tFwNfZBxS9O3NWkAcHqR606k/n/3coKAoV/Y7vQ/OYCZevlrmv3c0GsFh06u3/f4KMABvSWfDHmbK2gAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-avi { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpsU8tu00AUPXZcN0nzTpq2KQ3pAwkIAnWHqCoeexBb+AQ+ABZ8A2s+AIkdm266QUJIFWKBkHg1KpRHi5omJGkbJ3bGHj+4M1EQrTvSyGPPueeec++1EgQBxHp+/9mbyuriRZdxjJaiKBD3W+u1+p9a856max+gDO8ebT+WT20Ezi9NZi/crqadvn2MQBAGfpCOpqNru2937vxPIpY6Onjccx3Twck9MBiSU0ncfHirXFmZX3Md9wqCUwiEVN/zaQfHt0vfbBe5uQyuPVgpl5Zn11ybL4/i/lkICOw5niQRGQShoiqI6Bo43W2ub8n3hRtLZT7gTynk6gkCX9gAOxpAnxhHZDwC1/aI1EViJolu/QhKRMHZ1UX0Gr1USIEn5FPWHy+/wTokkrQOq2vBaHZBN4hmY9Jwfr4An/teiEB45ZZDwDiMhoExT0N+sYDCuUkkplLIlXP4/XEXdo+RUhdhBSSfUwtVTUG8MIHK9QVqI7D/uY6vr2pwmCPrkz+Tk9gwARWQ9WxppbXZhNnpw+ya4A5HZi6L4lIR8WyCcL6sTZiAWjWgAmpxkn5+kqTamK6WkCwmERmLDLvjB0ML9ikWXPLFuozYOap3L8HYN6DHdbS/d5CeTVBndBz87FCBLYkNTyIjBQemnIEsSY5lYrK1+UoWcToLMjEHAyIQ2BCBSx/NVh+ZUhrqmEqBebS3WyhdLg0zt/ugAaIklsSGLHCLa6zDMGhZ2HjyGsnpFPqNHnY2fmHv3R5SMymYbROszSQ2ROAY9qHiofvlxSc5xsKKqqnY3diRE9h4X5d/pzg7lnM4ivsrwADe9Wg/CQJgFAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-bmp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmZJREFUeNp0U+1rUlEY/13v9YV0vq2wttI5CdpL9aEGBZUDv0df668I6n+or0UQ/RuuD0EgVDAZrsKF4AR1a6COKW5qXvXec27PuVeda3bgcF6e8/ye5/d7niMZhgExnK9fbTrm5pbBGMZDkgCyq+VyhTUaT6Eo2ZHJePPWXJXRhez3B1yxmM/QdctXUSCgtV4Py4CvY3cky4e1x5DlLCaGbbzjXDcousG5OQe5HPRSCQPK4PpsEM/XH4WvhS4noeu3JwHGGRiULhsMoKZS4I0GtEIB9mgULJGA0+9DPBpBT7sffvf1W/Lg6OgJufw8C0CRGEXWazUwiiyFQjA8bsjVKjaJzovMD/Q5gxyJhG2cvyeXe2cAuADQNGBmBvLaGuTFRaDfh31lBTWi9pumjbK0B4JQul3vOQpM8JdskOLrdCvDcDjAsjtg5TIkoiKLaokMNR2cnZbqNAMycqG7XbHKR2fMzwO/dsxSwu0BiBJsNsv2LwAJAJCI5ux2gXYbqNetcz5PoORI1cDS0n8AxGW7A+zvEYBKZ2ZlcsEtJLbedMjePBaCTQMghx45ulyWkzxMVUQ2RMQhLfFO16YAqCrixPnm6iqKrRb2W23EfF4cUNSrHg90cr7hDyB33MTnSmUKALVs4uIlROjxg+AsPhGVl3fuIl2tIOB0Ya91gkOi9mxhAal0ekork1ic/kGLBORMxy2K1qS9V1ZQbNThIj2EGh+2tsyOnSai8r1UxMNIBB+LRTTULr4Uds0K1tU/uOLxIrmbNz8XXSrnASSpubG9fbKRyVh1n/zSw29t9oC1b47MfwUYAAUsLiWr4QUJAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-c { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcxJREFUeNqEUk1rE0EYfmZnkgoJCaGNCehuJTalhJZSUZB66a0HwXsP/Qn+FM+9+hty0LNYCr2I7UVLIW0Fc0hpQpSS7O7MrO9MspuvVV8YMnk/nn2e5x0WRRFMvP/w6WSz5jbi/9NxfP693Wp3DrJCnMW5d28P7a+IE15lufR8o1ZEStwPhkWHsWbrZ+eNEPxsuubEF6m0TBv2Q4liPofXuzveulttSqW2UwH+GjqC0horpSL2njU89+FyMwjlTlxOJMTa9ZQHzDQIjgwdom9zLzfXPc75kbnOAswBJTlC2XrqQRMLxhi442DgB4UFBhgPpm3B5pgBHNUUxQKAHs8pHf3TEuFMetM9IKr/i2mWMwC0SnuSFTG2YKyppwKYVdGO7TFhzBqGIenVeLCUtfURgErucx5ECKREKBU4d3B718PHz6cICGT/1Qs8qpQtGOdyhtGEARWDQFqQJSeDL98u4VbLaKw9IRAJPwjtoJGlVAoDQ800+fRFTTYXcjlcXN2g++s36p5Lzzlve1iEROa8BGH1EbrSAeqrjxEqicHQt8/YSDHMpaNs7wJAp9vvfb287idboAVkRAa5fBYXP9rxO4Mgf0xvPPdHgAEA8OoGd40i1j0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-cpp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfJJREFUeNqEUs9PE0EU/mZ2WgqpXX+QIDFdalVslh8NlAOQaOKFAwfvHvwT/FM8e/U/MOnBmwcj8WD0ACEGghIkbU0baaEthe3OTJ0ZWV26q37JZt68ee/b9733yGAwgMbL12/fz+azbnAPY2Nrt7Zfqz9JMrYZ+J4/e2pOFjiciRvXlgp5GzHonXk2o6S8V6k/TjBrM/xGA4MLyeOSPZ8jkx7D+uqCU3Amy1yIYizB36AlCSkwfjWDR4uu40yMl/s+XwjeWThQQ4Z6QNSnSkYykcDXasP4lmfvOZTSF9q8TDBEFPbN5bOqCglCCCxK0TvvZyIV4CIxbgpC+4gm/PUmFCIE8iJPyME/e8Lon9j4HvyHYLjKSwRCSEUgf9+15mFbx8QS6CZJMzJ9SlBCwX3fJDLG4PX7ykcwkmQmJtpEhWa7g1dvNlSwjwelebz7tAXLolh0p/Fxe9fErK2WDFGEgKjxfNjegX0lDTc/heNuF99/HGEslcKXwyoazWNDdlCr6+DoJgrBzdI0T9rYO6yg2zszMlaKM3Dv5OBzbuyZuzm1B16U4Nzz2f3cFOx0Gq12F9cztpExncsqYoaHpSIKtx0zJdVIFpHQ6py29muNk1uTN829o/6SHEnh80HFaE6NjmLnWxUJy1LyTltB3k8BBgBeEeQTiWRskAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-css { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNpsUktvUlEQ/u5DoCLl/RAKKKUvWmIxjYntQtcu3LvwJ/hTXLt16coFC2PsojEaMKZtCqFaTdGmjbS0CG3By+vei3OOBSGXSU7uzNyZ78z3zRF6vR6YvXzzPrMUCyf68bB9zO+VfpROn5hkOdfPPX/2lH/lfiLidztX5mN2jLGG0rKLENIE8liWpdzwP7HvqJqujmvudFU4bFY8Wk1FZsOBtKppd8YCDNu77CZevd3gflfTUFcUhP0ePLibiIR9rjSBpgwAfe4dVcV6dhtep4PH5msylGYLrzeybErcT85FYiH/CyPAf74gObC2vMhzsiRhPhpC6eQUM+EA1pJzILEnjRSuJsju7MJqsUCSRei6Dp3yXqcdGlHZ/rLPazQWGCn8+6YW4pAkEW0SjzUzanWlCa/LgcR0lNfovTEi6lcIkzesnM/R8RlN0INGp3h4DHoDsE5YRvQyiKiRSMzikRAOS2WoqoZWu41K7RwzlOOAVDMMMHhIGvFlRxJFrKYW0ep0IYgC3SDh4b1lTJjNfENsrazOAMAw680mPuW+8lFno1P4XDigRhOiwQAyJK7TbsNS/PaA7giAIAhYz2yRgBIfsVA8wIetPG6FAqhdNrC5u0f+TUyHgyMTDDToEt/ftQsEvW4EPG5OZcrvw0mlimarTXkPfpXPcNlQoGtjACgpryQXsPNtH/nvRXqBJpoKHMzGNkNB0Odls7LNyAYKpUq1dt1iuvB7fRDp9kr9D1xOFwkpoksXusmXaZWFn0coV89r/b6/AgwAkUENaQaRxswAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-dat { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNqMU01PE1EUPe/Na0uptmlASg3MoiZgCA3hQ8PHAjbqwsS9C3+CP8W1W/+BSReyYUPwI4QAVkAgUEgIbVIg1FZb2pl5b3zv2cHBjsaTTOa+e989OffcGeK6LhTevFv+OJoZHPHOfrz/sl86KpWfhxnLe7lXL1/oN/MSZqonOXU/k0AA6lfNhEFIrlAsP2PMyPtr1AscLpyg5pbtIHErhqez4+awmc45nI8FEvwNaiQuBHqTcSxMjJhmX0/Osp1xr878FxWEzwMinxAzEA4xFIpnOjedHTKpYbxW4U2CP4j8uWxmUKsghMCgFI2mFe9QgHZj0Ba4yhFF+KvGJToIRLuPC/efnjD6+26wB1Lq/xgbSCBXKeWJG/OTdky8cWTdT3C9RmWSGk2XCLlWo4xTNbfN5qh7PpXM72GjZeHt0gpq9QbmH4whGb+NpU/reDQ7hcWVVXxvXOHxzCQopQEKXKEbL6o1ZIcy+LC5g62DY2zsHeC0fA4zndIrHOjvg2XbAQRSfsuy9XxC2qzi/H5B6/68W0AsGkW0KyJPBLbDO0fg3JX/CUM81i0bD6WKe6j9qOPJ3EMcF0tSNsFA6g6alqW+VtZBUL78Vtk+Oqne7U9rs5qOQCjSheJFBeFIFOfVujSUYu3rIc4uqxWv76cAAwCwbvRb3SgYxQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-dmg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNpsU01rE1EUPe9lkk47yWTStCmtNhFSWxos2EXVhSsRcasuxYV05V8Qf4DgD/AvCK5EV1oFI7iUBqmCNdDvppq2mWSSzEzy3vPOpFFq+uDNfR/3nnvueXeYUgrBWH1/9/NE7k5BKRnuRcfF2qdnmJq9DeF9tQ+2isuMsxXGWHh/a1mEVsPJSI5fSU3OPEj291IIlN49RXz0KqzEQjIeZS/L5Y/3wPGhDxIM/i/A7fZWgVG0t5EaG0ZUa0JGM8gvPrZmLt58QYwv91mfAqCIE0sAqgumBFITGQzpUYhuF0KfRa7waDyXXXolpVrsh/0tgSLDr5I+wUZo1UHCSkAficPzY6juFSmbRPrC/azjq+fkcO00gAqoU7B0ETKkfWbuCTjTYeq5oESAauexcTScX+ZACWFm0YQSLZKhHdr67+/wW0e0dgjYo3sCEXXybYtBDVSHLp2es3IpsILS24c42lkBg6DzRjgRzCDZ/xr0GNRJwwYiWgzt+hYMawleu0V3wbkT+kUirOc7IGJAz68R/Qak1BAlx3hqASPGBJRXpXOv58dkz3eAgQoOm4hyj57NgZm0MHvpBmK6QdUdg/DAg9cRkhicBSDaKJdeo1bdxmR2DtWDDUxl51HZ+QHTysD3XdQO95Gfv06aeGcAdBrY3Chi8lwO3768QWX7J5q1XWyVSxgajiOXLyBG2hzurRKV9lmt7ISNkkjo6HhNyjoK+2gXRsKE57ZIE2ot10Z1fz0Ue4ABVw3NMjnW14rInh8jTYywoTg3EOFpOM4mXNfH9PQUfGlrAwBOs3I8ljbtuMWhRWzIIPrkn+GcYcgIWEowbZ+0qB334/4IMADESjqbnHbH0gAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-doc { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAppJREFUeNpsU79PFEEU/mZ39vZu77g7DokcP04BBSUmiEKCSCxs7Ei00JAYO2NlTKyMrX+CJhaGwopSQ0dMtFEsbDRBgiZEQIF4IHcg+2t2Z8eZ5QDlnM1mZ9+8973vfe8NEUJArfSNhzPG0VIfeIiDRSDkw1cWVt3N8rhG6SdSO2Gvn8dfuueqZwuNZqk3Jxg7iNcIfBbgXD6ZC8u5qffzX8eoYeyDxC77uygKhcouovgVUQj1H4YB2ovNuD9+tTTU0zMVBmG/+C8AIYh8F361DL/yE5HnADKYlVdg6MDAmW7cuz5WGuw+PsWDYGAvbL8ECFUt4K7/AHd/I9c7BLaxinD2Ld5Zo7g78RLuRhlBS2cpWbGfStfhfwCEpK0nUjCbWuGsLciSOELPhkq/YgdY3l6HsLfRcLYf+pHNbH0JigEPkLAyMsiEJ7NrqQzM1i7wyhoMZqOhvQs6Z0ovXgdAJACRoulEg5HOwrOroKk0zOY2BDtVpTF0CU6kLkQJXa+BNEoG0lMSsBBKQXWNQktmoGcaYeSaQCIVWOvUYQAiWZFQtk5mSMoSzEILtBrTfEcviC5bwVwQmoh96wA0ic5dB57ngeoaTIPCdb34zDITYNLOOIeVSsW+dQC+7+NSWx6jJ4tY/rWNV7PfcGv0tBoPTM7M4eKJVgx2FTE9u4QPS6x+kHzfw/mOAjarW2hJG3hy8zIceweuY+PRtREMdzbjzcd5WBqPB6xeRGUMGRzHjWvMmxQ7tiOF1JBN6FiTd6Sy9RuFbHpX7MMMqOD088Ii+op5OUAO7jyeRGfBwrF8Cg8mXuDL4neMXzgFwhwZz+hf7a9d5yu3Z6DTPjVQIY9k7erO7Y63Lvc8ErEeyq6JaM6efjai4v4IMABI0DEPqPKkigAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-dotx { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAndJREFUeNpsU01rE1EUPTPzJk0y+WhMStW2qdVWxUVEQUF0I+4ELQiC7lz4N9z0T+hG9wrdZKUgLqulhrbSag1CKpT0g7RpYjqZmffle5NEKdMHlzfvvXvPPffcO4aUEno9f3Vt4dTp+BXOe+fB0u/NbVpv7h89NU1j1TCM8H7+xY9wJwPHZMbOjRadLAvE/2gToJTiTPx89k+OlVd/LT+0TPIPpO/SzyQk40xCMxBSZ9Z3CoAx5DOjeHT7SbE0XSpzwa8OWB9jINELolQg8AR0EgUKn1PIlIWpkUt4cPNxkTOU12trs8p95RiAXpqaztqou8q6SKQJJmZSqGwsodFsIJk1kcyLYv7IeafcLx4HUNkFF4jFTExMZ0B9DrfD4HUEusYhWs4GPEJg5wly/tBYRIOeDhpEwlS34xcyajdQr3UwOT2MlJOEBRuGNHWp9AQRVXDfQiFV/U5GBSiQ5p6ngBEa5z3fiIhC6g6IMDBwOdoHPkYnHPVyhN0tF7E4QSpr94CEOKELffq+y9Bq+DCJ7rWBoQQBVbPR2O6G4OlsLASJMtCZfQqm0NP5IVWnamdAkUxbyuIYtD7wWegb0YAzAVMkkI6NwPM9xEwHloyDGAmk7AKS9rAS0FKOdugbYeAHPu7OPEM+MY7q3hIKqTFQHmC3XcONc/fxdfMDrk/ew/edzyhvvTmBAddocVRqH3Frahau56qpZDho7+PnTgXffi/gbHYmLEvPSIQBp5JU62sYz13G609zKBXvoOMdYn2zgm7Xg2MVML/4Eu3uPgxhk2gXmNl8v/i2pcXTP8tKdTEcbWLZqDQXwu/l6pfwbEnSGsT9FWAA4mdHv2/9YJ4AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-dwg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpsU0tPE2EUPfOg006hD4rQh8WgbCSwkKgbF2owujaCiQsXxpX+D6MmbtXEsHCLmIAbE6NLo8YlGIxREIshIqVl+mQ6j8/zFVCb4UtuZua795577rl3FCEE5Bl79vPd5LHYiOP7cH1AUWi85ytmvlas1bJ9E5ryBntH3BpuP/X9i7ovkluuiE8N9SDepaLpCcRCCqa/VDCaMuIjSWP25Upl6n+QDoCz6Yh7KKzh3sI2LuUimPtRRyaqodj0MDloYiITSTi+mH29Wu0AUf9CsZPJoW5czJl48LmCc5kIKo5Al67B9gUGYxrun+5NnMlFZ+GKiQADj2a7AquseLIvjMv5KMaSBu4sWVir+3i8VIVKYSby0UTdFU8Znu8AYBHQgVOJEN5uOXi4UsdawwU0FSf6TaSoyw6DRvukPkgGWpDKy4F8a3jImCrqFDFn6rhKPR4VGnhvOTAY3WLcjifcQAsqRfhUc/Gq1MKNbBh9nIAMDjEppocxs9HCMktfGTCwP/oOBkUKNk/qF3pDYC6Ktk8RfWzyaaoKrqdDaBDwya8W1m0/CPCR3kFy7CcnmWQRUJqcRJFUKtTnPCeR71LwoeYF92CYyVnCFZpCTrRtCv5to2St8SOrKxiPqEEA4fkYT+mI0rdoeUiH1XZVuQPpsIKqw2QmfifTsnOABiWySlH9uU0Hh2MqjsZV5LtpPSoGeN9rKnhBX7ehoOSLIIPfnGONXGMMWN7xUfVldYDbjM3mrh5HCDgS17DhHgDQcIU+XbBxnDTn1x1UuQcJ9iv7l5Q5e1zLGri92EDJFnoAgHtcfr6wbbVXUqq193+0z97n3UJt1+d51n7aHwEGAAHXJoAuZNlzAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-dxf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo5JREFUeNpsU0trE1EYPfNMmtdoH2kDNmJbaVFcaBVFpAsREQpFwY0bu3HjQnTj1mVd+ANcuC3qQixmry6E0kWFVIQ+bKy2tbFJm3emyXTujGca+4DkwsedfLnn3POd77uS67rw1vC79ek7fZEzpu3AYUqS9tKQGZPLpa3VXP0uFCmJ/8t9OLC3q/uJbcs5bkIybvdHoMsSbLKENRmvU2WcNnTjRFD7ML1WGSPJHI6sA4KRWMAWVDPxLYex3iCmfpuIh1QsFSyMxQO4GvXHHwOJ6XWSyIck8v6HQsnjAxFc7vTj2VwBg4aG78VdBHQFCk+dbVcxMdwev9gTSEC455sIBOu2KLsoJFzqasP9vjCeDBlYqzn4VXXwarGKZN7Crd5QfLDT/7KpBM84c9fFUFjFp2wdk6smflRsKKqMa7EgfJJ3Ac2OKlit2pEmBTQfngdpnupoU7BUtRGiiTe7fXiRqmK+KuDn6TpvYogmBRJcrOwIJLIWxmM+dOsyLKryQAaJpjJ1/AxrGO3SqdZt7kKZJrzJWBg5piHENuY8vV6e0UOye1TyftvC5l+gZB8SHJTwpSx4q4JeTUKaxhXoR57h7Rn+3iFolJ3xvPhab6HgJG/pJ7jsNP4sUX+jZiCgEsWd/DjH5IrSYpBUAr0yHpzSoXKOP25a6OBhndh0zcX1qIYM2RIbu6i0KiHD5B/GTMHG03kTGpEL7H80wHFOWwhqDZ+SpkBOtCDYJDhZE4gRcKNbYynAqbCMbXpwpVPFbEng0aKJGbYzK1p4wIegLlcEPmdt+DjXbzcsxFlCynRwwVAwW6hjqeg0Zt521SYCWCJvbe0Un29UDx7Hgrs3IEitHXkw3jOv2fl92D8BBgAJeyqBh90ENQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-eps { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNp0U01vElEUPfMFCEVArdoSqEA0KV246UJdUJM2Lo2JK/9FjXu3utJqTNz4D9worrsQExbFpAFT0TYp0CZ8pIAiyMfMvBnvm2Foa9uX3Lw7c98979x77hNM0wRf7ufPsq7Z2SQYw2QJAkDxQalUZa3WI8hy3gmZr15bu+z8kILBkCeRCJi6bufKMji0NhwiCQR6iitdatTvQ5LyOLLEiWcYukm3m4Zhmbq1BX13FyoxuH7xAlbvpqKRK1fT0PWbRwEmDEyiy1QVg/V1GO02tO1tKLEY2PIy3KEAlmJRDLXb0TeZL+n9g4MHlLJ5HIBuYnSzXq+DlcsQLk/D9Hoh1WrIUjlPcpsYGQzS3LWoaBhvKeXWMQCDA1D9pt8PaXERUjwOjEZQFhZQp9L2yERiqYRCkPt/z58ogTGqHQLE1BLgUmC6XGD5AlipBIFKkbhanKHGYLBDqQ4ZED0OAbfLlo8OIxwGvhVgyTHlA3xkomjH/gegBgDURMv6faDbBZpN+/tHkUApkdTA/PwZAPxntwdUyjYA/+ZMqJHjLgM9iv/6zRt2GgMaIE21aVIjnSm0DGPfmhzyde0UAE2Dj+p7urKCPvkZku9eJILOSMUnkvVhIo7GYIB3xSKYdhoA1erXGVKXpvFxZwdBonnD68PQ7YEwM4O4xwMPxc8RYE87g4FIcz+kvfmnA0YzIJIy77/m0OCqsTkkCTysKPjJG3viLei63Gm3kCO6UWqcMejjxecMPmxsoFKtYop6UNirYL9Wtc5OHqzznIXHq1na7OfMJROcK8a6O7MjW7nfzZdrd7jzT4ABACh3NGsh3GcdAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-exe { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNp0k8tPE1EUxr+ZzvRJO62lUAQaKIQ0FVJFjBBdoIkrDDHuXJi4NnHtX+HCjW408Q/QmHTRaCRRohIJifgiiBICTQu29mHfnc7MHc+MlECKdxZz595zf+c737nD6boOYzxJLC6Nhwej7e/24HkO779s7G6mMjcEwfKZ21+/d+em+RbagaFev28qEpZwzKg3ZckqCPH1nfS8hScIdyhBe6JqTG3PfyTTeLrwFhvbKdy9/xi5QglXL0yGJsKDccZY7LDIAwWHpSferWBh+RN8ni4UylVER8MY6PHj0uSpUK0hxzfTmWsUtnoEwO3rer64jEyxim6/Hy67DXaHExvJX3jw7CX8XjfORUdDlOohhU4fAVjILCPbm9V1yIqK2FgYt+ZmsZcv4lH8Nb5upXD7+hVMjIRQa8qeDg8UTYPU5cTcxSk4nS709XTD53ZhpD+IYMAPj+TBz93fZiz5oHV4AP1fGdlyHZIkIZkrI7GyhnK9CZXy+Aig6p1+HQAY003AcF8AVtGGfLWG9XTO4MLZ5cL0WAixoT4zVmPHADSiMo3hzHA/xgeDWFjbNg8H3A7kKnX0koEcPdTu/ylgRGZgOjNv38zoSXC8BZJDRKOlwGEV0VJVGM0y4joAPO1spXbx6sNHeD1uRIYGUCxVSRlDt1fC8rfvcDnsmJ+dOaLgoAs6AVLZPJJ7WdhEkUyT8GJpBflSBcVKDTvpDBw2GzQqQT1OgaZqUOhtFQUTUKnVTVWNpgy51YLVKph7sqKYkA4A1ScEfT66vm5kC3+ofh6Xz59FQ5bpkvE4QW3M5Apoyorhl9ABIKnFgNdTOh2NkJG6WSf9eRBJtmFwLDJmriUzeaOkYvvcXwEGAIVNH6cDA1DkAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-flv { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsUl1PE0EUPbssLYUCXdpaC9gWoSTgAyFigiRGY+KjvuuTr/4A44MP/gx/gMYfwIsan0RjIjGiJIZgSIGFIoXSD0t3Z3dnd70zpITazuZmJzP3nnvumaMEQQCx3jx69SV3a3KWMxetpSgKxP3m242Do43SQy2k/YRydvds67n8a63k+FRSn7l/bdg5tdsAuM3he/5weDC8vLdqPLgIIpba2niux52mg//DqlsYSg3iztO7mczN3DJ3+ByCLgCBH4hOFEF7cDpzPCRyOpaeLGXSc2PL3HbnW3XaRQCPEgWI2MsRVAVqrwbX9bHxbhOKpiJ/bzpDOr2k68V2BtRNzMtqDEqPejY/4zSGjb54BM0mQ8k4xsDoIMauXxnqYOD7PmwScP31d0SS/eAuh1lrolFpIBQNQw2pqJdqsAlIceB1AJCIkkE/FZskXDQVRXw6IYHiE0nBEcaPXSSvJnGwWkQXAE4acAhbxPMJpOdHweoMhc9b2F8zwKizbdlyPLVH7QLg+JKBYzoorxzjz3oRzUoToaEw9KyO8XQW5AE5jrFT6AbAYVVNxCZ0Ka3So+DSTAoDiej5ywTySbls1OEDobhFlMcXxrHw+AbINEjNXgb7y6BndLhk8cRkHHbD7g4gEhiJFxsdhrDqaamBaDKKerGGSKwPI9kR9EZCaNA5ubE7A5s8IFhsrxQkgJhZoa/06xC5xRz2v+3BOjFlbqcGlquxsondT9vY+2pAJdeZR6fI355CgQCN2A4O1w7gkQ7cdLUOAKdhV6uFSv3kd/n8mT68eC8dKWLnY4FsfeZQh7nVVt0/AQYAsf5g+SvepeQAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-gif { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmVJREFUeNp0U0tPE1EU/trplAqlL0laiw40xASByEJIZFGVnSvj1j+gWxNXJq7VrbrwF7h10cSNhMRHojEuACVBKmH6SJQyJeXRxzzv9dyZPiCtN5lMe8853znf953xcc4hztDzZ1+C6fQMHAfd4/MBFG+p6h/n4OAeAoGNToi/eOm+A50LKRaLh6amoty2vVpZdotNXccMEK3LwZxa2bsDSdrAqePv/mLM5tSdMwYBYqyvw9zdhUn/L59P4OGtG8qlZCoH254/DdCdQBCxqZu+ugqnWoW9swN5ehp2NotgIo6bGQWGtaS8+vQ5V9a0u5S+1gfABEilAqdUgm98HDwUQkDT8JXoPPq+BoM5kCYmFT9jryn1+hkAt7heBx8dhbSwACmTAUwTgdlZ/CVKJaLnI1GD8TikZiPSR8Gxib8chH95mZTxgwWHwH7+gFMswqcokIRbjMO2HDCnZ1VvArpjEmnKZc8+cZJJYGsLsMiZ8AgwEqaY6Mb6RQR33JFhGECzCRyfAFXNu9v+RVNRZWIMuDJNuYMAaDycUFGhCOgtuAtFVDA83G5A8TrFDw+F5QMAxAKJJxz2xnW3RPJGbm+rCyjotZetH4DGzaSSeDA3h4Zl4R0JOEZWTpIzF4n/m995bNdqZwB6m0gFft3Ak6vz+KYWwFsGlqIxXItEcDt1ARMEtKdVgZb+fwA0G2C2hXM0ZTZNRcSf0b1pmXi7uYnjI+Lfanm5fRQsK8BIxKcrK7i/uIgP+Tw+FlREqHN5fx/vyU4uHBE6UO4gDWqk/JFaLuMxcXeFk6TuJ90V0HOk1in7J8AAjmgkPfjU+isAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-h { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbRJREFUeNqMUk1Lw0AQnf0woK0ttVqp0hwqVCl+UBERT94F7x78Cf4Uz179DT14F8WbYHtRkBYRLNqDtdaPZLObuLs1NGlXcWDJZGbey+x7QUEQgIqT07PL5WKhHL5H46J+22q22vsWpbWwdnR4oJ80LNiz2czGUjENhvj4ctIE4Wrj8XmPUlKL9nCYcOFzE9j1OKSTCdjdrtiLdr7KhVgzEvwW6krC92E6k4Kd9bJt57JV5vFK2KfRQRV+RAMkzxglYI1RaDy2dW1rpWRjQo5VGicYIorWVooFvQVCCAjG8Omw1MgG8AM0uSBUDSnCfk/IGCHwf3DCD/7UhOLBrFkDuep/hDUSSCv1iYo4rIfqGwmUSNJjfYbBcQKhZw0aBMA4B48LwBhBt/cON80HmM9NQ6fXg/Wlku4TwmNWDzaQqzHG+0PSKod5cH5Vh2RiAhYKc8DlV1UPSyuFMGygVlMg1/P6BC6DqXQK8jNZDXAYA1f21V34wMXYFaiyVw0rJyzLgs3VMkxOjGtix/V0XWChZ0cI2i/dzvXdfTd0Qf91BMPrhyNzgKfOmxaWypqaDXHfAgwAtCL8XOfF47gAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-hpp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNqEUk1v00AUHK/XKf1yZdESVRBXjRSRFqMQVBA5Ic5I3DnwE/gpnLnyG3LgXglx4UDDLZS0RWkDLiRxSusk9u6GXSembmLgWZbX7+2bnZl92mg0goo3b3ffO/ncdvyfjHef6q2Dlvs8Q2ktzr16+SL60jhhZ69bO8X8ClLC7w9XdKJVG8fuM0r1WrJG4gXjgqU1D0MGc2kBTytl+7a9XmWcl1IB/hZKEhccq5aJJ/e3bTu7Wg1CVo7rNLlRhUh4oMnXoDoyhoHGyWmUe+QUbELIa7W8CjAFlMzdzeckCwFN06ATAn8QmDMMMGlMuwWucpoCHNe4jBkAMenjYvRPTyi53JvuwX8AplleAeBcRFrH6rXIxLim9I/pi3QA1RhKaYxdjkN8IwalCMIwWs9ljMkh0wzk+9M7w179C3LZNXxve2h+c3Hu91HeKmD/6zHOLnw83ilB1/V0CeqU3Q81LC/O41b2Btx2N2JVP2riR8eTUxmi0TzBwrKZMsqMoz8MsDh/DWuWhUBKURLKxQIeOMWoptYPnS1c+INZBkwISomOSsmBZS7B+3WOzZvrKGzkMAiGqNy7g+LmRkRfekBnANy2163PZXrSbrQ6vch19Xz8fPDHyL39QzkHBKedXjfu+y3AAGU37INBJto1AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-html { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNqEUktPE1EU/mY605a+hhZTBNKRDApNrWIRA4nEBUZdmCgLNi4MK5f+FNdu3bFv1J1EXODCR1JJSMTwpqUP6NiCpe10Zjz3hj5Mm3iSybl37jnf+c53jmDbNpi9eb+6Ftcisea909bWNzNb6dwzSXKkhIt/r14+515qBqmDA8HpqKagh53XaopblpIbe+knDpFAhPab2Dw0TKvRK7lmNODzePBgZlK9oUWSpmVNdpIU8T+jaMsyMaD4MDcZVa+NhJMN00w0n6V2nN3yQgdHWZag+LzYPTomIAtT0THVtPGanmb/BbjwLFkvn2IttYGYplKyDzsHh7gdmyAWfh5zVq0Guhg4RAHFUhmfvq3j134aXo8bd+ITnMFOOovU5jbGRoZwNxFn1cxuAIcDW/sZDjA/c4u+BNxOJyxqaenpI3z88gMfPn9Hv98HQZS6RazW6kjExvFi8TGdDSy/W0Emf4LS6R8sv11BmfzSwkPcm74Jo9Ei0GZgmkw8QCOao8OXcaz/5vSZnPdnp3ApqBBLkWJE0Ci7ASzbIhCLLQ1E0iOkBDh9NpUgiUejo8oNuJwyn0YPABtn51UYFFivG3yBGCNZkuDtc/MW+ZQI3OrYpBaARCKufk3B5XIiWyhiL5ODp8+FfFHH+KiKSqWKUL8fC/NznGlPBmz+24dZjKnD0CJDcMoyW0SqXuMtHBFw7rhIAD1ErNUNafxKBNevapwu65NpEQ4FqXIA+RMd6VwBP3cPSERb6gLIFIq61+UqGWaFdcrVt/lmAuWjAi2aiMFwmOYuIJ/N6M28vwIMAMoNDyg4rcU9AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ics { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhRJREFUeNqEUkFPE0EU/mZ2dra7bLNpi2AxQFKalkJrohICiYkXPagXrx78Df4K48GDBzmQePLMhUODNxQ5ciEkJVqDtJGmMWrCATRbd2ecoS5u3aovmezsvu9973vfPiKlhI4XL7c2r5YL81LIELEghLA3u/udxmHnPmfGW/Wuv+LpwwdneRYBx7PeWK0wOYYhcXxyckGV1fdbnbuMsXcklqPRJQxFMKz4RxDCtVO4s3xlRjWoB0FYjlQPEEBieChwKCRGMx5uLtaKs1P5ei8IKlGa/YkXMXYtlTEDlsnw/mMXhBJcqxSK6vlcpa4PEpCooUyIqs5M6hG1o2CUwqA091cFcYLf/sjzcX75EiQIojI9779CTYR4jwTBf+r7GAwh0AxCiL6JMT/04vQ79u8aI2O/7Jzg69o6Go8ewycUahtBpADhHKLnK/eVbkMdtROWIv80NQ2sPhncA9Htwn+9hZG0rY6DzFwJl+7dhs0ZstUy8rduwPS/wd/ehmi3kwq4zTHiWUgXp+EuL8FvNvFl5Rn4xAS86iyI2kY3n0Mv48ByrOQmancdi8I0Kcj3U5iuA29xAelKCUHrEIayzltagG2E4IwkFaQgSC6lYI09iN0d8It5uNV5nG5sgJdKYC0G8WoTOZvBISFNEBxnsuzD3GX4vfDsszzqAu0jkJQDedCGbB6AWg54pYbPo+NGVPdTgAEAqQq70PytIL0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-iso { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNp0kstrU0EUxr/k5qbJzdPYpGkpsUJoA2q1oLjTdiGiIC5cuXHlxv9BEOrStTvBnQvRrSAIsejCrlqpsURq2hCJNQ+TNLm5uc/x3MmzJh34mDNnvvnNzOE4GGOwx8+t9XQkfn0VE0Y5/7Z+kHm+dvOhtd3P9c/xwNZh7nWaMYtNUmX/Fct/vlN7/8J5aRRgyzm8xzpRDjGE2aVH4VTqdnoUYg/XkEhmy+Cx3DhA5tMzdFolvg5Mx3Fx9SmH0JIg79Zo3j4GADMIokJTKtjbfAKXU4Y/2NvSfyH75TFOxa9Cmr0XnlPFl5ReOQ6wNMDsoFX6AElqQlNV1KsOuNwS/AGFjEUIDhmn5+/DMM16/9igBowAzFKIswPJr6MjlxFP3sV04gaP7RzMPe6xvWM1gNUBM2UKYlBau3QghGphg29J3gDlLLilWNdD3gkvIIDRhD9yGe2mCV0V4HFXuCxT5Dlv8Dz3sIkAs03FalDxBMQSt9BRBMhNncuO7dyU28c9tnf8C/Q0ZtR4GImeQSj8APLRH772BWcgiFODffCv/t8H9tO0v3RjV7VqkeeXLlzDfvYjj88uXhl4JwIsrYxmLY/M1gYclIvGE9jZfNPrSCD3/QgLyeWTADV6wW9AryIcCkB0u1Aq/oCPumlufoF72vIheaLDr4wCLIOqrYnULA14PSoqpSJEAUilZrD77Sv3LK+cI0+Be8cAbbmAOrob0agtD491LYfkoqvnyZLsWRkA/gkwABL4S3L78XYyAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-java { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjxJREFUeNp8U01v00AUnNiOEyepQyhQobRBSlVIoRCBEPTAjQsSEneE+An8FM5cuXLNoQduIAE3qopKNJAIIppA2jrOR93aa6/N8yZuUxyxkrXr3ffmzczbTQRBgHC83nj3ca28dD36nx6fvnzrNNrdp4oibyUmey9fPBezEgWVFuYLdyvlPGaMY4fl1aRS+9pqP5ElAkmcnknRwuO+Nyt5u/ETYfyj9WrpZnmpxn2/Ok1Swn/GvtnH5k4TLue4kNfxoFoprRQv1TzOb8cAIu3+ZD7oD/Hm7XuxzqRUNDtdkuLiTmW5tFxceBXlnXgQTAORSMt2oGezUJJJrK9dFWdEH7Ik4dB29LiESeUEJXd7/dAT3L+1ivlCHr8NEzutXTBvbJPPSdO/AH5wysChwM/1HzCGlmAzOrKxu2eCud6Z2Jke2MwThpUXL6Nn2ZAVFTlNw70bK0iRnGAq9qwHtOmTRpsx1NsHyKRVnNPnoMoK9kc2BjbD4vk5JGV5NkBoEPM4FFnCteJFWOS4ntHEfphQyKaFTWFLw704AJ26ZFx/ZEEi3YyY0O1Dmr4EKTUHA8hUnS6siI0DEHLYog+b28RCRuNXR/iQUpPUEQ+NVht6Lodnjx+GXYgDSFRnq97Ed2pXSlXhUSeGhxYc5sKlNXM5DGLR2TMwfZVPAIi+otGNWy1fEZUKeo4qc4ysI+F8VksLIJfYcD9QYgB/DNPMptWBlsnBIS86xmDMTBo/PWd0LB6VZfdEbJT3V4ABAA5HIzlv9dtdAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-jpeg, +.ipfs-jpg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8luUlEY/s4dmMpkWxRopGJNNbiwhk1tItbGtXHr0hcwmvgOdWld6Bu4coXumtREE3ZKu8FgOlC1kIoXtC3jPfdc/8PUIpzkBM7wf+f/hsts24YczuerGUc0moBlYTAYA+i8sbdXtAzjITRtq39kr73s/Gr9DTUYPOeamwvYnHdrdR0SnDebuCbswJGqpX+Uf92Hqm7hzFAG/4TgNr1uCwEJ0trcBC8U0Kb1/PQkHt9JxSLnL6TB+Y2zAIMOJBGLXmtsbEAYBsx8HnqCGKVScAX8uHf5EpqmGXv18VO6VDEe0PXsKABN8+AAgiabmYFNNJTDQ2RUFc8+Z9G0OPR4PKYwvKari0MAgiY/OQGCAajhMNR4nDZMaInrKBGl70SPMScck1NQG3X/CAWLE3/dAWV5hRRVIJxOWNksrP19sFgMqqAebUGYHMI6teq0A9oTVAhqu2sfbYYjsL7lCZ3683gA70T3TK7/B4BNoO020GwB9TpwfAz8LgMtWn/NkV8EHgoB81c7nYwCyBZlEVkHcqMTKFnkmehJTOPvEfCnKi0fAyADJKfXC/h83TaZTJjaa5lANLpOFqAXtlEAorAwO9u5syT5UxLfU0e3o1FMu1x4u7ODYq02BKAMAVSrSNLrK1MhLPj8mNF0vFm+C1ZvwKBwXXE4AGn1WAASazESwUW3BzUSMeJ2o1Aq4sPurvQYSRLwlhRR6mSaYyi0WlpAJrFRx3ouh5/lMt5lv8BLwXp0M4lSpYL17e2uK5wP6lj/c2ZPn2RI+YT8fDvqoyegVLyfG5kBKaQQOfvF2pLc+ifAABiQH3PEc1i/AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-js { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUQ5ODY5Q0NGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUQ5ODY5Q0RGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFRDk4NjlDQUYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFRDk4NjlDQkYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoT8zQ8AAAJdSURBVHjadFNbTxNREP52t7S0bktbKFAvTUVaw60YqkExUTD6oD74qC/yD/wp/gh885XEEI0RAyYQUiMpIBGMkYR6o23abi+73e2uc04v1LROMtnZPTPffvPNHMGyLDB7sbJ2ciUSli3U35smkK9t7x9v7n2dD/g8KUkUwWqeP3vKz23NxJGzgwOx0RC6mSgIo+WKuvP56MeUzy2nJEk8PWsGJVVTuhWbpgmHw47FB7d98Wg4mVWK52o1sxOg3Va3PmFp+Q2PdUquaFUM9/vw+O6cP3bxwm46Xwh1ALR3/vL1e+hGjcc9koScUsTSq3coVDQsXJ3wzo5HEs3clgZNMTVdx1T0Ep7cn6//QRQwMhzA6uZHLD5cIFEFSKIU+G8LK+tb0KsGZKcTJoEyP08AbpcLy6sbPKdQrigdAGaDwWxsDH1uGbliCYIgcM8WFPg8Mq5Pjzdyu4jYbCE44EepXMHuwXe+A8x3KKYxYsjvbUzmlPGpBmYdgI1oYjSMbL4Ao1YXMkcM2Dd2xnbAamPQAqg1GORLZdycmYTdJqFKk2DPR3fmwI4zBDrg9RADqxPAbPBif2WTSB584/3/TGegEOit+DRcvQ4OZJi1LgwIQKVCg2i6nb1I7H3Br3QWqT9pBAP9uDY5xjdSM3RqxeoUkfVnEOW8UkLykERTNXjkM7h3Iw6NNvHw6JjuhAhVrba0+QeALozcI9nQR0VvNxJc/ZmxCNGvIBQcpDG6udA22kyW29HC72wu8yG579ZoiSYuR/ly2+y9CA4NceWLmo717T1i5ULqJNtapL8CDACskxPFZRxLwQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-key { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNpsU11PE0EUPbM7u/2AtJUWU6qiiSYYo5EmmPDCD9AH46sx8cEnja/+CB989z+Y+MKPgMiDsYQACcbaWBBogYD92t2Zud7ZlQZsbzKZ3bl3zj3n3IwgItjYeDO3MlWme0bjUth8e8/fO2tHzx3XqUEk50uft+Ndnhdmc3SlfNPkVZT8Cy600DoIISvVfKYtlvfX1p66XmoIYsMZdjJQWvEFbbsC/S5g2QhSkKUK7rx6OzvzqLpsovAhaAxA3DUBQn2TUFsl7KwTfm4Z9DoO5LW7uPXi9Wxpfn7ZKF09vyPxX2iWcNRkKGZz0mQWKoNs8AVB6x1yRY2pYnc2LLofuXTxMgAlmlXIfngCxNxEzM+DPv6NQa2BygLgZyX6JT83ngHTN5GAL0WSoUQkSQnXkyBh/k0GegTAaldM20sTKvet+yyhIZApECamL0jUSe3oFChx3TopM4TeEQP2gc6BgGIwb4KGNXRhCkMGxgg2kJeybRiZM45D8W61qEAknSmpHStBhywu0nFVupSCTAcM4ECwqapv+NQ6LS9JGALoMIIoPYDjZiEL1xHtbyO39AQUDaA7R1AH23DSeSA4hv5RG/VAhxomPYP8sw9A4TaC9iHkjUWmrtGvbyC18BLe3GP0m3WW4I5hEBEnPIStXzyuFIxb4EkMEJ79Qa/xHbKxCdM7xeCwzUZOjgEwnuzt7qLz6T3cySmQP43uzjeIiTJM6io6W19B/NLCKMVGCzkCoLR/0lrfOI2fNy/huKC1FTsK/rbGNeMRC8dHpHByfu+vAAMAL/0jvAVZQl0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-less { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjZERjZENTJGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjZERjZENTNGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGNkRGNkQ1MEYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNkRGNkQ1MUYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pl1w97IAAAJhSURBVHjahJNLbxJRFMf/wPAIMIxMkUI7tS0VYqlGDLGhjdKkqyZ24cJFN925de+XcONHaHRj4k7TND6SGo1VWwmp2kSLhlqMDbQ87gzPYcY7k4GgoJ6bmdw598zvnvM/95pUVYVma+svcovx8yMnFZHAMJPJBJfDzq5vpX6+/vD5qo/z7DOMBdo/d26t6jFMJ3iY51jBz4M+LP6wxEw40Gy23qYzB3HO7fpmpZCOmfEfa7Xb4NxOrC4lvbPToe2yKE3K1PdPwNOtHdx79ESfq4qKkijB5/XgevIyHxEC24USmewDqD2ABxubaLRkfW6zMqjWGlh7/ByyAtxYnOPnL0Q2+gGGmKRaw8zUBJaTiS5QOO1FJnuIAM8hciaIWHgi8NcSNt+loVDY8JBXh2ojJAR1HbTSNFMUpV8Dxcjg0nSYBrtBxdLbqI1iheCUh9XXNGurAwCdEkb9QyBSFam9TDfoPZ1LUg1BH28IiwEARTVAQOzcFKRaHZpLoa9avY6L1Gfs0c32t4PU6W2lWsV8LAorw0Cs1nXftYWE3qZGqwWHzYp2zzlgetuolVFvtiDLbRRKFTAWCxx2G/KlMtXFhWPqOzsWHJwBx7rxKv2R7mwFz3lw9/5DLC/M4Us2RwV0g3U58XJnF7dvrsBOoX0Abbej/DFKRMKI30fTVGC32WA2m5H9cQQvhYi0vE/7Wdgczn6ARA9QPBrBszcp/XvpyqxebzQ0Tlsq6llxLhe9bD4cFMr9XdjLHpLv+SLGBYHAYiVu1kNOpAaRTWbCejgiw0zGhFGSK1aw+zXbvfK/BBgAPwADAs5GpGsAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-logo { + background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 553 235.3'%3E%3Cdefs%3E%3C/defs%3E%3Cpath fill='%23ffffff' d='M239 63h17.8v105H239V63zm35.6 0h36.3c7.9 0 14.5.9 19.6 2.6s9.2 4.1 12.1 7.1a24.45 24.45 0 0 1 6.2 10.2 40.75 40.75 0 0 1 1.8 12.1 45.69 45.69 0 0 1-1.8 12.9 26.58 26.58 0 0 1-6.2 10.8 30.59 30.59 0 0 1-12.1 7.3c-5.1 1.8-11.5 2.7-19.3 2.7h-19.1V168h-17.5V63zm36.2 51a38.37 38.37 0 0 0 11.1-1.3 16.3 16.3 0 0 0 6.8-3.7 13.34 13.34 0 0 0 3.5-5.8 29.75 29.75 0 0 0 1-7.6 25.68 25.68 0 0 0-1-7.7 12 12 0 0 0-3.6-5.5 17.15 17.15 0 0 0-6.9-3.4 41.58 41.58 0 0 0-10.9-1.2h-18.5V114h18.5zm119.9-51v15.3h-49.2V108h46.3v15.4h-46.3V168h-17.8V63h67zm26.2 72.9c.8 6.9 3.3 11.9 7.4 15s10.4 4.7 18.6 4.7a32.61 32.61 0 0 0 10.1-1.3 20.52 20.52 0 0 0 6.6-3.5 12 12 0 0 0 3.5-5.2 19.08 19.08 0 0 0 1-6.4 16.14 16.14 0 0 0-.7-4.9 12.87 12.87 0 0 0-2.6-4.5 16.59 16.59 0 0 0-5.1-3.6 35 35 0 0 0-8.2-2.4l-13.4-2.5a89.76 89.76 0 0 1-14.1-3.7 33.51 33.51 0 0 1-10.4-5.8 22.28 22.28 0 0 1-6.3-8.8 34.1 34.1 0 0 1-2.1-12.7 26 26 0 0 1 11.3-22.4 36.35 36.35 0 0 1 12.6-5.6 65.89 65.89 0 0 1 15.8-1.8c7.2 0 13.3.8 18.2 2.5a34.46 34.46 0 0 1 11.9 6.5 28.21 28.21 0 0 1 6.9 9.3 42.1 42.1 0 0 1 3.2 11l-16.8 2.6c-1.4-5.9-3.7-10.2-7.1-13.1s-8.7-4.3-16.1-4.3a43.9 43.9 0 0 0-10.5 1.1 19.47 19.47 0 0 0-6.8 3.1 11.63 11.63 0 0 0-3.7 4.6 14.08 14.08 0 0 0-1.1 5.4c0 4.6 1.2 8 3.7 10.3s6.9 4 13.2 5.3l14.5 2.8c11.1 2.1 19.2 5.6 24.4 10.5s7.8 12.1 7.8 21.4a31.37 31.37 0 0 1-2.4 12.3 25.27 25.27 0 0 1-7.4 9.8 36.58 36.58 0 0 1-12.4 6.6 56 56 0 0 1-17.3 2.4c-13.4 0-24-2.8-31.6-8.5s-11.9-14.4-12.6-26.2h18z'/%3E%3Cpath fill='%23469ea2' d='M30.3 164l84 48.5 84-48.5V67l-84-48.5-84 48.5v97z'/%3E%3Cpath fill='%236acad1' d='M105.7 30.1l-61 35.2a18.19 18.19 0 0 1 0 3.3l60.9 35.2a14.55 14.55 0 0 1 17.3 0l60.9-35.2a18.19 18.19 0 0 1 0-3.3L123 30.1a14.55 14.55 0 0 1-17.3 0zm84 48.2l-61 35.6a14.73 14.73 0 0 1-8.6 15l.1 70a15.57 15.57 0 0 1 2.8 1.6l60.9-35.2a14.73 14.73 0 0 1 8.6-15V79.9a20 20 0 0 1-2.8-1.6zm-150.8.4a15.57 15.57 0 0 1-2.8 1.6v70.4a14.38 14.38 0 0 1 8.6 15l60.9 35.2a15.57 15.57 0 0 1 2.8-1.6v-70.4a14.38 14.38 0 0 1-8.6-15L38.9 78.7z'/%3E%3Cpath fill='%23469ea2' d='M114.3 29l75.1 43.4v86.7l-75.1 43.4-75.1-43.4V72.3L114.3 29m0-10.3l-84 48.5v97l84 48.5 84-48.5v-97l-84-48.5z'/%3E%3Cpath fill='%23469ea2' d='M114.9 132h-1.2A15.66 15.66 0 0 1 98 116.3v-1.2a15.66 15.66 0 0 1 15.7-15.7h1.2a15.66 15.66 0 0 1 15.7 15.7v1.2a15.66 15.66 0 0 1-15.7 15.7zm0 64.5h-1.2a15.65 15.65 0 0 0-13.7 8l14.3 8.2 14.3-8.2a15.65 15.65 0 0 0-13.7-8zm83.5-48.5h-.6a15.66 15.66 0 0 0-15.7 15.7v1.2a15.13 15.13 0 0 0 2 7.6l14.3-8.3V148zm-14.3-89a15.4 15.4 0 0 0-2 7.6v1.2a15.66 15.66 0 0 0 15.7 15.7h.6V67.2L184.1 59zm-69.8-40.3L100 26.9a15.73 15.73 0 0 0 13.7 8.1h1.2a15.65 15.65 0 0 0 13.7-8l-14.3-8.3zM44.6 58.9l-14.3 8.3v16.3h.6a15.66 15.66 0 0 0 15.7-15.7v-1.2a16.63 16.63 0 0 0-2-7.7zM30.9 148h-.6v16.2l14.3 8.3a15.4 15.4 0 0 0 2-7.6v-1.2A15.66 15.66 0 0 0 30.9 148z'/%3E%3Cpath fill='%23083b54' fill-opacity='0.15' d='M114.3 213.2v-97.1l-84-48.5v97.1z'/%3E%3Cpath fill='%23083b54' fill-opacity='0.05' d='M198.4 163.8v-97l-84 48.5v97.1z'/%3E%3C/svg%3E%0A"); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mid { + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mkv { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M7.5 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H7.5zm23.5 0V71.2h4V80l8.2-8.8h5.4L41.1 79l8 12.1h-5.2l-5.5-9.3-3.4 3.3v6h-4zm25.2 0L49 71.3h4.4L58.5 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mov { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M6.1 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H6.1zm22.6-9.8c0-2 .3-3.7.9-5.1.5-1 1.1-1.9 1.9-2.7.8-.8 1.7-1.4 2.6-1.8 1.2-.5 2.7-.8 4.3-.8 3 0 5.3.9 7.1 2.7 1.8 1.8 2.7 4.3 2.7 7.6 0 3.2-.9 5.7-2.6 7.5-1.8 1.8-4.1 2.7-7.1 2.7s-5.4-.9-7.1-2.7c-1.8-1.8-2.7-4.3-2.7-7.4zm4.1-.2c0 2.2.5 4 1.6 5.1 1 1.2 2.4 1.7 4 1.7s2.9-.6 4-1.7c1-1.2 1.6-2.9 1.6-5.2 0-2.3-.5-4-1.5-5.1-1-1.1-2.3-1.7-4-1.7s-3 .6-4 1.7c-1.1 1.2-1.7 3-1.7 5.2zm23.6 10l-7.2-19.8h4.4L58.7 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mp3 { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra7XWxpSsFYIbVQf9REFBHkYBRIPJh4wrN3DsZ4MPGP8b/wUCIHEw5EY0w04o9ILcREGmwVgaXbbXdnd2bXNxPahGyczebtzrz3ve99740WRRHkWn5cebu4cH6SMY7e0jRAHr9c3WxsVvcemmbys9yT6+uHJ8oaPefypdPDD5Ymh5w26wMkEho8JtDtuEOZFCrvN/4uJZNGH0T59D58X/C27aFNAL3Xthmsww5GCyN4+uzu+OLtQsUPxPQx6ZMAoQjBAw7O+bEVCMMQgqygs+LFs1h+dGd8bna0QmXO9OL6JYgwAvOFZKKoy3V44CgNfv7Yx8oLH+lUEgvzF8Ydhz+n41snAGRG5gUEwClzhHdvttFxfNyYK0EnJozKK5eGcf1qHo1GOxtjwI+pfvm4g/W1qtJgerYE2SXJSIL9+W0jk0mCShAxDXgQKgbNXxZq35vQKCiKQkSUXdc1+gcch1FHGPmKuIgBCdc66qJQHMG9+1NIpUylxxHtuW6gEiTIu+N4yjdWgty0yTmdNjFzcwKjY0MU7MLt+IjoSad16FoIx3b/A0DZ7FYXnsdpAjUMDOjI5zPgfoBsRodhhGhZHfBBU/nGAGRtxWIOg5lT2NtrI5dL0SB5KJzLodloqXaOEatPGztKq5gG3S5DNjuAK5NjKJfPYKI0okBkSdemCiSgS/rkQNLSePtxBj4LSCwfFtE0krqqX7ZVMnu9XlMXy2l7ME0dzA3iANQyY6vWxC61UY41zTyNcYh6/QCNXQvzi5dR39nHVq1BUyuMGAARsF6tbbe4iKD1r7Om5iFBdmW1SsDflLiuB6sX90+AAQDHAW7dW0YnzgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mp4 { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnBJREFUeNpsk99r01AUx79psrTrujVtbceabnZs4DYRHSoMh6Dgq77rn+AfoA/+If4Bok+C0CfxVRDBh+I2NqZzrpS1DVvbtU3SJPcm8SSlsJlecsn9dT73nO85V/B9H0H78OLdt/LDlQ1uMYybIAgI9n99OWxoe83nkiz9hDDae330JvxL48O51Xxm/enNtKPbVwAh0Ec6kYpXat9Pnl2GBC02HrjM5Y7h4P8+7FtIFVJ49OrxUnl7ucIdfhv+BIDv+fBcj7p/tXMPrs2RXVTw4OX2UnFTrXCbbY7tpMsA13FDSDAOQ4gJEGUJLs0PPh9CkESsPrmxxEz2lra3rnpAt3G6adgdQhBpmeLkFodNmsjpOPoXBrQTDcmFFNS7i3MRDzzPCw/vva8ikU+COQxm14BBhvJcHLGpGPTOAJxxeLbrRgAkYujBdH4G5oWJWXUW19YL4XqunAMFhnq1BqWYgaY1MAHASQOiU96zKzkU76mwehaOvx6h9uMv7KFN3RopL4oTAI4HRh4wSl399xla+00YbR3yrIzM9SzSqgJJnoKcklGrH08CcJjnBtLLCsSEGGpSWJvHtDKNoFippsJ0ulIsDDUCCATMlBQkNuahEyiZTcLsmFBKaQxaOk53TlHeKkM70AjAooCghBOk9sKtIvqtPqS4FBaRnJSRX8tj2DOh3lFB5Qw2ZNFK5LRo6w4sKt2ggAzywidAMN/9uIPSZglBLDO5FF3mRD3wHE9qVRvoHrUpfn+UEQK0/7ShtwboHJ6jdH8RZxSC57hSVETb7e5/2u0FxqPHJow+8iZ4lYY2QGu3idhIxO7Y7p8AAwALCGZKEPBGCgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-mpg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNpsU0tPE1EU/ubRdlqmnUBboa0UeUQDiUGCC1+JmrhxoXt/gBvXJi74If4AV0Y3sNKF0YUaICqoIfjgVShEiGF4tDOdO/fOeOaSKtie5GZu7pzz3e/c7ztKGIaI4vn9p+/P3h4e4a6Pv6EoQBDiy7P5rc1P1Xt6XP8M5ejXo6UJ+dWbuemeTGdpvNdiNe9YvQLe4Bi4PmTpRmyq8m71rp74BxKF2twIHvAo+f/l1T2Yp0zceHizfOZa/xRnfBRhG4CQqAYioBWeXDyA8Di6ei1ceXC1XBwrTXHPH2vW6ccBBBMI6BsSUEQzakGL6xB0tvjyBxRNxdCtc2Xf8R9TyaWTDOg2TjfVdw6hqIoE9B2GxkEDWlLH7s4ette2kSp0oDRezrQwCIIA3oGHr0/mKMmE53qo23W4+w5S+Q5ohob9X3tgHgO8ULQACC7gMx9mKQP30EW6mEHpYi8xcJEdzMucjfkKcrTfmqmiFYBxCF/Id+gayKJwoQjHdrA5v4HK7Cq44KjZNWpagaqp7QACks0H9znW365ia24DzoEDozOJbH8eVtGShXHTwNracnsG7q6LzsEuaAlNPm9h7DSSVjLyCMkppDI+GS2StQWA1RlKo0X56n2X+6QHkmkDakxF9WMVqWyK+s/BrthYfvWz1Ug+zUDcjMPMm0h3pxEjFma3CbIuCud7oMc0LL1ZgmElpGJtW3B+15HIGNITrMYIlOH7i0U41NrInREylYbu4R5qQbQBaAh95fVKZCnpQCnb9DrWZyrRERS6NDeUw+yHaXh7rt4C4B8y+9vkwn7kwKNRpDoa9aiFKBYnF+RcREqQ2e1m3R8BBgAy9kz9ysCE6QAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-odf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAi5JREFUeNp0UktrU0EU/mbu3FfE1KRRUpWYheALNBURUVy7cy9UkO6KW/+Lbt0IPsFui4gLBbUqFaUuXETUKCYa0jS5yZ2ZO557b5MmTXpgmDPnfOc7jznMGINYPi0de5UvmpORxpjE/kbNqW005DVu8TWw1H758ZfkFgNgJmtyxSPRjJIj0QTW/RDiYGXGb7Dl32/eXrVsd0gSCx9miqC0ooCdp69g5Q/h6OLN0ty5ynIkwzMwUwh2FwMdcbDiCZQXlkqFCpEoPT/wih1YjLInANcD+/Ua9bu3wJlGvrBZCmet2+S6ME5g4oGlZ9A/I70XCDhhDexPNTFmswJBwcnuXkF86VSNZxVu0ukLSGnBcqlnN4HoCQIaIuIv7LUooMOgQ7q75LAAb59B9gCBHSKgqemRr94mMKmD24CfM8nb7THYGQNLpAkUkcb66JyGBFFEWRVL57gFEH5qj8Lxwca2qS3EZaugmzAw24dR/XQgwtsCSBjPIdWbUoE2UJLBnV8Ac/ciWHsK9/glWLnD6K2vgPszsOdOQdfeQ1c/ThKoTgDn9A3KUED/52d45xchZsvorD6Bf/Z60riV3Q9Z/0bbGU1uopYGkfERSQ3VbsMwl0qlqoIARmSoPYXWy0dor79LfBMEEd8jGs/uQ3Yl7PJFNFbuEXiV2riCf88fovXhBbo/vqP3t02/ZYmJFqTkzY160Go9uEMbFK8hR/NrdXtFuUVmnmySVGgO4v4LMAAjRgmO+SJJiQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ods { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAetJREFUeNqMUj1IHEEU/i7u7Z23e8tGgneGQPw3hZDkkhQiSuwMQREba4uUgpVlCrvEQhurkCoWqcQQ0oTAaYKNqJygGEwgHCSB6Knn7eXcdX/GmdHVPWYFP3gw78173/vmvYkQQsAwNvckq96UnyIEh7/d4t7uUd/8y+85P+bXSX4grkhI6nJYPW7LrXpBK2YxiSoShhu4Buq1NPofDeqdrZ3Z4cl7D4J3UtA5VyVAlmJoru9Af2ZAp1lcCQ3nqgiuKmbY3l/BH+MnHM9GVLP0Ww3KNA33CQoQQnL834Fj74PUGkANEIkCSSsa8gQqgYTIcB0PVsXB318GInRiCVWCkpRFAs+j5gKlA4t29Ggh4d0t04FKt9PQqF4UFgumSEA8ApeaElilWbYRVy/lsns/N1QBkxtENF4jxPxcgcB1CZVOrvMteK5IQDtJJIGh++PcX9iYwWjXK37+vP0WdYk0Ht99jtX8JywWFkQChw4tc+cZcvlF7rMze+ubbxN40fMalRMDP/6twaiUeK7wlZ0TD0a5hLTWxo2d45KKprqHKJslTsy209s2wnMFBTYNZjc/oLt9gPvLOx+hxVJIKS2YW5pCbSyJTGMK775O8VyBwDJd2LTDl/X5i8v3S7NVw9vJb51tITDEUwEGANCx2/rXEEFFAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-odt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAepJREFUeNqMkz1II1EQx/+7Ca6JkqyYiJ8cKEpAQbBQFDm0sVOsFBS9wt5KOTgEG5twxVlZ+XEnKNiIghYKxx5nwEpIIXaiSAgKGmMi0d23u8+3T7OaZJEMLG9mmPnN/w1vBUopLPNNhRWXHOyDg0nx82TiJtZPlPVoNpftc2cTotcHtxx06kdXpSQ/BvzKESZzIDmAz6y+NojOjpDMZiqRPIgNoFyWM8DrKUV7axO+gcp4g7AzmquAdVNqOgL2z2I4id1B0wgeygOyt/rLL5buLwAIDgA9dY+L+DkuDQOCrkMgBsRglcMOqAGwIstMg8AkGsuZMNUMRMkLqE+QGloglvlA7uIOAKvZajR0qJkUj/XHe0BTIclVKKlrfKsj9qA8gA6wqSJzPaXlr7ky//tdLEUfawsBjExUFGVWbT7AxSa42H2LMfODmvd3wKb7RAMLYwM8nts8xJ/pEe7/3PmP2eGv3D+9usb35W0bINoA7RmjXSHsH0f5Z/mUSZ0Ir2JmsBtD80s8/rGyzWsLFTD5yUQCbfUBHl9d38LvkdDTXIuHVBo0k+bbt06qO+yAPGXwe/cA4wO9PN44jKDG70GougIzi2tQ00ms7/3lpwnBBgjZ37Kkd1Shht5XzBIFl/ufFtniT/lFgAEAU//g6kvdGBMAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-otp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcJJREFUeNqMkssvA1EUxr+ZjkdbrfFKVD12ErYSRELY2fkH+BMsLcQaSwsrSzZi47EjJEQkEhYkFlhYSVtFpdqOqpk717l3jKZmiC+5mZlzv/s795wzCuccQncz3YeRBj4KHz0/RrOZe2NsZPP20o255zQ3EAxzEAC+6uzTw13G4TFQAakA/CWtIYbY0KBOrx7IvwDQqlHV1o3YxKTOvyAUvfQCfqmA3e4ikyS/zRAKvOot7eoSHEgZIHrCfQAfBqBaKQQDKScQAExd8emBANg+2U2CvNMkkgSqBmrCxFB8mujeoJBWwEqARcssKTAJEGrmaGrjqK1zvNknH4BtyxKl2VUpRxmj5W+x73q9AEaZrR/ND1EJluIpS3i9JQiA+a+hSq8HwJjTsLrRaWitPTCOlhEZn5N75sM1qigmlN+dB3u++Qao5W4TtbEXXIsiszGL4PA00itTsu6XnQWo0TjMTAJqfMDx/ryBJcaVzSNSH4fW0Q+rkIf5rsjRiid7yyN7uoXS3Zn0egE0NiORAN9bQ017D1Lri7CLlP2EDr3Rf7C/itzV2bfXA/igLDaRixfngFhSCooH2xVPCWBlwKcAAwBX1suA6te+hAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ots { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfZJREFUeNqMUk1rE1EUPS8zmabJdDKB2glEwY9ExJYiBUEQpV25qgtBXfgbpEtXuujKf+AfEKRddOdOGHClbYVCvyKWaijT2mhjphk7Sd7Me76ZONp0EsiBYWbOvfe88+69hHOOAE9f3zTVnDKNHvhlsfqPw/rM0ovyWsRFdXJEpDIyRnSlVz0KSkmvabaJeXSJBEhgAJzTDNybmtUnS5Pmg/lrN07H5NM/f13FoMgpXDSuhiIiK3Qi6LUugX7FAbaPPsJqfIHHKCStqRsXVFPQuZgD9BBxjikSiRq41AAkgCQBzVf0+BWEBX7GBm0xgHHUqk1UbBuEcIydzyCZlOI9YEGuDxwduCCitS3Xh3viCZ4jrcq4PJ6DLHd67tjtuAAXib54dCPVEfQ5XIcik/0/2iDeOYz3ceCxrisMi904y0XiMQFfkB7lg6xFHwFxEqUMV0anUNBLWKm8xd3i4zBWOzmASx0UsiW831mA59Xjm+h7HCOygduXHqJatzA7Poey9QnXjTuoVD/j/sRcmDOWLgqnLC5A2wwST+Pn8T629lahSCo291bwu9XA7vcy3m2+gTaUR14thrk9BXasbdiOjSe3nmPpwys0xSi/HpbDd3bIQC6dx/q3ZbRb/j8BEi3Po5cTJpHI9CBNDEa++GyDBN9/BBgAwfDlCVUQaNAAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ott { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdFJREFUeNqMU89r02AYfpJ0iVm7EqhVOxw7dDBEdpiCE1RoEZRddvUgbIex/Rs7eehppyF4LOzQu4MxwYp0HgShIuwwUVSCVtl0s13afl+SzzcpyZYmyF74eN583/s+PO+PSEIIeJZdrtQVI19Cgmk/Ph39bpllXq82g7sgLxVcyKNZpIx8Uj5u5zSjc9Gov8ZihCRC8D+7On4JczevGeTGSEIC4ctKJtB1DTPXi1iCCEkIm1EFlC2Em0iwtWfinXkIzjiO0jljtDC5TtflGIGUQMB+mfja/oPv2Rx9MMjpMdJxOXyXTwkcwIkewfqQ1QtQNB385zcI14FrtQexsSb6SRysZ4Fbf+F6eHwATc9gJGNAm5iCTL5n/LCVRGADNoeaGoHqyaXj5gqQlTODovcwNk5Aj6wXqV8eCo7EDhMonEHpW+dZC7gUG98D3geo7vkb01h9cAvPdt76OGy1xntUd3bjUxAk3+l2sHJ/FgtrT0MUJNfDSm0bjQ/72Hzxxo+NK+h3B7XRNO4UrwymQtMIkdTBU0m+sBOayLsn8Ka78mQDjx/e87HXPkb1+UsfP37+AmZ1fP/suknBb6nefVQXjl06TxMlJfWKNWr+Kv8TYAAkUueexJF47QAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-pdf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNp0U0trU0EYPTP35qYxaW6TlDapNKWGbgo2FkF8rARB6rboXusf0F/hyq2U4krFqugqSBeuAyL4SERBstHa0iR9JKZJ7mvu+M0tqZGkH3x8987jzDnnm2FSSqh4ns0VU1ybFzj674Wa3uWiWbfsFQb+jrGj8Xvbm0HlvYVRxhJprpmTlGmum+OMm5uNPZNbtjk3l82ey8++8oW4Jv/H/wdA456g2kvH99FyHNiuAz2dwflbN8YW8zMK5Go/CMfQkAhpGsyQgRCtlpE4jIULyC9fHzu7MPPEl/5ib6WOE0JJNRiHHg6j86mMjw/2gG4bkbY4PW4Yj2j64skA5FTHdaEMPiAJszt1sK0d4suJmY4k0+IDDGRfqmh0u5gejQc+fG8eYCIahRQCEfgQnIuhEkgtONE+dGxYxEDj1DhiEycZ+1YXdUpHCqTMJIYyEES5aXXQsi2kYlGEia5GtHVKn+amPBeCutPgfLALPuVu+xDVPw2EQyFEjHDghbpYNm1yKVVnYjTOerepn4E6XQmLGSPkPkOXWATMSDcjQEkAaqOu6+i/rccALtFL53LI3r0Nq1ZD4/MXZJaWYFer+PXiJc6s3IEgY3+uPYZHTAcAHM+DTE8gnM1CSyaCulv+GrRy8uYyElcu4XfhLVpkpNtn/DGA5Uu0abFH36WnzzCayWAkmYJvWeCkfb9SwY+NDbSoOx4bYqJF8rZqVRRXV/HhzWtUSmWwmWl0RmN4v76OUqGASrmMOkntSHF8MOs954dT08W248wzYsJDOujRBAaqqikTpRo/qqd0/dv97c3Lat9fAQYA4z8bX9nTsb8AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-php { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhNJREFUeNqMkltrE0EUx//ZbDaXNrvZzdIkbYOXGgxYQlCK2IIY6EufxGdB8Av44AdR8AP44JOPBR+Ego0PClUKTTXQSmkTYtOkmubSJrQ1e3H2yJSEJNIDs3PmP+f89pyZcdm2DcdWvn7LzkxFHmCIra7nm9ulg8yLZ09yXON55Dgjt1PM2iPs0+aW/frdh8bzV2/SvQBnCLiEqcFxLKSSodlrU9leiGPihWePBkgeEZO6ShC2dCAZNuf6ADb+ldQ5PUPx4BCFcgXfdwq4Ph1Dtd5CZi4Nw7SQiMdCXkl6yVIy/QBWgcU+yx/XsLK2cdHndqlK/lZxH/OpJO7fnsWY3z/YAq+g0TmHpoUH2vB5PXi8RD9Fo10aAmDJTgWyIuOupmK38rsPcOvqJO33XWEvwLJsmKxHRVEwf/MKWl/yUMf8mIloWN8rw+sP0D6PHQmYuzGNgCRiMZVA17IQV4OIaTI8buH/AJMFd02Tkp05PO4jnWvc57EDAINt7u1X8Pb9KgI+Lxbv3cFR8xjx6AQ+b+Txs/qL9KePlih2CMBCq92hg2qzt1AoV7H5YxdhdqhHzRbgcpFeqdUplpvQW4FhmAixZ/sws4BoWCM/qmsE5XqE3dDQCrqGAYWdejqZgK6GUD8+IV9VghBFN1RZJv3sT5diBwC15gncggCPJKF0WCPN8dun55jQdVpz3Ynl9leAAQAJhiGatD9AOgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-png { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsU9tOE1EUXXPp0CAUWmJbC04xBANNTF+kKhG8fID6aqL/gPEj9E0lIf6Dj30HL03wxQtVIC0QKrWxNG1Dk9Z2Oj1zxn1m0oIZTnIyZ8/ee+211z5Hsm0bYg29fLGpxWIJWBYGS5IA8ncKhT9Wvf4Yqprtu+w3q85X7f9QxseD/pmZMZsxN9fnc5JNw0ACGGv6tPSvyvEDKEoWZ5Y8OHHObKpucw4B0t3agnl4CJPs2YkQVu4s61ORaBqMJc8CDBiIRhhVM9bXYdVqYAcH8M3NgS0tQQsFcfdKHEbvlr6WyaR/V6uPKPy7B4DT7lUq4MUipMlJ2MPDUKtVfKZ2nn/5BoNbkONxXeb8LYXe/A9AJLNWCxgdhZJagDI9DZg9qIkEytRSkdqTSFQtGILSbgc8LViM+tc0yPfukzIyOJ359k9YR0eQdB2KmBbpwXoM3Dod1SkD+scpEapCI5DdpsJhIJcjajQZagcjI+5oLe4VkeQnyiZgdIH2X6BJ7dSqQLfrggjw0AQwP+/GegCIHppNoFAgEMO1RZKo7BQgRi3yN05cnwdA0BQMAgF3C6pnbuNg92M9AFT1diSCh6kb+FGvo2MxnBB9ocZxp4Mns1cde213B81e7xwAcl4jkaa0IUSjUdLJwkL0Ej6VSvArCt7l81iku6GrKnYEU89VJlSJRmR0Dax+fI9suYxSo4HlWIw6M3FBlnD9YhiXabyOsOeIqG7TzDeIYo6EDGp+ZPb2kKKqH8h+mkxiI5/D1/19J3bwYPvPWXq2skkiJVxesqt0XzghpKM8nRVV2Lv2q9eLIvSfAAMAaacnllcFBmYAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-ppt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkhJREFUeNpsU11rE0EUPTM7ySZpmzT9DNamWAtFfSiCigr+AxF9zKtv/hvf/Aki+FEi6ov4ItWHPGiwiBUKoUqqTUJImmR3M7Mz3t0kNe1m4LIwc+65595zlxljEJzdR5uf5nLmsvZx6gSvtd9W9bjhF7jg5dH9nRc/wq8YXaTSJptb0xklx7IZoKUEz1zJ2DUU69/37vFYrDxegJ9U0lC+AoIIVGg9CL+vIObP48KDQn7x0sWiVnJrnEDg7KGk+i/Ac4iUM/R7BsmrSSxtXMfa3X7el8+Kjf3KfUJ+iRJQw4w0Tc8BRyWGRAZY3rBR/VlC+XED2ayDhZyXl03+hNA3TxNQshlGLAnE44zCIL1goXZwiMNvB1i6zbC0KuAsxNITWwgNMYPeLVJiFEO9ArjHAivrAjNzBr4f4vwIgdGD4YUACsZCE8AtYGWT5jCsGQw5wEYJzP/pj5RwYTA1b07eQmfZ8P0sgdaM2FlYwWkMgMpl6NQAO33GKM0wsQWflkh1uqGVmVWblsiDkQyqxwfag35SqcktaEWTUTHYNx4iGU/C29+BvX4Lpu/C7zYgFjegSY63WySsHyXwpYHU00ieu0bAOuJbBTArBkiXKiaAmTzcvRJUV9E8rOgqBwqlY8ASs/AadbRLb8CzeTjVClqft6FdB17tL7yeCbFRBYoLr6vR/PiSEl5BZJaBD0/R2nkOZqfQ2fsKt+0SEQ+GLSIEUvJm+6jbah2+pS2aon+4g/afd4SYJVuA7vvXdC/IHQtSoTnK+yfAAIEaId1m+vudAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-psd { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAqxJREFUeNpsU01ME0EYfbtdKKWGtoItRWgJHApCBE2I0YuoiSaaeDJeOJh41YN3TfTixcRwMfEk8eDJGA+Eg0YTTRRMg02KKFooCBbTlkJLS7f7P+u3K9Xo8iWT3Zn55s173/uGM00TVlwZfzJztD92iKO5ouvQGQPHcQDN380vlDPr65fdLj4Oa41i9sFt+ytgN7o7woGOrqgvvpLBaF8vWj1NUAwGTVNRM3mf5vU/zaU+XySQuTqIFXz9hxmGLkoS7r+YxvVnrzGzlgXPDOzUZPT4m3Dt/KlIuH9oUjXYEHZZ/wOgGQZi4TZcGI5hLb+FO++TSOSKcLtcMA0dI0EPrp4+HtnfG5skiUecDGwQE2MjAwiGWlFVNDz+tIyCokJhPKYSX7Gdz2I01hOJdnY9rJ/7UwPGTEiqjtbmJtw4MYx78S/4Wa3h5UoOYwPdIOp2Xi/t18rlFgcDw6o+ydiWVRwOBnCpL0oOAMmNEhLZIgSeoxwGSWcERon/M9DoBknTIdNQNAMnO4PIVGpIFXcwndlA2OtGc4MAxml27p4AIulWSIa9QVadiYSoJxhqBJivKgh5ad3k9gaw6JdlDaqq7q5wINY4F22HaLHSDZQkBW72O9cBYFEviBIURQH7a7MN0uDisUW12ZZcaGlmdq4DwCqeTo1zNtZuW7hUqGIw7MNqSUS2ImNsKEpSdEwt5lGhfQdAkQBEoub3NNrDJfAIeBuRrcrY5xGQ2RFJAjl00I8PCckJUCB9q1URBnk38XEJEuk41tmGwZAf66s1VOh2keqwoUnYpFxHH4iKIixkN3HzVQKP3iQR/5GDKMuYmE3h+fx3MHqh1sMafztHLuiCg0FAk0uFdLqcpGY5QEXbTC/j7mIaVjc18DxufUtBJ/vcggs+3ijVz/0SYABsJHPUtu/OYwAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-py { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlVJREFUeNpsUktvEmEUPTPzTUFmgJK2UqXQFG3pA6OBLrQxamJcaYwuu3Dp0l9iXLvVtRuDpgt3JIYaTVSaxtRHsJq2xEJBHgXmifebMhECXzKZme+ee+65516h2+2Cn2cb2VwyHl12//vP2/zOQaF4uD7GWN69e/LogfNm7kUsPBFaXYwHMeK0OlpQEJApHJTuykzK98dE98O0bLM/UNgr4v32Dj1fwSQRt9dSsfmZcMa0rIv9ODaqYrPVxuPnL1Cu1aEbJu7fvIZUIo4bqeVYRzcyv/8c3SPYpwECt/dmu4ON3Ed4TymI+hQc1ZqoE+F+uQLDsnHlwkKMscJTgl4eJOi9fxZLePNhGx6ZQRRFqH4VjZaGSv0Y6cQcJLpra0ZguIWegqDiw7lYBBZV6xiGk9DQDLzK5bEyF4Hi9VLMsoYI7J6Es5PjeHjnOl5ubqHaaJGBEkzbxplQAKIgDmBHekDTgI+qKKqKLvNApgmEgyquLs1CoFn2Y4cIeLJpkjoCLkWnUSIF3JxISIUsCjAoxhWNJLBIJs3YeXj/08oYZkOKY65HllE/bkMmY504YUd40HUq2JSSyW6iVPmLiXE/ZMYQCU+hXK3h1toqdNN0sEObyKtqtDQ6kXDwcadDS2TBryp4nX2HxXjsJK6bDnZIAZem6Tp5YMMmicn5OC4lztNWtvB9cg+hQABtWjKL2jH/T3GgBcYDXEE6mcDM6SlaJAGMWkivLBC54ZgniZaDHSI4rNSqn7/t1vgkGJPwZXffSeCjk2iUWz9+nSTQN8e6ef8EGAClUi/qoiOc3wAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-qt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpsU8tu00AUPU5sp41NkzRxpfSZqi0VIIQqEEJUZYXECvbwCWxYsuBD+ABUFrDrCnWBQEJdIWigBSr6pqRJ1ebhxrE9M7aZmSrQ4o505fHMnXPPPWdGiaIIYrx89GKpNDdxmXkU3aEoCsT+z8W1Sm21+jCpJctQTvaerj+TX7WbnJ+0cpfuX8mQtn8GgJ4AZtIFY2Hz3foDVRcgyt+cRHcS0IARh+D/8G0PpmVi7smd0dLs+AIjwTVEiANEYYQwCHlEZyJgIQKfoX84g9uPZ0cHZ4YWmE9nuufU0wABCSSImMsWEgqSuoqA/39/swZFTWLy7vQo7dDnfPvWWQa8GuOV3IYLJXmyzDzG2/ChZ3pwbHdQ267BKJoYuj7SF2MQhiF8LuDK/Gf0DKTBKINz1IbTbEMzU1ANDW7LAfEIQKIgBsBFlAx6LYOz6MAcvoDCtAVGGPKlAiIu/F55F33FDA6W93EOAOMaMOl7biKPwRtD8Foetj5sYPfTDtxjl1f3Ubo5jkQieQ4ACSUD2iE4XDpAdbUiW9D7UsiN9WNkZgxajwbd0LGzt3keAJPUc1N5SVeENT0Ao2BKV6QzwlZeRBSKAYhe3aYHcZWn7l1EfjyPypcK9LQGa8qCvW9j9+MvaasQOHaRhGWdhsNLR8hwodYWf6B4tYjDjSOovRqq32rSYq/lytw4A77o1V2ERiAtzY5kkUrrsH+3QF2KY87ArTtQuQ6nAf4x6FCV1D001+vYersBM2vA4y1Rm2D7/Rac/TZIw4d/6MrcGAPf9htN0miJh7Lyuoyvr8rQeP9iVJcrSKgJ+TrFcyYebXTP/RFgAFQobmIOBxbsAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-rar { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpsUktPE1EU/u68OgylZXi0hZACQU1LEKKCMcat7jTRnQsXxsQtv4E/4M74P1iriUaNCw1FgxpjCJQKKAU60+m8mJnrmSll4XCTc8+959zz3e88GOcc8aq9evChOHl/lvMoubvWX/z4+BwTlbvw7bXdg8b7h6LE1gGW+O88CRMt4XTlR6/rYxce5Xv3jlHH19fPkBu+gWy5mlcFb3Wn/umeKOEMJF5C7xCFbtA9dRXjFoYKGiTRAlPGUV1aKU9O3VwNQ74A8DQAIZxqAuAhBPIMFYpQVAVB4CPSZjEzv1weH5tbDQN+JQ2Abu488mnzIbAAA3o/VK2PwDJo7r5Fy7ZRuvi4PFS6+qIXdVYD8Jg6BUcuOD8BozSLlRWyicgVKkTMQWwUlFF0Ooe5FIPk57BD7G0SiywyjD8bCDyHsOkeeeR3SUxEkROmU6BfQYFJMHfhWXV8efkUrb13VPMTsrcTQSzxZ/+n0GVA6EGbSGdgG9vo15fg2nFgbO8k70SRdd+mahDT81vUxTZRlJBRMsjq89C0EXCvSf7TIBZ136YZUJEiE7LgJ2dN01BZuE0dkIhxE7KcQTK1QUj+cwAEyrPZ+IydzRoyah+mLy2isbWBweESJEnB9q+1RM9Ub9GQOWkABg8HjRr2d9Yh0hTlBlRsfn+D4vg0BvUC9rZqECUJuk7Tzr1zahCYlB6HJAREPwfbbMBzLBzsbUKVI0qBgQkc+SxgWUYaIAqOpKwKXJ6bgGlaaDV/YvHaFNrtDsKTfVSrJeqIg/bRNwjclFIALeP3saybhu8SC4VBHwnhBXXIKocYRXD9QzBi4Xgchmkd9+L+CTAAMqwy+ZzluBgAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-rb { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAixJREFUeNqEUktvElEU/mag5f2yJhXLwxIt0kiqsVEXujP+A925cu1Pce3WtXVtYuJCF7KtTY0NrVQIpRVKeXTkMcO9F8+9ZVooJJ5kcmbmfOe733fO1YbDIWS8+/g1dycVX7W/xyO3vdsuVKqvnE7HZ230783rlyo7bVBicSGyfjsVwozomVbIPe/c+FmsPHfoRKJd1HT7hXHBZjVbA4aA14NnD9bC2VR8gwuxPi5Sx39Cp+M0XUP0ahhP1jLhW7HFD4zze3b93ILtXYyyVKlR8/5hFbnvO9gtlrGSjOF+OpXkYviWyo8mCS4R6bqO4p86vm3v4fC4DrPfw4unj1XN6JvBaQtjChzUXK43sVU4wNFJA43Tv/B73edQwTmfIhAjCVL6UdPAj1IVFSKhCdAcAI9rnjBiAjtBYEu3GEeh1sKJ0YXR68sVIujzIhzwY8DEBHZqiLRKkicQDfvABxaiQTc4Y/C65pCOXwcjcmlvJgHtlwi4epYifiQWgmoLZwPW6HQG07LgcOgKO0UglAKOTt/E+09fwAiUWU7QAE9xUK3jbvomsispZVHMVEDSZdHo9rCZ/4VIMKAu0XGjpU7d2S8hk0pCELHEzrjKnCQOYJoD+Dxu1RyiwUm5LaMDo9NFt2cqDLvY4oQFp/QpfT/MrmI5FkWebt+NpWto0j2QmQkOjZ9hpwhqjXZzM/+7LU+cc7lRrjXh8/lVLRK5ovLWXglOsiOxdt8/AQYAzv8qbmu6vgEAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-rtf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe5JREFUeNqEU01PE0EYfnZmd5FSvgLYFuwWt9EgHyEaox68eDJevHvwJ/hTPHv1N/QgZ2NC4g3kUAQKFKGhjVKqRrvbnRlnht262FHfy+y8877PPM8z71pCCKh4/ebt+rJfXEz26Vjf2mnsN5rPKKWbVpx7+eK5Xu2kyMtNTd5d8MdhiJ9BOO7atFI9ajy1UyAqSPIRMR6ZmoNehNHMMB7fX/UWvEKFMbYKE8DfQnAhwRmmJkbx6M6S5+WmK2Evup2c9yUk2nnKA0XVcSiGXAe1k5beP1i+4RFCXqnPywB/AKVzK34RjHNYlgVKCH50w7EBBogbTa/AVM5SgBdn0gc2AMDjPsbFPz2xye9asweS6n+NTbG8BCCfUtLjff2WoVnVpAH6z6hMUtJE3EykYfpF4vUiL3QNS7FMeSAQRBHW3r1Hq91B+VoBQRji4+ExFsvz6Hz7jm7Yw5OH92AcJKW9G4SoHhzhy/lXbB98Qmm2oCXN5WawsV2TACEoJXqwTKOsb3BtR2ucmZxANpPB8JUhyPnHWDaDpfJ1eZFALzJJ4MKO5MEtv4TSXB7V/br8iQLMz+almRZWbvoo5q9qRlxwewCgeXbe3qrVO5ZkUD/9jJGRLPaOm6COi92TU1DbxYe9umRD0DrrtJO+XwIMABWp9nS+FgaoAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-sass { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDNDMTBBM0JGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDNDMTBBM0NGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowM0MxMEEzOUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowM0MxMEEzQUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Po72XUcAAAJcSURBVHjahFJdTxNBFD1bykc/ttvdtttWGgI0bYrUgDZoNYqRJ014kMRXHvwB/hQTH/wFhMREJfFBQxBjhMRIFEQSCAlQxKYGggiU3e3HbnfX2bFt1EU9k9m9mblz5p4zlzFNExYmpue/jmTSZw5PZAl1MAwDT0c7O72wvPdudeNakPNtOZ0tsM7cvzdOc5yN5LDAsTFRAJks/kC2PxFRVe39Si6f4byez62EpAEH/gNN18F53Ri/Ocxf7OtdLMpKT42s/ZPg1cISJp/P0tg0TBzLCoK8D7eHh4RkLLJ4cCz12AjMXwgez8yhqtVo3NbqRKlcxcSL16gZwJ2Ry8KVc8kZO0HdTKlURn+8G6PD2SZhLMQj96WAiMAh2RXFYKI78lcJcx9WYBCycICnpNbojUWpD5Y0C4Zh2D0w6hWc70uQZC+IWfQZrXF0IsHvY+meBd08haAhoVMMQFJKWF7PNZM+klhRyogGhbqxOIXAMOtEwGAqDqVcgbVkkE+5UsEAWavf0az2t0ZqvK2qabh6IU3joizDwTgwej1LdVfJXkdbK8mt2QkayO99A0/0trQ46I1lVcX+UREhnsP34yLp1AD1xibBMuntpzU8mJyi3Tc1O4+l9U06n7x8Q/8PHz1DrrALt8tlr0CrkbJMHTop9Sk5sLa1g8L+ARJdnShKClY3tunN69t5iGLYTlCtakjFY7gxNABdN3B37BaqqoYT8pyX0in4ORbRkIA46YlDRbUTbBZ2Jb/Pw4qiKFnapcpPo9pdbrg8DjAOBsFgELJmsGs7eWkkc5bu/xBgAHkWC6UPADTOAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-scss { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RkM4QjYyNDVGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RkM4QjYyNDZGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGQzhCNjI0M0YxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGQzhCNjI0NEYxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pkf1yeMAAAJbSURBVHjahFNdTxNBFD0tLULpB91uodVWPmorUIxo0VSiNSExMYYHE33l0Ud/in+C+OSjYgjRGDBRCKJIUkIEWi0WKlja0ul22+5219lJ26gLeiezuXvn7rnnnrlrUFUVms3Mvd2bjIyezRVLBA0zGAzo6jhjm1te+7EU37rFO+w7JlMbtG+ePJ5mOaZmci/nsPl6ONBtw18WDQc9tZq0sp7YjTisXV/NFKRpRvzHpHodDqsF03djzuvDg6vHJWFAprF/Arxe/oins6+YryoqCiUBvNOO+7FrXMjnWc0WyIAOQP0N4Nn8IqqSzPx2swllsYqZl28gK8DDyRvcxKXQvB6gISYpiwgH+jEVi7YAfW4nEqk0PJwDofNejAX7Pae2sPhhHQoF63U5Gai2Bn1epoPWmmaKoug1UBoMrgwHabIVVCx2jdrKFwm67TZ2plldPQGg2cK5HheIUMbaZqKV9In6giDCy3MNYXECgKI2gICxoQAEsQItpNCHWKngMo01arTY/jFIzbutShJuXh1Fm9FImYiM7tTtKOtbO+toN9Nc+fQ5SGUOIVYl7HzPIH2YRZ0y2KZ+sVzBHn2v1mpMGx0DTaR3nzfwfGEJdybGkdo/wEigDyvxLzg4yiESvojZhfd49OAeLJ2degaSLIPOO6vwgiYaaRErTRREEdn8MeJbSVZ5M7nLdNExqFLaQwEfFfACQn1+HBWKSKb3MT4Sgstuh9vVDa+bQ4DORE6o6RlspzMk9TOPfr+fiLJCLFYr3TZSKNcI7+aJwWQmPM+TkqRg49tu65f/JcAAMwMas6WUKd8AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-sql { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh5JREFUeNp8kctrE1EUxr+ZyXMkoa1NBROaSkpTBE23PhZ25cql2y5duvAPUdGFS1FxIRRBXZlFQ9GVdDENIhGJxkDsw2mneZnM83ruNZlOmNoDhzlzz3d/9zv3Sowx8Ch/qlYK2XM3cEJsbH0+qjV/rd6/u6aN18b7RMFT+9aosP/Ex+0ae/puw7j36PlKEMAzctKJ3aGFamMHjV0d+wcGitkMrpWWp6hVIciEk2MAOwbUWjosx0UiFoWqJpGMx5DNzODq5aIPoa82AWBg/lyKLMH1PMp/a9XvLXLzG1cuFlBaWpiKxaIPSLY6CaC93ggQjyiQZRkeQSzLRovGaPciWLt5faSWEBoh6KBvOhiaNga0+Y9pwaFxvu7rfp8F5pWDt+qNMp2IijHGwddWCvN+33/CoAOP5nVdT9SdoQ1JkggiQ6Yvr7V60+9z7akA2gfH9cRF8hO5F5Ve4lQAF9uuK+qFsylkzsQxrcaQm04hdWkR83Mzfp9rQ3fAFzu9Ph6+WMfjl6/pGBdb2jbKmx8QlRjWy5vkyhUZBPgOeGNHN9AbDLGUz6He2hVj3Ll9C8/evsdgaMK0HV8bcmDTU0UUBYXcedR+NLGnH0I3jvDk1Rsy46FP4C/1BtrdntCGHNiOAzWZgEKQ5Qt5lIqLojbaXSQTcRy2OwT4SZqk0IYAOgkVWUE+lxX/zb0DpFNpkTzmZmfFtzewhHYcfwUYAMZmVaZQlLFHAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-tga { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra725K22ILRGipb22pMG6JcSEQTbUIwnozxpBcvepeEP0KPogcT/wlNT17kIKbEmChFUYKGVtL0R2gLtNCl3Z1Z3+zSAlonmezOe/O+973vvZEsy4JYnqdPMu6RkSQYQ29JEkB+PZcrslrtPhQl23VZc8/tr9I1yMHg0EA8HrBM04lVFAhoY38fSSDQVN3pfKV8G7KcxZHl6v1xblqU3eLc3p2VFZjr6+gQgwsnhzGTuq6Nhs6kYZqXjwL0GFhEl3U60OfnwWs1GGtrUKNRsKkpeIIBpKIRtI1J7cX7hXRhc/MOhXw5DkCZGG2zXAajzFIoBMvng1ypIKOqmP30GW3OIEcimovzlxRy5RgAFwDEAIODkCcmIMdiQLsNdWwMZdJlg8pzEUt1aBhKq3XinxKYqF9yQbqRIqsMy+0Gyy47bKgUWXSLtDENE5wdtuqQATm50F1VnPbRGeEw8HXZbiV8fsDvI9ldju9vADAyihLEbrWAZhOoVp3z6iqBUiB1A4nEfwCEsbkL/M4TgE5n5jDx+oTEzp1d8m9tC8H6MaAB0imzx0NU/WKUYE+loEyawDBo2ui6TGfT6ANAxrvx87gYCGCxXEKVJvCWFsG3eh1vN/J4OD6Od4UC8o0G3TX7TGLHwI9iEQmvF9X6Fh7F4/iYy+GcLOMSlfEgGsP0qdNOmX0BiGKpVkV1bw/1nW2b/gCpf1PTcI+Y7eg6ps+G4bG4PR99SjAVo9HE4q+fKNE0vl5awuSohjeijbRefVjAtUgEQRK7Yhi9OKn7nKWZxxlSPWl3QwgnaIrW8QMhD542vUbx/W49m7sq4v4IMABOqi3Ej7bAEAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-tgz { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpsU1trE0EYPbMzSTfdtInFtkkpiaXVWou2FRUEn/so6JugL/oH/Af+B1988if40jcFERQURNBSQdDWlLQN2lsue8neZsZvc7FoOrDszM75znfOmVmmtUYyvry++36yfOeS1qqzDtvH2P76ApPlW3Drb2sHex/uccHWAdbZX30kO2+B3siN3zhTnHuQ66+95i423jzFzOVljBdKOZNHazvVT7e5wF+SZBj9iZJ+3J11mbW2kR8T4LwFli5i4fqTUvnczTUp9RLtDhKgJx0q4dEwWAxrREKICHEsoYYXMXvlcWmquLgmY71yCkG/c0AkARgLMZpnMDMpGNzEYe0dGp6HwvmHpbHC1Wf9MnFCkHQOyYEPzSJwQ2B65Tm5NZG3Fshim6wbMNJn4bpHowMKtIqo2COgR2IcAptwjvcgo6i77igjEmVDqbY8xQJ1VwRULhiBI6+G9Zf3cbTziuzIDkmHSNqECTFgQScEcYuc2NA8TcdYwXD+GkK/TYVN+u72WrIudiAD8o6oAR2RRCmQMjis3CIy1iSpPySCXhFTXeyAgh4BR+JVw8pauLi0Cp4yCX9A90FQhnSBYtnF/k+Q+HYam9itfIZB3QvT8zj8XSW5EhNTs9ivbSLwPUzPLNPJBIMEKnaQYg6aB9+RGR5F5VsNgnNKXMI1NdJGG5WfHzFVLJ7k8c8xUngpVodlDSGbFYj8Y4yMpOG09lHf3yIFPzA3fwHZTAQVtU4JUTeFDrdgDdlI8wAz5Qy2KxswReI7QODZcOr0ZH3q2hIDBI7zq16tuk3FNPxAI4wN+pkoccYoE4YJU5EdUtM4Qst26v26PwIMAKj3P/2YUKgYAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-tiff { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNp0UktPE1EU/qYzHWstlrYJNcWUElyUJsaNGh9B0g1Lo0v9Ey78EbrVxBhXuHShm25YGBJRQpAYBDEWpaEPEhksdVpbyjzveO4MfZDCTWbauefc736PIziOA77OPH2yJCcSGdg2uksQAKofFou/7VrtASRpvVNynj13f6XOhjg8HAlMTIQdy/LO+v3uYUPTkAHCTb+cK+0pdyGK6+hbvu4/xiyHbncYAwfR19ZgbG/DoO9LsSgeTd9JXoxfyMG2rvQDdBlwIZauQ5ufh12twioU4E+nYU1NIRCNIDs+Bt28mXzx8VNuZ796j9q/DgAwomwqClilAmF0FE4wCInAlkjO4y+r0JgNX2os6XPYS2q/cQyAcQatFjA0BPH6NYipccAwIGUy2CVJFZInkKlyJAqx3T4/IMGmJkeWIWSz5KgI5pdhb3yDXS5DSCYh8rTID8s0wexeVD0GtMd85KkkefFxUfE47M1NokbJkByEQl6tL+ouAI+MUwbFhnYbaJKc/Sqg0x4H4eDRGDA56fUOABA9/GsCpaIHwr8FOhQ823O5RfW66tUGADhNy3RNRDjcN41HLxdQ8J6jYTsOQLfOJBK4f+s2/uoathoNGKT1MtFeVHZxdWTEZfEq/wMKl3rCJOIzTV6ADs2R5ulYDDNkYjp0DhrF+zCVgkw31+v1UxjQZkNV0SADd2o1MIuc9gmY+/kLxb0/UFoHePd9A1qzeUoKpilx9xcLWzgg+u/zeVfuQqkM9bCN1ysrWKXxdtPgvScwUAm58XZ52W16QyPtifRUzi588GbEi1ztHPsvwAC4uC9qhnsZvwAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-txt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeJJREFUeNp8UrtOG1EQPfsyXiyzBguIJSyChZBBEFCKpKHLo6egpErNn8CHgH8gkZIiTSIXLhJAWCgkoMgRMSiRBSK29z4y9+I1d/HCrFb3MTPnnjkzlpQSynY+fP70fGF2gQuByCz6lfdd9Uurfvrrjes6762eb3tzQ69uFJwPsqOPC+MBEmxxphi4tlU5OGmsOzaBWLc+O9oIIVhScidkyGZ8vH62nHtSKlaI4cse6TjAfSaFBBcco0EWqyvzubmpyQrj/FXk75cQaSEMeMXU8xykPA/Hjd/6/LRcyjEpt2i7HAe4A2TeLZWKUOJaVLxj27j813EHGKCXaAJExu/4BOdiAED08riQD2riOrexyRoYc3CvsAbLGAAjZga7vgZG23WMCdBvoxKJc36TRBlMiaa2JByjNqqD8qkYc1pjDK7abey+/YhrWlfKswhpiCR96aEU9o5+QE3g2ovVWDm2Sc22bBQm8vrVpbkS9r+doPr1EOWZaQ0yFoxg2PcREosEAI4uvZhJpzFMP+cSXRbq+043RManez+tNWKMI6GN0g0Z04HFR+NoNC/0yx717efZOSbzY3AcR4Op2AGA5p/W31r9e0vNgSrh9OwCrpeCkqvZuqTybnpRqx/r2CjvvwADAJC/7lzAzQmwAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-wav { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApFJREFUeNpsU1tPE0EYPXtpKbX0wqUQKVQMFdIXQBNCQBs06KP+B8ODGh+Mf4b/4IsGE54kxhcMBrkp7YOQgBRvSKG73fvsrt8Otoask0xmd+b7zpxzvm8E3/cRjPkniyulW0NFy2JoDkEAguOlpXJ9p3L8MBqVl4O9YHxae8pXuRlcGO7KPLhfTDVUqwUgigJMy4Whm6lEXHjxYf3XnByRN0QB/2KaH7btMlUxoRJAcyqKhdOaht7+DJ49n+2cvTnwynXcsb+kLwJ4rgfmMDDGWqvneXCZS9ND7mov5h9ND85M9y86Dpto5rUkuJ4Py3YDJpy6QGJPayqB+Njf+43XL220t0cwOZkfrNXsBUqZugDA6CbLdAiAwaek1ZU9LmP8Rh6S78GsGxjOp9FdzKJaVZIhBgGASzK21w/wbrnCk8euX+EMAjaaZuPHdwUdHVFYluuGPGCORwwYjg5rqOwccRk+3Ux0IEvntmsNG4ZmUayL/wAwKHUNfZfTKN0ZRaw9Cof8qJ/pMAyHy5KkAMTksSEJtnMenM7EMVMawbejMzJRh67bXEYiIXEAVTW50SEAhzqwfqrBcXx4VOhYm4RsNgHbsJFOyZTsQ1MN+hcohoUlkFiMT+TQFpMwXOjGpXgE+XwGk1N5pFJtKNCequgYGupCRBbCDOp0KBJc4VoP3dyBONW8uydBgBHUThqQKCk3mEZ/LoUG+RBioJO7VarAwEAntjYPiUUW9Hh4b2R7k9j98hN37xWx8fGAt3eIAdVMLn+uUv+b2KReSCZjZJiB9bV9jIz2ofr1BKvvd7G9dRC80lae0HzOt+cWVnrSKDrMJykifwNBpCgE/UAllEXufmDu8Zlffvvm8XSQ90eAAQA0pF7c08o4PAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-wmv { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M9.1 91.1L4.7 72.5h3.9l2.8 12.8 3.4-12.8h4.5l3.3 13 2.9-13h3.8l-4.6 18.6h-4L17 77.2l-3.7 13.9H9.1zm22.1 0V72.5h5.7l3.4 12.7 3.4-12.7h5.7v18.6h-3.5V76.4l-3.7 14.7h-3.7l-3.7-14.7v14.7h-3.6zm26.7 0l-6.7-18.6h4.1l4.8 13.8 4.6-13.8h4L62 91.1h-4.1z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-xls { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmxJREFUeNpsU0trFEEQ/mamZ3Y2+0zIC2MmITEkUYgERFQErx5E8KTi1b/h79A/4SW3nCNeYggBYZVEMU/y3N3Z7M7OTD/G6lk2ruw20zRdU/XV91VVG0mSQK/3n1a/jky6d6Xs3G8WXS+Pw5N6LXjLLGuna/78oZKerGsYKtrDE16uJGL1L9gEOOcYd2dL1fNwrbL//aXN7J1efPMmkUqEFAk0A0VZNbFEaQCBscIkXj975y3NLq9xye8PBkAniHOFph+j2eC4rsdoB4LsFubGl/Hq8RtvYWpxTQi52o1jvWiGYaRZL0/auDgOkC/Z8BYL2Pqxidp1FZkhoDxpeaXA/Ujuj/4HoOxKKjiOiek7RUShRNQWaNYFQuMafrYCxiw4ozZKfqbYJ0EvRdl1DQyyTs8XCNTA6UELMwvDyLpZWIZNNlNLlQOK2LMJRJ+5AkuZ1S7CFFzJzk56GnUjQWlYkqCoBWFbonEVYcLLA4dNnB624GQsDBWIgfZJEgxkoChzSFWvn4VpQemDm2VwXQsXJwF1h6c+gxlQ5jgSiEUEt0wdIe7tMES+nEG2aCLiJMOIIWIr9e0DEELAMUrwRuchVAyTKimUwO75Jm6VF3Bv7imOaj+xd7UFKVS/BPJF1b/E4tgTrE49J60O5kceoNqowiuuYKa8ghHXA48U9MT2AQgyRvTThE30bQiaSGa4yLMJNFo+Dq/2cHt4CYlwyFf2S6BHwwrMw/avDbR5C1k7h1YQ4KH3Amf+AcZyEbZPv9CItzQD1l9EbtYOjv74v/d3O9RMPTDrsEwGIWN8q2yk7XNYRs9JrRv3V4ABADSGR6eQ0/NQAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-xlsx { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8tqFEEUPVXdPY/ueWZIoiYZiSYKYhJc6EbduHOhgijo3t/wH1z6B0JAhOyMILhxo4kJGk1ASTAxwWF0Mpp5dHc9vFUzYwidaoqmq+8959xzbzGtNcx69PTS26ETmQtS9r4Hy/xv7MW7jV+th5yzVcaYPX/++It9u4NAv+CVR6tBUUTqMJsDcRzjZOZM8W9ZLKx+/XDb4e5/kH5In0lpIYWGUaC0YTZnBCAEKoVR3L36oDo7NbsglZwbqD6iQKOXFMcKUVfBkBAoQhlD5xxMDp/HrSv3q1JgYW3z0x0KXzkCYJaRZljru23aHWTzLiamAyytv0O9UYdf5PArqlppBfMUfu4oALErqZBKcUxMFRCHEp0DgW5Lo4N9NIN1dF0XXsVFOUyPJTzo+WBANDidjp8tgHGG3c0DnJ4uIRf4cOCBaW5KjY8xkZL72xpJ9QcFz5bVqHUJGHZL2YtNmKi06YCyiVFb4s/vEKMTAf1p4edOG6mMi1zR6wEpdUwX+vLDtkCzHoK7ptcM6ayLmGajvtex4PliyoIkFRjmUEASelB2rXQRSfjUCT9PlWpmW21iTGzCAyEkUixPRqXhe2V4zKczbdmybgkpJ0cGOuA6Y2MTCsKoi5HsNK7N3MN+uwYaWbxYfoLLkzdxcew6lrYWaZhm8PHHG3zffp1UwJSHz9vvkU8PodbcQYYYS5lxYkxTkGdVDQdV1Js1qPgYD6JIuIE7gsXVefIhIuM05k7dwMbeMmh87a18ufIMaVYyprrJLgje2Nr+1tzYXANnDnr3zRhHj37Vvy2wpXHtNAd5/wQYAD6WMuT2CwoVAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-xml { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAilJREFUeNqMks1PE0EYxh+g3W2t1G0sEqyISynUFJsSOShNwCamiYZED3LgIkcuxoN/iCZePZiYGD2aGD+i0F5KMChxlVaakAK2ykcAt+WzdLu7zkxo3WZL4pu8mXfmeeY3885ug67roPFh5nvc62m9hjoR+5LMp7MrkYf370qVtco+VtCUFpbj+jGR+JbWn76OyQ8ePwsZATQb8R/hanZgINgj9IqeuBFCw1Kt9OMBnNWCs24XwkG/QKYUEiGjVAPQof/rq0783pShET3ULQo8xz0iS5FaANmrHQH2DoqY+DSLSz6RzecWlnD9ymU47LYjd4O5BXqDTG4FM3NpTEkpdJ5rw0AowLRMbhUfp58gTOaD/UHmNQPI6YmvKWRX1zESHUJ/oBs2nmPa+Mgw0ZIM3tZyGoJwygzQNB2jNyJIZX7iB0lpPoM70UGmPX8zCU+rG8NDVxHwdiC5mKsPUFUN/gvtLLf39sFzVqaN3YrC6TjBauqhXhNA1TQoqloV7Da+pjZq1FsXUCamF29j6LvYhf3iISamZ3Fv9DZevouhRzzPfOG+3hpA9U9UyioOlTJ7pFeTCQS6RGzIebyf+oz5pSzWtmSW1EO9phvQ00slBRt/8qR3DoWdXbiczUiTzd52D+tdLmyTB14mx1rMAKVcRpEATjrsuElee/HXGmnFRyBOGD30C/nEDjNgs7CDpsYmnHG3YPegBCvHs9oYfm8nG9dJa5X4K8AAQzQX4KSN3wcAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-yml { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdxJREFUeNqMUl1rE0EUPbM7m5Y0Zptu21AwWwhYpfSDFh+kvvRd8N0Hf4I/xWdf/Q158F0QoQ+CVsFKaLSQpt/dpmvztTOzzky6cetOpWcZZvbO3MO5514SxzEU3r57/3GpWllM/tP4sL3TarROXuSo/SWJvX71Uu80Cfhlr/T4UdWFAVfdnmsTUtvdP35OUyQKVnJgXDBTcj9icAsTeLax7j/052qM81UjwW1QJXEhMF0qYnN90fdnvdogYmvJPU0/VBApD4hcDrWRcyikfB17srzgW7b9Rh1vEvxDlI4tVytaBSEEtmWh0xsUMwpwnWjqAlcxogiHd1wiQyCu87iI/+sJtf6+NXsgpd7FWCMB50KvkYMGMbLdZgLlfj+K9K4+FnFQ2x7WntIs50AbmiGwLILt+k+EvzvSNIHzdigdJ/AmXQRhiHv5POSwYmG+cqPVo0HqDxj8uTK2vn1Hfa+JmdIkvtZ/4fOPXU3WPDpFeNWVyUKryCiIGMN4zsH98gym3CIcOTwT+XHdXrdQQHAZotE8kBPpSqPNHtBOr48HUmLOcXRJT9dWNMGYJFby91pHOAvaykSaITg+bwefdhrteDRTMSwyrFCgI88E056Hy+4Ah2cXQZL3R4ABALUe7fqXWFN6AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.ipfs-zip { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm9JREFUeNpsk0tv00AUhc+MY6dOmgeFJg1FoVVpUWlFC0s2IFF1jxBbhKj4BSxYdscPYcEmQmIDq0gsERIViy4TpD7VFzF1Ho5je2a4thOqNhlp5Mz4zudzzp0wpRTC8fPrk0/TC6+fDtYicLH97T1Kc2vQDcs+rH3eUAxVznn0fn1DRM8E+iOdv5ct3XmZG6yVlNj6solUbgVTt0q5FGtX6vXqC6VklTE+KAO/OODHSIQPRQpsXC+kkEz2ELA0ystv84tLzyucsbWByisAGf+QAS2CCDRRLMJMmxC+i8C4jdLCm/zM7OOKFGptcO6/BTpJ0yeQB0Y+mfKQuZZG0jQgeRbW8Xdomobs9LN8scc+UPHNy4Dwq8IljotIIQEm59/RoSyM1CKkXKZNBm7kIVgyM6wgAnSgRK9vqQfHPiMFDHqyFVsLR9Cm0o4YzoAASrSjCelQfRPb1Vc4qn0EY5L2W9GEaBLcxQgFHpGbkMIDJ69e+wjJ8VXqRgKid0r7ftQdxkRs9SqA2kgAm14SSIQh9uhuLGPMnKJs/5KquL1x0N0RCsizigoDaLqBdHoMiyvrlBsHVx1wphD4BCewoqxGKKDwAgtOy8JufYuk+5golGGaGZwc1sIGoDz3AOPZSVLaHgVwydoJDM1H4DbQODughB3YpOD44HfoHgnu4e7So0uAi0stHLJ3Aud8B9bpHu6vPoSu9TtDl6tUuoFiIYOgu0+158MKmOxomtyD3Qi/3MTR7i8K0EDG1GHO5DE3X4DvNahZlJOwEkOATvdPc2//hx3mXJ5lFJaF8K8bStd0YGfnOJbMGex21x6c+yfAAOlIPDJzr7cLAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +/* Source - fileicons.org */ + +.btfs-_blank { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNqEUj1LxEAQnd1MVA4lyIEWx6UIKEGUExGsbC3tLfwJ/hT/g7VlCnubqxXBwg/Q4hQP/LhKL5nZuBsvuGfW5MGyuzM7jzdvVuR5DgYnZ+f99ai7Vt5t9K9unu4HLweI3qWYxI6PDosdy0fhcntxO44CcOBzPA7mfEyuHwf7ntQk4jcnywOxIlfxOCNYaLVgb6cXbkTdhJXq2SIlNMC0xIqhHczDbi8OVzpLSUa0WebRfmigLHqj1EcPZnwf7gbDIrYVRyEinurj6jTBHyI7pqVrFQqEbt6TEmZ9v1NRAJNC1xTYxIQh/MmRUlmFQE3qWOW1nqB2TWk1/3tgJV0waVvkFIEeZbHq4ElyKzAmEXOx6gnEVJuWBzmkRJBRPYGZBDsVaOlpSgVJE2yVaAe/0kx/3azBRO0VsbMFZE3CDSZKweZfYIVg+DZ6v7h9GDVOwZPw/PoxKu/fAgwALbDAXf7DdQkAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-_page { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpsUztv01AYPfdhOy/XTZ80VV1VoCqlA2zQqUgwMEErWBALv4GJDfEDmOEHsFTqVCTExAiiSI2QEKJKESVFFBWo04TESRzfy2c7LY/kLtf2d8+555zvM9NaI1ora5svby9OnbUEBxgDlIKiWjXQeLy19/X17sEtcPY2rtHS96/Hu0RvXXLz+cUzM87zShsI29DpHCYt4E6Box4IZzTnbDx7V74GjhOSfwgE0H2638K9h08A3iHGVbjTw7g6YmAyw/BgecHNGGJjvfQhIfmfIFDAXJpjuugi7djIFVI4P0plctgJQ0xnFe5eOO02OwEp2VkhSCnC8WOCdqgwnzFx4/IyppwRVN+XYXsecqZA1pB48ekAnw9/4GZx3L04N/GoTwEjX4cNH5vlPfjtAIYp8cWrQutxrC5Mod3VsXVTMFSqtaE+gl9dhaUxE2tXZiF7nYiiatJ3v5s8R/1yOCNLOuwjkELiTbmC9dJHpIaGASsDkoFQGJQwHWMcHWJYOmUj1OjvQotuytt5nHMLEGkCyx6QU384jwkUAd2sxJbS/QShZtg/8rHzzQOzSaFhxQrA6YgQMQHojCUlgnCAAvKFBoXXaHfArSCZDE0gyWJgFIKmvUFKO4MUNIk2a4+hODtDUVuJ/J732AKS6ZtImdTyAQQB3bZN8l9t75IFh0JMUdVKsohsUPqRgnka0tYgggYpCHkKGTsHI5NOMojB4iTICCepvX53AIEfQta1iUCmoTiBmdEri2RgddKFhuJoqb/af/yw/d3zTNM6UkaOfis62aUgddAbnz+rXuPY+Vnzjt9/CzAAbmLjCrfBiRgAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-aac { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNp0Uk1PE0EYftruVlvAUkhVEPoBcsEoLRJBY01MPHjCs3cvogcT/4qJJN5NvHhoohcOnPw4YEGIkCh+oLGBKm3Z7nZ3dme2vjOhTcjiJJvZzPvOM8/HG2q325Dr3kLp7Y1ibpIxjs4KhQBZfvV6s7K5Vb0bjeof5ZlcGysP1a51mifODybvzE8mzCbrAoTDIThMoGXZiZ4YSiurf+Z1XeuCqJ7Oj+sK3jQcNAmg8xkGQ71mYejcAB49vpmeuzJccl0+dUj6KIAvfHCPg3N+uAv4vg9BOxcCmfEzuP/genpmeqhEMgude10Jwm+DuUIyUdTlqu2byoMfX/dRermBeExHsTiWNi3+lMpzRwDki8zxCIATmzbevfmClukiP5NFhJgwkjeRTeLShdOoVJqnAgwkgCAZ6+UdLC9twjQZ8pdzioFkZBHY3q6B3l4dJEEEPOCeD4cYVH7Xsf15F+FImC775INAJBJSkVoWo0QY9YqgiR4ZZzRaGBkdwK3bFxGLRZUfB3Rm2x4x9CGtsUxH9QYkKICDFuLxKAozGZwdTqBRs2FbLlXbiPdECMCHadj/AaDXZNFqedCIvnRcS4UpRo7+hC5zUmw8Ope9wUFinvpmZ7NKt2RTmB4hKZo6n8qP4Oq1HBkKlVYAQBrUlziB0XQSif4YmQhksgNIJk9iaLhPaV9b/Um+uJSCdzyDbGZQRSkvjo+n4JNxubGUSsCj+ZCpODYjkGMAND2k7exUsfhkCd+29yguB88Wl7FW/o6tT7/gcXqAgGv7hhx1LWBireHVn79YP6ChQ3njb/eFlfWqGqT3H3ZlGIhGI2i2UO/U/wkwAAmoalcxlNA1AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ai { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk5JREFUeNpsU01vElEUPTPzZqBAQaSFQiJYUmlKYhoTF41L3Tbu/Q/+AvsX3Bp/gPsuWLrqyqQ7TUxMtAvF1tYGoXwNw7wv7zwYgtKX3Lw379575p5z77O01ohW+/DVh8zj7aYKhflGdG9ZsGwLNydffgVfr19YHvsEa+Zu/nxndob5StQK+dyzvZzyw/gKlmMj7IygFM+xvNcanp4/t5dAomXHBy2UUBOO2MAl/B9/cPb6PULuoHx0WM0e3GvpUOxD3wZAJWutZqYUYmqpSg5OMgH3YQObL59W0/ullpryR3HegkKEqiWBSGV4R3vQ7sIhScTZFTpHx3A215B5sluVY/WWMg7+ATB/lcLsKpTonHzD+OMFEuTz8ikkt9Kwt9YJZB38cpBdoQAZJdLvCGByfoPB6Xdk90pYy6Xg3c/DaWwArg09DaG5lCsUFN0pckZAojdC8m4auBqaALuSgez7VB1RtDSUWOQvUaBLFUzJBMJ2DwmPgd1Jwm0WoSgJfjDvrTKxtwAIyEkAOQ5hU//Zdg5uowDlUNMnwZLW0sSuUuACYhwQRwFvJxupCjEYUUccOkoaKmdOlZnY1TkgAcXAhxhOwLsDsHoN3u4O5JTDfVCH6I9nfjId3gIgSUATFJk/hVevGtOMwS0XwQ3AzB/FrlKg8Q27I2javVoZrFgwD4qVipAEyMlnaFArzaj/D0DiMXlJAFQyK2r8fnMMRZp4lQ1MaSL5tU/1kqAkMCh2tYI+7+kh70cjPbr4bEZ51jZr8TJnB9PJXpz3V4ABAPOQVJn2Q60GAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-aiff { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAohJREFUeNpkU9tqE1EUXZmZpE3aTBLbJFPTtFURtSCthr7UCyKKFJ/9An3og6Ag/oXfoUj7og9asCBYKT6UIPHaWtpq7NU2aZK5z5wZ9xxMpMwZDuewz9prr32ZiO/7CNaDx3OLt6fOjBqGg/aKRCIInp8+KzfKH7fudnVF58nE16el+/yU2mBFSWZKpWJKVc0OgUBo02K4NDmU6o75Mx+Wdu9IUXFeiOA/pn1xHeYaugVDdzpbp91qGlAKGTx8dC19/Wpxhjnsxj/RRwk85hGJC9d1O6fneWAuoztDYSSLe9OT6SuXB2ccx73Z9uukwDwfls1g0xZIY/Ad/Gnyt/XVfbyYrSDRE8PExHB6/8B6QuaxIwRBFMt0iIAiMx+LCys8jfGJEUik2WpZOD2SQf9oDtVqQwopCAiY66FS/om3b75CVS2MlU7AJ2WiJBCZjZ2dJuRkDJZFwFAR7UCBja3fNfxY2YEoCtRCj9em3Tpds6FpJseGCBxS0GgYGBzqw62p84gnYnAI2CSbSbPhEpFAaE2zODaUAlWWwDoS5DheGqbWpVE/0CmqCY9qkEyINBceb2uADRNQ8bSWAVVzIFKomCQim+0luS4yKYlsHlRyZo7EsSEC23K5vAsXh/H92zZkuRvxeBS5nEx2yp2KqhxPoV5TYS/8CtdApylM9sZQKKSQzyeRTseRV2QoAzIYY8jme5DN9fI0dQoUIjANGydP9VM7PZw9p/AiBpNYrdbw/t0yTJqRtdU9UrfJCUMpSJIgbWzsYe51BcViHzLHeqCRqhZ1YX1tFwNfZBxS9O3NWkAcHqR606k/n/3coKAoV/Y7vQ/OYCZevlrmv3c0GsFh06u3/f4KMABvSWfDHmbK2gAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-avi { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpsU8tu00AUPXZcN0nzTpq2KQ3pAwkIAnWHqCoeexBb+AQ+ABZ8A2s+AIkdm266QUJIFWKBkHg1KpRHi5omJGkbJ3bGHj+4M1EQrTvSyGPPueeec++1EgQBxHp+/9mbyuriRZdxjJaiKBD3W+u1+p9a856max+gDO8ebT+WT20Ezi9NZi/crqadvn2MQBAGfpCOpqNru2937vxPIpY6Onjccx3Twck9MBiSU0ncfHirXFmZX3Md9wqCUwiEVN/zaQfHt0vfbBe5uQyuPVgpl5Zn11ybL4/i/lkICOw5niQRGQShoiqI6Bo43W2ub8n3hRtLZT7gTynk6gkCX9gAOxpAnxhHZDwC1/aI1EViJolu/QhKRMHZ1UX0Gr1USIEn5FPWHy+/wTokkrQOq2vBaHZBN4hmY9Jwfr4An/teiEB45ZZDwDiMhoExT0N+sYDCuUkkplLIlXP4/XEXdo+RUhdhBSSfUwtVTUG8MIHK9QVqI7D/uY6vr2pwmCPrkz+Tk9gwARWQ9WxppbXZhNnpw+ya4A5HZi6L4lIR8WyCcL6sTZiAWjWgAmpxkn5+kqTamK6WkCwmERmLDLvjB0ML9ikWXPLFuozYOap3L8HYN6DHdbS/d5CeTVBndBz87FCBLYkNTyIjBQemnIEsSY5lYrK1+UoWcToLMjEHAyIQ2BCBSx/NVh+ZUhrqmEqBebS3WyhdLg0zt/ugAaIklsSGLHCLa6zDMGhZ2HjyGsnpFPqNHnY2fmHv3R5SMymYbROszSQ2ROAY9qHiofvlxSc5xsKKqqnY3diRE9h4X5d/pzg7lnM4ivsrwADe9Wg/CQJgFAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-bmp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmZJREFUeNp0U+1rUlEY/13v9YV0vq2wttI5CdpL9aEGBZUDv0df668I6n+or0UQ/RuuD0EgVDAZrsKF4AR1a6COKW5qXvXec27PuVeda3bgcF6e8/ye5/d7niMZhgExnK9fbTrm5pbBGMZDkgCyq+VyhTUaT6Eo2ZHJePPWXJXRhez3B1yxmM/QdctXUSCgtV4Py4CvY3cky4e1x5DlLCaGbbzjXDcousG5OQe5HPRSCQPK4PpsEM/XH4WvhS4noeu3JwHGGRiULhsMoKZS4I0GtEIB9mgULJGA0+9DPBpBT7sffvf1W/Lg6OgJufw8C0CRGEXWazUwiiyFQjA8bsjVKjaJzovMD/Q5gxyJhG2cvyeXe2cAuADQNGBmBvLaGuTFRaDfh31lBTWi9pumjbK0B4JQul3vOQpM8JdskOLrdCvDcDjAsjtg5TIkoiKLaokMNR2cnZbqNAMycqG7XbHKR2fMzwO/dsxSwu0BiBJsNsv2LwAJAJCI5ux2gXYbqNetcz5PoORI1cDS0n8AxGW7A+zvEYBKZ2ZlcsEtJLbedMjePBaCTQMghx45ulyWkzxMVUQ2RMQhLfFO16YAqCrixPnm6iqKrRb2W23EfF4cUNSrHg90cr7hDyB33MTnSmUKALVs4uIlROjxg+AsPhGVl3fuIl2tIOB0Ya91gkOi9mxhAal0ekork1ic/kGLBORMxy2K1qS9V1ZQbNThIj2EGh+2tsyOnSai8r1UxMNIBB+LRTTULr4Uds0K1tU/uOLxIrmbNz8XXSrnASSpubG9fbKRyVh1n/zSw29t9oC1b47MfwUYAAUsLiWr4QUJAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-c { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcxJREFUeNqEUk1rE0EYfmZnkgoJCaGNCehuJTalhJZSUZB66a0HwXsP/Qn+FM+9+hty0LNYCr2I7UVLIW0Fc0hpQpSS7O7MrO9MspuvVV8YMnk/nn2e5x0WRRFMvP/w6WSz5jbi/9NxfP693Wp3DrJCnMW5d28P7a+IE15lufR8o1ZEStwPhkWHsWbrZ+eNEPxsuubEF6m0TBv2Q4liPofXuzveulttSqW2UwH+GjqC0horpSL2njU89+FyMwjlTlxOJMTa9ZQHzDQIjgwdom9zLzfXPc75kbnOAswBJTlC2XrqQRMLxhi442DgB4UFBhgPpm3B5pgBHNUUxQKAHs8pHf3TEuFMetM9IKr/i2mWMwC0SnuSFTG2YKyppwKYVdGO7TFhzBqGIenVeLCUtfURgErucx5ECKREKBU4d3B718PHz6cICGT/1Qs8qpQtGOdyhtGEARWDQFqQJSeDL98u4VbLaKw9IRAJPwjtoJGlVAoDQ800+fRFTTYXcjlcXN2g++s36p5Lzzlve1iEROa8BGH1EbrSAeqrjxEqicHQt8/YSDHMpaNs7wJAp9vvfb287idboAVkRAa5fBYXP9rxO4Mgf0xvPPdHgAEA8OoGd40i1j0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-cpp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfJJREFUeNqEUs9PE0EU/mZ2WgqpXX+QIDFdalVslh8NlAOQaOKFAwfvHvwT/FM8e/U/MOnBmwcj8WD0ACEGghIkbU0baaEthe3OTJ0ZWV26q37JZt68ee/b9733yGAwgMbL12/fz+azbnAPY2Nrt7Zfqz9JMrYZ+J4/e2pOFjiciRvXlgp5GzHonXk2o6S8V6k/TjBrM/xGA4MLyeOSPZ8jkx7D+uqCU3Amy1yIYizB36AlCSkwfjWDR4uu40yMl/s+XwjeWThQQ4Z6QNSnSkYykcDXasP4lmfvOZTSF9q8TDBEFPbN5bOqCglCCCxK0TvvZyIV4CIxbgpC+4gm/PUmFCIE8iJPyME/e8Lon9j4HvyHYLjKSwRCSEUgf9+15mFbx8QS6CZJMzJ9SlBCwX3fJDLG4PX7ykcwkmQmJtpEhWa7g1dvNlSwjwelebz7tAXLolh0p/Fxe9fErK2WDFGEgKjxfNjegX0lDTc/heNuF99/HGEslcKXwyoazWNDdlCr6+DoJgrBzdI0T9rYO6yg2zszMlaKM3Dv5OBzbuyZuzm1B16U4Nzz2f3cFOx0Gq12F9cztpExncsqYoaHpSIKtx0zJdVIFpHQ6py29muNk1uTN829o/6SHEnh80HFaE6NjmLnWxUJy1LyTltB3k8BBgBeEeQTiWRskAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-css { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNpsUktvUlEQ/u5DoCLl/RAKKKUvWmIxjYntQtcu3LvwJ/hTXLt16coFC2PsojEaMKZtCqFaTdGmjbS0CG3By+vei3OOBSGXSU7uzNyZ78z3zRF6vR6YvXzzPrMUCyf68bB9zO+VfpROn5hkOdfPPX/2lH/lfiLidztX5mN2jLGG0rKLENIE8liWpdzwP7HvqJqujmvudFU4bFY8Wk1FZsOBtKppd8YCDNu77CZevd3gflfTUFcUhP0ePLibiIR9rjSBpgwAfe4dVcV6dhtep4PH5msylGYLrzeybErcT85FYiH/CyPAf74gObC2vMhzsiRhPhpC6eQUM+EA1pJzILEnjRSuJsju7MJqsUCSRei6Dp3yXqcdGlHZ/rLPazQWGCn8+6YW4pAkEW0SjzUzanWlCa/LgcR0lNfovTEi6lcIkzesnM/R8RlN0INGp3h4DHoDsE5YRvQyiKiRSMzikRAOS2WoqoZWu41K7RwzlOOAVDMMMHhIGvFlRxJFrKYW0ep0IYgC3SDh4b1lTJjNfENsrazOAMAw680mPuW+8lFno1P4XDigRhOiwQAyJK7TbsNS/PaA7giAIAhYz2yRgBIfsVA8wIetPG6FAqhdNrC5u0f+TUyHgyMTDDToEt/ftQsEvW4EPG5OZcrvw0mlimarTXkPfpXPcNlQoGtjACgpryQXsPNtH/nvRXqBJpoKHMzGNkNB0Odls7LNyAYKpUq1dt1iuvB7fRDp9kr9D1xOFwkpoksXusmXaZWFn0coV89r/b6/AgwAkUENaQaRxswAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-dat { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNqMU01PE1EUPe/Na0uptmlASg3MoiZgCA3hQ8PHAjbqwsS9C3+CP8W1W/+BSReyYUPwI4QAVkAgUEgIbVIg1FZb2pl5b3zv2cHBjsaTTOa+e989OffcGeK6LhTevFv+OJoZHPHOfrz/sl86KpWfhxnLe7lXL1/oN/MSZqonOXU/k0AA6lfNhEFIrlAsP2PMyPtr1AscLpyg5pbtIHErhqez4+awmc45nI8FEvwNaiQuBHqTcSxMjJhmX0/Osp1xr878FxWEzwMinxAzEA4xFIpnOjedHTKpYbxW4U2CP4j8uWxmUKsghMCgFI2mFe9QgHZj0Ba4yhFF+KvGJToIRLuPC/efnjD6+26wB1Lq/xgbSCBXKeWJG/OTdky8cWTdT3C9RmWSGk2XCLlWo4xTNbfN5qh7PpXM72GjZeHt0gpq9QbmH4whGb+NpU/reDQ7hcWVVXxvXOHxzCQopQEKXKEbL6o1ZIcy+LC5g62DY2zsHeC0fA4zndIrHOjvg2XbAQRSfsuy9XxC2qzi/H5B6/68W0AsGkW0KyJPBLbDO0fg3JX/CUM81i0bD6WKe6j9qOPJ3EMcF0tSNsFA6g6alqW+VtZBUL78Vtk+Oqne7U9rs5qOQCjSheJFBeFIFOfVujSUYu3rIc4uqxWv76cAAwCwbvRb3SgYxQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-dmg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNpsU01rE1EUPe9lkk47yWTStCmtNhFSWxos2EXVhSsRcasuxYV05V8Qf4DgD/AvCK5EV1oFI7iUBqmCNdDvppq2mWSSzEzy3vPOpFFq+uDNfR/3nnvueXeYUgrBWH1/9/NE7k5BKRnuRcfF2qdnmJq9DeF9tQ+2isuMsxXGWHh/a1mEVsPJSI5fSU3OPEj291IIlN49RXz0KqzEQjIeZS/L5Y/3wPGhDxIM/i/A7fZWgVG0t5EaG0ZUa0JGM8gvPrZmLt58QYwv91mfAqCIE0sAqgumBFITGQzpUYhuF0KfRa7waDyXXXolpVrsh/0tgSLDr5I+wUZo1UHCSkAficPzY6juFSmbRPrC/azjq+fkcO00gAqoU7B0ETKkfWbuCTjTYeq5oESAauexcTScX+ZACWFm0YQSLZKhHdr67+/wW0e0dgjYo3sCEXXybYtBDVSHLp2es3IpsILS24c42lkBg6DzRjgRzCDZ/xr0GNRJwwYiWgzt+hYMawleu0V3wbkT+kUirOc7IGJAz68R/Qak1BAlx3hqASPGBJRXpXOv58dkz3eAgQoOm4hyj57NgZm0MHvpBmK6QdUdg/DAg9cRkhicBSDaKJdeo1bdxmR2DtWDDUxl51HZ+QHTysD3XdQO95Gfv06aeGcAdBrY3Chi8lwO3768QWX7J5q1XWyVSxgajiOXLyBG2hzurRKV9lmt7ISNkkjo6HhNyjoK+2gXRsKE57ZIE2ot10Z1fz0Ue4ABVw3NMjnW14rInh8jTYywoTg3EOFpOM4mXNfH9PQUfGlrAwBOs3I8ljbtuMWhRWzIIPrkn+GcYcgIWEowbZ+0qB334/4IMADESjqbnHbH0gAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-doc { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAppJREFUeNpsU79PFEEU/mZ39vZu77g7DokcP04BBSUmiEKCSCxs7Ei00JAYO2NlTKyMrX+CJhaGwopSQ0dMtFEsbDRBgiZEQIF4IHcg+2t2Z8eZ5QDlnM1mZ9+8973vfe8NEUJArfSNhzPG0VIfeIiDRSDkw1cWVt3N8rhG6SdSO2Gvn8dfuueqZwuNZqk3Jxg7iNcIfBbgXD6ZC8u5qffzX8eoYeyDxC77uygKhcouovgVUQj1H4YB2ovNuD9+tTTU0zMVBmG/+C8AIYh8F361DL/yE5HnADKYlVdg6MDAmW7cuz5WGuw+PsWDYGAvbL8ECFUt4K7/AHd/I9c7BLaxinD2Ld5Zo7g78RLuRhlBS2cpWbGfStfhfwCEpK0nUjCbWuGsLciSOELPhkq/YgdY3l6HsLfRcLYf+pHNbH0JigEPkLAyMsiEJ7NrqQzM1i7wyhoMZqOhvQs6Z0ovXgdAJACRoulEg5HOwrOroKk0zOY2BDtVpTF0CU6kLkQJXa+BNEoG0lMSsBBKQXWNQktmoGcaYeSaQCIVWOvUYQAiWZFQtk5mSMoSzEILtBrTfEcviC5bwVwQmoh96wA0ic5dB57ngeoaTIPCdb34zDITYNLOOIeVSsW+dQC+7+NSWx6jJ4tY/rWNV7PfcGv0tBoPTM7M4eKJVgx2FTE9u4QPS6x+kHzfw/mOAjarW2hJG3hy8zIceweuY+PRtREMdzbjzcd5WBqPB6xeRGUMGRzHjWvMmxQ7tiOF1JBN6FiTd6Sy9RuFbHpX7MMMqOD088Ii+op5OUAO7jyeRGfBwrF8Cg8mXuDL4neMXzgFwhwZz+hf7a9d5yu3Z6DTPjVQIY9k7erO7Y63Lvc8ErEeyq6JaM6efjai4v4IMABI0DEPqPKkigAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-dotx { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAndJREFUeNpsU01rE1EUPTPzJk0y+WhMStW2qdVWxUVEQUF0I+4ELQiC7lz4N9z0T+hG9wrdZKUgLqulhrbSag1CKpT0g7RpYjqZmffle5NEKdMHlzfvvXvPPffcO4aUEno9f3Vt4dTp+BXOe+fB0u/NbVpv7h89NU1j1TCM8H7+xY9wJwPHZMbOjRadLAvE/2gToJTiTPx89k+OlVd/LT+0TPIPpO/SzyQk40xCMxBSZ9Z3CoAx5DOjeHT7SbE0XSpzwa8OWB9jINELolQg8AR0EgUKn1PIlIWpkUt4cPNxkTOU12trs8p95RiAXpqaztqou8q6SKQJJmZSqGwsodFsIJk1kcyLYv7IeafcLx4HUNkFF4jFTExMZ0B9DrfD4HUEusYhWs4GPEJg5wly/tBYRIOeDhpEwlS34xcyajdQr3UwOT2MlJOEBRuGNHWp9AQRVXDfQiFV/U5GBSiQ5p6ngBEa5z3fiIhC6g6IMDBwOdoHPkYnHPVyhN0tF7E4QSpr94CEOKELffq+y9Bq+DCJ7rWBoQQBVbPR2O6G4OlsLASJMtCZfQqm0NP5IVWnamdAkUxbyuIYtD7wWegb0YAzAVMkkI6NwPM9xEwHloyDGAmk7AKS9rAS0FKOdugbYeAHPu7OPEM+MY7q3hIKqTFQHmC3XcONc/fxdfMDrk/ew/edzyhvvTmBAddocVRqH3Frahau56qpZDho7+PnTgXffi/gbHYmLEvPSIQBp5JU62sYz13G609zKBXvoOMdYn2zgm7Xg2MVML/4Eu3uPgxhk2gXmNl8v/i2pcXTP8tKdTEcbWLZqDQXwu/l6pfwbEnSGsT9FWAA4mdHv2/9YJ4AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-dwg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpsU0tPE2EUPfOg006hD4rQh8WgbCSwkKgbF2owujaCiQsXxpX+D6MmbtXEsHCLmIAbE6NLo8YlGIxREIshIqVl+mQ6j8/zFVCb4UtuZua795577rl3FCEE5Bl79vPd5LHYiOP7cH1AUWi85ytmvlas1bJ9E5ryBntH3BpuP/X9i7ovkluuiE8N9SDepaLpCcRCCqa/VDCaMuIjSWP25Upl6n+QDoCz6Yh7KKzh3sI2LuUimPtRRyaqodj0MDloYiITSTi+mH29Wu0AUf9CsZPJoW5czJl48LmCc5kIKo5Al67B9gUGYxrun+5NnMlFZ+GKiQADj2a7AquseLIvjMv5KMaSBu4sWVir+3i8VIVKYSby0UTdFU8Znu8AYBHQgVOJEN5uOXi4UsdawwU0FSf6TaSoyw6DRvukPkgGWpDKy4F8a3jImCrqFDFn6rhKPR4VGnhvOTAY3WLcjifcQAsqRfhUc/Gq1MKNbBh9nIAMDjEppocxs9HCMktfGTCwP/oOBkUKNk/qF3pDYC6Ktk8RfWzyaaoKrqdDaBDwya8W1m0/CPCR3kFy7CcnmWQRUJqcRJFUKtTnPCeR71LwoeYF92CYyVnCFZpCTrRtCv5to2St8SOrKxiPqEEA4fkYT+mI0rdoeUiH1XZVuQPpsIKqw2QmfifTsnOABiWySlH9uU0Hh2MqjsZV5LtpPSoGeN9rKnhBX7ehoOSLIIPfnGONXGMMWN7xUfVldYDbjM3mrh5HCDgS17DhHgDQcIU+XbBxnDTn1x1UuQcJ9iv7l5Q5e1zLGri92EDJFnoAgHtcfr6wbbVXUqq193+0z97n3UJt1+d51n7aHwEGAAHXJoAuZNlzAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-dxf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo5JREFUeNpsU0trE1EYPfNMmtdoH2kDNmJbaVFcaBVFpAsREQpFwY0bu3HjQnTj1mVd+ANcuC3qQixmry6E0kWFVIQ+bKy2tbFJm3emyXTujGca+4DkwsedfLnn3POd77uS67rw1vC79ek7fZEzpu3AYUqS9tKQGZPLpa3VXP0uFCmJ/8t9OLC3q/uJbcs5bkIybvdHoMsSbLKENRmvU2WcNnTjRFD7ML1WGSPJHI6sA4KRWMAWVDPxLYex3iCmfpuIh1QsFSyMxQO4GvXHHwOJ6XWSyIck8v6HQsnjAxFc7vTj2VwBg4aG78VdBHQFCk+dbVcxMdwev9gTSEC455sIBOu2KLsoJFzqasP9vjCeDBlYqzn4VXXwarGKZN7Crd5QfLDT/7KpBM84c9fFUFjFp2wdk6smflRsKKqMa7EgfJJ3Ac2OKlit2pEmBTQfngdpnupoU7BUtRGiiTe7fXiRqmK+KuDn6TpvYogmBRJcrOwIJLIWxmM+dOsyLKryQAaJpjJ1/AxrGO3SqdZt7kKZJrzJWBg5piHENuY8vV6e0UOye1TyftvC5l+gZB8SHJTwpSx4q4JeTUKaxhXoR57h7Rn+3iFolJ3xvPhab6HgJG/pJ7jsNP4sUX+jZiCgEsWd/DjH5IrSYpBUAr0yHpzSoXKOP25a6OBhndh0zcX1qIYM2RIbu6i0KiHD5B/GTMHG03kTGpEL7H80wHFOWwhqDZ+SpkBOtCDYJDhZE4gRcKNbYynAqbCMbXpwpVPFbEng0aKJGbYzK1p4wIegLlcEPmdt+DjXbzcsxFlCynRwwVAwW6hjqeg0Zt521SYCWCJvbe0Un29UDx7Hgrs3IEitHXkw3jOv2fl92D8BBgAJeyqBh90ENQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-eps { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNp0U01vElEUPfMFCEVArdoSqEA0KV246UJdUJM2Lo2JK/9FjXu3utJqTNz4D9worrsQExbFpAFT0TYp0CZ8pIAiyMfMvBnvm2Foa9uX3Lw7c98979x77hNM0wRf7ufPsq7Z2SQYw2QJAkDxQalUZa3WI8hy3gmZr15bu+z8kILBkCeRCJi6bufKMji0NhwiCQR6iitdatTvQ5LyOLLEiWcYukm3m4Zhmbq1BX13FyoxuH7xAlbvpqKRK1fT0PWbRwEmDEyiy1QVg/V1GO02tO1tKLEY2PIy3KEAlmJRDLXb0TeZL+n9g4MHlLJ5HIBuYnSzXq+DlcsQLk/D9Hoh1WrIUjlPcpsYGQzS3LWoaBhvKeXWMQCDA1D9pt8PaXERUjwOjEZQFhZQp9L2yERiqYRCkPt/z58ogTGqHQLE1BLgUmC6XGD5AlipBIFKkbhanKHGYLBDqQ4ZED0OAbfLlo8OIxwGvhVgyTHlA3xkomjH/gegBgDURMv6faDbBZpN+/tHkUApkdTA/PwZAPxntwdUyjYA/+ZMqJHjLgM9iv/6zRt2GgMaIE21aVIjnSm0DGPfmhzyde0UAE2Dj+p7urKCPvkZku9eJILOSMUnkvVhIo7GYIB3xSKYdhoA1erXGVKXpvFxZwdBonnD68PQ7YEwM4O4xwMPxc8RYE87g4FIcz+kvfmnA0YzIJIy77/m0OCqsTkkCTysKPjJG3viLei63Gm3kCO6UWqcMejjxecMPmxsoFKtYop6UNirYL9Wtc5OHqzznIXHq1na7OfMJROcK8a6O7MjW7nfzZdrd7jzT4ABACh3NGsh3GcdAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-exe { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNp0k8tPE1EUxr+ZzvRJO62lUAQaKIQ0FVJFjBBdoIkrDDHuXJi4NnHtX+HCjW408Q/QmHTRaCRRohIJifgiiBICTQu29mHfnc7MHc+MlECKdxZz595zf+c737nD6boOYzxJLC6Nhwej7e/24HkO779s7G6mMjcEwfKZ21+/d+em+RbagaFev28qEpZwzKg3ZckqCPH1nfS8hScIdyhBe6JqTG3PfyTTeLrwFhvbKdy9/xi5QglXL0yGJsKDccZY7LDIAwWHpSferWBh+RN8ni4UylVER8MY6PHj0uSpUK0hxzfTmWsUtnoEwO3rer64jEyxim6/Hy67DXaHExvJX3jw7CX8XjfORUdDlOohhU4fAVjILCPbm9V1yIqK2FgYt+ZmsZcv4lH8Nb5upXD7+hVMjIRQa8qeDg8UTYPU5cTcxSk4nS709XTD53ZhpD+IYMAPj+TBz93fZiz5oHV4AP1fGdlyHZIkIZkrI7GyhnK9CZXy+Aig6p1+HQAY003AcF8AVtGGfLWG9XTO4MLZ5cL0WAixoT4zVmPHADSiMo3hzHA/xgeDWFjbNg8H3A7kKnX0koEcPdTu/ylgRGZgOjNv38zoSXC8BZJDRKOlwGEV0VJVGM0y4joAPO1spXbx6sNHeD1uRIYGUCxVSRlDt1fC8rfvcDnsmJ+dOaLgoAs6AVLZPJJ7WdhEkUyT8GJpBflSBcVKDTvpDBw2GzQqQT1OgaZqUOhtFQUTUKnVTVWNpgy51YLVKph7sqKYkA4A1ScEfT66vm5kC3+ofh6Xz59FQ5bpkvE4QW3M5Apoyorhl9ABIKnFgNdTOh2NkJG6WSf9eRBJtmFwLDJmriUzeaOkYvvcXwEGAIVNH6cDA1DkAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-flv { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsUl1PE0EUPbssLYUCXdpaC9gWoSTgAyFigiRGY+KjvuuTr/4A44MP/gx/gMYfwIsan0RjIjGiJIZgSIGFIoXSD0t3Z3dnd70zpITazuZmJzP3nnvumaMEQQCx3jx69SV3a3KWMxetpSgKxP3m242Do43SQy2k/YRydvds67n8a63k+FRSn7l/bdg5tdsAuM3he/5weDC8vLdqPLgIIpba2niux52mg//DqlsYSg3iztO7mczN3DJ3+ByCLgCBH4hOFEF7cDpzPCRyOpaeLGXSc2PL3HbnW3XaRQCPEgWI2MsRVAVqrwbX9bHxbhOKpiJ/bzpDOr2k68V2BtRNzMtqDEqPejY/4zSGjb54BM0mQ8k4xsDoIMauXxnqYOD7PmwScP31d0SS/eAuh1lrolFpIBQNQw2pqJdqsAlIceB1AJCIkkE/FZskXDQVRXw6IYHiE0nBEcaPXSSvJnGwWkQXAE4acAhbxPMJpOdHweoMhc9b2F8zwKizbdlyPLVH7QLg+JKBYzoorxzjz3oRzUoToaEw9KyO8XQW5AE5jrFT6AbAYVVNxCZ0Ka3So+DSTAoDiej5ywTySbls1OEDobhFlMcXxrHw+AbINEjNXgb7y6BndLhk8cRkHHbD7g4gEhiJFxsdhrDqaamBaDKKerGGSKwPI9kR9EZCaNA5ubE7A5s8IFhsrxQkgJhZoa/06xC5xRz2v+3BOjFlbqcGlquxsondT9vY+2pAJdeZR6fI355CgQCN2A4O1w7gkQ7cdLUOAKdhV6uFSv3kd/n8mT68eC8dKWLnY4FsfeZQh7nVVt0/AQYAsf5g+SvepeQAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-gif { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmVJREFUeNp0U0tPE1EU/trplAqlL0laiw40xASByEJIZFGVnSvj1j+gWxNXJq7VrbrwF7h10cSNhMRHojEuACVBKmH6SJQyJeXRxzzv9dyZPiCtN5lMe8853znf953xcc4hztDzZ1+C6fQMHAfd4/MBFG+p6h/n4OAeAoGNToi/eOm+A50LKRaLh6amoty2vVpZdotNXccMEK3LwZxa2bsDSdrAqePv/mLM5tSdMwYBYqyvw9zdhUn/L59P4OGtG8qlZCoH254/DdCdQBCxqZu+ugqnWoW9swN5ehp2NotgIo6bGQWGtaS8+vQ5V9a0u5S+1gfABEilAqdUgm98HDwUQkDT8JXoPPq+BoM5kCYmFT9jryn1+hkAt7heBx8dhbSwACmTAUwTgdlZ/CVKJaLnI1GD8TikZiPSR8Gxib8chH95mZTxgwWHwH7+gFMswqcokIRbjMO2HDCnZ1VvArpjEmnKZc8+cZJJYGsLsMiZ8AgwEqaY6Mb6RQR33JFhGECzCRyfAFXNu9v+RVNRZWIMuDJNuYMAaDycUFGhCOgtuAtFVDA83G5A8TrFDw+F5QMAxAKJJxz2xnW3RPJGbm+rCyjotZetH4DGzaSSeDA3h4Zl4R0JOEZWTpIzF4n/m995bNdqZwB6m0gFft3Ak6vz+KYWwFsGlqIxXItEcDt1ARMEtKdVgZb+fwA0G2C2hXM0ZTZNRcSf0b1pmXi7uYnjI+Lfanm5fRQsK8BIxKcrK7i/uIgP+Tw+FlREqHN5fx/vyU4uHBE6UO4gDWqk/JFaLuMxcXeFk6TuJ90V0HOk1in7J8AAjmgkPfjU+isAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-h { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbRJREFUeNqMUk1Lw0AQnf0woK0ttVqp0hwqVCl+UBERT94F7x78Cf4Uz179DT14F8WbYHtRkBYRLNqDtdaPZLObuLs1NGlXcWDJZGbey+x7QUEQgIqT07PL5WKhHL5H46J+22q22vsWpbWwdnR4oJ80LNiz2czGUjENhvj4ctIE4Wrj8XmPUlKL9nCYcOFzE9j1OKSTCdjdrtiLdr7KhVgzEvwW6krC92E6k4Kd9bJt57JV5vFK2KfRQRV+RAMkzxglYI1RaDy2dW1rpWRjQo5VGicYIorWVooFvQVCCAjG8Omw1MgG8AM0uSBUDSnCfk/IGCHwf3DCD/7UhOLBrFkDuep/hDUSSCv1iYo4rIfqGwmUSNJjfYbBcQKhZw0aBMA4B48LwBhBt/cON80HmM9NQ6fXg/Wlku4TwmNWDzaQqzHG+0PSKod5cH5Vh2RiAhYKc8DlV1UPSyuFMGygVlMg1/P6BC6DqXQK8jNZDXAYA1f21V34wMXYFaiyVw0rJyzLgs3VMkxOjGtix/V0XWChZ0cI2i/dzvXdfTd0Qf91BMPrhyNzgKfOmxaWypqaDXHfAgwAtCL8XOfF47gAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-hpp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNqEUk1v00AUHK/XKf1yZdESVRBXjRSRFqMQVBA5Ic5I3DnwE/gpnLnyG3LgXglx4UDDLZS0RWkDLiRxSusk9u6GXSembmLgWZbX7+2bnZl92mg0goo3b3ffO/ncdvyfjHef6q2Dlvs8Q2ktzr16+SL60jhhZ69bO8X8ClLC7w9XdKJVG8fuM0r1WrJG4gXjgqU1D0MGc2kBTytl+7a9XmWcl1IB/hZKEhccq5aJJ/e3bTu7Wg1CVo7rNLlRhUh4oMnXoDoyhoHGyWmUe+QUbELIa7W8CjAFlMzdzeckCwFN06ATAn8QmDMMMGlMuwWucpoCHNe4jBkAMenjYvRPTyi53JvuwX8AplleAeBcRFrH6rXIxLim9I/pi3QA1RhKaYxdjkN8IwalCMIwWs9ljMkh0wzk+9M7w179C3LZNXxve2h+c3Hu91HeKmD/6zHOLnw83ilB1/V0CeqU3Q81LC/O41b2Btx2N2JVP2riR8eTUxmi0TzBwrKZMsqMoz8MsDh/DWuWhUBKURLKxQIeOMWoptYPnS1c+INZBkwISomOSsmBZS7B+3WOzZvrKGzkMAiGqNy7g+LmRkRfekBnANy2163PZXrSbrQ6vch19Xz8fPDHyL39QzkHBKedXjfu+y3AAGU37INBJto1AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-html { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNqEUktPE1EU/mY605a+hhZTBNKRDApNrWIRA4nEBUZdmCgLNi4MK5f+FNdu3bFv1J1EXODCR1JJSMTwpqUP6NiCpe10Zjz3hj5Mm3iSybl37jnf+c53jmDbNpi9eb+6Ftcisea909bWNzNb6dwzSXKkhIt/r14+515qBqmDA8HpqKagh53XaopblpIbe+knDpFAhPab2Dw0TKvRK7lmNODzePBgZlK9oUWSpmVNdpIU8T+jaMsyMaD4MDcZVa+NhJMN00w0n6V2nN3yQgdHWZag+LzYPTomIAtT0THVtPGanmb/BbjwLFkvn2IttYGYplKyDzsHh7gdmyAWfh5zVq0Guhg4RAHFUhmfvq3j134aXo8bd+ITnMFOOovU5jbGRoZwNxFn1cxuAIcDW/sZDjA/c4u+BNxOJyxqaenpI3z88gMfPn9Hv98HQZS6RazW6kjExvFi8TGdDSy/W0Emf4LS6R8sv11BmfzSwkPcm74Jo9Ei0GZgmkw8QCOao8OXcaz/5vSZnPdnp3ApqBBLkWJE0Ci7ASzbIhCLLQ1E0iOkBDh9NpUgiUejo8oNuJwyn0YPABtn51UYFFivG3yBGCNZkuDtc/MW+ZQI3OrYpBaARCKufk3B5XIiWyhiL5ODp8+FfFHH+KiKSqWKUL8fC/NznGlPBmz+24dZjKnD0CJDcMoyW0SqXuMtHBFw7rhIAD1ErNUNafxKBNevapwu65NpEQ4FqXIA+RMd6VwBP3cPSERb6gLIFIq61+UqGWaFdcrVt/lmAuWjAi2aiMFwmOYuIJ/N6M28vwIMAMoNDyg4rcU9AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ics { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhRJREFUeNqEUkFPE0EU/mZ2dra7bLNpi2AxQFKalkJrohICiYkXPagXrx78Df4K48GDBzmQePLMhUODNxQ5ciEkJVqDtJGmMWrCATRbd2ecoS5u3aovmezsvu9973vfPiKlhI4XL7c2r5YL81LIELEghLA3u/udxmHnPmfGW/Wuv+LpwwdneRYBx7PeWK0wOYYhcXxyckGV1fdbnbuMsXcklqPRJQxFMKz4RxDCtVO4s3xlRjWoB0FYjlQPEEBieChwKCRGMx5uLtaKs1P5ei8IKlGa/YkXMXYtlTEDlsnw/mMXhBJcqxSK6vlcpa4PEpCooUyIqs5M6hG1o2CUwqA091cFcYLf/sjzcX75EiQIojI9779CTYR4jwTBf+r7GAwh0AxCiL6JMT/04vQ79u8aI2O/7Jzg69o6Go8ewycUahtBpADhHKLnK/eVbkMdtROWIv80NQ2sPhncA9Htwn+9hZG0rY6DzFwJl+7dhs0ZstUy8rduwPS/wd/ehmi3kwq4zTHiWUgXp+EuL8FvNvFl5Rn4xAS86iyI2kY3n0Mv48ByrOQmancdi8I0Kcj3U5iuA29xAelKCUHrEIayzltagG2E4IwkFaQgSC6lYI09iN0d8It5uNV5nG5sgJdKYC0G8WoTOZvBISFNEBxnsuzD3GX4vfDsszzqAu0jkJQDedCGbB6AWg54pYbPo+NGVPdTgAEAqQq70PytIL0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-iso { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNp0kstrU0EUxr/k5qbJzdPYpGkpsUJoA2q1oLjTdiGiIC5cuXHlxv9BEOrStTvBnQvRrSAIsejCrlqpsURq2hCJNQ+TNLm5uc/x3MmzJh34mDNnvvnNzOE4GGOwx8+t9XQkfn0VE0Y5/7Z+kHm+dvOhtd3P9c/xwNZh7nWaMYtNUmX/Fct/vlN7/8J5aRRgyzm8xzpRDjGE2aVH4VTqdnoUYg/XkEhmy+Cx3DhA5tMzdFolvg5Mx3Fx9SmH0JIg79Zo3j4GADMIokJTKtjbfAKXU4Y/2NvSfyH75TFOxa9Cmr0XnlPFl5ReOQ6wNMDsoFX6AElqQlNV1KsOuNwS/AGFjEUIDhmn5+/DMM16/9igBowAzFKIswPJr6MjlxFP3sV04gaP7RzMPe6xvWM1gNUBM2UKYlBau3QghGphg29J3gDlLLilWNdD3gkvIIDRhD9yGe2mCV0V4HFXuCxT5Dlv8Dz3sIkAs03FalDxBMQSt9BRBMhNncuO7dyU28c9tnf8C/Q0ZtR4GImeQSj8APLRH772BWcgiFODffCv/t8H9tO0v3RjV7VqkeeXLlzDfvYjj88uXhl4JwIsrYxmLY/M1gYclIvGE9jZfNPrSCD3/QgLyeWTADV6wW9AryIcCkB0u1Aq/oCPumlufoF72vIheaLDr4wCLIOqrYnULA14PSoqpSJEAUilZrD77Sv3LK+cI0+Be8cAbbmAOrob0agtD491LYfkoqvnyZLsWRkA/gkwABL4S3L78XYyAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-java { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjxJREFUeNp8U01v00AUnNiOEyepQyhQobRBSlVIoRCBEPTAjQsSEneE+An8FM5cuXLNoQduIAE3qopKNJAIIppA2jrOR93aa6/N8yZuUxyxkrXr3ffmzczbTQRBgHC83nj3ca28dD36nx6fvnzrNNrdp4oibyUmey9fPBezEgWVFuYLdyvlPGaMY4fl1aRS+9pqP5ElAkmcnknRwuO+Nyt5u/ETYfyj9WrpZnmpxn2/Ok1Swn/GvtnH5k4TLue4kNfxoFoprRQv1TzOb8cAIu3+ZD7oD/Hm7XuxzqRUNDtdkuLiTmW5tFxceBXlnXgQTAORSMt2oGezUJJJrK9dFWdEH7Ik4dB29LiESeUEJXd7/dAT3L+1ivlCHr8NEzutXTBvbJPPSdO/AH5wysChwM/1HzCGlmAzOrKxu2eCud6Z2Jke2MwThpUXL6Nn2ZAVFTlNw70bK0iRnGAq9qwHtOmTRpsx1NsHyKRVnNPnoMoK9kc2BjbD4vk5JGV5NkBoEPM4FFnCteJFWOS4ntHEfphQyKaFTWFLw704AJ26ZFx/ZEEi3YyY0O1Dmr4EKTUHA8hUnS6siI0DEHLYog+b28RCRuNXR/iQUpPUEQ+NVht6Lodnjx+GXYgDSFRnq97Ed2pXSlXhUSeGhxYc5sKlNXM5DGLR2TMwfZVPAIi+otGNWy1fEZUKeo4qc4ysI+F8VksLIJfYcD9QYgB/DNPMptWBlsnBIS86xmDMTBo/PWd0LB6VZfdEbJT3V4ABAA5HIzlv9dtdAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-jpeg, +.btfs-jpg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8luUlEY/s4dmMpkWxRopGJNNbiwhk1tItbGtXHr0hcwmvgOdWld6Bu4coXumtREE3ZKu8FgOlC1kIoXtC3jPfdc/8PUIpzkBM7wf+f/hsts24YczuerGUc0moBlYTAYA+i8sbdXtAzjITRtq39kr73s/Gr9DTUYPOeamwvYnHdrdR0SnDebuCbswJGqpX+Uf92Hqm7hzFAG/4TgNr1uCwEJ0trcBC8U0Kb1/PQkHt9JxSLnL6TB+Y2zAIMOJBGLXmtsbEAYBsx8HnqCGKVScAX8uHf5EpqmGXv18VO6VDEe0PXsKABN8+AAgiabmYFNNJTDQ2RUFc8+Z9G0OPR4PKYwvKari0MAgiY/OQGCAajhMNR4nDZMaInrKBGl70SPMScck1NQG3X/CAWLE3/dAWV5hRRVIJxOWNksrP19sFgMqqAebUGYHMI6teq0A9oTVAhqu2sfbYYjsL7lCZ3683gA70T3TK7/B4BNoO020GwB9TpwfAz8LgMtWn/NkV8EHgoB81c7nYwCyBZlEVkHcqMTKFnkmehJTOPvEfCnKi0fAyADJKfXC/h83TaZTJjaa5lANLpOFqAXtlEAorAwO9u5syT5UxLfU0e3o1FMu1x4u7ODYq02BKAMAVSrSNLrK1MhLPj8mNF0vFm+C1ZvwKBwXXE4AGn1WAASazESwUW3BzUSMeJ2o1Aq4sPurvQYSRLwlhRR6mSaYyi0WlpAJrFRx3ouh5/lMt5lv8BLwXp0M4lSpYL17e2uK5wP6lj/c2ZPn2RI+YT8fDvqoyegVLyfG5kBKaQQOfvF2pLc+ifAABiQH3PEc1i/AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-js { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUQ5ODY5Q0NGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUQ5ODY5Q0RGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFRDk4NjlDQUYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFRDk4NjlDQkYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoT8zQ8AAAJdSURBVHjadFNbTxNREP52t7S0bktbKFAvTUVaw60YqkExUTD6oD74qC/yD/wp/gh885XEEI0RAyYQUiMpIBGMkYR6o23abi+73e2uc04v1LROMtnZPTPffvPNHMGyLDB7sbJ2ciUSli3U35smkK9t7x9v7n2dD/g8KUkUwWqeP3vKz23NxJGzgwOx0RC6mSgIo+WKuvP56MeUzy2nJEk8PWsGJVVTuhWbpgmHw47FB7d98Wg4mVWK52o1sxOg3Va3PmFp+Q2PdUquaFUM9/vw+O6cP3bxwm46Xwh1ALR3/vL1e+hGjcc9koScUsTSq3coVDQsXJ3wzo5HEs3clgZNMTVdx1T0Ep7cn6//QRQwMhzA6uZHLD5cIFEFSKIU+G8LK+tb0KsGZKcTJoEyP08AbpcLy6sbPKdQrigdAGaDwWxsDH1uGbliCYIgcM8WFPg8Mq5Pjzdyu4jYbCE44EepXMHuwXe+A8x3KKYxYsjvbUzmlPGpBmYdgI1oYjSMbL4Ao1YXMkcM2Dd2xnbAamPQAqg1GORLZdycmYTdJqFKk2DPR3fmwI4zBDrg9RADqxPAbPBif2WTSB584/3/TGegEOit+DRcvQ4OZJi1LgwIQKVCg2i6nb1I7H3Br3QWqT9pBAP9uDY5xjdSM3RqxeoUkfVnEOW8UkLykERTNXjkM7h3Iw6NNvHw6JjuhAhVrba0+QeALozcI9nQR0VvNxJc/ZmxCNGvIBQcpDG6udA22kyW29HC72wu8yG579ZoiSYuR/ly2+y9CA4NceWLmo717T1i5ULqJNtapL8CDACskxPFZRxLwQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-key { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNpsU11PE0EUPbM7u/2AtJUWU6qiiSYYo5EmmPDCD9AH46sx8cEnja/+CB989z+Y+MKPgMiDsYQACcbaWBBogYD92t2Zud7ZlQZsbzKZ3bl3zj3n3IwgItjYeDO3MlWme0bjUth8e8/fO2tHzx3XqUEk50uft+Ndnhdmc3SlfNPkVZT8Cy600DoIISvVfKYtlvfX1p66XmoIYsMZdjJQWvEFbbsC/S5g2QhSkKUK7rx6OzvzqLpsovAhaAxA3DUBQn2TUFsl7KwTfm4Z9DoO5LW7uPXi9Wxpfn7ZKF09vyPxX2iWcNRkKGZz0mQWKoNs8AVB6x1yRY2pYnc2LLofuXTxMgAlmlXIfngCxNxEzM+DPv6NQa2BygLgZyX6JT83ngHTN5GAL0WSoUQkSQnXkyBh/k0GegTAaldM20sTKvet+yyhIZApECamL0jUSe3oFChx3TopM4TeEQP2gc6BgGIwb4KGNXRhCkMGxgg2kJeybRiZM45D8W61qEAknSmpHStBhywu0nFVupSCTAcM4ECwqapv+NQ6LS9JGALoMIIoPYDjZiEL1xHtbyO39AQUDaA7R1AH23DSeSA4hv5RG/VAhxomPYP8sw9A4TaC9iHkjUWmrtGvbyC18BLe3GP0m3WW4I5hEBEnPIStXzyuFIxb4EkMEJ79Qa/xHbKxCdM7xeCwzUZOjgEwnuzt7qLz6T3cySmQP43uzjeIiTJM6io6W19B/NLCKMVGCzkCoLR/0lrfOI2fNy/huKC1FTsK/rbGNeMRC8dHpHByfu+vAAMAL/0jvAVZQl0AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-less { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjZERjZENTJGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjZERjZENTNGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGNkRGNkQ1MEYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNkRGNkQ1MUYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pl1w97IAAAJhSURBVHjahJNLbxJRFMf/wPAIMIxMkUI7tS0VYqlGDLGhjdKkqyZ24cJFN925de+XcONHaHRj4k7TND6SGo1VWwmp2kSLhlqMDbQ87gzPYcY7k4GgoJ6bmdw598zvnvM/95pUVYVma+svcovx8yMnFZHAMJPJBJfDzq5vpX6+/vD5qo/z7DOMBdo/d26t6jFMJ3iY51jBz4M+LP6wxEw40Gy23qYzB3HO7fpmpZCOmfEfa7Xb4NxOrC4lvbPToe2yKE3K1PdPwNOtHdx79ESfq4qKkijB5/XgevIyHxEC24USmewDqD2ABxubaLRkfW6zMqjWGlh7/ByyAtxYnOPnL0Q2+gGGmKRaw8zUBJaTiS5QOO1FJnuIAM8hciaIWHgi8NcSNt+loVDY8JBXh2ojJAR1HbTSNFMUpV8Dxcjg0nSYBrtBxdLbqI1iheCUh9XXNGurAwCdEkb9QyBSFam9TDfoPZ1LUg1BH28IiwEARTVAQOzcFKRaHZpLoa9avY6L1Gfs0c32t4PU6W2lWsV8LAorw0Cs1nXftYWE3qZGqwWHzYp2zzlgetuolVFvtiDLbRRKFTAWCxx2G/KlMtXFhWPqOzsWHJwBx7rxKv2R7mwFz3lw9/5DLC/M4Us2RwV0g3U58XJnF7dvrsBOoX0Abbej/DFKRMKI30fTVGC32WA2m5H9cQQvhYi0vE/7Wdgczn6ARA9QPBrBszcp/XvpyqxebzQ0Tlsq6llxLhe9bD4cFMr9XdjLHpLv+SLGBYHAYiVu1kNOpAaRTWbCejgiw0zGhFGSK1aw+zXbvfK/BBgAPwADAs5GpGsAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-logo { + background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEzIiBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMjEzIDQwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5LjI2NTUgMjkuMTE3NVYyMC44NzczTDI2LjM4MjggMTYuNzU3MkwzMC40Njg5IDE0LjM5MThWMTkuNzM0MkMzMC40Njg5IDE5Ljg1MTIgMzAuNTk1MiAxOS45MjQzIDMwLjY5NjIgMTkuODY1OEwzMS4wNDggMTkuNjYyMkwzMy4xMjExIDE4LjQ2MjFDMzMuMzU1NiAxOC4zMjY0IDMzLjUgMTguMDc1NSAzMy41IDE3LjgwNDFWMTIuNjM3MVYxMS43NTk4QzMzLjUgMTEuMjE2OSAzMy4yMTExIDEwLjcxNTIgMzIuNzQyMiAxMC40NDM4TDMxLjk4NDUgMTAuMDA1MUwyNy41MjYxIDcuNDI0MjJDMjcuMjkxNiA3LjI4ODUgMjcuMDAyOCA3LjI4ODUgMjYuNzY4MyA3LjQyNDIyTDI0LjM0MzUgOC44Mjc5NkMyNC4yNDI0IDguODg2NDUgMjQuMjQyNCA5LjAzMjY3IDI0LjM0MzUgOS4wOTExNkwyOC45NTM0IDExLjc1OThMMjQuODY3MiAxNC4xMjUyTDE3Ljc1IDE4LjI0NTNMMTAuNjMyOCAxNC4xMjUyTDYuNjk1MjcgMTEuODQ1OEw2LjU0NjYzIDExLjc1OThMMTEuMTYzNCA5LjA4NzE2QzExLjI2NDQgOS4wMjg3IDExLjI2NDUgOC44ODI1OCAxMS4xNjM2IDguODI0MDNMOC43NDE2NyA3LjQxOTExQzguNTA3MTMgNy4yODMwNiA4LjIxNzk4IDcuMjgyOTMgNy45ODMzMSA3LjQxODc3TDMuNTE1NTQgMTAuMDA1MUwyLjc1Nzc3IDEwLjQ0MzhDMi4yODg4NiAxMC43MTUyIDIgMTEuMjE2OSAyIDExLjc1OThWMTIuNjM3MVYxNy43OTc2QzIgMTguMDY5MSAyLjE0NDQzIDE4LjMxOTkgMi4zNzg4OSAxOC40NTU2TDQuNDYzMjEgMTkuNjYyMkw0LjgwMzc2IDE5Ljg1OTRDNC45MDQ3OSAxOS45MTc4IDUuMDMxMDkgMTkuODQ0NyA1LjAzMTA5IDE5LjcyNzdWMTQuMzkxOEw1LjE3OTczIDE0LjQ3NzhMOS4xMTcyMyAxNi43NTcyTDE2LjIzNDUgMjAuODc3M1YyOS4xMTc1TDE2LjIzNDUgMzMuODQ4NEwxMS42MTc5IDMxLjE3NTlDMTEuNTE2OSAzMS4xMTc0IDExLjM5MDYgMzEuMTkwNSAxMS4zOTA2IDMxLjMwNzVWMzIuMDc1N1YzNC4xMTVDMTEuMzkwNiAzNC4zODY0IDExLjUzNSAzNC42MzczIDExLjc2OTUgMzQuNzczTDE2LjIzNDUgMzcuMzU3N0wxNi45OTIyIDM3Ljc5NjRDMTcuNDYxMSAzOC4wNjc5IDE4LjAzODkgMzguMDY3OSAxOC41MDc4IDM3Ljc5NjRMMTkuMjY1NSAzNy4zNTc3TDIzLjczMDUgMzQuNzczQzIzLjk2NSAzNC42MzczIDI0LjEwOTQgMzQuMzg2NCAyNC4xMDk0IDM0LjExNVYzMi4wNzU3VjMxLjMwNzVDMjQuMTA5NCAzMS4xOTA1IDIzLjk4MzEgMzEuMTE3NCAyMy44ODIxIDMxLjE3NTlMMTkuMjY1NSAzMy44NDg0TDE5LjI2NTUgMjkuMTE3NVpNMjcuNTE5NCAyOS4wNzAzQzI3LjI4NDkgMjkuMjA2IDI3LjE0MDUgMjkuNDU2OSAyNy4xNDA1IDI5LjcyODNWMzIuMDc1N1YzMi41MzU4QzI3LjE0MDUgMzIuNjUyOCAyNy4yNjY4IDMyLjcyNTkgMjcuMzY3OCAzMi42Njc0TDMyLjc0MjIgMjkuNTU2MkMzMy4yMTExIDI5LjI4NDggMzMuNSAyOC43ODMxIDMzLjUgMjguMjQwMlYyMi4wMTUzQzMzLjUgMjEuODk4MyAzMy4zNzM3IDIxLjgyNTIgMzMuMjcyNyAyMS44ODM3TDMyLjU2MzUgMjIuMjk0MkwzMC44NDc4IDIzLjI4NzRDMzAuNjEzMyAyMy40MjMyIDMwLjQ2ODkgMjMuNjc0IDMwLjQ2ODkgMjMuOTQ1NFYyNy4zNjI5TDI3LjUxOTQgMjkuMDcwM1pNOC4zNTk0OCAzMi41MzU4QzguMzU5NDggMzIuNjUyOCA4LjIzMzE4IDMyLjcyNTkgOC4xMzIxNSAzMi42Njc0TDIuNzU3NzcgMjkuNTU2MkMyLjI4ODg2IDI5LjI4NDggMiAyOC43ODMxIDIgMjguMjQwMlYyMi4wMDg4QzIgMjEuODkxOCAyLjEyNjMgMjEuODE4NyAyLjIyNzMzIDIxLjg3NzJMMi45NDc2NyAyMi4yOTQyTDQuNjUyMiAyMy4yODFDNC44ODY2NiAyMy40MTY3IDUuMDMxMDkgMjMuNjY3NSA1LjAzMTA5IDIzLjkzOVYyNy4zNjI5TDcuOTgwNTcgMjkuMDcwM0M4LjIxNTAzIDI5LjIwNiA4LjM1OTQ2IDI5LjQ1NjggOC4zNTk0NiAyOS43MjgzTDguMzU5NDggMzIuMDc1N1YzMi41MzU4Wk0xMS42MjEgNS4zMTI5NEMxMS41MiA1LjM3MTQzIDExLjUyIDUuNTE3NjUgMTEuNjIxIDUuNTc2MTRMMTMuMjE5NSA2LjUwMTQ5TDE0LjA0NTkgNi45Nzk4OEMxNC4yODAzIDcuMTE1NiAxNC41NjkyIDcuMTE1NiAxNC44MDM3IDYuOTc5ODhMMTcuNzUgNS4yNzQyNkwyMC43MDYxIDYuOTg1NTVDMjAuOTQwNiA3LjEyMTI4IDIxLjIyOTUgNy4xMjEyOCAyMS40NjM5IDYuOTg1NTVMMjIuMzAwMSA2LjUwMTQ5TDIzLjg4ODggNS41ODE4MUMyMy45ODk4IDUuNTIzMzMgMjMuOTg5OCA1LjM3NzEgMjMuODg4OCA1LjMxODYxTDE4LjUwNzggMi4yMDM1OUMxOC4wMzg5IDEuOTMyMTQgMTcuNDYxMSAxLjkzMjE0IDE2Ljk5MjIgMi4yMDM1OUwxMS42MjEgNS4zMTI5NFoiIGZpbGw9IiMzNDc3RkYiLz4KPHBhdGggZD0iTTQxLjUgMjYuOTZINDcuNzc1NkM0OS4xMDg5IDI2Ljk2IDUwLjE1NzggMjYuNjQgNTAuOTA0NCAyNi4wNTMzQzUxLjc0IDI1LjQxMzMgNTIuMjAyMiAyNC40ODg5IDUyLjIwMjIgMjMuMjA4OUM1Mi4yMDIyIDIxLjY5NzggNTEuMzEzMyAyMC41MjQ0IDQ5LjkyNjcgMjAuMDk3OFYyMC4wNDQ0QzUxLjA0NjcgMTkuNjE3OCA1MS43MDQ0IDE4LjgxNzggNTEuNzA0NCAxNy41NTU2QzUxLjcwNDQgMTYuNTQyMiA1MS4zMzExIDE1LjcyNDQgNTAuNjAyMiAxNS4xMkM0OS45MjY3IDE0LjU2ODkgNDguOTQ4OSAxNC4yNDg5IDQ3LjYzMzMgMTQuMjQ4OUg0MS41VjI2Ljk2Wk00NC42MTExIDI0LjQzNTZWMjEuNDEzM0g0Ny4zODQ0QzQ4LjM0NDQgMjEuNDEzMyA0OS4wOTExIDIxLjkyODkgNDkuMDkxMSAyMi45NDIyQzQ5LjA5MTEgMjMuODQ4OSA0OC40NTExIDI0LjQzNTYgNDcuNDIgMjQuNDM1Nkg0NC42MTExWk00NC42MTExIDE5LjI4VjE2LjczNzhINDcuMTg4OUM0OC4xMTMzIDE2LjczNzggNDguNyAxNy4yIDQ4LjcgMThDNDguNyAxOC44MzU2IDQ4LjA3NzggMTkuMjggNDcuMTcxMSAxOS4yOEg0NC42MTExWiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNNTMuMjQ5MyAyNi45Nkg1Ni4xNDcxVjE3Ljg1NzhINTMuMjQ5M1YyNi45NlpNNTMuMjQ5MyAxNi41OTU2SDU2LjE0NzFWMTQuMjQ4OUg1My4yNDkzVjE2LjU5NTZaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik02MS4xNDY0IDI3LjA2NjdDNjEuOTI4NiAyNy4wNjY3IDYyLjQ3OTcgMjYuOTk1NiA2Mi43Mjg2IDI2LjkyNDRWMjQuODA4OUM2Mi42MjE5IDI0LjgwODkgNjIuMzM3NSAyNC44MjY3IDYyLjA4ODYgMjQuODI2N0M2MS40NjY0IDI0LjgyNjcgNjEuMDc1MyAyNC42NDg5IDYxLjA3NTMgMjMuOTM3OFYxOS42NzExSDYyLjcyODZWMTcuODU3OEg2MS4wNzUzVjE0Ljk3NzhINTguMjQ4NlYxNy44NTc4SDU3LjAzOTdWMTkuNjcxMUg1OC4yNDg2VjI0LjU2QzU4LjI0ODYgMjYuNTE1NiA1OS40NTc1IDI3LjA2NjcgNjEuMTQ2NCAyNy4wNjY3WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNNjcuMDc2OCAyNi45Nkg3MC4yMjM1VjE2LjkxNTZINzMuOTU2OFYxNC4yNDg5SDYzLjM3OVYxNi45MTU2SDY3LjA3NjhWMjYuOTZaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik03Ny4yNTk3IDI1LjE4MjJDNzUuOTk3NSAyNS4xODIyIDc1LjMzOTcgMjQuMDggNzUuMzM5NyAyMi40MjY3Qzc1LjMzOTcgMjAuNzczMyA3NS45OTc1IDE5LjY1MzMgNzcuMjU5NyAxOS42NTMzQzc4LjUyMTkgMTkuNjUzMyA3OS4xOTc1IDIwLjc3MzMgNzkuMTk3NSAyMi40MjY3Qzc5LjE5NzUgMjQuMDggNzguNTIxOSAyNS4xODIyIDc3LjI1OTcgMjUuMTgyMlpNNzcuMjc3NSAyNy4yNDQ0QzgwLjIxMDggMjcuMjQ0NCA4Mi4xMzA4IDI1LjE2NDQgODIuMTMwOCAyMi40MjY3QzgyLjEzMDggMTkuNjg4OSA4MC4yMTA4IDE3LjYwODkgNzcuMjc3NSAxNy42MDg5Qzc0LjM2MTkgMTcuNjA4OSA3Mi40MDY0IDE5LjY4ODkgNzIuNDA2NCAyMi40MjY3QzcyLjQwNjQgMjUuMTY0NCA3NC4zNjE5IDI3LjI0NDQgNzcuMjc3NSAyNy4yNDQ0WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNODMuMTEwNCAyNi45Nkg4Ni4wMDgyVjIyLjg3MTFDODYuMDA4MiAyMC44OTc4IDg3LjE0NiAyMC4wMjY3IDg4LjkyMzcgMjAuMjc1Nkg4OC45OTQ5VjE3Ljc4NjdDODguODcwNCAxNy43MzMzIDg4LjY5MjYgMTcuNzE1NiA4OC40MjYgMTcuNzE1NkM4Ny4zMjM3IDE3LjcxNTYgODYuNTc3MSAxOC4xOTU2IDg1LjkzNzEgMTkuMjhIODUuODgzN1YxNy44NTc4SDgzLjExMDRWMjYuOTZaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik04OS45MTYgMjYuOTZIOTIuODEzN1YyMi44NzExQzkyLjgxMzcgMjAuODk3OCA5My45NTE1IDIwLjAyNjcgOTUuNzI5MyAyMC4yNzU2SDk1LjgwMDRWMTcuNzg2N0M5NS42NzYgMTcuNzMzMyA5NS40OTgyIDE3LjcxNTYgOTUuMjMxNSAxNy43MTU2Qzk0LjEyOTMgMTcuNzE1NiA5My4zODI2IDE4LjE5NTYgOTIuNzQyNiAxOS4yOEg5Mi42ODkzVjE3Ljg1NzhIODkuOTE2VjI2Ljk2WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTAwLjk5MiAyNy4yMjY3QzEwMi4xNDggMjcuMjI2NyAxMDMuMDcyIDI2LjkyNDQgMTAzLjgzNyAyNi4zOTExQzEwNC42MzcgMjUuODQgMTA1LjE3IDI1LjA1NzggMTA1LjM2NiAyNC4yNEgxMDIuNTM5QzEwMi4yOSAyNC44MDg5IDEwMS43OTIgMjUuMTQ2NyAxMDEuMDI4IDI1LjE0NjdDOTkuODM2OCAyNS4xNDY3IDk5LjE2MTIgMjQuMzgyMiA5OC45ODM1IDIzLjE1NTZIMTA1LjUyNkMxMDUuNTQzIDIxLjMwNjcgMTA1LjAxIDE5LjcyNDQgMTAzLjkyNiAxOC43Mjg5QzEwMy4xNDMgMTguMDE3OCAxMDIuMTEyIDE3LjU5MTEgMTAwLjgxNSAxNy41OTExQzk4LjA0MTIgMTcuNTkxMSA5Ni4xMzkgMTkuNjcxMSA5Ni4xMzkgMjIuMzkxMUM5Ni4xMzkgMjUuMTQ2NyA5Ny45ODc5IDI3LjIyNjcgMTAwLjk5MiAyNy4yMjY3Wk05OS4wMDEyIDIxLjQzMTFDOTkuMTk2OCAyMC4zNDY3IDk5Ljc2NTcgMTkuNjUzMyAxMDAuODY4IDE5LjY1MzNDMTAxLjgxIDE5LjY1MzMgMTAyLjQ4NiAyMC4zNDY3IDEwMi41OTIgMjEuNDMxMUg5OS4wMDEyWiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTA2LjQ5NiAyNi45NkgxMDkuMzk0VjIxLjkyODlDMTA5LjM5NCAyMC44MDg5IDExMC4wMzQgMjAuMDA4OSAxMTAuOTk0IDIwLjAwODlDMTExLjkzNiAyMC4wMDg5IDExMi40MTYgMjAuNjY2NyAxMTIuNDE2IDIxLjYyNjdWMjYuOTZIMTE1LjMxNFYyMS4wNEMxMTUuMzE0IDE5LjAxMzMgMTE0LjE0IDE3LjU5MTEgMTEyLjE0OSAxNy41OTExQzExMC44ODcgMTcuNTkxMSAxMTAuMDE2IDE4LjEyNDQgMTA5LjM0IDE5LjEwMjJIMTA5LjI4N1YxNy44NTc4SDEwNi40OTZWMjYuOTZaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik0xMjAuMDcgMjcuMDY2N0MxMjAuODUyIDI3LjA2NjcgMTIxLjQwMyAyNi45OTU2IDEyMS42NTIgMjYuOTI0NFYyNC44MDg5QzEyMS41NDYgMjQuODA4OSAxMjEuMjYxIDI0LjgyNjcgMTIxLjAxMiAyNC44MjY3QzEyMC4zOSAyNC44MjY3IDExOS45OTkgMjQuNjQ4OSAxMTkuOTk5IDIzLjkzNzhWMTkuNjcxMUgxMjEuNjUyVjE3Ljg1NzhIMTE5Ljk5OVYxNC45Nzc4SDExNy4xNzJWMTcuODU3OEgxMTUuOTYzVjE5LjY3MTFIMTE3LjE3MlYyNC41NkMxMTcuMTcyIDI2LjUxNTYgMTE4LjM4MSAyNy4wNjY3IDEyMC4wNyAyNy4wNjY3WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTI2LjAyMSAyNi45NkgxMjcuNjIxVjIwLjk2ODlIMTMzLjQ3VjE5LjYxNzhIMTI3LjYyMVYxNS42NTMzSDEzNC42NDRWMTQuMjQ4OUgxMjYuMDIxVjI2Ljk2WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTM2LjA4NiAyNi45NkgxMzcuNTI2VjE3Ljc2ODlIMTM2LjA4NlYyNi45NlpNMTM2LjA4NiAxNi4wMjY3SDEzNy41MjZWMTQuMjQ4OUgxMzYuMDg2VjE2LjAyNjdaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik0xMzkuNTQyIDI2Ljk2SDE0MC45ODJWMTQuMjQ4OUgxMzkuNTQyVjI2Ljk2WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTQ2LjkyNCAyNy4yMDg5QzE0OS4wMDQgMjcuMjA4OSAxNTAuNDQ0IDI2LjA4ODkgMTUwLjg3MSAyNC4xMTU2SDE0OS40NjdDMTQ5LjE0NyAyNS4zNiAxNDguMjU4IDI2IDE0Ni45MjQgMjZDMTQ1LjA3NiAyNiAxNDQuMDYyIDI0LjU3NzggMTQzLjk1NiAyMi43MTExSDE1MS4wNjdDMTUxLjA2NyAxOS42NzExIDE0OS41OTEgMTcuNTM3OCAxNDYuODM2IDE3LjUzNzhDMTQ0LjIyMiAxNy41Mzc4IDE0Mi40OCAxOS43MDY3IDE0Mi40OCAyMi4zNzMzQzE0Mi40OCAyNS4wNCAxNDQuMTE2IDI3LjIwODkgMTQ2LjkyNCAyNy4yMDg5Wk0xNDYuODM2IDE4LjY3NTZDMTQ4LjQ4OSAxOC42NzU2IDE0OS40MzEgMTkuNzk1NiAxNDkuNTIgMjEuNjI2N0gxNDMuOTkxQzE0NC4yMDQgMTkuOTU1NiAxNDUuMTI5IDE4LjY3NTYgMTQ2LjgzNiAxOC42NzU2WiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTYwLjE0IDI3LjIwODlDMTYyLjg3OCAyNy4yMDg5IDE2NC44MTYgMjUuODc1NiAxNjQuODE2IDIzLjQ5MzNDMTY0LjgxNiAyMC42MzExIDE2Mi41NTggMjAuMDI2NyAxNjAuMDg3IDE5LjUyODlDMTU4LjE4NSAxOS4xNTU2IDE1Ni43NjIgMTguNzI4OSAxNTYuNzYyIDE3LjI4ODlDMTU2Ljc2MiAxNS45NzMzIDE1Ny45IDE1LjI0NDQgMTU5LjYyNSAxNS4yNDQ0QzE2MS40OTEgMTUuMjQ0NCAxNjIuNjY1IDE2LjE1MTEgMTYyLjg5NiAxNy45NjQ0SDE2NC40MDdDMTY0LjA4NyAxNS42MTc4IDE2Mi43NzEgMTQgMTU5LjU4OSAxNEMxNTcuMDExIDE0IDE1NS4yNjkgMTUuMjYyMiAxNTUuMjY5IDE3LjM2QzE1NS4yNjkgMTkuNzYgMTU3LjE4OSAyMC40IDE1OS40MjkgMjAuODk3OEMxNjEuNzA1IDIxLjM5NTYgMTYzLjIzMyAyMS44MDQ0IDE2My4yMzMgMjMuNTQ2N0MxNjMuMjMzIDI1LjEyODkgMTYxLjk3MSAyNS45Mjg5IDE2MC4yMTEgMjUuOTI4OUMxNTcuODExIDI1LjkyODkgMTU2LjQ5NiAyNC44MDg5IDE1Ni4yODIgMjIuNjU3OEgxNTQuNzE4QzE1NC44NiAyNS4yMTc4IDE1Ni41NjcgMjcuMjA4OSAxNjAuMTQgMjcuMjA4OVoiIGZpbGw9IiMzMDMyMzMiLz4KPHBhdGggZD0iTTE2Ny42MDYgMzBDMTY4LjcyNiAzMCAxNjkuNDM3IDI5LjYwODkgMTcwLjE4NCAyNy43MDY3TDE3NC4xMTMgMTcuNzY4OUgxNzIuNTg0TDE3MC40ODYgMjMuNDkzM0MxNzAuMjAyIDI0LjI1NzggMTY5Ljg4MiAyNS4yODg5IDE2OS44ODIgMjUuMjg4OUgxNjkuODQ2QzE2OS44NDYgMjUuMjg4OSAxNjkuNTA5IDI0LjI1NzggMTY5LjIyNCAyMy40OTMzTDE2Ny4wNTUgMTcuNzY4OUgxNjUuNDkxTDE2OS4wODIgMjYuNjkzM0wxNjguNzI2IDI3LjZDMTY4LjM3MSAyOC40ODg5IDE2Ny45NjIgMjguNzIgMTY3LjM1NyAyOC43MkMxNjYuODc3IDI4LjcyIDE2Ni41NzUgMjguNjMxMSAxNjYuMzggMjguNTI0NEgxNjYuMzA5VjI5LjgwNDRDMTY2LjcgMjkuOTY0NCAxNjcuMDU1IDMwIDE2Ny42MDYgMzBaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik0xNzguNTI5IDI3LjIyNjdDMTgwLjY0NCAyNy4yMjY3IDE4Mi4xNTUgMjYuMjg0NCAxODIuMTU1IDI0LjQzNTZDMTgyLjE1NSAyMi4zMiAxODAuNjA5IDIxLjkxMTEgMTc4Ljc2IDIxLjUzNzhDMTc3LjE3NyAyMS4yMTc4IDE3Ni4yODkgMjEuMDIyMiAxNzYuMjg5IDIwLjAyNjdDMTc2LjI4OSAxOS4yOTc4IDE3Ni44MjIgMTguNzExMSAxNzguMTM3IDE4LjcxMTFDMTc5LjUwNiAxOC43MTExIDE4MC4xNjQgMTkuMjQ0NCAxODAuMzA2IDIwLjQ1MzNIMTgxLjc4MkMxODEuNTY5IDE4LjY1NzggMTgwLjQ0OSAxNy41NzMzIDE3OC4xNzMgMTcuNTczM0MxNzYuMDQgMTcuNTczMyAxNzQuODY2IDE4LjU2ODkgMTc0Ljg2NiAyMC4wOTc4QzE3NC44NjYgMjIuMTA2NyAxNzYuNDg0IDIyLjQ4IDE3OC4yOTcgMjIuODUzM0MxNzkuOTg2IDIzLjIwODkgMTgwLjY5NyAyMy40NzU2IDE4MC42OTcgMjQuNTA2N0MxODAuNjk3IDI1LjM2IDE4MC4wNzUgMjYuMDE3OCAxNzguNTY0IDI2LjAxNzhDMTc3LjE5NSAyNi4wMTc4IDE3Ni4xMjkgMjUuNTczMyAxNzUuOTMzIDIzLjk3MzNIMTc0LjQ1N0MxNzQuNiAyNi4wMzU2IDE3Ni4wMDQgMjcuMjI2NyAxNzguNTI5IDI3LjIyNjdaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik0xODIuNzczIDE4Ljk0MjJIMTg0LjEwN1YyNS4yMzU2QzE4NC4xMDcgMjYuNTMzMyAxODQuOTYgMjcuMDEzMyAxODYuMTMzIDI3LjAxMzNDMTg2LjU2IDI3LjAxMzMgMTg2Ljk1MSAyNi45NiAxODcuMjg5IDI2Ljg4ODlWMjUuNjQ0NEgxODcuMjM2QzE4Ny4wNzYgMjUuNjk3OCAxODYuNzU2IDI1Ljc2ODkgMTg2LjQ3MSAyNS43Njg5QzE4NS44ODUgMjUuNzY4OSAxODUuNTQ3IDI1LjU1NTYgMTg1LjU0NyAyNC45MTU2VjE4Ljk0MjJIMTg3LjMyNVYxNy43Njg5SDE4NS41NDdWMTQuODcxMUgxODQuMTA3VjE3Ljc2ODlIMTgyLjc3M1YxOC45NDIyWiIgZmlsbD0iIzMwMzIzMyIvPgo8cGF0aCBkPSJNMTkyLjY1NCAyNy4yMDg5QzE5NC43MzQgMjcuMjA4OSAxOTYuMTc0IDI2LjA4ODkgMTk2LjYgMjQuMTE1NkgxOTUuMTk2QzE5NC44NzYgMjUuMzYgMTkzLjk4NyAyNiAxOTIuNjU0IDI2QzE5MC44MDUgMjYgMTg5Ljc5MSAyNC41Nzc4IDE4OS42ODUgMjIuNzExMUgxOTYuNzk2QzE5Ni43OTYgMTkuNjcxMSAxOTUuMzIgMTcuNTM3OCAxOTIuNTY1IDE3LjUzNzhDMTg5Ljk1MSAxNy41Mzc4IDE4OC4yMDkgMTkuNzA2NyAxODguMjA5IDIyLjM3MzNDMTg4LjIwOSAyNS4wNCAxODkuODQ1IDI3LjIwODkgMTkyLjY1NCAyNy4yMDg5Wk0xOTIuNTY1IDE4LjY3NTZDMTk0LjIxOCAxOC42NzU2IDE5NS4xNiAxOS43OTU2IDE5NS4yNDkgMjEuNjI2N0gxODkuNzJDMTg5LjkzNCAxOS45NTU2IDE5MC44NTggMTguNjc1NiAxOTIuNTY1IDE4LjY3NTZaIiBmaWxsPSIjMzAzMjMzIi8+CjxwYXRoIGQ9Ik0xOTguMjA0IDI2Ljk2SDE5OS42NDRWMjEuMjM1NkMxOTkuNjQ0IDE5Ljc5NTYgMjAwLjg4OCAxOC43NjQ0IDIwMi4wMDggMTguNzY0NEMyMDIuOTUxIDE4Ljc2NDQgMjAzLjU5MSAxOS40NCAyMDMuNTkxIDIwLjU3NzhWMjYuOTZIMjA1LjAzMVYyMS4yMzU2QzIwNS4wMzEgMTkuNzk1NiAyMDYuMDk3IDE4Ljc2NDQgMjA3LjI4OCAxOC43NjQ0QzIwOC4yMTMgMTguNzY0NCAyMDguOTc3IDE5LjQ0IDIwOC45NzcgMjAuNTc3OFYyNi45NkgyMTAuNDE3VjIwLjUwNjdDMjEwLjQxNyAxOC42MDQ0IDIwOS4yNjIgMTcuNTU1NiAyMDcuNjI2IDE3LjU1NTZDMjA2LjUwNiAxNy41NTU2IDIwNS40MDQgMTguMTQyMiAyMDQuODE3IDE5LjE5MTFIMjA0Ljc4MkMyMDQuMzkxIDE4LjEyNDQgMjAzLjUwMiAxNy41NTU2IDIwMi4zODIgMTcuNTU1NkMyMDEuMjI2IDE3LjU1NTYgMjAwLjI0OCAxOC4xOTU2IDE5OS42NzkgMTkuMDg0NEgxOTkuNjQ0VjE3Ljc2ODlIMTk4LjIwNFYyNi45NloiIGZpbGw9IiMzMDMyMzMiLz4KPC9zdmc+Cg=="); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mid { + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mkv { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M7.5 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H7.5zm23.5 0V71.2h4V80l8.2-8.8h5.4L41.1 79l8 12.1h-5.2l-5.5-9.3-3.4 3.3v6h-4zm25.2 0L49 71.3h4.4L58.5 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mov { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M6.1 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H6.1zm22.6-9.8c0-2 .3-3.7.9-5.1.5-1 1.1-1.9 1.9-2.7.8-.8 1.7-1.4 2.6-1.8 1.2-.5 2.7-.8 4.3-.8 3 0 5.3.9 7.1 2.7 1.8 1.8 2.7 4.3 2.7 7.6 0 3.2-.9 5.7-2.6 7.5-1.8 1.8-4.1 2.7-7.1 2.7s-5.4-.9-7.1-2.7c-1.8-1.8-2.7-4.3-2.7-7.4zm4.1-.2c0 2.2.5 4 1.6 5.1 1 1.2 2.4 1.7 4 1.7s2.9-.6 4-1.7c1-1.2 1.6-2.9 1.6-5.2 0-2.3-.5-4-1.5-5.1-1-1.1-2.3-1.7-4-1.7s-3 .6-4 1.7c-1.1 1.2-1.7 3-1.7 5.2zm23.6 10l-7.2-19.8h4.4L58.7 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mp3 { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra7XWxpSsFYIbVQf9REFBHkYBRIPJh4wrN3DsZ4MPGP8b/wUCIHEw5EY0w04o9ILcREGmwVgaXbbXdnd2bXNxPahGyczebtzrz3ve99740WRRHkWn5cebu4cH6SMY7e0jRAHr9c3WxsVvcemmbys9yT6+uHJ8oaPefypdPDD5Ymh5w26wMkEho8JtDtuEOZFCrvN/4uJZNGH0T59D58X/C27aFNAL3Xthmsww5GCyN4+uzu+OLtQsUPxPQx6ZMAoQjBAw7O+bEVCMMQgqygs+LFs1h+dGd8bna0QmXO9OL6JYgwAvOFZKKoy3V44CgNfv7Yx8oLH+lUEgvzF8Ydhz+n41snAGRG5gUEwClzhHdvttFxfNyYK0EnJozKK5eGcf1qHo1GOxtjwI+pfvm4g/W1qtJgerYE2SXJSIL9+W0jk0mCShAxDXgQKgbNXxZq35vQKCiKQkSUXdc1+gcch1FHGPmKuIgBCdc66qJQHMG9+1NIpUylxxHtuW6gEiTIu+N4yjdWgty0yTmdNjFzcwKjY0MU7MLt+IjoSad16FoIx3b/A0DZ7FYXnsdpAjUMDOjI5zPgfoBsRodhhGhZHfBBU/nGAGRtxWIOg5lT2NtrI5dL0SB5KJzLodloqXaOEatPGztKq5gG3S5DNjuAK5NjKJfPYKI0okBkSdemCiSgS/rkQNLSePtxBj4LSCwfFtE0krqqX7ZVMnu9XlMXy2l7ME0dzA3iANQyY6vWxC61UY41zTyNcYh6/QCNXQvzi5dR39nHVq1BUyuMGAARsF6tbbe4iKD1r7Om5iFBdmW1SsDflLiuB6sX90+AAQDHAW7dW0YnzgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mp4 { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnBJREFUeNpsk99r01AUx79psrTrujVtbceabnZs4DYRHSoMh6Dgq77rn+AfoA/+If4Bok+C0CfxVRDBh+I2NqZzrpS1DVvbtU3SJPcm8SSlsJlecsn9dT73nO85V/B9H0H78OLdt/LDlQ1uMYybIAgI9n99OWxoe83nkiz9hDDae330JvxL48O51Xxm/enNtKPbVwAh0Ec6kYpXat9Pnl2GBC02HrjM5Y7h4P8+7FtIFVJ49OrxUnl7ucIdfhv+BIDv+fBcj7p/tXMPrs2RXVTw4OX2UnFTrXCbbY7tpMsA13FDSDAOQ4gJEGUJLs0PPh9CkESsPrmxxEz2lra3rnpAt3G6adgdQhBpmeLkFodNmsjpOPoXBrQTDcmFFNS7i3MRDzzPCw/vva8ikU+COQxm14BBhvJcHLGpGPTOAJxxeLbrRgAkYujBdH4G5oWJWXUW19YL4XqunAMFhnq1BqWYgaY1MAHASQOiU96zKzkU76mwehaOvx6h9uMv7KFN3RopL4oTAI4HRh4wSl399xla+00YbR3yrIzM9SzSqgJJnoKcklGrH08CcJjnBtLLCsSEGGpSWJvHtDKNoFippsJ0ulIsDDUCCATMlBQkNuahEyiZTcLsmFBKaQxaOk53TlHeKkM70AjAooCghBOk9sKtIvqtPqS4FBaRnJSRX8tj2DOh3lFB5Qw2ZNFK5LRo6w4sKt2ggAzywidAMN/9uIPSZglBLDO5FF3mRD3wHE9qVRvoHrUpfn+UEQK0/7ShtwboHJ6jdH8RZxSC57hSVETb7e5/2u0FxqPHJow+8iZ4lYY2QGu3idhIxO7Y7p8AAwALCGZKEPBGCgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-mpg { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNpsU0tPE1EU/ubRdlqmnUBboa0UeUQDiUGCC1+JmrhxoXt/gBvXJi74If4AV0Y3sNKF0YUaICqoIfjgVShEiGF4tDOdO/fOeOaSKtie5GZu7pzz3e/c7ztKGIaI4vn9p+/P3h4e4a6Pv6EoQBDiy7P5rc1P1Xt6XP8M5ejXo6UJ+dWbuemeTGdpvNdiNe9YvQLe4Bi4PmTpRmyq8m71rp74BxKF2twIHvAo+f/l1T2Yp0zceHizfOZa/xRnfBRhG4CQqAYioBWeXDyA8Di6ei1ceXC1XBwrTXHPH2vW6ccBBBMI6BsSUEQzakGL6xB0tvjyBxRNxdCtc2Xf8R9TyaWTDOg2TjfVdw6hqIoE9B2GxkEDWlLH7s4ette2kSp0oDRezrQwCIIA3oGHr0/mKMmE53qo23W4+w5S+Q5ohob9X3tgHgO8ULQACC7gMx9mKQP30EW6mEHpYi8xcJEdzMucjfkKcrTfmqmiFYBxCF/Id+gayKJwoQjHdrA5v4HK7Cq44KjZNWpagaqp7QACks0H9znW365ia24DzoEDozOJbH8eVtGShXHTwNracnsG7q6LzsEuaAlNPm9h7DSSVjLyCMkppDI+GS2StQWA1RlKo0X56n2X+6QHkmkDakxF9WMVqWyK+s/BrthYfvWz1Ug+zUDcjMPMm0h3pxEjFma3CbIuCud7oMc0LL1ZgmElpGJtW3B+15HIGNITrMYIlOH7i0U41NrInREylYbu4R5qQbQBaAh95fVKZCnpQCnb9DrWZyrRERS6NDeUw+yHaXh7rt4C4B8y+9vkwn7kwKNRpDoa9aiFKBYnF+RcREqQ2e1m3R8BBgAy9kz9ysCE6QAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-odf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAi5JREFUeNp0UktrU0EU/mbu3FfE1KRRUpWYheALNBURUVy7cy9UkO6KW/+Lbt0IPsFui4gLBbUqFaUuXETUKCYa0jS5yZ2ZO557b5MmTXpgmDPnfOc7jznMGINYPi0de5UvmpORxpjE/kbNqW005DVu8TWw1H758ZfkFgNgJmtyxSPRjJIj0QTW/RDiYGXGb7Dl32/eXrVsd0gSCx9miqC0ooCdp69g5Q/h6OLN0ty5ynIkwzMwUwh2FwMdcbDiCZQXlkqFCpEoPT/wih1YjLInANcD+/Ua9bu3wJlGvrBZCmet2+S6ME5g4oGlZ9A/I70XCDhhDexPNTFmswJBwcnuXkF86VSNZxVu0ukLSGnBcqlnN4HoCQIaIuIv7LUooMOgQ7q75LAAb59B9gCBHSKgqemRr94mMKmD24CfM8nb7THYGQNLpAkUkcb66JyGBFFEWRVL57gFEH5qj8Lxwca2qS3EZaugmzAw24dR/XQgwtsCSBjPIdWbUoE2UJLBnV8Ac/ciWHsK9/glWLnD6K2vgPszsOdOQdfeQ1c/ThKoTgDn9A3KUED/52d45xchZsvorD6Bf/Z60riV3Q9Z/0bbGU1uopYGkfERSQ3VbsMwl0qlqoIARmSoPYXWy0dor79LfBMEEd8jGs/uQ3Yl7PJFNFbuEXiV2riCf88fovXhBbo/vqP3t02/ZYmJFqTkzY160Go9uEMbFK8hR/NrdXtFuUVmnmySVGgO4v4LMAAjRgmO+SJJiQAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ods { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAetJREFUeNqMUj1IHEEU/i7u7Z23e8tGgneGQPw3hZDkkhQiSuwMQREba4uUgpVlCrvEQhurkCoWqcQQ0oTAaYKNqJygGEwgHCSB6Knn7eXcdX/GmdHVPWYFP3gw78173/vmvYkQQsAwNvckq96UnyIEh7/d4t7uUd/8y+85P+bXSX4grkhI6nJYPW7LrXpBK2YxiSoShhu4Buq1NPofDeqdrZ3Z4cl7D4J3UtA5VyVAlmJoru9Af2ZAp1lcCQ3nqgiuKmbY3l/BH+MnHM9GVLP0Ww3KNA33CQoQQnL834Fj74PUGkANEIkCSSsa8gQqgYTIcB0PVsXB318GInRiCVWCkpRFAs+j5gKlA4t29Ggh4d0t04FKt9PQqF4UFgumSEA8ApeaElilWbYRVy/lsns/N1QBkxtENF4jxPxcgcB1CZVOrvMteK5IQDtJJIGh++PcX9iYwWjXK37+vP0WdYk0Ht99jtX8JywWFkQChw4tc+cZcvlF7rMze+ubbxN40fMalRMDP/6twaiUeK7wlZ0TD0a5hLTWxo2d45KKprqHKJslTsy209s2wnMFBTYNZjc/oLt9gPvLOx+hxVJIKS2YW5pCbSyJTGMK775O8VyBwDJd2LTDl/X5i8v3S7NVw9vJb51tITDEUwEGANCx2/rXEEFFAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-odt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAepJREFUeNqMkz1II1EQx/+7Ca6JkqyYiJ8cKEpAQbBQFDm0sVOsFBS9wt5KOTgEG5twxVlZ+XEnKNiIghYKxx5nwEpIIXaiSAgKGmMi0d23u8+3T7OaZJEMLG9mmPnN/w1vBUopLPNNhRWXHOyDg0nx82TiJtZPlPVoNpftc2cTotcHtxx06kdXpSQ/BvzKESZzIDmAz6y+NojOjpDMZiqRPIgNoFyWM8DrKUV7axO+gcp4g7AzmquAdVNqOgL2z2I4id1B0wgeygOyt/rLL5buLwAIDgA9dY+L+DkuDQOCrkMgBsRglcMOqAGwIstMg8AkGsuZMNUMRMkLqE+QGloglvlA7uIOAKvZajR0qJkUj/XHe0BTIclVKKlrfKsj9qA8gA6wqSJzPaXlr7ky//tdLEUfawsBjExUFGVWbT7AxSa42H2LMfODmvd3wKb7RAMLYwM8nts8xJ/pEe7/3PmP2eGv3D+9usb35W0bINoA7RmjXSHsH0f5Z/mUSZ0Ir2JmsBtD80s8/rGyzWsLFTD5yUQCbfUBHl9d38LvkdDTXIuHVBo0k+bbt06qO+yAPGXwe/cA4wO9PN44jKDG70GougIzi2tQ00ms7/3lpwnBBgjZ37Kkd1Shht5XzBIFl/ufFtniT/lFgAEAU//g6kvdGBMAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-otp { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcJJREFUeNqMkssvA1EUxr+ZjkdbrfFKVD12ErYSRELY2fkH+BMsLcQaSwsrSzZi47EjJEQkEhYkFlhYSVtFpdqOqpk717l3jKZmiC+5mZlzv/s795wzCuccQncz3YeRBj4KHz0/RrOZe2NsZPP20o255zQ3EAxzEAC+6uzTw13G4TFQAakA/CWtIYbY0KBOrx7IvwDQqlHV1o3YxKTOvyAUvfQCfqmA3e4ikyS/zRAKvOot7eoSHEgZIHrCfQAfBqBaKQQDKScQAExd8emBANg+2U2CvNMkkgSqBmrCxFB8mujeoJBWwEqARcssKTAJEGrmaGrjqK1zvNknH4BtyxKl2VUpRxmj5W+x73q9AEaZrR/ND1EJluIpS3i9JQiA+a+hSq8HwJjTsLrRaWitPTCOlhEZn5N75sM1qigmlN+dB3u++Qao5W4TtbEXXIsiszGL4PA00itTsu6XnQWo0TjMTAJqfMDx/ryBJcaVzSNSH4fW0Q+rkIf5rsjRiid7yyN7uoXS3Zn0egE0NiORAN9bQ017D1Lri7CLlP2EDr3Rf7C/itzV2bfXA/igLDaRixfngFhSCooH2xVPCWBlwKcAAwBX1suA6te+hAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ots { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfZJREFUeNqMUk1rE1EUPS8zmabJdDKB2glEwY9ExJYiBUEQpV25qgtBXfgbpEtXuujKf+AfEKRddOdOGHClbYVCvyKWaijT2mhjphk7Sd7Me76ZONp0EsiBYWbOvfe88+69hHOOAE9f3zTVnDKNHvhlsfqPw/rM0ovyWsRFdXJEpDIyRnSlVz0KSkmvabaJeXSJBEhgAJzTDNybmtUnS5Pmg/lrN07H5NM/f13FoMgpXDSuhiIiK3Qi6LUugX7FAbaPPsJqfIHHKCStqRsXVFPQuZgD9BBxjikSiRq41AAkgCQBzVf0+BWEBX7GBm0xgHHUqk1UbBuEcIydzyCZlOI9YEGuDxwduCCitS3Xh3viCZ4jrcq4PJ6DLHd67tjtuAAXib54dCPVEfQ5XIcik/0/2iDeOYz3ceCxrisMi904y0XiMQFfkB7lg6xFHwFxEqUMV0anUNBLWKm8xd3i4zBWOzmASx0UsiW831mA59Xjm+h7HCOygduXHqJatzA7Poey9QnXjTuoVD/j/sRcmDOWLgqnLC5A2wwST+Pn8T629lahSCo291bwu9XA7vcy3m2+gTaUR14thrk9BXasbdiOjSe3nmPpwys0xSi/HpbDd3bIQC6dx/q3ZbRb/j8BEi3Po5cTJpHI9CBNDEa++GyDBN9/BBgAwfDlCVUQaNAAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ott { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdFJREFUeNqMU89r02AYfpJ0iVm7EqhVOxw7dDBEdpiCE1RoEZRddvUgbIex/Rs7eehppyF4LOzQu4MxwYp0HgShIuwwUVSCVtl0s13afl+SzzcpyZYmyF74eN583/s+PO+PSEIIeJZdrtQVI19Cgmk/Ph39bpllXq82g7sgLxVcyKNZpIx8Uj5u5zSjc9Gov8ZihCRC8D+7On4JczevGeTGSEIC4ctKJtB1DTPXi1iCCEkIm1EFlC2Em0iwtWfinXkIzjiO0jljtDC5TtflGIGUQMB+mfja/oPv2Rx9MMjpMdJxOXyXTwkcwIkewfqQ1QtQNB385zcI14FrtQexsSb6SRysZ4Fbf+F6eHwATc9gJGNAm5iCTL5n/LCVRGADNoeaGoHqyaXj5gqQlTODovcwNk5Aj6wXqV8eCo7EDhMonEHpW+dZC7gUG98D3geo7vkb01h9cAvPdt76OGy1xntUd3bjUxAk3+l2sHJ/FgtrT0MUJNfDSm0bjQ/72Hzxxo+NK+h3B7XRNO4UrwymQtMIkdTBU0m+sBOayLsn8Ka78mQDjx/e87HXPkb1+UsfP37+AmZ1fP/suknBb6nefVQXjl06TxMlJfWKNWr+Kv8TYAAkUueexJF47QAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-pdf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNp0U0trU0EYPTP35qYxaW6TlDapNKWGbgo2FkF8rARB6rboXusf0F/hyq2U4krFqugqSBeuAyL4SERBstHa0iR9JKZJ7mvu+M0tqZGkH3x8987jzDnnm2FSSqh4ns0VU1ybFzj674Wa3uWiWbfsFQb+jrGj8Xvbm0HlvYVRxhJprpmTlGmum+OMm5uNPZNbtjk3l82ey8++8oW4Jv/H/wdA456g2kvH99FyHNiuAz2dwflbN8YW8zMK5Go/CMfQkAhpGsyQgRCtlpE4jIULyC9fHzu7MPPEl/5ib6WOE0JJNRiHHg6j86mMjw/2gG4bkbY4PW4Yj2j64skA5FTHdaEMPiAJszt1sK0d4suJmY4k0+IDDGRfqmh0u5gejQc+fG8eYCIahRQCEfgQnIuhEkgtONE+dGxYxEDj1DhiEycZ+1YXdUpHCqTMJIYyEES5aXXQsi2kYlGEia5GtHVKn+amPBeCutPgfLALPuVu+xDVPw2EQyFEjHDghbpYNm1yKVVnYjTOerepn4E6XQmLGSPkPkOXWATMSDcjQEkAaqOu6+i/rccALtFL53LI3r0Nq1ZD4/MXZJaWYFer+PXiJc6s3IEgY3+uPYZHTAcAHM+DTE8gnM1CSyaCulv+GrRy8uYyElcu4XfhLVpkpNtn/DGA5Uu0abFH36WnzzCayWAkmYJvWeCkfb9SwY+NDbSoOx4bYqJF8rZqVRRXV/HhzWtUSmWwmWl0RmN4v76OUqGASrmMOkntSHF8MOs954dT08W248wzYsJDOujRBAaqqikTpRo/qqd0/dv97c3Lat9fAQYA4z8bX9nTsb8AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-php { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhNJREFUeNqMkltrE0EUx//ZbDaXNrvZzdIkbYOXGgxYQlCK2IIY6EufxGdB8Av44AdR8AP44JOPBR+Ego0PClUKTTXQSmkTYtOkmubSJrQ1e3H2yJSEJNIDs3PmP+f89pyZcdm2DcdWvn7LzkxFHmCIra7nm9ulg8yLZ09yXON55Dgjt1PM2iPs0+aW/frdh8bzV2/SvQBnCLiEqcFxLKSSodlrU9leiGPihWePBkgeEZO6ShC2dCAZNuf6ADb+ldQ5PUPx4BCFcgXfdwq4Ph1Dtd5CZi4Nw7SQiMdCXkl6yVIy/QBWgcU+yx/XsLK2cdHndqlK/lZxH/OpJO7fnsWY3z/YAq+g0TmHpoUH2vB5PXi8RD9Fo10aAmDJTgWyIuOupmK38rsPcOvqJO33XWEvwLJsmKxHRVEwf/MKWl/yUMf8mIloWN8rw+sP0D6PHQmYuzGNgCRiMZVA17IQV4OIaTI8buH/AJMFd02Tkp05PO4jnWvc57EDAINt7u1X8Pb9KgI+Lxbv3cFR8xjx6AQ+b+Txs/qL9KePlih2CMBCq92hg2qzt1AoV7H5YxdhdqhHzRbgcpFeqdUplpvQW4FhmAixZ/sws4BoWCM/qmsE5XqE3dDQCrqGAYWdejqZgK6GUD8+IV9VghBFN1RZJv3sT5diBwC15gncggCPJKF0WCPN8dun55jQdVpz3Ynl9leAAQAJhiGatD9AOgAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-png { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsU9tOE1EUXXPp0CAUWmJbC04xBANNTF+kKhG8fID6aqL/gPEj9E0lIf6Dj30HL03wxQtVIC0QKrWxNG1Dk9Z2Oj1zxn1m0oIZTnIyZ8/ee+211z5Hsm0bYg29fLGpxWIJWBYGS5IA8ncKhT9Wvf4Yqprtu+w3q85X7f9QxseD/pmZMZsxN9fnc5JNw0ACGGv6tPSvyvEDKEoWZ5Y8OHHObKpucw4B0t3agnl4CJPs2YkQVu4s61ORaBqMJc8CDBiIRhhVM9bXYdVqYAcH8M3NgS0tQQsFcfdKHEbvlr6WyaR/V6uPKPy7B4DT7lUq4MUipMlJ2MPDUKtVfKZ2nn/5BoNbkONxXeb8LYXe/A9AJLNWCxgdhZJagDI9DZg9qIkEytRSkdqTSFQtGILSbgc8LViM+tc0yPfukzIyOJ359k9YR0eQdB2KmBbpwXoM3Dod1SkD+scpEapCI5DdpsJhIJcjajQZagcjI+5oLe4VkeQnyiZgdIH2X6BJ7dSqQLfrggjw0AQwP+/GegCIHppNoFAgEMO1RZKo7BQgRi3yN05cnwdA0BQMAgF3C6pnbuNg92M9AFT1diSCh6kb+FGvo2MxnBB9ocZxp4Mns1cde213B81e7xwAcl4jkaa0IUSjUdLJwkL0Ej6VSvArCt7l81iku6GrKnYEU89VJlSJRmR0Dax+fI9suYxSo4HlWIw6M3FBlnD9YhiXabyOsOeIqG7TzDeIYo6EDGp+ZPb2kKKqH8h+mkxiI5/D1/19J3bwYPvPWXq2skkiJVxesqt0XzghpKM8nRVV2Lv2q9eLIvSfAAMAaacnllcFBmYAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-ppt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkhJREFUeNpsU11rE0EUPTM7ySZpmzT9DNamWAtFfSiCigr+AxF9zKtv/hvf/Aki+FEi6ov4ItWHPGiwiBUKoUqqTUJImmR3M7Mz3t0kNe1m4LIwc+65595zlxljEJzdR5uf5nLmsvZx6gSvtd9W9bjhF7jg5dH9nRc/wq8YXaTSJptb0xklx7IZoKUEz1zJ2DUU69/37vFYrDxegJ9U0lC+AoIIVGg9CL+vIObP48KDQn7x0sWiVnJrnEDg7KGk+i/Ac4iUM/R7BsmrSSxtXMfa3X7el8+Kjf3KfUJ+iRJQw4w0Tc8BRyWGRAZY3rBR/VlC+XED2ayDhZyXl03+hNA3TxNQshlGLAnE44zCIL1goXZwiMNvB1i6zbC0KuAsxNITWwgNMYPeLVJiFEO9ArjHAivrAjNzBr4f4vwIgdGD4YUACsZCE8AtYGWT5jCsGQw5wEYJzP/pj5RwYTA1b07eQmfZ8P0sgdaM2FlYwWkMgMpl6NQAO33GKM0wsQWflkh1uqGVmVWblsiDkQyqxwfag35SqcktaEWTUTHYNx4iGU/C29+BvX4Lpu/C7zYgFjegSY63WySsHyXwpYHU00ieu0bAOuJbBTArBkiXKiaAmTzcvRJUV9E8rOgqBwqlY8ASs/AadbRLb8CzeTjVClqft6FdB17tL7yeCbFRBYoLr6vR/PiSEl5BZJaBD0/R2nkOZqfQ2fsKt+0SEQ+GLSIEUvJm+6jbah2+pS2aon+4g/afd4SYJVuA7vvXdC/IHQtSoTnK+yfAAIEaId1m+vudAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-psd { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAqxJREFUeNpsU01ME0EYfbtdKKWGtoItRWgJHApCBE2I0YuoiSaaeDJeOJh41YN3TfTixcRwMfEk8eDJGA+Eg0YTTRRMg02KKFooCBbTlkJLS7f7P+u3K9Xo8iWT3Zn55s173/uGM00TVlwZfzJztD92iKO5ouvQGQPHcQDN380vlDPr65fdLj4Oa41i9sFt+ytgN7o7woGOrqgvvpLBaF8vWj1NUAwGTVNRM3mf5vU/zaU+XySQuTqIFXz9hxmGLkoS7r+YxvVnrzGzlgXPDOzUZPT4m3Dt/KlIuH9oUjXYEHZZ/wOgGQZi4TZcGI5hLb+FO++TSOSKcLtcMA0dI0EPrp4+HtnfG5skiUecDGwQE2MjAwiGWlFVNDz+tIyCokJhPKYSX7Gdz2I01hOJdnY9rJ/7UwPGTEiqjtbmJtw4MYx78S/4Wa3h5UoOYwPdIOp2Xi/t18rlFgcDw6o+ydiWVRwOBnCpL0oOAMmNEhLZIgSeoxwGSWcERon/M9DoBknTIdNQNAMnO4PIVGpIFXcwndlA2OtGc4MAxml27p4AIulWSIa9QVadiYSoJxhqBJivKgh5ad3k9gaw6JdlDaqq7q5wINY4F22HaLHSDZQkBW72O9cBYFEviBIURQH7a7MN0uDisUW12ZZcaGlmdq4DwCqeTo1zNtZuW7hUqGIw7MNqSUS2ImNsKEpSdEwt5lGhfQdAkQBEoub3NNrDJfAIeBuRrcrY5xGQ2RFJAjl00I8PCckJUCB9q1URBnk38XEJEuk41tmGwZAf66s1VOh2keqwoUnYpFxHH4iKIixkN3HzVQKP3iQR/5GDKMuYmE3h+fx3MHqh1sMafztHLuiCg0FAk0uFdLqcpGY5QEXbTC/j7mIaVjc18DxufUtBJ/vcggs+3ijVz/0SYABsJHPUtu/OYwAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-py { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlVJREFUeNpsUktvEmEUPTPzTUFmgJK2UqXQFG3pA6OBLrQxamJcaYwuu3Dp0l9iXLvVtRuDpgt3JIYaTVSaxtRHsJq2xEJBHgXmifebMhECXzKZme+ee+65516h2+2Cn2cb2VwyHl12//vP2/zOQaF4uD7GWN69e/LogfNm7kUsPBFaXYwHMeK0OlpQEJApHJTuykzK98dE98O0bLM/UNgr4v32Dj1fwSQRt9dSsfmZcMa0rIv9ODaqYrPVxuPnL1Cu1aEbJu7fvIZUIo4bqeVYRzcyv/8c3SPYpwECt/dmu4ON3Ed4TymI+hQc1ZqoE+F+uQLDsnHlwkKMscJTgl4eJOi9fxZLePNhGx6ZQRRFqH4VjZaGSv0Y6cQcJLpra0ZguIWegqDiw7lYBBZV6xiGk9DQDLzK5bEyF4Hi9VLMsoYI7J6Es5PjeHjnOl5ubqHaaJGBEkzbxplQAKIgDmBHekDTgI+qKKqKLvNApgmEgyquLs1CoFn2Y4cIeLJpkjoCLkWnUSIF3JxISIUsCjAoxhWNJLBIJs3YeXj/08oYZkOKY65HllE/bkMmY504YUd40HUq2JSSyW6iVPmLiXE/ZMYQCU+hXK3h1toqdNN0sEObyKtqtDQ6kXDwcadDS2TBryp4nX2HxXjsJK6bDnZIAZem6Tp5YMMmicn5OC4lztNWtvB9cg+hQABtWjKL2jH/T3GgBcYDXEE6mcDM6SlaJAGMWkivLBC54ZgniZaDHSI4rNSqn7/t1vgkGJPwZXffSeCjk2iUWz9+nSTQN8e6ef8EGAClUi/qoiOc3wAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-qt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpsU8tu00AUPU5sp41NkzRxpfSZqi0VIIQqEEJUZYXECvbwCWxYsuBD+ABUFrDrCnWBQEJdIWigBSr6pqRJ1ebhxrE9M7aZmSrQ4o505fHMnXPPPWdGiaIIYrx89GKpNDdxmXkU3aEoCsT+z8W1Sm21+jCpJctQTvaerj+TX7WbnJ+0cpfuX8mQtn8GgJ4AZtIFY2Hz3foDVRcgyt+cRHcS0IARh+D/8G0PpmVi7smd0dLs+AIjwTVEiANEYYQwCHlEZyJgIQKfoX84g9uPZ0cHZ4YWmE9nuufU0wABCSSImMsWEgqSuoqA/39/swZFTWLy7vQo7dDnfPvWWQa8GuOV3IYLJXmyzDzG2/ChZ3pwbHdQ267BKJoYuj7SF2MQhiF8LuDK/Gf0DKTBKINz1IbTbEMzU1ANDW7LAfEIQKIgBsBFlAx6LYOz6MAcvoDCtAVGGPKlAiIu/F55F33FDA6W93EOAOMaMOl7biKPwRtD8Foetj5sYPfTDtxjl1f3Ubo5jkQieQ4ACSUD2iE4XDpAdbUiW9D7UsiN9WNkZgxajwbd0LGzt3keAJPUc1N5SVeENT0Ao2BKV6QzwlZeRBSKAYhe3aYHcZWn7l1EfjyPypcK9LQGa8qCvW9j9+MvaasQOHaRhGWdhsNLR8hwodYWf6B4tYjDjSOovRqq32rSYq/lytw4A77o1V2ERiAtzY5kkUrrsH+3QF2KY87ArTtQuQ6nAf4x6FCV1D001+vYersBM2vA4y1Rm2D7/Rac/TZIw4d/6MrcGAPf9htN0miJh7Lyuoyvr8rQeP9iVJcrSKgJ+TrFcyYebXTP/RFgAFQobmIOBxbsAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-rar { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpsUktPE1EU/u68OgylZXi0hZACQU1LEKKCMcat7jTRnQsXxsQtv4E/4M74P1iriUaNCw1FgxpjCJQKKAU60+m8mJnrmSll4XCTc8+959zz3e88GOcc8aq9evChOHl/lvMoubvWX/z4+BwTlbvw7bXdg8b7h6LE1gGW+O88CRMt4XTlR6/rYxce5Xv3jlHH19fPkBu+gWy5mlcFb3Wn/umeKOEMJF5C7xCFbtA9dRXjFoYKGiTRAlPGUV1aKU9O3VwNQ74A8DQAIZxqAuAhBPIMFYpQVAVB4CPSZjEzv1weH5tbDQN+JQ2Abu488mnzIbAAA3o/VK2PwDJo7r5Fy7ZRuvi4PFS6+qIXdVYD8Jg6BUcuOD8BozSLlRWyicgVKkTMQWwUlFF0Ooe5FIPk57BD7G0SiywyjD8bCDyHsOkeeeR3SUxEkROmU6BfQYFJMHfhWXV8efkUrb13VPMTsrcTQSzxZ/+n0GVA6EGbSGdgG9vo15fg2nFgbO8k70SRdd+mahDT81vUxTZRlJBRMsjq89C0EXCvSf7TIBZ136YZUJEiE7LgJ2dN01BZuE0dkIhxE7KcQTK1QUj+cwAEyrPZ+IydzRoyah+mLy2isbWBweESJEnB9q+1RM9Ub9GQOWkABg8HjRr2d9Yh0hTlBlRsfn+D4vg0BvUC9rZqECUJuk7Tzr1zahCYlB6HJAREPwfbbMBzLBzsbUKVI0qBgQkc+SxgWUYaIAqOpKwKXJ6bgGlaaDV/YvHaFNrtDsKTfVSrJeqIg/bRNwjclFIALeP3saybhu8SC4VBHwnhBXXIKocYRXD9QzBi4Xgchmkd9+L+CTAAMqwy+ZzluBgAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-rb { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAixJREFUeNqEUktvElEU/mag5f2yJhXLwxIt0kiqsVEXujP+A925cu1Pce3WtXVtYuJCF7KtTY0NrVQIpRVKeXTkMcO9F8+9ZVooJJ5kcmbmfOe733fO1YbDIWS8+/g1dycVX7W/xyO3vdsuVKqvnE7HZ230783rlyo7bVBicSGyfjsVwozomVbIPe/c+FmsPHfoRKJd1HT7hXHBZjVbA4aA14NnD9bC2VR8gwuxPi5Sx39Cp+M0XUP0ahhP1jLhW7HFD4zze3b93ILtXYyyVKlR8/5hFbnvO9gtlrGSjOF+OpXkYviWyo8mCS4R6bqO4p86vm3v4fC4DrPfw4unj1XN6JvBaQtjChzUXK43sVU4wNFJA43Tv/B73edQwTmfIhAjCVL6UdPAj1IVFSKhCdAcAI9rnjBiAjtBYEu3GEeh1sKJ0YXR68sVIujzIhzwY8DEBHZqiLRKkicQDfvABxaiQTc4Y/C65pCOXwcjcmlvJgHtlwi4epYifiQWgmoLZwPW6HQG07LgcOgKO0UglAKOTt/E+09fwAiUWU7QAE9xUK3jbvomsispZVHMVEDSZdHo9rCZ/4VIMKAu0XGjpU7d2S8hk0pCELHEzrjKnCQOYJoD+Dxu1RyiwUm5LaMDo9NFt2cqDLvY4oQFp/QpfT/MrmI5FkWebt+NpWto0j2QmQkOjZ9hpwhqjXZzM/+7LU+cc7lRrjXh8/lVLRK5ovLWXglOsiOxdt8/AQYAzv8qbmu6vgEAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-rtf { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe5JREFUeNqEU01PE0EYfnZmd5FSvgLYFuwWt9EgHyEaox68eDJevHvwJ/hTPHv1N/QgZ2NC4g3kUAQKFKGhjVKqRrvbnRlnht262FHfy+y8877PPM8z71pCCKh4/ebt+rJfXEz26Vjf2mnsN5rPKKWbVpx7+eK5Xu2kyMtNTd5d8MdhiJ9BOO7atFI9ajy1UyAqSPIRMR6ZmoNehNHMMB7fX/UWvEKFMbYKE8DfQnAhwRmmJkbx6M6S5+WmK2Evup2c9yUk2nnKA0XVcSiGXAe1k5beP1i+4RFCXqnPywB/AKVzK34RjHNYlgVKCH50w7EBBogbTa/AVM5SgBdn0gc2AMDjPsbFPz2xye9asweS6n+NTbG8BCCfUtLjff2WoVnVpAH6z6hMUtJE3EykYfpF4vUiL3QNS7FMeSAQRBHW3r1Hq91B+VoBQRji4+ExFsvz6Hz7jm7Yw5OH92AcJKW9G4SoHhzhy/lXbB98Qmm2oCXN5WawsV2TACEoJXqwTKOsb3BtR2ucmZxANpPB8JUhyPnHWDaDpfJ1eZFALzJJ4MKO5MEtv4TSXB7V/br8iQLMz+almRZWbvoo5q9qRlxwewCgeXbe3qrVO5ZkUD/9jJGRLPaOm6COi92TU1DbxYe9umRD0DrrtJO+XwIMABWp9nS+FgaoAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-sass { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDNDMTBBM0JGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDNDMTBBM0NGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowM0MxMEEzOUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowM0MxMEEzQUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Po72XUcAAAJcSURBVHjahFJdTxNBFD1bykc/ttvdtttWGgI0bYrUgDZoNYqRJ014kMRXHvwB/hQTH/wFhMREJfFBQxBjhMRIFEQSCAlQxKYGggiU3e3HbnfX2bFt1EU9k9m9mblz5p4zlzFNExYmpue/jmTSZw5PZAl1MAwDT0c7O72wvPdudeNakPNtOZ0tsM7cvzdOc5yN5LDAsTFRAJks/kC2PxFRVe39Si6f4byez62EpAEH/gNN18F53Ri/Ocxf7OtdLMpKT42s/ZPg1cISJp/P0tg0TBzLCoK8D7eHh4RkLLJ4cCz12AjMXwgez8yhqtVo3NbqRKlcxcSL16gZwJ2Ry8KVc8kZO0HdTKlURn+8G6PD2SZhLMQj96WAiMAh2RXFYKI78lcJcx9WYBCycICnpNbojUWpD5Y0C4Zh2D0w6hWc70uQZC+IWfQZrXF0IsHvY+meBd08haAhoVMMQFJKWF7PNZM+klhRyogGhbqxOIXAMOtEwGAqDqVcgbVkkE+5UsEAWavf0az2t0ZqvK2qabh6IU3joizDwTgwej1LdVfJXkdbK8mt2QkayO99A0/0trQ46I1lVcX+UREhnsP34yLp1AD1xibBMuntpzU8mJyi3Tc1O4+l9U06n7x8Q/8PHz1DrrALt8tlr0CrkbJMHTop9Sk5sLa1g8L+ARJdnShKClY3tunN69t5iGLYTlCtakjFY7gxNABdN3B37BaqqoYT8pyX0in4ORbRkIA46YlDRbUTbBZ2Jb/Pw4qiKFnapcpPo9pdbrg8DjAOBsFgELJmsGs7eWkkc5bu/xBgAHkWC6UPADTOAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-scss { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RkM4QjYyNDVGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RkM4QjYyNDZGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGQzhCNjI0M0YxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGQzhCNjI0NEYxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pkf1yeMAAAJbSURBVHjahFNdTxNBFD0tLULpB91uodVWPmorUIxo0VSiNSExMYYHE33l0Ud/in+C+OSjYgjRGDBRCKJIUkIEWi0WKlja0ul22+5219lJ26gLeiezuXvn7rnnnrlrUFUVms3Mvd2bjIyezRVLBA0zGAzo6jhjm1te+7EU37rFO+w7JlMbtG+ePJ5mOaZmci/nsPl6ONBtw18WDQc9tZq0sp7YjTisXV/NFKRpRvzHpHodDqsF03djzuvDg6vHJWFAprF/Arxe/oins6+YryoqCiUBvNOO+7FrXMjnWc0WyIAOQP0N4Nn8IqqSzPx2swllsYqZl28gK8DDyRvcxKXQvB6gISYpiwgH+jEVi7YAfW4nEqk0PJwDofNejAX7Pae2sPhhHQoF63U5Gai2Bn1epoPWmmaKoug1UBoMrgwHabIVVCx2jdrKFwm67TZ2plldPQGg2cK5HheIUMbaZqKV9In6giDCy3MNYXECgKI2gICxoQAEsQItpNCHWKngMo01arTY/jFIzbutShJuXh1Fm9FImYiM7tTtKOtbO+toN9Nc+fQ5SGUOIVYl7HzPIH2YRZ0y2KZ+sVzBHn2v1mpMGx0DTaR3nzfwfGEJdybGkdo/wEigDyvxLzg4yiESvojZhfd49OAeLJ2degaSLIPOO6vwgiYaaRErTRREEdn8MeJbSVZ5M7nLdNExqFLaQwEfFfACQn1+HBWKSKb3MT4Sgstuh9vVDa+bQ4DORE6o6RlspzMk9TOPfr+fiLJCLFYr3TZSKNcI7+aJwWQmPM+TkqRg49tu65f/JcAAMwMas6WUKd8AAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-sql { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh5JREFUeNp8kctrE1EUxr+ZyXMkoa1NBROaSkpTBE23PhZ25cql2y5duvAPUdGFS1FxIRRBXZlFQ9GVdDENIhGJxkDsw2mneZnM83ruNZlOmNoDhzlzz3d/9zv3Sowx8Ch/qlYK2XM3cEJsbH0+qjV/rd6/u6aN18b7RMFT+9aosP/Ex+0ae/puw7j36PlKEMAzctKJ3aGFamMHjV0d+wcGitkMrpWWp6hVIciEk2MAOwbUWjosx0UiFoWqJpGMx5DNzODq5aIPoa82AWBg/lyKLMH1PMp/a9XvLXLzG1cuFlBaWpiKxaIPSLY6CaC93ggQjyiQZRkeQSzLRovGaPciWLt5faSWEBoh6KBvOhiaNga0+Y9pwaFxvu7rfp8F5pWDt+qNMp2IijHGwddWCvN+33/CoAOP5nVdT9SdoQ1JkggiQ6Yvr7V60+9z7akA2gfH9cRF8hO5F5Ve4lQAF9uuK+qFsylkzsQxrcaQm04hdWkR83Mzfp9rQ3fAFzu9Ph6+WMfjl6/pGBdb2jbKmx8QlRjWy5vkyhUZBPgOeGNHN9AbDLGUz6He2hVj3Ll9C8/evsdgaMK0HV8bcmDTU0UUBYXcedR+NLGnH0I3jvDk1Rsy46FP4C/1BtrdntCGHNiOAzWZgEKQ5Qt5lIqLojbaXSQTcRy2OwT4SZqk0IYAOgkVWUE+lxX/zb0DpFNpkTzmZmfFtzewhHYcfwUYAMZmVaZQlLFHAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-tga { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra725K22ILRGipb22pMG6JcSEQTbUIwnozxpBcvepeEP0KPogcT/wlNT17kIKbEmChFUYKGVtL0R2gLtNCl3Z1Z3+zSAlonmezOe/O+973vvZEsy4JYnqdPMu6RkSQYQ29JEkB+PZcrslrtPhQl23VZc8/tr9I1yMHg0EA8HrBM04lVFAhoY38fSSDQVN3pfKV8G7KcxZHl6v1xblqU3eLc3p2VFZjr6+gQgwsnhzGTuq6Nhs6kYZqXjwL0GFhEl3U60OfnwWs1GGtrUKNRsKkpeIIBpKIRtI1J7cX7hXRhc/MOhXw5DkCZGG2zXAajzFIoBMvng1ypIKOqmP30GW3OIEcimovzlxRy5RgAFwDEAIODkCcmIMdiQLsNdWwMZdJlg8pzEUt1aBhKq3XinxKYqF9yQbqRIqsMy+0Gyy47bKgUWXSLtDENE5wdtuqQATm50F1VnPbRGeEw8HXZbiV8fsDvI9ldju9vADAyihLEbrWAZhOoVp3z6iqBUiB1A4nEfwCEsbkL/M4TgE5n5jDx+oTEzp1d8m9tC8H6MaAB0imzx0NU/WKUYE+loEyawDBo2ui6TGfT6ANAxrvx87gYCGCxXEKVJvCWFsG3eh1vN/J4OD6Od4UC8o0G3TX7TGLHwI9iEQmvF9X6Fh7F4/iYy+GcLOMSlfEgGsP0qdNOmX0BiGKpVkV1bw/1nW2b/gCpf1PTcI+Y7eg6ps+G4bG4PR99SjAVo9HE4q+fKNE0vl5awuSohjeijbRefVjAtUgEQRK7Yhi9OKn7nKWZxxlSPWl3QwgnaIrW8QMhD542vUbx/W49m7sq4v4IMABOqi3Ej7bAEAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-tgz { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpsU1trE0EYPbMzSTfdtInFtkkpiaXVWou2FRUEn/so6JugL/oH/Af+B1988if40jcFERQURNBSQdDWlLQN2lsue8neZsZvc7FoOrDszM75znfOmVmmtUYyvry++36yfOeS1qqzDtvH2P76ApPlW3Drb2sHex/uccHWAdbZX30kO2+B3siN3zhTnHuQ66+95i423jzFzOVljBdKOZNHazvVT7e5wF+SZBj9iZJ+3J11mbW2kR8T4LwFli5i4fqTUvnczTUp9RLtDhKgJx0q4dEwWAxrREKICHEsoYYXMXvlcWmquLgmY71yCkG/c0AkARgLMZpnMDMpGNzEYe0dGp6HwvmHpbHC1Wf9MnFCkHQOyYEPzSJwQ2B65Tm5NZG3Fshim6wbMNJn4bpHowMKtIqo2COgR2IcAptwjvcgo6i77igjEmVDqbY8xQJ1VwRULhiBI6+G9Zf3cbTziuzIDkmHSNqECTFgQScEcYuc2NA8TcdYwXD+GkK/TYVN+u72WrIudiAD8o6oAR2RRCmQMjis3CIy1iSpPySCXhFTXeyAgh4BR+JVw8pauLi0Cp4yCX9A90FQhnSBYtnF/k+Q+HYam9itfIZB3QvT8zj8XSW5EhNTs9ivbSLwPUzPLNPJBIMEKnaQYg6aB9+RGR5F5VsNgnNKXMI1NdJGG5WfHzFVLJ7k8c8xUngpVodlDSGbFYj8Y4yMpOG09lHf3yIFPzA3fwHZTAQVtU4JUTeFDrdgDdlI8wAz5Qy2KxswReI7QODZcOr0ZH3q2hIDBI7zq16tuk3FNPxAI4wN+pkoccYoE4YJU5EdUtM4Qst26v26PwIMAKj3P/2YUKgYAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-tiff { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNp0UktPE1EU/qYzHWstlrYJNcWUElyUJsaNGh9B0g1Lo0v9Ey78EbrVxBhXuHShm25YGBJRQpAYBDEWpaEPEhksdVpbyjzveO4MfZDCTWbauefc736PIziOA77OPH2yJCcSGdg2uksQAKofFou/7VrtASRpvVNynj13f6XOhjg8HAlMTIQdy/LO+v3uYUPTkAHCTb+cK+0pdyGK6+hbvu4/xiyHbncYAwfR19ZgbG/DoO9LsSgeTd9JXoxfyMG2rvQDdBlwIZauQ5ufh12twioU4E+nYU1NIRCNIDs+Bt28mXzx8VNuZ796j9q/DgAwomwqClilAmF0FE4wCInAlkjO4y+r0JgNX2os6XPYS2q/cQyAcQatFjA0BPH6NYipccAwIGUy2CVJFZInkKlyJAqx3T4/IMGmJkeWIWSz5KgI5pdhb3yDXS5DSCYh8rTID8s0wexeVD0GtMd85KkkefFxUfE47M1NokbJkByEQl6tL+ouAI+MUwbFhnYbaJKc/Sqg0x4H4eDRGDA56fUOABA9/GsCpaIHwr8FOhQ823O5RfW66tUGADhNy3RNRDjcN41HLxdQ8J6jYTsOQLfOJBK4f+s2/uoathoNGKT1MtFeVHZxdWTEZfEq/wMKl3rCJOIzTV6ADs2R5ulYDDNkYjp0DhrF+zCVgkw31+v1UxjQZkNV0SADd2o1MIuc9gmY+/kLxb0/UFoHePd9A1qzeUoKpilx9xcLWzgg+u/zeVfuQqkM9bCN1ysrWKXxdtPgvScwUAm58XZ52W16QyPtifRUzi588GbEi1ztHPsvwAC4uC9qhnsZvwAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-txt { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeJJREFUeNp8UrtOG1EQPfsyXiyzBguIJSyChZBBEFCKpKHLo6egpErNn8CHgH8gkZIiTSIXLhJAWCgkoMgRMSiRBSK29z4y9+I1d/HCrFb3MTPnnjkzlpQSynY+fP70fGF2gQuByCz6lfdd9Uurfvrrjes6762eb3tzQ69uFJwPsqOPC+MBEmxxphi4tlU5OGmsOzaBWLc+O9oIIVhScidkyGZ8vH62nHtSKlaI4cse6TjAfSaFBBcco0EWqyvzubmpyQrj/FXk75cQaSEMeMXU8xykPA/Hjd/6/LRcyjEpt2i7HAe4A2TeLZWKUOJaVLxj27j813EHGKCXaAJExu/4BOdiAED08riQD2riOrexyRoYc3CvsAbLGAAjZga7vgZG23WMCdBvoxKJc36TRBlMiaa2JByjNqqD8qkYc1pjDK7abey+/YhrWlfKswhpiCR96aEU9o5+QE3g2ovVWDm2Sc22bBQm8vrVpbkS9r+doPr1EOWZaQ0yFoxg2PcREosEAI4uvZhJpzFMP+cSXRbq+043RManez+tNWKMI6GN0g0Z04HFR+NoNC/0yx717efZOSbzY3AcR4Op2AGA5p/W31r9e0vNgSrh9OwCrpeCkqvZuqTybnpRqx/r2CjvvwADAJC/7lzAzQmwAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-wav { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApFJREFUeNpsU1tPE0EYPXtpKbX0wqUQKVQMFdIXQBNCQBs06KP+B8ODGh+Mf4b/4IsGE54kxhcMBrkp7YOQgBRvSKG73fvsrt8Otoask0xmd+b7zpxzvm8E3/cRjPkniyulW0NFy2JoDkEAguOlpXJ9p3L8MBqVl4O9YHxae8pXuRlcGO7KPLhfTDVUqwUgigJMy4Whm6lEXHjxYf3XnByRN0QB/2KaH7btMlUxoRJAcyqKhdOaht7+DJ49n+2cvTnwynXcsb+kLwJ4rgfmMDDGWqvneXCZS9ND7mov5h9ND85M9y86Dpto5rUkuJ4Py3YDJpy6QGJPayqB+Njf+43XL220t0cwOZkfrNXsBUqZugDA6CbLdAiAwaek1ZU9LmP8Rh6S78GsGxjOp9FdzKJaVZIhBgGASzK21w/wbrnCk8euX+EMAjaaZuPHdwUdHVFYluuGPGCORwwYjg5rqOwccRk+3Ux0IEvntmsNG4ZmUayL/wAwKHUNfZfTKN0ZRaw9Cof8qJ/pMAyHy5KkAMTksSEJtnMenM7EMVMawbejMzJRh67bXEYiIXEAVTW50SEAhzqwfqrBcXx4VOhYm4RsNgHbsJFOyZTsQ1MN+hcohoUlkFiMT+TQFpMwXOjGpXgE+XwGk1N5pFJtKNCequgYGupCRBbCDOp0KBJc4VoP3dyBONW8uydBgBHUThqQKCk3mEZ/LoUG+RBioJO7VarAwEAntjYPiUUW9Hh4b2R7k9j98hN37xWx8fGAt3eIAdVMLn+uUv+b2KReSCZjZJiB9bV9jIz2ofr1BKvvd7G9dRC80lae0HzOt+cWVnrSKDrMJykifwNBpCgE/UAllEXufmDu8Zlffvvm8XSQ90eAAQA0pF7c08o4PAAAAABJRU5ErkJggg==); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-wmv { + background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M9.1 91.1L4.7 72.5h3.9l2.8 12.8 3.4-12.8h4.5l3.3 13 2.9-13h3.8l-4.6 18.6h-4L17 77.2l-3.7 13.9H9.1zm22.1 0V72.5h5.7l3.4 12.7 3.4-12.7h5.7v18.6h-3.5V76.4l-3.7 14.7h-3.7l-3.7-14.7v14.7h-3.6zm26.7 0l-6.7-18.6h4.1l4.8 13.8 4.6-13.8h4L62 91.1h-4.1z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-xls { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmxJREFUeNpsU0trFEEQ/mamZ3Y2+0zIC2MmITEkUYgERFQErx5E8KTi1b/h79A/4SW3nCNeYggBYZVEMU/y3N3Z7M7OTD/G6lk2ruw20zRdU/XV91VVG0mSQK/3n1a/jky6d6Xs3G8WXS+Pw5N6LXjLLGuna/78oZKerGsYKtrDE16uJGL1L9gEOOcYd2dL1fNwrbL//aXN7J1efPMmkUqEFAk0A0VZNbFEaQCBscIkXj975y3NLq9xye8PBkAniHOFph+j2eC4rsdoB4LsFubGl/Hq8RtvYWpxTQi52o1jvWiGYaRZL0/auDgOkC/Z8BYL2Pqxidp1FZkhoDxpeaXA/Ujuj/4HoOxKKjiOiek7RUShRNQWaNYFQuMafrYCxiw4ozZKfqbYJ0EvRdl1DQyyTs8XCNTA6UELMwvDyLpZWIZNNlNLlQOK2LMJRJ+5AkuZ1S7CFFzJzk56GnUjQWlYkqCoBWFbonEVYcLLA4dNnB624GQsDBWIgfZJEgxkoChzSFWvn4VpQemDm2VwXQsXJwF1h6c+gxlQ5jgSiEUEt0wdIe7tMES+nEG2aCLiJMOIIWIr9e0DEELAMUrwRuchVAyTKimUwO75Jm6VF3Bv7imOaj+xd7UFKVS/BPJF1b/E4tgTrE49J60O5kceoNqowiuuYKa8ghHXA48U9MT2AQgyRvTThE30bQiaSGa4yLMJNFo+Dq/2cHt4CYlwyFf2S6BHwwrMw/avDbR5C1k7h1YQ4KH3Amf+AcZyEbZPv9CItzQD1l9EbtYOjv74v/d3O9RMPTDrsEwGIWN8q2yk7XNYRs9JrRv3V4ABADSGR6eQ0/NQAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-xlsx { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8tqFEEUPVXdPY/ueWZIoiYZiSYKYhJc6EbduHOhgijo3t/wH1z6B0JAhOyMILhxo4kJGk1ASTAxwWF0Mpp5dHc9vFUzYwidaoqmq+8959xzbzGtNcx69PTS26ETmQtS9r4Hy/xv7MW7jV+th5yzVcaYPX/++It9u4NAv+CVR6tBUUTqMJsDcRzjZOZM8W9ZLKx+/XDb4e5/kH5In0lpIYWGUaC0YTZnBCAEKoVR3L36oDo7NbsglZwbqD6iQKOXFMcKUVfBkBAoQhlD5xxMDp/HrSv3q1JgYW3z0x0KXzkCYJaRZljru23aHWTzLiamAyytv0O9UYdf5PArqlppBfMUfu4oALErqZBKcUxMFRCHEp0DgW5Lo4N9NIN1dF0XXsVFOUyPJTzo+WBANDidjp8tgHGG3c0DnJ4uIRf4cOCBaW5KjY8xkZL72xpJ9QcFz5bVqHUJGHZL2YtNmKi06YCyiVFb4s/vEKMTAf1p4edOG6mMi1zR6wEpdUwX+vLDtkCzHoK7ptcM6ayLmGajvtex4PliyoIkFRjmUEASelB2rXQRSfjUCT9PlWpmW21iTGzCAyEkUixPRqXhe2V4zKczbdmybgkpJ0cGOuA6Y2MTCsKoi5HsNK7N3MN+uwYaWbxYfoLLkzdxcew6lrYWaZhm8PHHG3zffp1UwJSHz9vvkU8PodbcQYYYS5lxYkxTkGdVDQdV1Js1qPgYD6JIuIE7gsXVefIhIuM05k7dwMbeMmh87a18ufIMaVYyprrJLgje2Nr+1tzYXANnDnr3zRhHj37Vvy2wpXHtNAd5/wQYAD6WMuT2CwoVAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-xml { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAilJREFUeNqMks1PE0EYxh+g3W2t1G0sEqyISynUFJsSOShNwCamiYZED3LgIkcuxoN/iCZePZiYGD2aGD+i0F5KMChxlVaakAK2ykcAt+WzdLu7zkxo3WZL4pu8mXfmeeY3885ug67roPFh5nvc62m9hjoR+5LMp7MrkYf370qVtco+VtCUFpbj+jGR+JbWn76OyQ8ePwsZATQb8R/hanZgINgj9IqeuBFCw1Kt9OMBnNWCs24XwkG/QKYUEiGjVAPQof/rq0783pShET3ULQo8xz0iS5FaANmrHQH2DoqY+DSLSz6RzecWlnD9ymU47LYjd4O5BXqDTG4FM3NpTEkpdJ5rw0AowLRMbhUfp58gTOaD/UHmNQPI6YmvKWRX1zESHUJ/oBs2nmPa+Mgw0ZIM3tZyGoJwygzQNB2jNyJIZX7iB0lpPoM70UGmPX8zCU+rG8NDVxHwdiC5mKsPUFUN/gvtLLf39sFzVqaN3YrC6TjBauqhXhNA1TQoqloV7Da+pjZq1FsXUCamF29j6LvYhf3iISamZ3Fv9DZevouhRzzPfOG+3hpA9U9UyioOlTJ7pFeTCQS6RGzIebyf+oz5pSzWtmSW1EO9phvQ00slBRt/8qR3DoWdXbiczUiTzd52D+tdLmyTB14mx1rMAKVcRpEATjrsuElee/HXGmnFRyBOGD30C/nEDjNgs7CDpsYmnHG3YPegBCvHs9oYfm8nG9dJa5X4K8AAQzQX4KSN3wcAAAAASUVORK5CYII=); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-yml { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdxJREFUeNqMUl1rE0EUPbM7m5Y0Zptu21AwWwhYpfSDFh+kvvRd8N0Hf4I/xWdf/Q158F0QoQ+CVsFKaLSQpt/dpmvztTOzzky6cetOpWcZZvbO3MO5514SxzEU3r57/3GpWllM/tP4sL3TarROXuSo/SWJvX71Uu80Cfhlr/T4UdWFAVfdnmsTUtvdP35OUyQKVnJgXDBTcj9icAsTeLax7j/052qM81UjwW1QJXEhMF0qYnN90fdnvdogYmvJPU0/VBApD4hcDrWRcyikfB17srzgW7b9Rh1vEvxDlI4tVytaBSEEtmWh0xsUMwpwnWjqAlcxogiHd1wiQyCu87iI/+sJtf6+NXsgpd7FWCMB50KvkYMGMbLdZgLlfj+K9K4+FnFQ2x7WntIs50AbmiGwLILt+k+EvzvSNIHzdigdJ/AmXQRhiHv5POSwYmG+cqPVo0HqDxj8uTK2vn1Hfa+JmdIkvtZ/4fOPXU3WPDpFeNWVyUKryCiIGMN4zsH98gym3CIcOTwT+XHdXrdQQHAZotE8kBPpSqPNHtBOr48HUmLOcXRJT9dWNMGYJFby91pHOAvaykSaITg+bwefdhrteDRTMSwyrFCgI88E056Hy+4Ah2cXQZL3R4ABALUe7fqXWFN6AAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} + +.btfs-zip { + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm9JREFUeNpsk0tv00AUhc+MY6dOmgeFJg1FoVVpUWlFC0s2IFF1jxBbhKj4BSxYdscPYcEmQmIDq0gsERIViy4TpD7VFzF1Ho5je2a4thOqNhlp5Mz4zudzzp0wpRTC8fPrk0/TC6+fDtYicLH97T1Kc2vQDcs+rH3eUAxVznn0fn1DRM8E+iOdv5ct3XmZG6yVlNj6solUbgVTt0q5FGtX6vXqC6VklTE+KAO/OODHSIQPRQpsXC+kkEz2ELA0ystv84tLzyucsbWByisAGf+QAS2CCDRRLMJMmxC+i8C4jdLCm/zM7OOKFGptcO6/BTpJ0yeQB0Y+mfKQuZZG0jQgeRbW8Xdomobs9LN8scc+UPHNy4Dwq8IljotIIQEm59/RoSyM1CKkXKZNBm7kIVgyM6wgAnSgRK9vqQfHPiMFDHqyFVsLR9Cm0o4YzoAASrSjCelQfRPb1Vc4qn0EY5L2W9GEaBLcxQgFHpGbkMIDJ69e+wjJ8VXqRgKid0r7ftQdxkRs9SqA2kgAm14SSIQh9uhuLGPMnKJs/5KquL1x0N0RCsizigoDaLqBdHoMiyvrlBsHVx1wphD4BCewoqxGKKDwAgtOy8JufYuk+5golGGaGZwc1sIGoDz3AOPZSVLaHgVwydoJDM1H4DbQODughB3YpOD44HfoHgnu4e7So0uAi0stHLJ3Aud8B9bpHu6vPoSu9TtDl6tUuoFiIYOgu0+158MKmOxomtyD3Qi/3MTR7i8K0EDG1GHO5DE3X4DvNahZlJOwEkOATvdPc2//hx3mXJ5lFJaF8K8bStd0YGfnOJbMGex21x6c+yfAAOlIPDJzr7cLAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-size:contain +} \ No newline at end of file diff --git a/core/corehttp/gateway/assets/src/style.css b/core/corehttp/gateway/assets/src/style.css new file mode 100644 index 000000000..930c2b95c --- /dev/null +++ b/core/corehttp/gateway/assets/src/style.css @@ -0,0 +1,213 @@ +body { + color:#34373f; + font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; + font-size:14px; + line-height:1.43; + margin:0; + word-break:break-all; + -webkit-text-size-adjust:100%; + -ms-text-size-adjust:100%; + -webkit-tap-highlight-color:transparent +} + +a { + color:#117eb3; + text-decoration:none +} + +a:hover { + color:#00b0e9; + text-decoration:underline +} + +a:active, +a:visited { + color:#00b0e9 +} + +strong { + font-weight:700 +} + +table { + border-collapse:collapse; + border-spacing:0; + max-width:100%; + width:100% +} + +table:last-child { + border-bottom-left-radius:3px; + border-bottom-right-radius:3px +} + +tr:first-child td { + border-top:0 +} + +tr:nth-of-type(even) { + background-color:#f7f8fa +} + +td { + border-top:1px solid #d9dbe2; + padding:.65em; + vertical-align:top +} + +#page-header { + align-items:center; + background:#f7f8fa; + border-bottom:4px solid #edf0f4; + color:#34373f; + display:flex; + font-size:1.12em; + font-weight:500; + justify-content:space-between; + padding:0 1em +} + +#page-header a { + color:#233b70 +} + +#page-header a:active { + color:#9ad4db +} + +#page-header a:hover { + color:#6b96f8 +} + +#page-header-logo { + height:2.25em; + margin:.7em .7em .7em 0; + width:12.15em +} + +#page-header-menu { + align-items:center; + display:flex; + margin:.65em 0 +} + +#page-header-menu div { + margin:0 .6em +} + +#page-header-menu div:last-child { + margin:0 0 0 .6em +} + +#page-header-menu svg { + fill:#233b70; + height:1.8em; + margin-top:.125em +} + +#page-header-menu svg:hover { + fill:#6b96f8 +} + +.menu-item-narrow { + display:none +} + +#content { + border:1px solid #d9dbe2; + border-radius:4px; + margin:1em +} + +#content-header { + background-color:#edf0f4; + border-bottom:1px solid #d9dbe2; + border-top-left-radius:3px; + border-top-right-radius:3px; + padding:.7em 1em +} + +.type-icon, +.type-icon>* { + width:1.15em +} + +.no-linebreak { + white-space:nowrap +} + +.ipfs-hash, .btfs-hash { + color:#7f8491; + font-family:monospace +} + +@media only screen and (max-width:500px) { + .menu-item-narrow { + display:inline + } + .menu-item-wide { + display:none + } +} + +@media print { + #page-header { + display:none + } + #content-header, + .ipfs-hash, + .btfs-hash, + body { + color:#000 + } + #content-header { + border-bottom:1px solid #000 + } + #content { + border:1px solid #000 + } + a, + a:visited { + color:#000; + text-decoration:underline + } + a[href]:after { + content:" (" attr(href) ")" + } + tr { + page-break-inside:avoid + } + tr:nth-of-type(even) { + background-color:transparent + } + td { + border-top:1px solid #000 + } +} + +@-ms-viewport { + width:device-width +} + +.d-flex { + display:flex +} + +.flex-wrap { + flex-flow:wrap +} + +.flex-shrink-1 { + flex-shrink:1 +} + +.ml-auto { + margin-left:auto +} + +.table-responsive { + display:block; + width:100%; + overflow-x:auto; + -webkit-overflow-scrolling:touch +} diff --git a/core/corehttp/gateway/assets/test/go.mod b/core/corehttp/gateway/assets/test/go.mod new file mode 100644 index 000000000..8980d9a71 --- /dev/null +++ b/core/corehttp/gateway/assets/test/go.mod @@ -0,0 +1,3 @@ +module gateway-test + +go 1.19 diff --git a/core/corehttp/gateway/assets/test/main.go b/core/corehttp/gateway/assets/test/main.go new file mode 100644 index 000000000..96d940496 --- /dev/null +++ b/core/corehttp/gateway/assets/test/main.go @@ -0,0 +1,156 @@ +package main + +import ( + "fmt" + "html/template" + "net/http" + "net/url" + "os" +) + +const ( + directoryTemplateFile = "../directory-index.html" + dagTemplateFile = "../dag-index.html" + + testPath = "/ipfs/QmFooBarQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7/a/b/c" +) + +var directoryTestData = DirectoryTemplateData{ + GatewayURL: "//localhost:3000", + DNSLink: true, + Listing: []DirectoryItem{{ + Size: "25 MiB", + Name: "short-film.mov", + Path: testPath + "/short-film.mov", + Hash: "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", + ShortHash: "QmbW\u2026sMnR", + }, { + Size: "23 KiB", + Name: "250pxيوسف_الوزاني_صورة_ملتقطة_بواسطة_مرصد_هابل_الفضائي_توضح_سديم_السرطان،_وهو_بقايا_مستعر_أعظم._.jpg", + Path: testPath + "/250pxيوسف_الوزاني_صورة_ملتقطة_بواسطة_مرصد_هابل_الفضائي_توضح_سديم_السرطان،_وهو_بقايا_مستعر_أعظم._.jpg", + Hash: "QmUwrKrMTrNv8QjWGKMMH5QV9FMPUtRCoQ6zxTdgxATQW6", + ShortHash: "QmUw\u2026TQW6", + }, { + Size: "1 KiB", + Name: "this-piece-of-papers-got-47-words-37-sentences-58-words-we-wanna-know.txt", + Path: testPath + "/this-piece-of-papers-got-47-words-37-sentences-58-words-we-wanna-know.txt", + Hash: "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", + ShortHash: "bafy\u2026bzdi", + }}, + Size: "25 MiB", + Path: testPath, + Breadcrumbs: []Breadcrumb{{ + Name: "ipfs", + }, { + Name: "QmFooBarQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7", + Path: testPath + "/../../..", + }, { + Name: "a", + Path: testPath + "/../..", + }, { + Name: "b", + Path: testPath + "/..", + }, { + Name: "c", + Path: testPath, + }}, + BackLink: testPath + "/..", + Hash: "QmFooBazBar2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7", +} + +var dagTestData = DagTemplateData{ + Path: "/ipfs/baguqeerabn4wonmz6icnk7dfckuizcsf4e4igua2ohdboecku225xxmujepa", + CID: "baguqeerabn4wonmz6icnk7dfckuizcsf4e4igua2ohdboecku225xxmujepa", + CodecName: "dag-json", + CodecHex: "0x129", +} + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/dag": + dagTemplate, err := template.New("dag-index.html").ParseFiles(dagTemplateFile) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse template file: %s", err), http.StatusInternalServerError) + return + } + err = dagTemplate.Execute(w, &dagTestData) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %s", err), http.StatusInternalServerError) + return + } + case "/directory": + directoryTemplate, err := template.New("directory-index.html").Funcs(template.FuncMap{ + "iconFromExt": func(name string) string { + return "ipfs-_blank" // place-holder + }, + "urlEscape": func(rawUrl string) string { + pathURL := url.URL{Path: rawUrl} + return pathURL.String() + }, + }).ParseFiles(directoryTemplateFile) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse template file: %s", err), http.StatusInternalServerError) + return + } + err = directoryTemplate.Execute(w, &directoryTestData) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %s", err), http.StatusInternalServerError) + return + } + case "/": + html := `

Test paths: DAG, Directory.` + _, _ = w.Write([]byte(html)) + default: + http.Redirect(w, r, "/", http.StatusSeeOther) + } + }) + + if _, err := os.Stat(directoryTemplateFile); err != nil { + wd, _ := os.Getwd() + fmt.Printf("could not open template file %q, relative to %q: %s\n", directoryTemplateFile, wd, err) + os.Exit(1) + } + + if _, err := os.Stat(dagTemplateFile); err != nil { + wd, _ := os.Getwd() + fmt.Printf("could not open template file %q, relative to %q: %s\n", dagTemplateFile, wd, err) + os.Exit(1) + } + + fmt.Printf("listening on localhost:3000\n") + _ = http.ListenAndServe("localhost:3000", mux) +} + +// Copied from ../assets.go +type DagTemplateData struct { + Path string + CID string + CodecName string + CodecHex string +} + +type DirectoryTemplateData struct { + GatewayURL string + DNSLink bool + Listing []DirectoryItem + Size string + Path string + Breadcrumbs []Breadcrumb + BackLink string + Hash string +} + +type DirectoryItem struct { + Size string + Name string + Path string + Hash string + ShortHash string +} + +type Breadcrumb struct { + Name string + Path string +} diff --git a/core/corehttp/gateway/blocks_gateway.go b/core/corehttp/gateway/blocks_gateway.go new file mode 100644 index 000000000..bc2e12d3c --- /dev/null +++ b/core/corehttp/gateway/blocks_gateway.go @@ -0,0 +1,455 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + gopath "path" + "strings" + + "go.uber.org/multierr" + + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-btfs/namesys" + "github.com/bittorrent/go-btfs/namesys/resolve" + ufile "github.com/bittorrent/go-unixfs/file" + uio "github.com/bittorrent/go-unixfs/io" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" + ifacepath "github.com/bittorrent/interface-go-btfs-core/path" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-cid" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" + blockstore "github.com/ipfs/go-ipfs-blockstore" + format "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-merkledag" + ipfspath "github.com/ipfs/go-path" + "github.com/ipfs/go-path/resolver" + "github.com/ipfs/go-unixfsnode" + car "github.com/ipld/go-car" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/schema" + selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" + routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + mc "github.com/multiformats/go-multicodec" + + // Ensure basic codecs are registered. + _ "github.com/ipld/go-ipld-prime/codec/cbor" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + _ "github.com/ipld/go-ipld-prime/codec/dagjson" + _ "github.com/ipld/go-ipld-prime/codec/json" +) + +type BlocksGateway struct { + blockStore blockstore.Blockstore + blockService blockservice.BlockService + dagService format.DAGService + resolver resolver.Resolver + + // Optional routing system to handle /ipns addresses. + namesys namesys.NameSystem + routing routing.ValueStore +} + +var _ IPFSBackend = (*BlocksGateway)(nil) + +type gwOptions struct { + ns namesys.NameSystem + vs routing.ValueStore +} + +// WithNameSystem sets the name system to use for the gateway. If not set it will use a default DNSLink resolver +// along with any configured ValueStore +func WithNameSystem(ns namesys.NameSystem) BlockGatewayOption { + return func(opts *gwOptions) error { + opts.ns = ns + return nil + } +} + +// WithValueStore sets the ValueStore to use for the gateway +func WithValueStore(vs routing.ValueStore) BlockGatewayOption { + return func(opts *gwOptions) error { + opts.vs = vs + return nil + } +} + +type BlockGatewayOption func(gwOptions *gwOptions) error + +func NewBlocksGateway(blockService blockservice.BlockService, opts ...BlockGatewayOption) (*BlocksGateway, error) { + var compiledOptions gwOptions + for _, o := range opts { + if err := o(&compiledOptions); err != nil { + return nil, err + } + } + + // Setup the DAG services, which use the CAR block store. + dagService := merkledag.NewDAGService(blockService) + + // Setup the UnixFS resolver. + fetcherConfig := bsfetcher.NewFetcherConfig(blockService) + fetcherConfig.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }) + fetcher := fetcherConfig.WithReifier(unixfsnode.Reify) + r := resolver.NewBasicResolver(fetcher) + + // Setup a name system so that we are able to resolve /ipns links. + var ( + ns namesys.NameSystem + vs routing.ValueStore + ) + + vs = compiledOptions.vs + if vs == nil { + vs = routinghelpers.Null{} + } + + ns = compiledOptions.ns + if ns == nil { + dns, err := NewDNSResolver(nil, nil) + if err != nil { + return nil, err + } + + ns, err = namesys.NewNameSystem(vs, namesys.WithDNSResolver(dns)) + if err != nil { + return nil, err + } + } + + return &BlocksGateway{ + blockStore: blockService.Blockstore(), + blockService: blockService, + dagService: dagService, + resolver: r, + routing: vs, + namesys: ns, + }, nil +} + +func (api *BlocksGateway) Get(ctx context.Context, path ImmutablePath, ranges ...ByteRange) (ContentPathMetadata, *GetResponse, error) { + md, nd, err := api.getNode(ctx, path) + if err != nil { + return md, nil, err + } + + rootCodec := nd.Cid().Prefix().GetCodec() + // This covers both Raw blocks and terminal IPLD codecs like dag-cbor and dag-json + // Note: while only cbor, json, dag-cbor, and dag-json are currently supported by gateways this could change + if rootCodec != uint64(mc.DagPb) { + return md, NewGetResponseFromFile(files.NewBytesFile(nd.RawData())), nil + } + + // This code path covers full graph, single file/directory, and range requests + f, err := ufile.NewUnixfsFile(ctx, api.dagService, nd, ufile.UnixfsFileOptions{}) + // Note: there is an assumption here that non-UnixFS dag-pb should not be returned which is currently valid + if err != nil { + return md, nil, err + } + + if d, ok := f.(files.Directory); ok { + dir, err := uio.NewDirectoryFromNode(api.dagService, nd) + if err != nil { + return md, nil, err + } + sz, err := d.Size() + if err != nil { + return ContentPathMetadata{}, nil, fmt.Errorf("could not get cumulative directory DAG size: %w", err) + } + if sz < 0 { + return ContentPathMetadata{}, nil, fmt.Errorf("directory cumulative DAG size cannot be negative") + } + return md, NewGetResponseFromDirectoryListing(uint64(sz), dir.EnumLinksAsync(ctx)), nil + } + if file, ok := f.(files.File); ok { + return md, NewGetResponseFromFile(file), nil + } + + return ContentPathMetadata{}, nil, fmt.Errorf("data was not a valid file or directory: %w", ErrInternalServerError) // TODO: should there be a gateway invalid content type to abstract over the various IPLD error types? +} + +func (api *BlocksGateway) GetAll(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + md, nd, err := api.getNode(ctx, path) + if err != nil { + return md, nil, err + } + + // This code path covers full graph, single file/directory, and range requests + n, err := ufile.NewUnixfsFile(ctx, api.dagService, nd, ufile.UnixfsFileOptions{}) + if err != nil { + return md, nil, err + } + return md, n, nil +} + +func (api *BlocksGateway) GetBlock(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.File, error) { + md, nd, err := api.getNode(ctx, path) + if err != nil { + return md, nil, err + } + + return md, files.NewBytesFile(nd.RawData()), nil +} + +func (api *BlocksGateway) Head(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + md, nd, err := api.getNode(ctx, path) + if err != nil { + return md, nil, err + } + + rootCodec := nd.Cid().Prefix().GetCodec() + if rootCodec != uint64(mc.DagPb) { + return md, files.NewBytesFile(nd.RawData()), nil + } + + // TODO: We're not handling non-UnixFS dag-pb. There's a bit of a discrepancy between what we want from a HEAD request and a Resolve request here and we're using this for both + fileNode, err := ufile.NewUnixfsFile(ctx, api.dagService, nd, ufile.UnixfsFileOptions{}) + if err != nil { + return ContentPathMetadata{}, nil, err + } + + return md, fileNode, nil +} + +func (api *BlocksGateway) GetCAR(ctx context.Context, path ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) { + // Same go-car settings as dag.export command + store := dagStore{api: api, ctx: ctx} + + // TODO: When switching to exposing path blocks we'll want to add these as well + roots, lastSeg, err := api.getPathRoots(ctx, path) + if err != nil { + return ContentPathMetadata{}, nil, nil, err + } + + md := ContentPathMetadata{ + PathSegmentRoots: roots, + LastSegment: lastSeg, + } + + rootCid := lastSeg.Cid() + + // TODO: support selectors passed as request param: https://github.com/ipfs/kubo/issues/8769 + // TODO: this is very slow if blocks are remote due to linear traversal. Do we need deterministic traversals here? + dag := car.Dag{Root: rootCid, Selector: selectorparse.CommonSelector_ExploreAllRecursively} + c := car.NewSelectiveCar(ctx, store, []car.Dag{dag}, car.TraverseLinksOnlyOnce()) + r, w := io.Pipe() + + errCh := make(chan error, 1) + go func() { + carWriteErr := c.Write(w) + pipeCloseErr := w.Close() + errCh <- multierr.Combine(carWriteErr, pipeCloseErr) + close(errCh) + }() + + return md, r, errCh, nil +} + +func (api *BlocksGateway) getNode(ctx context.Context, path ImmutablePath) (ContentPathMetadata, format.Node, error) { + roots, lastSeg, err := api.getPathRoots(ctx, path) + if err != nil { + return ContentPathMetadata{}, nil, err + } + + md := ContentPathMetadata{ + PathSegmentRoots: roots, + LastSegment: lastSeg, + } + + lastRoot := lastSeg.Cid() + + nd, err := api.dagService.Get(ctx, lastRoot) + if err != nil { + return ContentPathMetadata{}, nil, err + } + + return md, nd, err +} + +func (api *BlocksGateway) getPathRoots(ctx context.Context, contentPath ImmutablePath) ([]cid.Cid, ifacepath.Resolved, error) { + /* + These are logical roots where each CID represent one path segment + and resolves to either a directory or the root block of a file. + The main purpose of this header is allow HTTP caches to do smarter decisions + around cache invalidation (eg. keep specific subdirectory/file if it did not change) + A good example is Wikipedia, which is HAMT-sharded, but we only care about + logical roots that represent each segment of the human-readable content + path: + Given contentPath = /ipns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey + rootCidList is a generated by doing `ipfs resolve -r` on each sub path: + /ipns/en.wikipedia-on-ipfs.org → bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze + /ipns/en.wikipedia-on-ipfs.org/wiki/ → bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4 + /ipns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey → bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma + The result is an ordered array of values: + X-Ipfs-Roots: bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze,bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4,bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma + Note that while the top one will change every time any article is changed, + the last root (responsible for specific article) may not change at all. + */ + var sp strings.Builder + var pathRoots []cid.Cid + contentPathStr := contentPath.String() + pathSegments := strings.Split(contentPathStr[6:], "/") + sp.WriteString(contentPathStr[:5]) // /ipfs or /ipns + var lastPath ifacepath.Resolved + for _, root := range pathSegments { + if root == "" { + continue + } + sp.WriteString("/") + sp.WriteString(root) + resolvedSubPath, err := api.resolvePath(ctx, ifacepath.New(sp.String())) + if err != nil { + // TODO: should we be more explicit here and is this part of the Gateway API contract? + // The issue here was that we returned datamodel.ErrWrongKind instead of this resolver error + if isErrNotFound(err) { + return nil, nil, resolver.ErrNoLink{Name: root, Node: lastPath.Cid()} + } + return nil, nil, err + } + lastPath = resolvedSubPath + pathRoots = append(pathRoots, lastPath.Cid()) + } + + pathRoots = pathRoots[:len(pathRoots)-1] + return pathRoots, lastPath, nil +} + +// FIXME(@Jorropo): https://github.com/ipld/go-car/issues/315 +type dagStore struct { + api *BlocksGateway + ctx context.Context +} + +func (ds dagStore) Get(_ context.Context, c cid.Cid) (blocks.Block, error) { + return ds.api.blockService.GetBlock(ds.ctx, c) +} + +func (api *BlocksGateway) ResolveMutable(ctx context.Context, p ifacepath.Path) (ImmutablePath, error) { + err := p.IsValid() + if err != nil { + return ImmutablePath{}, err + } + + ipath := ipfspath.Path(p.String()) + switch ipath.Segments()[0] { + case "btns": + ipath, err = resolve.ResolveIPNS(ctx, api.namesys, ipath) + if err != nil { + return ImmutablePath{}, err + } + imPath, err := NewImmutablePath(ifacepath.New(ipath.String())) + if err != nil { + return ImmutablePath{}, err + } + return imPath, nil + case "btfs": + imPath, err := NewImmutablePath(ifacepath.New(ipath.String())) + if err != nil { + return ImmutablePath{}, err + } + return imPath, nil + default: + return ImmutablePath{}, NewErrorResponse(fmt.Errorf("unsupported path namespace: %s", p.Namespace()), http.StatusNotImplemented) + } +} + +func (api *BlocksGateway) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + if api.routing == nil { + return nil, NewErrorResponse(errors.New("IPNS Record responses are not supported by this gateway"), http.StatusNotImplemented) + } + + // Fails fast if the CID is not an encoded Libp2p Key, avoids wasteful + // round trips to the remote routing provider. + if mc.Code(c.Type()) != mc.Libp2pKey { + return nil, NewErrorResponse(errors.New("cid codec must be libp2p-key"), http.StatusBadRequest) + } + + // The value store expects the key itself to be encoded as a multihash. + id, err := peer.FromCid(c) + if err != nil { + return nil, err + } + + return api.routing.GetValue(ctx, "/btns/"+string(id)) +} + +func (api *BlocksGateway) GetDNSLinkRecord(ctx context.Context, hostname string) (ifacepath.Path, error) { + if api.namesys != nil { + p, err := api.namesys.Resolve(ctx, "/btns/"+hostname, nsopts.Depth(1)) + if err == namesys.ErrResolveRecursion { + err = nil + } + return ifacepath.New(p.String()), err + } + + return nil, NewErrorResponse(errors.New("not implemented"), http.StatusNotImplemented) +} + +func (api *BlocksGateway) IsCached(ctx context.Context, p ifacepath.Path) bool { + rp, err := api.resolvePath(ctx, p) + if err != nil { + return false + } + + has, _ := api.blockStore.Has(ctx, rp.Cid()) + return has +} + +func (api *BlocksGateway) ResolvePath(ctx context.Context, path ImmutablePath) (ContentPathMetadata, error) { + roots, lastSeg, err := api.getPathRoots(ctx, path) + if err != nil { + return ContentPathMetadata{}, err + } + md := ContentPathMetadata{ + PathSegmentRoots: roots, + LastSegment: lastSeg, + } + return md, nil +} + +func (api *BlocksGateway) resolvePath(ctx context.Context, p ifacepath.Path) (ifacepath.Resolved, error) { + if _, ok := p.(ifacepath.Resolved); ok { + return p.(ifacepath.Resolved), nil + } + + err := p.IsValid() + if err != nil { + return nil, err + } + + ipath := ipfspath.Path(p.String()) + if ipath.Segments()[0] == "btns" { + ipath, err = resolve.ResolveIPNS(ctx, api.namesys, ipath) + if err != nil { + return nil, err + } + } + + if ipath.Segments()[0] != "btfs" { + return nil, fmt.Errorf("unsupported path namespace: %s", p.Namespace()) + } + + node, rest, err := api.resolver.ResolveToLastNode(ctx, ipath) + if err != nil { + return nil, err + } + + root, err := cid.Parse(ipath.Segments()[1]) + if err != nil { + return nil, err + } + + return ifacepath.NewResolvedPath(ipath, node, root, gopath.Join(rest...)), nil +} diff --git a/core/corehttp/gateway/dns.go b/core/corehttp/gateway/dns.go new file mode 100644 index 000000000..504bb8311 --- /dev/null +++ b/core/corehttp/gateway/dns.go @@ -0,0 +1,92 @@ +package gateway + +import ( + "fmt" + "strings" + + "github.com/libp2p/go-doh-resolver" + dns "github.com/miekg/dns" + madns "github.com/multiformats/go-multiaddr-dns" +) + +var defaultResolvers = map[string]string{ + "eth.": "https://resolver.cloudflare-eth.com/dns-query", + "crypto.": "https://resolver.cloudflare-eth.com/dns-query", +} + +func newResolver(url string, opts ...doh.Option) (madns.BasicResolver, error) { + if !strings.HasPrefix(url, "https://") { + return nil, fmt.Errorf("invalid resolver url: %s", url) + } + + return doh.NewResolver(url, opts...) +} + +// NewDNSResolver creates a new DNS resolver based on the default resolvers and +// the provided resolvers. +// +// The argument 'resolvers' is a map of FQDNs to URLs for custom DNS resolution. +// URLs starting with `https://` indicate DoH endpoints. Support for other resolver +// types may be added in the future. +// +// https://en.wikipedia.org/wiki/Fully_qualified_domain_name +// https://en.wikipedia.org/wiki/DNS_over_HTTPS +// +// Example: +// - Custom resolver for ENS: `eth.` → `https://eth.link/dns-query` +// - Override the default OS resolver: `.` → `https://doh.applied-privacy.net/query` +func NewDNSResolver(resolvers map[string]string, dohOpts ...doh.Option) (*madns.Resolver, error) { + var opts []madns.Option + var err error + + domains := make(map[string]struct{}) // to track overridden default resolvers + rslvrs := make(map[string]madns.BasicResolver) // to reuse resolvers for the same URL + + for domain, url := range resolvers { + if domain != "." && !dns.IsFqdn(domain) { + return nil, fmt.Errorf("invalid domain %s; must be FQDN", domain) + } + + domains[domain] = struct{}{} + if url == "" { + // allow overriding of implicit defaults with the default resolver + continue + } + + rslv, ok := rslvrs[url] + if !ok { + rslv, err = newResolver(url, dohOpts...) + if err != nil { + return nil, fmt.Errorf("bad resolver for %s: %w", domain, err) + } + rslvrs[url] = rslv + } + + if domain != "." { + opts = append(opts, madns.WithDomainResolver(domain, rslv)) + } else { + opts = append(opts, madns.WithDefaultResolver(rslv)) + } + } + + // fill in defaults if not overridden by the user + for domain, url := range defaultResolvers { + _, ok := domains[domain] + if ok { + continue + } + + rslv, ok := rslvrs[url] + if !ok { + rslv, err = newResolver(url) + if err != nil { + return nil, fmt.Errorf("bad resolver for %s: %w", domain, err) + } + rslvrs[url] = rslv + } + + opts = append(opts, madns.WithDomainResolver(domain, rslv)) + } + + return madns.NewResolver(opts...) +} diff --git a/core/corehttp/gateway/errors.go b/core/corehttp/gateway/errors.go new file mode 100644 index 000000000..fb03022f6 --- /dev/null +++ b/core/corehttp/gateway/errors.go @@ -0,0 +1,191 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "net/http" + "strconv" + "time" + + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-path/resolver" + "github.com/ipld/go-ipld-prime/datamodel" +) + +var ( + ErrInternalServerError = NewErrorResponseForCode(http.StatusInternalServerError) + ErrGatewayTimeout = NewErrorResponseForCode(http.StatusGatewayTimeout) + ErrBadGateway = NewErrorResponseForCode(http.StatusBadGateway) + ErrServiceUnavailable = NewErrorResponseForCode(http.StatusServiceUnavailable) + ErrTooManyRequests = NewErrorResponseForCode(http.StatusTooManyRequests) +) + +type ErrorRetryAfter struct { + Err error + RetryAfter time.Duration +} + +// NewErrorWithRetryAfter wraps any error in RetryAfter hint that +// gets passed to HTTP clients in Retry-After HTTP header. +func NewErrorRetryAfter(err error, retryAfter time.Duration) *ErrorRetryAfter { + if err == nil { + err = ErrServiceUnavailable + } + if retryAfter < 0 { + retryAfter = 0 + } + return &ErrorRetryAfter{ + RetryAfter: retryAfter, + Err: err, + } +} + +func (e *ErrorRetryAfter) Error() string { + var text string + if e.Err != nil { + text = e.Err.Error() + } + if e.RetryAfter != 0 { + text += fmt.Sprintf(", retry after %s", e.Humanized()) + } + return text +} + +func (e *ErrorRetryAfter) Unwrap() error { + return e.Err +} + +func (e *ErrorRetryAfter) Is(err error) bool { + switch err.(type) { + case *ErrorRetryAfter: + return true + default: + return false + } +} + +func (e *ErrorRetryAfter) RoundSeconds() time.Duration { + return e.RetryAfter.Round(time.Second) +} + +func (e *ErrorRetryAfter) Humanized() string { + return e.RoundSeconds().String() +} + +// HTTPHeaderValue returns the Retry-After header value as a string, representing the number +// of seconds to wait before making a new request, rounded to the nearest second. +// This function follows the Retry-After header definition as specified in RFC 9110. +func (e *ErrorRetryAfter) HTTPHeaderValue() string { + return strconv.Itoa(int(e.RoundSeconds().Seconds())) +} + +// Custom type for collecting error details to be handled by `webError`. When an error +// of this type is returned to the gateway handler, the StatusCode will be used for +// the response status. +type ErrorResponse struct { + StatusCode int + Err error +} + +func NewErrorResponseForCode(statusCode int) *ErrorResponse { + return NewErrorResponse(errors.New(http.StatusText(statusCode)), statusCode) +} + +func NewErrorResponse(err error, statusCode int) *ErrorResponse { + return &ErrorResponse{ + Err: err, + StatusCode: statusCode, + } +} + +func (e *ErrorResponse) Is(err error) bool { + switch err.(type) { + case *ErrorResponse: + return true + default: + return false + } +} + +func (e *ErrorResponse) Error() string { + var text string + if e.Err != nil { + text = e.Err.Error() + } + return text +} + +func (e *ErrorResponse) Unwrap() error { + return e.Err +} + +func webError(w http.ResponseWriter, err error, defaultCode int) { + code := defaultCode + + // Pass Retry-After hint to the client + var era *ErrorRetryAfter + if errors.As(err, &era) { + if era.RetryAfter > 0 { + w.Header().Set("Retry-After", era.HTTPHeaderValue()) + // Adjust defaultCode if needed + if code != http.StatusTooManyRequests && code != http.StatusServiceUnavailable { + code = http.StatusTooManyRequests + } + } + err = era.Unwrap() + } + + // Handle status code + switch { + case errors.Is(err, &cid.ErrInvalidCid{}): + code = http.StatusBadRequest + case isErrNotFound(err): + code = http.StatusNotFound + case errors.Is(err, context.DeadlineExceeded): + code = http.StatusGatewayTimeout + } + + // Handle explicit code in ErrorResponse + var gwErr *ErrorResponse + if errors.As(err, &gwErr) { + code = gwErr.StatusCode + } + + http.Error(w, err.Error(), code) +} + +func isErrNotFound(err error) bool { + if ipld.IsNotFound(err) { + return true + } + + // Checks if err is of a type that does not implement the .Is interface and + // cannot be directly compared to. Therefore, errors.Is cannot be used. + for { + _, ok := err.(resolver.ErrNoLink) + if ok { + return true + } + + _, ok = err.(datamodel.ErrWrongKind) + if ok { + return true + } + + _, ok = err.(datamodel.ErrNotExists) + if ok { + return true + } + + err = errors.Unwrap(err) + if err == nil { + return false + } + } +} + +func webRequestError(w http.ResponseWriter, err *ErrorResponse) { + webError(w, err.Err, err.StatusCode) +} diff --git a/core/corehttp/gateway/errors_test.go b/core/corehttp/gateway/errors_test.go new file mode 100644 index 000000000..05e6ca887 --- /dev/null +++ b/core/corehttp/gateway/errors_test.go @@ -0,0 +1,65 @@ +package gateway + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestErrRetryAfterIs(t *testing.T) { + var err error + + err = NewErrorRetryAfter(errors.New("test"), 10*time.Second) + assert.True(t, errors.Is(err, &ErrorRetryAfter{}), "pointer to error must be error") + + err = fmt.Errorf("wrapped: %w", err) + assert.True(t, errors.Is(err, &ErrorRetryAfter{}), "wrapped pointer to error must be error") +} + +func TestErrRetryAfterAs(t *testing.T) { + var ( + err error + errRA *ErrorRetryAfter + ) + + err = NewErrorRetryAfter(errors.New("test"), 25*time.Second) + assert.True(t, errors.As(err, &errRA), "pointer to error must be error") + assert.EqualValues(t, errRA.RetryAfter, 25*time.Second) + + err = fmt.Errorf("wrapped: %w", err) + assert.True(t, errors.As(err, &errRA), "wrapped pointer to error must be error") + assert.EqualValues(t, errRA.RetryAfter, 25*time.Second) +} + +func TestWebError(t *testing.T) { + t.Parallel() + + t.Run("429 Too Many Requests", func(t *testing.T) { + err := fmt.Errorf("wrapped for testing: %w", NewErrorRetryAfter(ErrTooManyRequests, 0)) + w := httptest.NewRecorder() + webError(w, err, http.StatusInternalServerError) + assert.Equal(t, http.StatusTooManyRequests, w.Result().StatusCode) + assert.Zero(t, len(w.Result().Header.Values("Retry-After"))) + }) + + t.Run("429 Too Many Requests with Retry-After header", func(t *testing.T) { + err := NewErrorRetryAfter(ErrTooManyRequests, 25*time.Second) + w := httptest.NewRecorder() + webError(w, err, http.StatusInternalServerError) + assert.Equal(t, http.StatusTooManyRequests, w.Result().StatusCode) + assert.Equal(t, "25", w.Result().Header.Get("Retry-After")) + }) + + t.Run("503 Service Unavailable with Retry-After header", func(t *testing.T) { + err := NewErrorRetryAfter(ErrServiceUnavailable, 50*time.Second) + w := httptest.NewRecorder() + webError(w, err, http.StatusInternalServerError) + assert.Equal(t, http.StatusServiceUnavailable, w.Result().StatusCode) + assert.Equal(t, "50", w.Result().Header.Get("Retry-After")) + }) +} diff --git a/core/corehttp/gateway/gateway.go b/core/corehttp/gateway/gateway.go new file mode 100644 index 000000000..1b9423f72 --- /dev/null +++ b/core/corehttp/gateway/gateway.go @@ -0,0 +1,227 @@ +package gateway + +import ( + "context" + "fmt" + "io" + "net/http" + "sort" + + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-unixfs" + "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/ipfs/go-cid" +) + +// Config is the configuration used when creating a new gateway handler. +type Config struct { + Headers map[string][]string +} + +// TODO: Is this what we want for ImmutablePath? +type ImmutablePath struct { + p path.Path +} + +func NewImmutablePath(p path.Path) (ImmutablePath, error) { + if p.Mutable() { + return ImmutablePath{}, fmt.Errorf("path cannot be mutable") + } + return ImmutablePath{p: p}, nil +} + +func (i ImmutablePath) String() string { + return i.p.String() +} + +func (i ImmutablePath) Namespace() string { + return i.p.Namespace() +} + +func (i ImmutablePath) Mutable() bool { + return false +} + +func (i ImmutablePath) IsValid() error { + return i.p.IsValid() +} + +var _ path.Path = (*ImmutablePath)(nil) + +type ContentPathMetadata struct { + PathSegmentRoots []cid.Cid + LastSegment path.Resolved + ContentType string // Only used for UnixFS requests +} + +// ByteRange describes a range request within a UnixFS file. From and To mostly follow [HTTP Byte Range] Request semantics. +// From >= 0 and To = nil: Get the file (From, Length) +// From >= 0 and To >= 0: Get the range (From, To) +// From >= 0 and To <0: Get the range (From, Length - To) +// +// [HTTP Byte Range]: https://httpwg.org/specs/rfc9110.html#rfc.section.14.1.2 +type ByteRange struct { + From uint64 + To *int64 +} + +type GetResponse struct { + bytes files.File + directoryMetadata *directoryMetadata +} + +type directoryMetadata struct { + dagSize uint64 + entries <-chan unixfs.LinkResult +} + +func NewGetResponseFromFile(file files.File) *GetResponse { + return &GetResponse{bytes: file} +} + +func NewGetResponseFromDirectoryListing(dagSize uint64, entries <-chan unixfs.LinkResult) *GetResponse { + return &GetResponse{directoryMetadata: &directoryMetadata{dagSize, entries}} +} + +// IPFSBackend is the required set of functionality used to implement the IPFS HTTP Gateway specification. +// To signal error types to the gateway code (so that not everything is a HTTP 500) return an error wrapped with NewErrorResponse. +// There are also some existing error types that the gateway code knows how to handle (e.g. context.DeadlineExceeded +// and various IPLD pathing related errors). +type IPFSBackend interface { + + // Get returns a GetResponse with UnixFS file, directory or a block in IPLD + // format e.g., (DAG-)CBOR/JSON. + // + // Returned Directories are preferably a minimum info required for enumeration: Name, Size, and Cid. + // + // Optional ranges follow [HTTP Byte Ranges] notation and can be used for + // pre-fetching specific sections of a file or a block. + // + // Range notes: + // - Generating response to a range request may require additional data + // beyond the passed ranges (e.g. a single byte range from the middle of a + // file will still need magic bytes from the very beginning for content + // type sniffing). + // - A range request for a directory currently holds no semantic meaning. + // + // [HTTP Byte Ranges]: https://httpwg.org/specs/rfc9110.html#rfc.section.14.1.2 + Get(context.Context, ImmutablePath, ...ByteRange) (ContentPathMetadata, *GetResponse, error) + + // GetAll returns a UnixFS file or directory depending on what the path is that has been requested. Directories should + // include all content recursively. + GetAll(context.Context, ImmutablePath) (ContentPathMetadata, files.Node, error) + + // GetBlock returns a single block of data + GetBlock(context.Context, ImmutablePath) (ContentPathMetadata, files.File, error) + + // Head returns a file or directory depending on what the path is that has been requested. + // For UnixFS files should return a file which has the correct file size and either returns the ContentType in ContentPathMetadata or + // enough data (e.g. 3kiB) such that the content type can be determined by sniffing. + // For all other data types returning just size information is sufficient + // TODO: give function more explicit return types + Head(context.Context, ImmutablePath) (ContentPathMetadata, files.Node, error) + + // ResolvePath resolves the path using UnixFS resolver. If the path does not + // exist due to a missing link, it should return an error of type: + // NewErrorResponse(fmt.Errorf("no link named %q under %s", name, cid), http.StatusNotFound) + ResolvePath(context.Context, ImmutablePath) (ContentPathMetadata, error) + + // GetCAR returns a CAR file for the given immutable path + // Returns an initial error if there was an issue before the CAR streaming begins as well as a channel with a single + // that may contain a single error for if any errors occur during the streaming. If there was an initial error the + // error channel is nil + // TODO: Make this function signature better + GetCAR(context.Context, ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) + + // IsCached returns whether or not the path exists locally. + IsCached(context.Context, path.Path) bool + + // GetIPNSRecord retrieves the best IPNS record for a given CID (libp2p-key) + // from the routing system. + GetIPNSRecord(context.Context, cid.Cid) ([]byte, error) + + // ResolveMutable takes a mutable path and resolves it into an immutable one. This means recursively resolving any + // DNSLink or IPNS records. + // + // For example, given a mapping from `/ipns/dnslink.tld -> /ipns/ipns-id/mydirectory` and `/ipns/ipns-id` to + // `/ipfs/some-cid`, the result of passing `/ipns/dnslink.tld/myfile` would be `/ipfs/some-cid/mydirectory/myfile`. + ResolveMutable(context.Context, path.Path) (ImmutablePath, error) + + // GetDNSLinkRecord returns the DNSLink TXT record for the provided FQDN. + // Unlike ResolvePath, it does not perform recursive resolution. It only + // checks for the existence of a DNSLink TXT record with path starting with + // /ipfs/ or /ipns/ and returns the path as-is. + GetDNSLinkRecord(context.Context, string) (path.Path, error) +} + +// A helper function to clean up a set of headers: +// 1. Canonicalizes. +// 2. Deduplicates. +// 3. Sorts. +func cleanHeaderSet(headers []string) []string { + // Deduplicate and canonicalize. + m := make(map[string]struct{}, len(headers)) + for _, h := range headers { + m[http.CanonicalHeaderKey(h)] = struct{}{} + } + result := make([]string, 0, len(m)) + for k := range m { + result = append(result, k) + } + + // Sort + sort.Strings(result) + return result +} + +// AddAccessControlHeaders adds default headers used for controlling +// cross-origin requests. This function adds several values to the +// Access-Control-Allow-Headers and Access-Control-Expose-Headers entries. +// If the Access-Control-Allow-Origin entry is missing a value of '*' is +// added, indicating that browsers should allow requesting code from any +// origin to access the resource. +// If the Access-Control-Allow-Methods entry is missing a value of 'GET' is +// added, indicating that browsers may use the GET method when issuing cross +// origin requests. +func AddAccessControlHeaders(headers map[string][]string) { + // Hard-coded headers. + const ACAHeadersName = "Access-Control-Allow-Headers" + const ACEHeadersName = "Access-Control-Expose-Headers" + const ACAOriginName = "Access-Control-Allow-Origin" + const ACAMethodsName = "Access-Control-Allow-Methods" + + if _, ok := headers[ACAOriginName]; !ok { + // Default to *all* + headers[ACAOriginName] = []string{"*"} + } + if _, ok := headers[ACAMethodsName]; !ok { + // Default to GET + headers[ACAMethodsName] = []string{http.MethodGet} + } + + headers[ACAHeadersName] = cleanHeaderSet( + append([]string{ + "Content-Type", + "User-Agent", + "Range", + "X-Requested-With", + }, headers[ACAHeadersName]...)) + + headers[ACEHeadersName] = cleanHeaderSet( + append([]string{ + "Content-Length", + "Content-Range", + "X-Chunked-Output", + "X-Stream-Output", + "X-Ipfs-Path", + "X-Ipfs-Roots", + }, headers[ACEHeadersName]...)) +} + +type RequestContextKey string + +const ( + DNSLinkHostnameKey RequestContextKey = "dnslink-hostname" + GatewayHostnameKey RequestContextKey = "gw-hostname" + ContentPathKey RequestContextKey = "content-path" +) diff --git a/core/corehttp/gateway/gateway_test.go b/core/corehttp/gateway/gateway_test.go new file mode 100644 index 000000000..304051a95 --- /dev/null +++ b/core/corehttp/gateway/gateway_test.go @@ -0,0 +1,545 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" + + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-btfs/namesys" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-cid" + offline "github.com/ipfs/go-ipfs-exchange-offline" + path "github.com/ipfs/go-path" + carblockstore "github.com/ipld/go-car/v2/blockstore" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/stretchr/testify/assert" +) + +type mockNamesys map[string]path.Path + +func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...nsopts.ResolveOpt) (value path.Path, err error) { + cfg := nsopts.DefaultResolveOpts() + for _, o := range opts { + o(&cfg) + } + depth := cfg.Depth + if depth == nsopts.UnlimitedDepth { + // max uint + depth = ^uint(0) + } + for strings.HasPrefix(name, "/btns/") { + if depth == 0 { + return value, namesys.ErrResolveRecursion + } + depth-- + + var ok bool + value, ok = m[name] + if !ok { + return "", namesys.ErrResolveFailed + } + name = value.String() + } + return value, nil +} + +func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...nsopts.ResolveOpt) <-chan namesys.Result { + out := make(chan namesys.Result, 1) + v, err := m.Resolve(ctx, name, opts...) + out <- namesys.Result{Path: v, Err: err} + close(out) + return out +} + +func (m mockNamesys) Publish(ctx context.Context, name crypto.PrivKey, value path.Path) error { + return errors.New("not implemented for mockNamesys") +} +func (m mockNamesys) PublishWithEOL(ctx context.Context, name crypto.PrivKey, value path.Path, eol time.Time) error { + return errors.New("not implemented for mockNamesys") +} +func (m mockNamesys) GetResolver(subs string) (namesys.Resolver, bool) { + return nil, false +} + +type mockAPI struct { + gw IPFSBackend + namesys mockNamesys +} + +var _ IPFSBackend = (*mockAPI)(nil) + +func newMockAPI(t *testing.T) (*mockAPI, cid.Cid) { + r, err := os.Open("./testdata/fixtures.car") + assert.Nil(t, err) + + blockStore, err := carblockstore.NewReadOnly(r, nil) + assert.Nil(t, err) + + t.Cleanup(func() { + blockStore.Close() + r.Close() + }) + + cids, err := blockStore.Roots() + assert.Nil(t, err) + assert.Len(t, cids, 1) + + blockService := blockservice.New(blockStore, offline.Exchange(blockStore)) + + n := mockNamesys{} + gwApi, err := NewBlocksGateway(blockService, WithNameSystem(n)) + if err != nil { + t.Fatal(err) + } + + return &mockAPI{ + gw: gwApi, + namesys: n, + }, cids[0] +} + +func (api *mockAPI) Get(ctx context.Context, immutablePath ImmutablePath, ranges ...ByteRange) (ContentPathMetadata, *GetResponse, error) { + return api.gw.Get(ctx, immutablePath, ranges...) +} + +func (api *mockAPI) GetAll(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.Node, error) { + return api.gw.GetAll(ctx, immutablePath) +} + +func (api *mockAPI) GetBlock(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.File, error) { + return api.gw.GetBlock(ctx, immutablePath) +} + +func (api *mockAPI) Head(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.Node, error) { + return api.gw.Head(ctx, immutablePath) +} + +func (api *mockAPI) GetCAR(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) { + return api.gw.GetCAR(ctx, immutablePath) +} + +func (api *mockAPI) ResolveMutable(ctx context.Context, p ipath.Path) (ImmutablePath, error) { + return api.gw.ResolveMutable(ctx, p) +} + +func (api *mockAPI) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + return nil, routing.ErrNotSupported +} + +func (api *mockAPI) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.Path, error) { + if api.namesys != nil { + p, err := api.namesys.Resolve(ctx, "/btns/"+hostname, nsopts.Depth(1)) + if err == namesys.ErrResolveRecursion { + err = nil + } + return ipath.New(p.String()), err + } + + return nil, errors.New("not implemented") +} + +func (api *mockAPI) IsCached(ctx context.Context, p ipath.Path) bool { + return api.gw.IsCached(ctx, p) +} + +func (api *mockAPI) ResolvePath(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, error) { + return api.gw.ResolvePath(ctx, immutablePath) +} + +func (api *mockAPI) resolvePathNoRootsReturned(ctx context.Context, ip ipath.Path) (ipath.Resolved, error) { + var imPath ImmutablePath + var err error + if ip.Mutable() { + imPath, err = api.ResolveMutable(ctx, ip) + if err != nil { + return nil, err + } + } else { + imPath, err = NewImmutablePath(ip) + if err != nil { + return nil, err + } + } + + md, err := api.ResolvePath(ctx, imPath) + if err != nil { + return nil, err + } + return md.LastSegment, nil +} + +func doWithoutRedirect(req *http.Request) (*http.Response, error) { + tag := "without-redirect" + c := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return errors.New(tag) + }, + } + res, err := c.Do(req) + if err != nil && !strings.Contains(err.Error(), tag) { + return nil, err + } + return res, nil +} + +func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *mockAPI, cid.Cid) { + api, root := newMockAPI(t) + ts := newTestServer(t, api) + return ts, api, root +} + +func newTestServer(t *testing.T, api IPFSBackend) *httptest.Server { + config := Config{Headers: map[string][]string{}} + AddAccessControlHeaders(config.Headers) + + handler := NewHandler(config, api) + mux := http.NewServeMux() + mux.Handle("/btfs/", handler) + mux.Handle("/btns/", handler) + handler = WithHostname(mux, api, map[string]*Specification{}, false) + + ts := httptest.NewServer(handler) + t.Cleanup(func() { ts.Close() }) + + return ts +} + +func TestGatewayGet(t *testing.T) { + ts, api, root := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + k, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(ipath.IpfsPath(root), t.Name(), "fnord")) + assert.Nil(t, err) + + api.namesys["/btns/example.com"] = path.FromCid(k.Cid()) + api.namesys["/btns/working.example.com"] = path.FromString(k.String()) + api.namesys["/btns/double.example.com"] = path.FromString("/btns/working.example.com") + api.namesys["/btns/triple.example.com"] = path.FromString("/btns/double.example.com") + api.namesys["/btns/broken.example.com"] = path.FromString("/btns/" + k.Cid().String()) + // We picked .man because: + // 1. It's a valid TLD. + // 2. Go treats it as the file extension for "man" files (even though + // nobody actually *uses* this extension, AFAIK). + // + // Unfortunately, this may not work on all platforms as file type + // detection is platform dependent. + api.namesys["/btns/example.man"] = path.FromString(k.String()) + + t.Log(ts.URL) + for _, test := range []struct { + host string + path string + status int + text string + }{ + {"127.0.0.1:8080", "/", http.StatusNotFound, "404 page not found\n"}, + {"127.0.0.1:8080", "/btfs", http.StatusBadRequest, "invalid path \"/btfs/\": not enough path components\n"}, + {"127.0.0.1:8080", "/btns", http.StatusBadRequest, "invalid path \"/btns/\": not enough path components\n"}, + {"127.0.0.1:8080", "/" + k.Cid().String(), http.StatusNotFound, "404 page not found\n"}, + {"127.0.0.1:8080", "/btfs/this-is-not-a-cid", http.StatusBadRequest, "invalid path \"/btfs/this-is-not-a-cid\": invalid CID: invalid cid: illegal base32 data at input byte 3\n"}, + // {"127.0.0.1:8080", k.String(), http.StatusOK, "fnord"}, + {"127.0.0.1:8080", "/btns/nxdomain.example.com", http.StatusInternalServerError, "failed to resolve /btns/nxdomain.example.com: " + namesys.ErrResolveFailed.Error() + "\n"}, + {"127.0.0.1:8080", "/btns/%0D%0A%0D%0Ahello", http.StatusInternalServerError, "failed to resolve /btns/\\r\\n\\r\\nhello: " + namesys.ErrResolveFailed.Error() + "\n"}, + {"127.0.0.1:8080", "/btns/k51qzi5uqu5djucgtwlxrbfiyfez1nb0ct58q5s4owg6se02evza05dfgi6tw5", http.StatusInternalServerError, "failed to resolve /btns/k51qzi5uqu5djucgtwlxrbfiyfez1nb0ct58q5s4owg6se02evza05dfgi6tw5: " + namesys.ErrResolveFailed.Error() + "\n"}, + // {"127.0.0.1:8080", "/btns/example.com", http.StatusOK, "fnord"}, + // {"example.com", "/", http.StatusOK, "fnord"}, + + // {"working.example.com", "/", http.StatusOK, "fnord"}, + // {"double.example.com", "/", http.StatusOK, "fnord"}, + // {"triple.example.com", "/", http.StatusOK, "fnord"}, + {"working.example.com", k.String(), http.StatusNotFound, "failed to resolve /btns/working.example.com" + k.String() + ": no link named \"btfs\" under " + k.Cid().String() + "\n"}, + {"broken.example.com", "/", http.StatusInternalServerError, "failed to resolve /btns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"}, + {"broken.example.com", k.String(), http.StatusInternalServerError, "failed to resolve /btns/broken.example.com" + k.String() + ": " + namesys.ErrResolveFailed.Error() + "\n"}, + // This test case ensures we don't treat the TLD as a file extension. + // {"example.man", "/", http.StatusOK, "fnord"}, + } { + testName := "http://" + test.host + test.path + t.Run(testName, func(t *testing.T) { + var c http.Client + r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) + assert.Nil(t, err) + r.Host = test.host + resp, err := c.Do(r) + assert.Nil(t, err) + defer resp.Body.Close() + assert.Equal(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + assert.Equal(t, test.status, resp.StatusCode, "body", body) + assert.Equal(t, test.text, string(body)) + }) + } +} + +func TestUriQueryRedirect(t *testing.T) { + ts, _, _ := newTestServerAndNode(t, mockNamesys{}) + + cid := "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR" + for _, test := range []struct { + path string + status int + location string + }{ + // - Browsers will send original URI in URL-escaped form + // - We expect query parameters to be persisted + // - We drop fragments, as those should not be sent by a browser + {"/btfs/?uri=btfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/btfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, + {"/btfs/?uri=btns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/btns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, + {"/btfs/?uri=btfs://" + cid, http.StatusMovedPermanently, "/btfs/" + cid}, + {"/btfs?uri=btfs://" + cid, http.StatusMovedPermanently, "/btfs/?uri=btfs://" + cid}, + {"/btfs/?uri=btns://" + cid, http.StatusMovedPermanently, "/btns/" + cid}, + {"/btns/?uri=btfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/btfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, + {"/btns/?uri=btns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/btns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, + {"/btns?uri=btns://" + cid, http.StatusMovedPermanently, "/btns/?uri=btns://" + cid}, + {"/btns/?uri=btns://" + cid, http.StatusMovedPermanently, "/btns/" + cid}, + {"/btns/?uri=btfs://" + cid, http.StatusMovedPermanently, "/btfs/" + cid}, + {"/btfs/?uri=unsupported://" + cid, http.StatusBadRequest, ""}, + {"/btfs/?uri=invaliduri", http.StatusBadRequest, ""}, + {"/btfs/?uri=" + cid, http.StatusBadRequest, ""}, + } { + testName := ts.URL + test.path + t.Run(testName, func(t *testing.T) { + r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) + assert.Nil(t, err) + resp, err := doWithoutRedirect(r) + assert.Nil(t, err) + defer resp.Body.Close() + assert.Equal(t, test.status, resp.StatusCode) + assert.Equal(t, test.location, resp.Header.Get("Location")) + }) + } +} + +func TestIPNSHostnameRedirect(t *testing.T) { + ts, api, root := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + k, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(ipath.IpfsPath(root), t.Name())) + assert.Nil(t, err) + + t.Logf("k: %s\n", k) + api.namesys["/btns/example.net"] = path.FromString(k.String()) + + // make request to directory containing index.html + req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) + assert.Nil(t, err) + req.Host = "example.net" + + res, err := doWithoutRedirect(req) + assert.Nil(t, err) + + // expect 301 redirect to same path, but with trailing slash + assert.Equal(t, http.StatusMovedPermanently, res.StatusCode) + hdr := res.Header["Location"] + assert.Positive(t, len(hdr), "location header not present") + assert.Equal(t, hdr[0], "/foo/") + + // make request with prefix to directory containing index.html + req, err = http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) + assert.Nil(t, err) + req.Host = "example.net" + + res, err = doWithoutRedirect(req) + assert.Nil(t, err) + // expect 301 redirect to same path, but with prefix and trailing slash + assert.Equal(t, http.StatusMovedPermanently, res.StatusCode) + + hdr = res.Header["Location"] + assert.Positive(t, len(hdr), "location header not present") + assert.Equal(t, hdr[0], "/foo/") + + // make sure /version isn't exposed + req, err = http.NewRequest(http.MethodGet, ts.URL+"/version", nil) + assert.Nil(t, err) + req.Host = "example.net" + + res, err = doWithoutRedirect(req) + assert.Nil(t, err) + assert.Equal(t, http.StatusNotFound, res.StatusCode) +} + +// Test directory listing on DNSLink website +// (scenario when Host header is the same as URL hostname) +// This is basic regression test: additional end-to-end tests +// can be found in test/sharness/t0115-gateway-dir-listing.sh +// func TestIPNSHostnameBacklinks(t *testing.T) { +// ts, api, root := newTestServerAndNode(t, nil) +// t.Logf("test server url: %s", ts.URL) + +// ctx, cancel := context.WithCancel(context.Background()) +// defer cancel() + +// k, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(ipath.IpfsPath(root), t.Name())) +// assert.Nil(t, err) + +// // create /btns/example.net/foo/ +// k2, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(k, "foo? #<'")) +// assert.Nil(t, err) + +// k3, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(k, "foo? #<'/bar")) +// assert.Nil(t, err) + +// t.Logf("k: %s\n", k) +// api.namesys["/btns/example.net"] = path.FromString(k.String()) + +// // make request to directory listing +// req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo%3F%20%23%3C%27/", nil) +// assert.Nil(t, err) +// req.Host = "example.net" + +// res, err := doWithoutRedirect(req) +// assert.Nil(t, err) + +// // expect correct links +// body, err := io.ReadAll(res.Body) +// assert.Nil(t, err) +// s := string(body) +// t.Logf("body: %s\n", string(body)) + +// assert.True(t, matchPathOrBreadcrumbs(s, "/btns/example.net/foo? #<'"), "expected a path in directory listing") +// // https://github.com/btfs/dir-index-html/issues/42 +// assert.Contains(t, s, "", "expected backlink in directory listing") +// assert.Contains(t, s, "", "expected file in directory listing") +// assert.Contains(t, s, s, k2.Cid().String(), "expected hash in directory listing") + +// // make request to directory listing at root +// req, err = http.NewRequest(http.MethodGet, ts.URL, nil) +// assert.Nil(t, err) +// req.Host = "example.net" + +// res, err = doWithoutRedirect(req) +// assert.Nil(t, err) + +// // expect correct backlinks at root +// body, err = io.ReadAll(res.Body) +// assert.Nil(t, err) + +// s = string(body) +// t.Logf("body: %s\n", string(body)) + +// assert.True(t, matchPathOrBreadcrumbs(s, "/"), "expected a path in directory listing") +// assert.NotContains(t, s, "", "expected no backlink in directory listing of the root CID") +// assert.Contains(t, s, "", "expected file in directory listing") +// // https://github.com/btfs/dir-index-html/issues/42 +// assert.Contains(t, s, "example.net/foo? #<'/bar"), "expected a path in directory listing") +// assert.Contains(t, s, "", "expected backlink in directory listing") +// assert.Contains(t, s, "", "expected file in directory listing") +// assert.Contains(t, s, k3.Cid().String(), "expected hash in directory listing") +// } + +func TestPretty404(t *testing.T) { + ts, api, root := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + k, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(ipath.IpfsPath(root), t.Name())) + assert.Nil(t, err) + + host := "example.net" + api.namesys["/btns/"+host] = path.FromString(k.String()) + + for _, test := range []struct { + path string + accept string + status int + text string + }{ + {"/ipfs-404.html", "text/html", http.StatusOK, "Custom 404"}, + {"/nope", "text/html", http.StatusNotFound, "Custom 404"}, + {"/nope", "text/*", http.StatusNotFound, "Custom 404"}, + {"/nope", "*/*", http.StatusNotFound, "Custom 404"}, + {"/nope", "application/json", http.StatusNotFound, fmt.Sprintf("failed to resolve /btns/example.net/nope: no link named \"nope\" under %s\n", k.Cid().String())}, + {"/deeper/nope", "text/html", http.StatusNotFound, "Deep custom 404"}, + {"/deeper/", "text/html", http.StatusOK, ""}, + {"/deeper", "text/html", http.StatusOK, ""}, + {"/nope/nope", "text/html", http.StatusNotFound, "Custom 404"}, + } { + testName := fmt.Sprintf("%s %s", test.path, test.accept) + t.Run(testName, func(t *testing.T) { + var c http.Client + req, err := http.NewRequest("GET", ts.URL+test.path, nil) + assert.Nil(t, err) + req.Header.Add("Accept", test.accept) + req.Host = host + resp, err := c.Do(req) + assert.Nil(t, err) + defer resp.Body.Close() + assert.Equal(t, test.status, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + if test.text != "" { + assert.Equal(t, test.text, string(body)) + } + }) + } +} + +func TestCacheControlImmutable(t *testing.T) { + ts, _, root := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btfs/"+root.String()+"/", nil) + assert.Nil(t, err) + + res, err := doWithoutRedirect(req) + assert.Nil(t, err) + + // check the immutable tag isn't set + hdrs, ok := res.Header["Cache-Control"] + if ok { + for _, hdr := range hdrs { + assert.NotContains(t, hdr, "immutable", "unexpected Cache-Control: immutable on directory listing") + } + } +} + +func TestGoGetSupport(t *testing.T) { + ts, _, root := newTestServerAndNode(t, nil) + t.Logf("test server url: %s", ts.URL) + + // mimic go-get + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btfs/"+root.String()+"?go-get=1", nil) + assert.Nil(t, err) + + res, err := doWithoutRedirect(req) + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) +} diff --git a/core/corehttp/gateway/handler.go b/core/corehttp/gateway/handler.go new file mode 100644 index 000000000..62a73cabb --- /dev/null +++ b/core/corehttp/gateway/handler.go @@ -0,0 +1,773 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "html/template" + "io" + "mime" + "net/http" + "net/textproto" + "net/url" + gopath "path" + "regexp" + "runtime/debug" + "strings" + "time" + + ipath "github.com/bittorrent/interface-go-btfs-core/path" + cid "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log" + prometheus "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +var log = logging.Logger("core/server") + +const ( + ipfsPathPrefix = "/btfs/" + ipnsPathPrefix = "/btns/" + immutableCacheControl = "public, max-age=29030400, immutable" +) + +var ( + onlyASCII = regexp.MustCompile("[[:^ascii:]]") + noModtime = time.Unix(0, 0) // disables Last-Modified header if passed as modtime +) + +// HTML-based redirect for errors which can be recovered from, but we want +// to provide hint to people that they should fix things on their end. +var redirectTemplate = template.Must(template.New("redirect").Parse(` + + + + + + + +

{{.ErrorMsg}}
(if a redirect does not happen in 10 seconds, use "{{.SuggestedPath}}" instead)
+ +`)) + +type redirectTemplateData struct { + RedirectURL string + SuggestedPath string + ErrorMsg string +} + +// handler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/) +// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) +type handler struct { + config Config + api IPFSBackend + + // response type metrics + getMetric *prometheus.HistogramVec + unixfsFileGetMetric *prometheus.HistogramVec + unixfsDirIndexGetMetric *prometheus.HistogramVec + unixfsGenDirListingGetMetric *prometheus.HistogramVec + carStreamGetMetric *prometheus.HistogramVec + rawBlockGetMetric *prometheus.HistogramVec + tarStreamGetMetric *prometheus.HistogramVec + jsoncborDocumentGetMetric *prometheus.HistogramVec + ipnsRecordGetMetric *prometheus.HistogramVec +} + +// NewHandler returns an http.Handler that can act as a gateway to IPFS content +// offlineApi is a version of the API that should not make network requests for missing data +func NewHandler(c Config, api IPFSBackend) http.Handler { + return newHandlerWithMetrics(c, api) +} + +// StatusResponseWriter enables us to override HTTP Status Code passed to +// WriteHeader function inside of http.ServeContent. Decision is based on +// presence of HTTP Headers such as Location. +type statusResponseWriter struct { + http.ResponseWriter +} + +func (sw *statusResponseWriter) WriteHeader(code int) { + // Check if we need to adjust Status Code to account for scheduled redirect + // This enables us to return payload along with HTTP 301 + // for subdomain redirect in web browsers while also returning body for cli + // tools which do not follow redirects by default (curl, wget). + redirect := sw.ResponseWriter.Header().Get("Location") + if redirect != "" && code == http.StatusOK { + code = http.StatusMovedPermanently + log.Debugw("subdomain redirect", "location", redirect, "status", code) + } + sw.ResponseWriter.WriteHeader(code) +} + +// ServeContent replies to the request using the content in the provided ReadSeeker +// and returns the status code written and any error encountered during a write. +// It wraps http.ServeContent which takes care of If-None-Match+Etag, +// Content-Length and range requests. +func ServeContent(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, content io.ReadSeeker) (int, bool, error) { + ew := &errRecordingResponseWriter{ResponseWriter: w} + http.ServeContent(ew, req, name, modtime, content) + + // When we calculate some metrics we want a flag that lets us to ignore + // errors and 304 Not Modified, and only care when requested data + // was sent in full. + dataSent := ew.code/100 == 2 && ew.err == nil + + return ew.code, dataSent, ew.err +} + +// errRecordingResponseWriter wraps a ResponseWriter to record the status code and any write error. +type errRecordingResponseWriter struct { + http.ResponseWriter + code int + err error +} + +func (w *errRecordingResponseWriter) WriteHeader(code int) { + if w.code == 0 { + w.code = code + } + w.ResponseWriter.WriteHeader(code) +} + +func (w *errRecordingResponseWriter) Write(p []byte) (int, error) { + n, err := w.ResponseWriter.Write(p) + if err != nil && w.err == nil { + w.err = err + } + return n, err +} + +// ReadFrom exposes errRecordingResponseWriter's underlying ResponseWriter to io.Copy +// to allow optimized methods to be taken advantage of. +func (w *errRecordingResponseWriter) ReadFrom(r io.Reader) (n int64, err error) { + n, err = io.Copy(w.ResponseWriter, r) + if err != nil && w.err == nil { + w.err = err + } + return n, err +} + +func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + defer panicHandler(w) + + // the hour is a hard fallback, we don't expect it to happen, but just in case + ctx, cancel := context.WithTimeout(r.Context(), time.Hour) + defer cancel() + r = r.WithContext(ctx) + + switch r.Method { + case http.MethodGet, http.MethodHead: + i.getOrHeadHandler(w, r) + return + case http.MethodOptions: + i.optionsHandler(w, r) + return + } + + w.Header().Add("Allow", http.MethodGet) + w.Header().Add("Allow", http.MethodHead) + w.Header().Add("Allow", http.MethodOptions) + + errmsg := "Method " + r.Method + " not allowed: read only access" + http.Error(w, errmsg, http.StatusMethodNotAllowed) +} + +func (i *handler) optionsHandler(w http.ResponseWriter, r *http.Request) { + /* + OPTIONS is a noop request that is used by the browsers to check + if server accepts cross-site XMLHttpRequest (indicated by the presence of CORS headers) + https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests + */ + i.addUserHeaders(w) // return all custom headers (including CORS ones, if set) +} + +func (i *handler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { + begin := time.Now() + + logger := log.With("from", r.RequestURI) + logger.Debug("http request received") + + if err := handleUnsupportedHeaders(r); err != nil { + webRequestError(w, err) + return + } + + if requestHandled := handleProtocolHandlerRedirect(w, r, logger); requestHandled { + return + } + + if err := handleServiceWorkerRegistration(r); err != nil { + webRequestError(w, err) + return + } + + contentPath := ipath.New(r.URL.Path) + ctx := context.WithValue(r.Context(), ContentPathKey, contentPath) + r = r.WithContext(ctx) + + if requestHandled := i.handleOnlyIfCached(w, r, contentPath, logger); requestHandled { + return + } + + if requestHandled := handleSuperfluousNamespace(w, r, contentPath); requestHandled { + return + } + + if err := contentPath.IsValid(); err != nil { + webError(w, err, http.StatusBadRequest) + return + } + + // Detect when explicit Accept header or ?format parameter are present + responseFormat, formatParams, err := customResponseFormat(r) + if err != nil { + webError(w, fmt.Errorf("error while processing the Accept header: %w", err), http.StatusBadRequest) + return + } + trace.SpanFromContext(r.Context()).SetAttributes(attribute.String("ResponseFormat", responseFormat)) + + i.addUserHeaders(w) // ok, _now_ write user's headers. + w.Header().Set("X-Ipfs-Path", contentPath.String()) + + // TODO: Why did the previous code do path resolution, was that a bug? + // TODO: Does If-None-Match apply here? + if responseFormat == "application/vnd.ipfs.btns-record" { + logger.Debugw("serving btns record", "path", contentPath) + success := i.serveIpnsRecord(r.Context(), w, r, contentPath, begin, logger) + if success { + i.getMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } + + return + } + + var immutableContentPath ImmutablePath + if contentPath.Mutable() { + immutableContentPath, err = i.api.ResolveMutable(r.Context(), contentPath) + if err != nil { + // Note: webError will replace http.StatusInternalServerError with a more appropriate error (e.g. StatusNotFound, StatusRequestTimeout, StatusServiceUnavailable, etc.) if necessary + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + return + } + } else { + immutableContentPath, err = NewImmutablePath(contentPath) + if err != nil { + err = fmt.Errorf("path was expected to be immutable, but was not %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + return + } + } + + // Detect when If-None-Match HTTP header allows returning HTTP 304 Not Modified + ifNoneMatchResolvedPath, ok := i.handleIfNoneMatch(w, r, responseFormat, contentPath, immutableContentPath, logger) + if !ok { + return + } + + // If we already did the path resolution no need to do it again + maybeResolvedImPath := immutableContentPath + if ifNoneMatchResolvedPath != nil { + maybeResolvedImPath, err = NewImmutablePath(ifNoneMatchResolvedPath) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return + } + } + + var success bool + + // Support custom response formats passed via ?format or Accept HTTP header + switch responseFormat { + case "", "application/json", "application/cbor": + success = i.serveDefaults(r.Context(), w, r, maybeResolvedImPath, immutableContentPath, contentPath, begin, responseFormat, logger) + case "application/vnd.ipld.raw": + logger.Debugw("serving raw block", "path", contentPath) + success = i.serveRawBlock(r.Context(), w, r, maybeResolvedImPath, contentPath, begin) + case "application/vnd.ipld.car": + logger.Debugw("serving car stream", "path", contentPath) + carVersion := formatParams["version"] + success = i.serveCAR(r.Context(), w, r, maybeResolvedImPath, contentPath, carVersion, begin) + case "application/x-tar": + logger.Debugw("serving tar file", "path", contentPath) + success = i.serveTAR(r.Context(), w, r, maybeResolvedImPath, contentPath, begin, logger) + case "application/vnd.ipld.dag-json", "application/vnd.ipld.dag-cbor": + logger.Debugw("serving codec", "path", contentPath) + success = i.serveCodec(r.Context(), w, r, maybeResolvedImPath, contentPath, begin, responseFormat) + case "application/vnd.ipfs.btns-record": + default: // catch-all for unsuported application/vnd.* + err := fmt.Errorf("unsupported format %q", responseFormat) + webError(w, err, http.StatusBadRequest) + return + } + + if success { + i.getMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } +} + +func (i *handler) addUserHeaders(w http.ResponseWriter) { + for k, v := range i.config.Headers { + w.Header()[k] = v + } +} + +func panicHandler(w http.ResponseWriter) { + if r := recover(); r != nil { + log.Error("A panic occurred in the gateway handler!") + log.Error(r) + debug.PrintStack() + w.WriteHeader(http.StatusInternalServerError) + } +} + +func addCacheControlHeaders(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, fileCid cid.Cid) (modtime time.Time) { + // Set Etag to based on CID (override whatever was set before) + w.Header().Set("Etag", getEtag(r, fileCid)) + + // Set Cache-Control and Last-Modified based on contentPath properties + if contentPath.Mutable() { + // mutable namespaces such as /btns/ can't be cached forever + + /* For now we set Last-Modified to Now() to leverage caching heuristics built into modern browsers: + * https://github.com/ipfs/kubo/pull/8074#pullrequestreview-645196768 + * but we should not set it to fake values and use Cache-Control based on TTL instead */ + modtime = time.Now() + + // TODO: set Cache-Control based on TTL of IPNS/DNSLink: https://github.com/ipfs/kubo/issues/1818#issuecomment-1015849462 + // TODO: set Last-Modified based on /btns/ publishing timestamp? + } else { + // immutable! CACHE ALL THE THINGS, FOREVER! wolololol + w.Header().Set("Cache-Control", immutableCacheControl) + + // Set modtime to 'zero time' to disable Last-Modified header (superseded by Cache-Control) + modtime = noModtime + + // TODO: set Last-Modified? - TBD - /ipfs/ modification metadata is present in unixfs 1.5 https://github.com/ipfs/kubo/issues/6920? + } + + return modtime +} + +// Set Content-Disposition if filename URL query param is present, return preferred filename +func addContentDispositionHeader(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) string { + /* This logic enables: + * - creation of HTML links that trigger "Save As.." dialog instead of being rendered by the browser + * - overriding the filename used when saving subresource assets on HTML page + * - providing a default filename for HTTP clients when downloading direct /ipfs/CID without any subpath + */ + + // URL param ?filename=cat.jpg triggers Content-Disposition: [..] filename + // which impacts default name used in "Save As.." dialog + name := getFilename(contentPath) + urlFilename := r.URL.Query().Get("filename") + if urlFilename != "" { + disposition := "inline" + // URL param ?download=true triggers Content-Disposition: [..] attachment + // which skips rendering and forces "Save As.." dialog in browsers + if r.URL.Query().Get("download") == "true" { + disposition = "attachment" + } + setContentDispositionHeader(w, urlFilename, disposition) + name = urlFilename + } + return name +} + +// Set Content-Disposition to arbitrary filename and disposition +func setContentDispositionHeader(w http.ResponseWriter, filename string, disposition string) { + utf8Name := url.PathEscape(filename) + asciiName := url.PathEscape(onlyASCII.ReplaceAllLiteralString(filename, "_")) + w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"; filename*=UTF-8''%s", disposition, asciiName, utf8Name)) +} + +// Set X-Ipfs-Roots with logical CID array for efficient HTTP cache invalidation. +func (i *handler) setIpfsRootsHeader(w http.ResponseWriter, pathMetadata ContentPathMetadata) *ErrorResponse { + /* + These are logical roots where each CID represent one path segment + and resolves to either a directory or the root block of a file. + The main purpose of this header is allow HTTP caches to do smarter decisions + around cache invalidation (eg. keep specific subdirectory/file if it did not change) + + A good example is Wikipedia, which is HAMT-sharded, but we only care about + logical roots that represent each segment of the human-readable content + path: + + Given contentPath = /btns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey + rootCidList is a generated by doing `ipfs resolve -r` on each sub path: + /btns/en.wikipedia-on-ipfs.org → bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze + /btns/en.wikipedia-on-ipfs.org/wiki/ → bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4 + /btns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey → bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma + + The result is an ordered array of values: + X-Ipfs-Roots: bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze,bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4,bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma + + Note that while the top one will change every time any article is changed, + the last root (responsible for specific article) may not change at all. + */ + + var pathRoots []string + for _, c := range pathMetadata.PathSegmentRoots { + pathRoots = append(pathRoots, c.String()) + } + pathRoots = append(pathRoots, pathMetadata.LastSegment.Cid().String()) + rootCidList := strings.Join(pathRoots, ",") // convention from rfc2616#sec4.2 + + w.Header().Set("X-Ipfs-Roots", rootCidList) + return nil +} + +func getFilename(contentPath ipath.Path) string { + s := contentPath.String() + if (strings.HasPrefix(s, ipfsPathPrefix) || strings.HasPrefix(s, ipnsPathPrefix)) && strings.Count(gopath.Clean(s), "/") <= 2 { + // Don't want to treat ipfs.io in /btns/ipfs.io as a filename. + return "" + } + return gopath.Base(s) +} + +// etagMatch evaluates if we can respond with HTTP 304 Not Modified +// It supports multiple weak and strong etags passed in If-None-Matc stringh +// including the wildcard one. +func etagMatch(ifNoneMatchHeader string, cidEtag string, dirEtag string) bool { + buf := ifNoneMatchHeader + for { + buf = textproto.TrimString(buf) + if len(buf) == 0 { + break + } + if buf[0] == ',' { + buf = buf[1:] + continue + } + // If-None-Match: * should match against any etag + if buf[0] == '*' { + return true + } + etag, remain := scanETag(buf) + if etag == "" { + break + } + // Check for match both strong and weak etags + if etagWeakMatch(etag, cidEtag) || etagWeakMatch(etag, dirEtag) { + return true + } + buf = remain + } + return false +} + +// scanETag determines if a syntactically valid ETag is present at s. If so, +// the ETag and remaining text after consuming ETag is returned. Otherwise, +// it returns "", "". +// (This is the same logic as one executed inside of http.ServeContent) +func scanETag(s string) (etag string, remain string) { + s = textproto.TrimString(s) + start := 0 + if strings.HasPrefix(s, "W/") { + start = 2 + } + if len(s[start:]) < 2 || s[start] != '"' { + return "", "" + } + // ETag is either W/"text" or "text". + // See RFC 7232 2.3. + for i := start + 1; i < len(s); i++ { + c := s[i] + switch { + // Character values allowed in ETags. + case c == 0x21 || c >= 0x23 && c <= 0x7E || c >= 0x80: + case c == '"': + return s[:i+1], s[i+1:] + default: + return "", "" + } + } + return "", "" +} + +// etagWeakMatch reports whether a and b match using weak ETag comparison. +func etagWeakMatch(a, b string) bool { + return strings.TrimPrefix(a, "W/") == strings.TrimPrefix(b, "W/") +} + +// generate Etag value based on HTTP request and CID +func getEtag(r *http.Request, cid cid.Cid) string { + prefix := `"` + suffix := `"` + responseFormat, _, err := customResponseFormat(r) + if err == nil && responseFormat != "" { + // application/vnd.ipld.foo → foo + // application/x-bar → x-bar + shortFormat := responseFormat[strings.LastIndexAny(responseFormat, "/.")+1:] + // Etag: "cid.shortFmt" (gives us nice compression together with Content-Disposition in block (raw) and car responses) + suffix = `.` + shortFormat + suffix + } + // TODO: include selector suffix when https://github.com/ipfs/kubo/issues/8769 lands + return prefix + cid.String() + suffix +} + +// return explicit response format if specified in request as query parameter or via Accept HTTP header +func customResponseFormat(r *http.Request) (mediaType string, params map[string]string, err error) { + if formatParam := r.URL.Query().Get("format"); formatParam != "" { + // translate query param to a content type + switch formatParam { + case "raw": + return "application/vnd.ipld.raw", nil, nil + case "car": + return "application/vnd.ipld.car", nil, nil + case "tar": + return "application/x-tar", nil, nil + case "json": + return "application/json", nil, nil + case "cbor": + return "application/cbor", nil, nil + case "dag-json": + return "application/vnd.ipld.dag-json", nil, nil + case "dag-cbor": + return "application/vnd.ipld.dag-cbor", nil, nil + case "btns-record": + return "application/vnd.ipfs.btns-record", nil, nil + } + } + // Browsers and other user agents will send Accept header with generic types like: + // Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + // We only care about explicit, vendor-specific content-types and respond to the first match (in order). + // TODO: make this RFC compliant and respect weights (eg. return CAR for Accept:application/vnd.ipld.dag-json;q=0.1,application/vnd.ipld.car;q=0.2) + for _, header := range r.Header.Values("Accept") { + for _, value := range strings.Split(header, ",") { + accept := strings.TrimSpace(value) + // respond to the very first matching content type + if strings.HasPrefix(accept, "application/vnd.ipld") || + strings.HasPrefix(accept, "application/x-tar") || + strings.HasPrefix(accept, "application/json") || + strings.HasPrefix(accept, "application/cbor") || + strings.HasPrefix(accept, "application/vnd.ipfs") { + mediatype, params, err := mime.ParseMediaType(accept) + if err != nil { + return "", nil, err + } + return mediatype, params, nil + } + } + } + // If none of special-cased content types is found, return empty string + // to indicate default, implicit UnixFS response should be prepared + return "", nil, nil +} + +// check if request was for one of known explicit formats, +// or should use the default, implicit Web+UnixFS behaviors. +func isWebRequest(responseFormat string) bool { + // The implicit response format is "" + return responseFormat == "" +} + +// returns unquoted path with all special characters revealed as \u codes +func debugStr(path string) string { + q := fmt.Sprintf("%+q", path) + if len(q) >= 3 { + q = q[1 : len(q)-1] + } + return q +} + +func (i *handler) handleIfNoneMatch(w http.ResponseWriter, r *http.Request, responseFormat string, contentPath ipath.Path, imPath ImmutablePath, logger *zap.SugaredLogger) (ipath.Resolved, bool) { + // Detect when If-None-Match HTTP header allows returning HTTP 304 Not Modified + if inm := r.Header.Get("If-None-Match"); inm != "" { + pathMetadata, err := i.api.ResolvePath(r.Context(), imPath) + if err != nil { + // Note: webError will replace http.StatusInternalServerError with a more appropriate error (e.g. StatusNotFound, StatusRequestTimeout, StatusServiceUnavailable, etc.) if necessary + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + return nil, false + } + + resolvedPath := pathMetadata.LastSegment + pathCid := resolvedPath.Cid() + // need to check against both File and Dir Etag variants + // because this inexpensive check happens before we do any I/O + cidEtag := getEtag(r, pathCid) + dirEtag := getDirListingEtag(pathCid) + if etagMatch(inm, cidEtag, dirEtag) { + // Finish early if client already has a matching Etag + w.WriteHeader(http.StatusNotModified) + return nil, false + } + + return resolvedPath, true + } + return nil, true +} + +// handleRequestErrors is used when request type is other than Web+UnixFS +func (i *handler) handleRequestErrors(w http.ResponseWriter, contentPath ipath.Path, err error) bool { + if err == nil { + return true + } + // Note: webError will replace http.StatusInternalServerError with a more appropriate error (e.g. StatusNotFound, StatusRequestTimeout, StatusServiceUnavailable, etc.) if necessary + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + return false +} + +// handleWebRequestErrors is used when request type is Web+UnixFS and err could +// be a 404 (Not Found) that should be recovered via _redirects file (IPIP-290) +func (i *handler) handleWebRequestErrors(w http.ResponseWriter, r *http.Request, maybeResolvedImPath, immutableContentPath ImmutablePath, contentPath ipath.Path, err error, logger *zap.SugaredLogger) (ImmutablePath, bool) { + if err == nil { + return maybeResolvedImPath, true + } + + if errors.Is(err, ErrServiceUnavailable) { + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusServiceUnavailable) + return ImmutablePath{}, false + } + + // If we have origin isolation (subdomain gw, DNSLink website), + // and response type is UnixFS (default for website hosting) + // we can leverage the presence of an _redirects file and apply rules defined there. + // See: https://github.com/ipfs/specs/pull/290 + if hasOriginIsolation(r) { + newContentPath, ok, hadMatchingRule := i.serveRedirectsIfPresent(w, r, maybeResolvedImPath, immutableContentPath, contentPath, logger) + if hadMatchingRule { + logger.Debugw("applied a rule from _redirects file") + return newContentPath, ok + } + } + + // if Accept is text/html, see if ipfs-404.html is present + // This logic isn't documented and will likely be removed at some point. + // Any 404 logic in _redirects above will have already run by this time, so it's really an extra fall back + // PLEASE do not use this for new websites, + // follow https://docs.ipfs.tech/how-to/websites-on-ipfs/redirects-and-custom-404s/ instead. + if i.serveLegacy404IfPresent(w, r, immutableContentPath) { + logger.Debugw("served legacy 404") + return ImmutablePath{}, false + } + + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + return ImmutablePath{}, false +} + +// Detect 'Cache-Control: only-if-cached' in request and return data if it is already in the local datastore. +// https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header +func (i *handler) handleOnlyIfCached(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) (requestHandled bool) { + if r.Header.Get("Cache-Control") == "only-if-cached" { + if !i.api.IsCached(r.Context(), contentPath) { + if r.Method == http.MethodHead { + w.WriteHeader(http.StatusPreconditionFailed) + return true + } + errMsg := fmt.Sprintf("%q not in local datastore", contentPath.String()) + http.Error(w, errMsg, http.StatusPreconditionFailed) + return true + } + if r.Method == http.MethodHead { + w.WriteHeader(http.StatusOK) + return true + } + } + return false +} + +func handleUnsupportedHeaders(r *http.Request) (err *ErrorResponse) { + // X-Ipfs-Gateway-Prefix was removed (https://github.com/ipfs/kubo/issues/7702) + // TODO: remove this after go-ipfs 0.13 ships + if prfx := r.Header.Get("X-Ipfs-Gateway-Prefix"); prfx != "" { + err := fmt.Errorf("unsupported HTTP header: X-Ipfs-Gateway-Prefix support was removed: https://github.com/ipfs/kubo/issues/7702") + return NewErrorResponse(err, http.StatusBadRequest) + } + return nil +} + +// ?uri query param support for requests produced by web browsers +// via navigator.registerProtocolHandler Web API +// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler +// TLDR: redirect /ipfs/?uri=ipfs%3A%2F%2Fcid%3Fquery%3Dval to /ipfs/cid?query=val +func handleProtocolHandlerRedirect(w http.ResponseWriter, r *http.Request, logger *zap.SugaredLogger) (requestHandled bool) { + if uriParam := r.URL.Query().Get("uri"); uriParam != "" { + u, err := url.Parse(uriParam) + if err != nil { + webError(w, fmt.Errorf("failed to parse uri query parameter: %w", err), http.StatusBadRequest) + return true + } + if u.Scheme != "btfs" && u.Scheme != "btns" { + webError(w, fmt.Errorf("uri query parameter scheme must be btfs or btns: %w", err), http.StatusBadRequest) + return true + } + path := u.Path + if u.RawQuery != "" { // preserve query if present + path = path + "?" + u.RawQuery + } + + redirectURL := gopath.Join("/", u.Scheme, u.Host, path) + logger.Debugw("uri param, redirect", "to", redirectURL, "status", http.StatusMovedPermanently) + http.Redirect(w, r, redirectURL, http.StatusMovedPermanently) + return true + } + + return false +} + +// Disallow Service Worker registration on namespace roots +// https://github.com/ipfs/kubo/issues/4025 +func handleServiceWorkerRegistration(r *http.Request) (err *ErrorResponse) { + if r.Header.Get("Service-Worker") == "script" { + matched, _ := regexp.MatchString(`^/bt[fn]s/[^/]+$`, r.URL.Path) + if matched { + err := fmt.Errorf("registration is not allowed for this scope") + return NewErrorResponse(fmt.Errorf("navigator.serviceWorker: %w", err), http.StatusBadRequest) + } + } + + return nil +} + +// Attempt to fix redundant /ipfs/ namespace as long as resulting +// 'intended' path is valid. This is in case gremlins were tickled +// wrong way and user ended up at /ipfs/ipfs/{cid} or /ipfs/btns/{id} +// like in bafybeien3m7mdn6imm425vc2s22erzyhbvk5n3ofzgikkhmdkh5cuqbpbq :^)) +func handleSuperfluousNamespace(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) (requestHandled bool) { + // If the path is valid, there's nothing to do + if pathErr := contentPath.IsValid(); pathErr == nil { + return false + } + + // If there's no superflous namespace, there's nothing to do + if !(strings.HasPrefix(r.URL.Path, "/btfs/btfs/") || strings.HasPrefix(r.URL.Path, "/btfs/btns/")) { + return false + } + + // Attempt to fix the superflous namespace + intendedPath := ipath.New(strings.TrimPrefix(r.URL.Path, "/btfs")) + if err := intendedPath.IsValid(); err != nil { + webError(w, fmt.Errorf("invalid btfs path: %w", err), http.StatusBadRequest) + return true + } + intendedURL := intendedPath.String() + if r.URL.RawQuery != "" { + // we render HTML, so ensure query entries are properly escaped + q, _ := url.ParseQuery(r.URL.RawQuery) + intendedURL = intendedURL + "?" + q.Encode() + } + // return HTTP 400 (Bad Request) with HTML error page that: + // - points at correct canonical path via header + // - displays human-readable error + // - redirects to intendedURL after a short delay + + w.WriteHeader(http.StatusBadRequest) + if err := redirectTemplate.Execute(w, redirectTemplateData{ + RedirectURL: intendedURL, + SuggestedPath: intendedPath.String(), + ErrorMsg: fmt.Sprintf("invalid path: %q should be %q", r.URL.Path, intendedPath.String()), + }); err != nil { + webError(w, fmt.Errorf("failed to redirect when fixing superfluous namespace: %w", err), http.StatusBadRequest) + } + + return true +} diff --git a/core/corehttp/gateway/handler_block.go b/core/corehttp/gateway/handler_block.go new file mode 100644 index 000000000..616c2f292 --- /dev/null +++ b/core/corehttp/gateway/handler_block.go @@ -0,0 +1,55 @@ +package gateway + +import ( + "context" + "net/http" + "time" + + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// serveRawBlock returns bytes behind a raw block +func (i *handler) serveRawBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, imPath ImmutablePath, contentPath ipath.Path, begin time.Time) bool { + ctx, span := spanTrace(ctx, "Handler.ServeRawBlock", trace.WithAttributes(attribute.String("path", imPath.String()))) + defer span.End() + + pathMetadata, data, err := i.api.GetBlock(ctx, imPath) + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + defer data.Close() + + if err := i.setIpfsRootsHeader(w, pathMetadata); err != nil { + webRequestError(w, err) + return false + } + + blockCid := pathMetadata.LastSegment.Cid() + + // Set Content-Disposition + var name string + if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { + name = urlFilename + } else { + name = blockCid.String() + ".bin" + } + setContentDispositionHeader(w, name, "attachment") + + // Set remaining headers + modtime := addCacheControlHeaders(w, r, contentPath, blockCid) + w.Header().Set("Content-Type", "application/vnd.ipld.raw") + w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) + + // ServeContent will take care of + // If-None-Match+Etag, Content-Length and range requests + _, dataSent, _ := ServeContent(w, r, name, modtime, data) + + if dataSent { + // Update metrics + i.rawBlockGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } + + return dataSent +} diff --git a/core/corehttp/gateway/handler_car.go b/core/corehttp/gateway/handler_car.go new file mode 100644 index 000000000..76da841d5 --- /dev/null +++ b/core/corehttp/gateway/handler_car.go @@ -0,0 +1,95 @@ +package gateway + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/multierr" +) + +// serveCAR returns a CAR stream for specific DAG+selector +func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.Request, imPath ImmutablePath, contentPath ipath.Path, carVersion string, begin time.Time) bool { + ctx, span := spanTrace(ctx, "Handler.ServeCAR", trace.WithAttributes(attribute.String("path", imPath.String()))) + defer span.End() + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + switch carVersion { + case "": // noop, client does not care about version + case "1": // noop, we support this + default: + err := fmt.Errorf("unsupported CAR version: only version=1 is supported") + webError(w, err, http.StatusBadRequest) + return false + } + + pathMetadata, carFile, errCh, err := i.api.GetCAR(ctx, imPath) + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + defer carFile.Close() + + if err := i.setIpfsRootsHeader(w, pathMetadata); err != nil { + webRequestError(w, err) + return false + } + + rootCid := pathMetadata.LastSegment.Cid() + + // Set Content-Disposition + var name string + if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { + name = urlFilename + } else { + name = rootCid.String() + ".car" + } + setContentDispositionHeader(w, name, "attachment") + + // Set Cache-Control (same logic as for a regular files) + addCacheControlHeaders(w, r, contentPath, rootCid) + + // Weak Etag W/ because we can't guarantee byte-for-byte identical + // responses, but still want to benefit from HTTP Caching. Two CAR + // responses for the same CID and selector will be logically equivalent, + // but when CAR is streamed, then in theory, blocks may arrive from + // datastore in non-deterministic order. + etag := `W/` + getEtag(r, rootCid) + w.Header().Set("Etag", etag) + + // Finish early if Etag match + if r.Header.Get("If-None-Match") == etag { + w.WriteHeader(http.StatusNotModified) + return false + } + + // Make it clear we don't support range-requests over a car stream + // Partial downloads and resumes should be handled using requests for + // sub-DAGs and IPLD selectors: https://github.com/ipfs/go-ipfs/issues/8769 + w.Header().Set("Accept-Ranges", "none") + + w.Header().Set("Content-Type", "application/vnd.ipld.car; version=1") + w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) + + _, copyErr := io.Copy(w, carFile) + carErr := <-errCh + streamErr := multierr.Combine(carErr, copyErr) + if streamErr != nil { + // We return error as a trailer, however it is not something browsers can access + // (https://github.com/mdn/browser-compat-data/issues/14703) + // Due to this, we suggest client always verify that + // the received CAR stream response is matching requested DAG selector + w.Header().Set("X-Stream-Error", streamErr.Error()) + return false + } + + // Update metrics + i.carStreamGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + return true +} diff --git a/core/corehttp/gateway/handler_codec.go b/core/corehttp/gateway/handler_codec.go new file mode 100644 index 000000000..f8807e3f3 --- /dev/null +++ b/core/corehttp/gateway/handler_codec.go @@ -0,0 +1,281 @@ +package gateway + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/bittorrent/go-btfs/core/corehttp/gateway/assets" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime/multicodec" + "github.com/ipld/go-ipld-prime/node/basicnode" + mc "github.com/multiformats/go-multicodec" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + // Ensure basic codecs are registered. + _ "github.com/ipld/go-ipld-prime/codec/cbor" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + _ "github.com/ipld/go-ipld-prime/codec/dagjson" + _ "github.com/ipld/go-ipld-prime/codec/json" +) + +// codecToContentType maps the supported IPLD codecs to the HTTP Content +// Type they should have. +var codecToContentType = map[mc.Code]string{ + mc.Json: "application/json", + mc.Cbor: "application/cbor", + mc.DagJson: "application/vnd.ipld.dag-json", + mc.DagCbor: "application/vnd.ipld.dag-cbor", +} + +// contentTypeToRaw maps the HTTP Content Type to the respective codec that +// allows raw response without any conversion. +var contentTypeToRaw = map[string][]mc.Code{ + "application/json": {mc.Json, mc.DagJson}, + "application/cbor": {mc.Cbor, mc.DagCbor}, +} + +// contentTypeToCodec maps the HTTP Content Type to the respective codec. We +// only add here the codecs that we want to convert-to-from. +var contentTypeToCodec = map[string]mc.Code{ + "application/vnd.ipld.dag-json": mc.DagJson, + "application/vnd.ipld.dag-cbor": mc.DagCbor, +} + +// contentTypeToExtension maps the HTTP Content Type to the respective file +// extension, used in Content-Disposition header when downloading the file. +var contentTypeToExtension = map[string]string{ + "application/json": ".json", + "application/vnd.ipld.dag-json": ".json", + "application/cbor": ".cbor", + "application/vnd.ipld.dag-cbor": ".cbor", +} + +func (i *handler) serveCodec(ctx context.Context, w http.ResponseWriter, r *http.Request, imPath ImmutablePath, contentPath ipath.Path, begin time.Time, requestedContentType string) bool { + ctx, span := spanTrace(ctx, "Handler.ServeCodec", trace.WithAttributes(attribute.String("path", imPath.String()), attribute.String("requestedContentType", requestedContentType))) + defer span.End() + + pathMetadata, data, err := i.api.GetBlock(ctx, imPath) + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + defer data.Close() + + if err := i.setIpfsRootsHeader(w, pathMetadata); err != nil { + webRequestError(w, err) + return false + } + + resolvedPath := pathMetadata.LastSegment + return i.renderCodec(ctx, w, r, resolvedPath, data, contentPath, begin, requestedContentType) +} + +func (i *handler) renderCodec(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, blockData io.ReadSeekCloser, contentPath ipath.Path, begin time.Time, requestedContentType string) bool { + ctx, span := spanTrace(ctx, "Handler.RenderCodec", trace.WithAttributes(attribute.String("path", resolvedPath.String()), attribute.String("requestedContentType", requestedContentType))) + defer span.End() + + blockCid := resolvedPath.Cid() + cidCodec := mc.Code(blockCid.Prefix().Codec) + responseContentType := requestedContentType + + // If the resolved path still has some remainder, return error for now. + // TODO: handle this when we have IPLD Patch (https://ipld.io/specs/patch/) via HTTP PUT + // TODO: (depends on https://github.com/ipfs/kubo/issues/4801 and https://github.com/ipfs/kubo/issues/4782) + if resolvedPath.Remainder() != "" { + path := strings.TrimSuffix(resolvedPath.String(), resolvedPath.Remainder()) + err := fmt.Errorf("%q of %q could not be returned: reading IPLD Kinds other than Links (CBOR Tag 42) is not implemented: try reading %q instead", resolvedPath.Remainder(), resolvedPath.String(), path) + webError(w, err, http.StatusNotImplemented) + return false + } + + // If no explicit content type was requested, the response will have one based on the codec from the CID + if requestedContentType == "" { + cidContentType, ok := codecToContentType[cidCodec] + if !ok { + // Should not happen unless function is called with wrong parameters. + err := fmt.Errorf("content type not found for codec: %v", cidCodec) + webError(w, err, http.StatusInternalServerError) + return false + } + responseContentType = cidContentType + } + + // Set HTTP headers (for caching etc) + modtime := addCacheControlHeaders(w, r, contentPath, resolvedPath.Cid()) + name := setCodecContentDisposition(w, r, resolvedPath, responseContentType) + w.Header().Set("Content-Type", responseContentType) + w.Header().Set("X-Content-Type-Options", "nosniff") + + // No content type is specified by the user (via Accept, or format=). However, + // we support this format. Let's handle it. + if requestedContentType == "" { + isDAG := cidCodec == mc.DagJson || cidCodec == mc.DagCbor + acceptsHTML := strings.Contains(r.Header.Get("Accept"), "text/html") + download := r.URL.Query().Get("download") == "true" + + if isDAG && acceptsHTML && !download { + return i.serveCodecHTML(ctx, w, r, resolvedPath, contentPath) + } else { + // This covers CIDs with codec 'json' and 'cbor' as those do not have + // an explicit requested content type. + return i.serveCodecRaw(ctx, w, r, blockData, contentPath, name, modtime, begin) + } + } + + // If DAG-JSON or DAG-CBOR was requested using corresponding plain content type + // return raw block as-is, without conversion + skipCodecs, ok := contentTypeToRaw[requestedContentType] + if ok { + for _, skipCodec := range skipCodecs { + if skipCodec == cidCodec { + return i.serveCodecRaw(ctx, w, r, blockData, contentPath, name, modtime, begin) + } + } + } + + // Otherwise, the user has requested a specific content type (a DAG-* variant). + // Let's first get the codecs that can be used with this content type. + toCodec, ok := contentTypeToCodec[requestedContentType] + if !ok { + // This is never supposed to happen unless function is called with wrong parameters. + err := fmt.Errorf("unsupported content type: %q", requestedContentType) + webError(w, err, http.StatusInternalServerError) + return false + } + + // This handles DAG-* conversions and validations. + return i.serveCodecConverted(ctx, w, r, blockCid, blockData, contentPath, toCodec, modtime, begin) +} + +func (i *handler) serveCodecHTML(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path) bool { + // A HTML directory index will be presented, be sure to set the correct + // type instead of relying on autodetection (which may fail). + w.Header().Set("Content-Type", "text/html") + + // Clear Content-Disposition -- we want HTML to be rendered inline + w.Header().Del("Content-Disposition") + + // Generated index requires custom Etag (output may change between Kubo versions) + dagEtag := getDagIndexEtag(resolvedPath.Cid()) + w.Header().Set("Etag", dagEtag) + + // Remove Cache-Control for now to match UnixFS dir-index-html responses + // (we don't want browser to cache HTML forever) + // TODO: if we ever change behavior for UnixFS dir listings, same changes should be applied here + w.Header().Del("Cache-Control") + + cidCodec := mc.Code(resolvedPath.Cid().Prefix().Codec) + if err := assets.DagTemplate.Execute(w, assets.DagTemplateData{ + Path: contentPath.String(), + CID: resolvedPath.Cid().String(), + CodecName: cidCodec.String(), + CodecHex: fmt.Sprintf("0x%x", uint64(cidCodec)), + }); err != nil { + err = fmt.Errorf("failed to generate HTML listing for this DAG: try fetching raw block with ?format=raw: %w", err) + webError(w, err, http.StatusInternalServerError) + return false + } + + return true +} + +// serveCodecRaw returns the raw block without any conversion +func (i *handler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *http.Request, blockData io.ReadSeekCloser, contentPath ipath.Path, name string, modtime, begin time.Time) bool { + // ServeContent will take care of + // If-None-Match+Etag, Content-Length and range requests + _, dataSent, _ := ServeContent(w, r, name, modtime, blockData) + + if dataSent { + // Update metrics + i.jsoncborDocumentGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } + + return dataSent +} + +// serveCodecConverted returns payload converted to codec specified in toCodec +func (i *handler) serveCodecConverted(ctx context.Context, w http.ResponseWriter, r *http.Request, blockCid cid.Cid, blockData io.ReadSeekCloser, contentPath ipath.Path, toCodec mc.Code, modtime, begin time.Time) bool { + codec := blockCid.Prefix().Codec + decoder, err := multicodec.LookupDecoder(codec) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + node := basicnode.Prototype.Any.NewBuilder() + err = decoder(node, blockData) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + encoder, err := multicodec.LookupEncoder(uint64(toCodec)) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // Ensure IPLD node conforms to the codec specification. + var buf bytes.Buffer + err = encoder(node.Build(), &buf) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // Sets correct Last-Modified header. This code is borrowed from the standard + // library (net/http/server.go) as we cannot use serveFile. + if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) { + w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) + } + + _, err = w.Write(buf.Bytes()) + if err == nil { + // Update metrics + i.jsoncborDocumentGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + return true + } + + return false +} + +func setCodecContentDisposition(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentType string) string { + var dispType, name string + + ext, ok := contentTypeToExtension[contentType] + if !ok { + // Should never happen. + ext = ".bin" + } + + if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { + name = urlFilename + } else { + name = resolvedPath.Cid().String() + ext + } + + // JSON should be inlined, but ?download=true should still override + if r.URL.Query().Get("download") == "true" { + dispType = "attachment" + } else { + switch ext { + case ".json": // codecs that serialize to JSON can be rendered by browsers + dispType = "inline" + default: // everything else is assumed binary / opaque bytes + dispType = "attachment" + } + } + + setContentDispositionHeader(w, name, dispType) + return name +} + +func getDagIndexEtag(dagCid cid.Cid) string { + return `"DagIndex-` + assets.AssetHash + `_CID-` + dagCid.String() + `"` +} diff --git a/core/corehttp/gateway/handler_defaults.go b/core/corehttp/gateway/handler_defaults.go new file mode 100644 index 000000000..9c5a20b5f --- /dev/null +++ b/core/corehttp/gateway/handler_defaults.go @@ -0,0 +1,192 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/textproto" + "strconv" + "strings" + "time" + + mc "github.com/multiformats/go-multicodec" + + files "github.com/bittorrent/go-btfs-files" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +func (i *handler) serveDefaults(ctx context.Context, w http.ResponseWriter, r *http.Request, maybeResolvedImPath ImmutablePath, immutableContentPath ImmutablePath, contentPath ipath.Path, begin time.Time, requestedContentType string, logger *zap.SugaredLogger) bool { + ctx, span := spanTrace(ctx, "Handler.ServeDefaults", trace.WithAttributes(attribute.String("path", contentPath.String()))) + defer span.End() + + var ( + pathMetadata ContentPathMetadata + bytesResponse files.File + isDirectoryHeadRequest bool + directoryMetadata *directoryMetadata + err error + ranges []ByteRange + ) + + switch r.Method { + case http.MethodHead: + var data files.Node + pathMetadata, data, err = i.api.Head(ctx, maybeResolvedImPath) + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + defer data.Close() + if _, ok := data.(files.Directory); ok { + isDirectoryHeadRequest = true + } else if f, ok := data.(files.File); ok { + bytesResponse = f + } else { + webError(w, fmt.Errorf("unsupported response type"), http.StatusInternalServerError) + return false + } + case http.MethodGet: + rangeHeader := r.Header.Get("Range") + if rangeHeader != "" { + // TODO: Add tests for range parsing + ranges, err = parseRange(rangeHeader) + if err != nil { + webError(w, fmt.Errorf("invalid range request: %w", err), http.StatusBadRequest) + return false + } + } + + var getResp *GetResponse + // TODO: passing only resolved path here, instead of contentPath is + // harming content routing. Knowing original immutableContentPath will + // allow backend to find providers for parents, even when internal + // CIDs are not announced, and will provide better key for caching + // related DAGs. + pathMetadata, getResp, err = i.api.Get(ctx, maybeResolvedImPath, ranges...) + if err != nil { + if isWebRequest(requestedContentType) { + forwardedPath, continueProcessing := i.handleWebRequestErrors(w, r, maybeResolvedImPath, immutableContentPath, contentPath, err, logger) + if !continueProcessing { + return false + } + pathMetadata, getResp, err = i.api.Get(ctx, forwardedPath, ranges...) + if err != nil { + err = fmt.Errorf("failed to resolve %s: %w", debugStr(contentPath.String()), err) + webError(w, err, http.StatusInternalServerError) + } + } else { + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + } + } + if getResp.bytes != nil { + bytesResponse = getResp.bytes + defer bytesResponse.Close() + } else { + directoryMetadata = getResp.directoryMetadata + } + + default: + // This shouldn't be possible to reach which is why it is a 500 rather than 4XX error + webError(w, fmt.Errorf("invalid method: cannot use this HTTP method with the given request"), http.StatusInternalServerError) + return false + } + + // TODO: check if we have a bug when maybeResolvedImPath is resolved and i.setIpfsRootsHeader works with pathMetadata returned by Get(maybeResolvedImPath) + if err := i.setIpfsRootsHeader(w, pathMetadata); err != nil { + webRequestError(w, err) + return false + } + + resolvedPath := pathMetadata.LastSegment + switch mc.Code(resolvedPath.Cid().Prefix().Codec) { + case mc.Json, mc.DagJson, mc.Cbor, mc.DagCbor: + if bytesResponse == nil { // This should never happen + webError(w, fmt.Errorf("decoding error: data not usable as a file"), http.StatusInternalServerError) + return false + } + logger.Debugw("serving codec", "path", contentPath) + return i.renderCodec(r.Context(), w, r, resolvedPath, bytesResponse, contentPath, begin, requestedContentType) + default: + logger.Debugw("serving unixfs", "path", contentPath) + ctx, span := spanTrace(ctx, "Handler.ServeUnixFS", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) + defer span.End() + + // Handling Unixfs file + if bytesResponse != nil { + logger.Debugw("serving unixfs file", "path", contentPath) + return i.serveFile(ctx, w, r, resolvedPath, contentPath, bytesResponse, pathMetadata.ContentType, begin) + } + + // Handling Unixfs directory + if directoryMetadata != nil || isDirectoryHeadRequest { + logger.Debugw("serving unixfs directory", "path", contentPath) + return i.serveDirectory(ctx, w, r, resolvedPath, contentPath, isDirectoryHeadRequest, directoryMetadata, ranges, begin, logger) + } + + webError(w, fmt.Errorf("unsupported UnixFS type"), http.StatusInternalServerError) + return false + } +} + +// parseRange parses a Range header string as per RFC 7233. +func parseRange(s string) ([]ByteRange, error) { + if s == "" { + return nil, nil // header not present + } + const b = "bytes=" + if !strings.HasPrefix(s, b) { + return nil, errors.New("invalid range") + } + var ranges []ByteRange + for _, ra := range strings.Split(s[len(b):], ",") { + ra = textproto.TrimString(ra) + if ra == "" { + continue + } + start, end, ok := strings.Cut(ra, "-") + if !ok { + return nil, errors.New("invalid range") + } + start, end = textproto.TrimString(start), textproto.TrimString(end) + var r ByteRange + if start == "" { + r.From = 0 + // If no start is specified, end specifies the + // range start relative to the end of the file, + // and we are dealing with + // which has to be a non-negative integer as per + // RFC 7233 Section 2.1 "Byte-Ranges". + if end == "" || end[0] == '-' { + return nil, errors.New("invalid range") + } + i, err := strconv.ParseInt(end, 10, 64) + if i < 0 || err != nil { + return nil, errors.New("invalid range") + } + r.To = &i + } else { + i, err := strconv.ParseUint(start, 10, 64) + if err != nil { + return nil, errors.New("invalid range") + } + r.From = i + if end == "" { + // If no end is specified, range extends to end of the file. + r.To = nil + } else { + i, err := strconv.ParseInt(end, 10, 64) + if err != nil || i < 0 || r.From > uint64(i) { + return nil, errors.New("invalid range") + } + r.To = &i + } + } + ranges = append(ranges, r) + } + return ranges, nil +} diff --git a/core/corehttp/gateway/handler_ipns_record.go b/core/corehttp/gateway/handler_ipns_record.go new file mode 100644 index 000000000..19e942922 --- /dev/null +++ b/core/corehttp/gateway/handler_ipns_record.go @@ -0,0 +1,93 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + ipns_pb "github.com/bittorrent/go-btns/pb" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/cespare/xxhash" + "github.com/gogo/protobuf/proto" + "github.com/ipfs/go-cid" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +func (i *handler) serveIpnsRecord(ctx context.Context, w http.ResponseWriter, r *http.Request, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) bool { + ctx, span := spanTrace(ctx, "Handler.ServeIPNSRecord", trace.WithAttributes(attribute.String("path", contentPath.String()))) + defer span.End() + + if contentPath.Namespace() != "btns" { + err := fmt.Errorf("%s is not an BTNS link", contentPath.String()) + webError(w, err, http.StatusBadRequest) + return false + } + + key := contentPath.String() + key = strings.TrimSuffix(key, "/") + key = strings.TrimPrefix(key, "/btns/") + if strings.Count(key, "/") != 0 { + err := errors.New("cannot find btns key for subpath") + webError(w, err, http.StatusBadRequest) + return false + } + + c, err := cid.Decode(key) + if err != nil { + webError(w, err, http.StatusBadRequest) + return false + } + + rawRecord, err := i.api.GetIPNSRecord(ctx, c) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + var record ipns_pb.IpnsEntry + err = proto.Unmarshal(rawRecord, &record) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // Set cache control headers based on the TTL set in the IPNS record. If the + // TTL is not present, we use the Last-Modified tag. We are tracking IPNS + // caching on: https://github.com/ipfs/kubo/issues/1818. + // TODO: use addCacheControlHeaders once #1818 is fixed. + recordEtag := strconv.FormatUint(xxhash.Sum64(rawRecord), 32) + w.Header().Set("Etag", recordEtag) + if record.Ttl != nil { + seconds := int(time.Duration(*record.Ttl).Seconds()) + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", seconds)) + } else { + w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) + } + + // Set Content-Disposition + var name string + if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { + name = urlFilename + } else { + name = key + ".btns-record" + } + setContentDispositionHeader(w, name, "attachment") + + w.Header().Set("Content-Type", "application/vnd.ipfs.btns-record") + w.Header().Set("X-Content-Type-Options", "nosniff") + + _, err = w.Write(rawRecord) + if err == nil { + // Update metrics + i.ipnsRecordGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + return true + } + + return false +} diff --git a/core/corehttp/gateway/handler_tar.go b/core/corehttp/gateway/handler_tar.go new file mode 100644 index 000000000..61bde59bb --- /dev/null +++ b/core/corehttp/gateway/handler_tar.go @@ -0,0 +1,98 @@ +package gateway + +import ( + "context" + "fmt" + "net/http" + "time" + + files "github.com/bittorrent/go-btfs-files" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +var unixEpochTime = time.Unix(0, 0) + +func (i *handler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, imPath ImmutablePath, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) bool { + ctx, span := spanTrace(ctx, "Handler.ServeTAR", trace.WithAttributes(attribute.String("path", imPath.String()))) + defer span.End() + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + // Get Unixfs file (or directory) + pathMetadata, file, err := i.api.GetAll(ctx, imPath) + if !i.handleRequestErrors(w, contentPath, err) { + return false + } + defer file.Close() + + if err := i.setIpfsRootsHeader(w, pathMetadata); err != nil { + webRequestError(w, err) + return false + } + rootCid := pathMetadata.LastSegment.Cid() + + // Set Cache-Control and read optional Last-Modified time + modtime := addCacheControlHeaders(w, r, contentPath, rootCid) + + // Weak Etag W/ because we can't guarantee byte-for-byte identical + // responses, but still want to benefit from HTTP Caching. Two TAR + // responses for the same CID will be logically equivalent, + // but when TAR is streamed, then in theory, files and directories + // may arrive in different order (depends on TAR lib and filesystem/inodes). + etag := `W/` + getEtag(r, rootCid) + w.Header().Set("Etag", etag) + + // Finish early if Etag match + if r.Header.Get("If-None-Match") == etag { + w.WriteHeader(http.StatusNotModified) + return false + } + + // Set Content-Disposition + var name string + if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { + name = urlFilename + } else { + name = rootCid.String() + ".tar" + } + setContentDispositionHeader(w, name, "attachment") + + // Construct the TAR writer + tarw, err := files.NewTarWriter(w) + if err != nil { + webError(w, fmt.Errorf("could not build tar writer: %w", err), http.StatusInternalServerError) + return false + } + defer tarw.Close() + + // Sets correct Last-Modified header. This code is borrowed from the standard + // library (net/http/server.go) as we cannot use serveFile without throwing the entire + // TAR into the memory first. + if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) { + w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) + } + + w.Header().Set("Content-Type", "application/x-tar") + w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) + + // The TAR has a top-level directory (or file) named by the CID. + if err := tarw.WriteFile(file, rootCid.String()); err != nil { + w.Header().Set("X-Stream-Error", err.Error()) + // Trailer headers do not work in web browsers + // (see https://github.com/mdn/browser-compat-data/issues/14703) + // and we have limited options around error handling in browser contexts. + // To improve UX/DX, we finish response stream with error message, allowing client to + // (1) detect error by having corrupted TAR + // (2) be able to reason what went wrong by instecting the tail of TAR stream + _, _ = w.Write([]byte(err.Error())) + return false + } + + // Update metrics + i.tarStreamGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + return true +} diff --git a/core/corehttp/gateway/handler_test.go b/core/corehttp/gateway/handler_test.go new file mode 100644 index 000000000..4b6491daf --- /dev/null +++ b/core/corehttp/gateway/handler_test.go @@ -0,0 +1,231 @@ +package gateway + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + + "testing" + "time" + + files "github.com/bittorrent/go-btfs-files" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-path/resolver" + "github.com/stretchr/testify/assert" +) + +func TestEtagMatch(t *testing.T) { + for _, test := range []struct { + header string // value in If-None-Match HTTP header + cidEtag string + dirEtag string + expected bool // expected result of etagMatch(header, cidEtag, dirEtag) + }{ + {"", `"etag"`, "", false}, // no If-None-Match + {"", "", `"etag"`, false}, // no If-None-Match + {`"etag"`, `"etag"`, "", true}, // file etag match + {`W/"etag"`, `"etag"`, "", true}, // file etag match + {`"foo", W/"bar", W/"etag"`, `"etag"`, "", true}, // file etag match (array) + {`"foo",W/"bar",W/"etag"`, `"etag"`, "", true}, // file etag match (compact array) + {`"etag"`, "", `W/"etag"`, true}, // dir etag match + {`"etag"`, "", `W/"etag"`, true}, // dir etag match + {`W/"etag"`, "", `W/"etag"`, true}, // dir etag match + {`*`, `"etag"`, "", true}, // wildcard etag match + } { + result := etagMatch(test.header, test.cidEtag, test.dirEtag) + assert.Equalf(t, test.expected, result, "etagMatch(%q, %q, %q)", test.header, test.cidEtag, test.dirEtag) + } +} + +type errorMockAPI struct { + err error +} + +func (api *errorMockAPI) Get(ctx context.Context, path ImmutablePath, getRange ...ByteRange) (ContentPathMetadata, *GetResponse, error) { + return ContentPathMetadata{}, nil, api.err +} + +func (api *errorMockAPI) GetAll(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + return ContentPathMetadata{}, nil, api.err +} + +func (api *errorMockAPI) GetBlock(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.File, error) { + return ContentPathMetadata{}, nil, api.err +} + +func (api *errorMockAPI) Head(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + return ContentPathMetadata{}, nil, api.err +} + +func (api *errorMockAPI) GetCAR(ctx context.Context, path ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) { + return ContentPathMetadata{}, nil, nil, api.err +} + +func (api *errorMockAPI) ResolveMutable(ctx context.Context, path ipath.Path) (ImmutablePath, error) { + return ImmutablePath{}, api.err +} + +func (api *errorMockAPI) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + return nil, api.err +} + +func (api *errorMockAPI) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.Path, error) { + return nil, api.err +} + +func (api *errorMockAPI) IsCached(ctx context.Context, p ipath.Path) bool { + return false +} + +func (api *errorMockAPI) ResolvePath(ctx context.Context, path ImmutablePath) (ContentPathMetadata, error) { + return ContentPathMetadata{}, api.err +} + +func TestGatewayBadRequestInvalidPath(t *testing.T) { + api, _ := newMockAPI(t) + ts := newTestServer(t, api) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btfs/QmInvalid/Path", nil) + assert.Nil(t, err) + + res, err := ts.Client().Do(req) + assert.Nil(t, err) + + assert.Equal(t, http.StatusBadRequest, res.StatusCode) +} + +func TestErrorBubblingFromAPI(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + name string + err error + status int + }{ + {"404 Not Found from IPLD", &ipld.ErrNotFound{}, http.StatusNotFound}, + {"404 Not Found from path resolver", resolver.ErrNoLink{}, http.StatusNotFound}, + {"502 Bad Gateway", ErrBadGateway, http.StatusBadGateway}, + {"504 Gateway Timeout", ErrGatewayTimeout, http.StatusGatewayTimeout}, + } { + t.Run(test.name, func(t *testing.T) { + api := &errorMockAPI{err: fmt.Errorf("wrapped for testing purposes: %w", test.err)} + ts := newTestServer(t, api) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btns/en.wikipedia-on-btfs.org", nil) + assert.Nil(t, err) + + res, err := ts.Client().Do(req) + assert.Nil(t, err) + assert.Equal(t, test.status, res.StatusCode) + }) + } + + for _, test := range []struct { + name string + err error + status int + headerName string + headerValue string + headerLength int // how many times was headerName set + }{ + {"429 Too Many Requests without Retry-After header", ErrTooManyRequests, http.StatusTooManyRequests, "Retry-After", "", 0}, + {"429 Too Many Requests without Retry-After header", NewErrorRetryAfter(ErrTooManyRequests, 0*time.Second), http.StatusTooManyRequests, "Retry-After", "", 0}, + {"429 Too Many Requests with Retry-After header", NewErrorRetryAfter(ErrTooManyRequests, 3600*time.Second), http.StatusTooManyRequests, "Retry-After", "3600", 1}, + } { + api := &errorMockAPI{err: fmt.Errorf("wrapped for testing purposes: %w", test.err)} + ts := newTestServer(t, api) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btns/en.wikipedia-on-btfs.org", nil) + assert.Nil(t, err) + + res, err := ts.Client().Do(req) + assert.Nil(t, err) + assert.Equal(t, test.status, res.StatusCode) + assert.Equal(t, test.headerValue, res.Header.Get(test.headerName)) + assert.Equal(t, test.headerLength, len(res.Header.Values(test.headerName))) + } +} + +type panicMockAPI struct { + panicOnHostnameHandler bool +} + +func (api *panicMockAPI) Get(ctx context.Context, immutablePath ImmutablePath, ranges ...ByteRange) (ContentPathMetadata, *GetResponse, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) GetAll(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.Node, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) GetBlock(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.File, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) Head(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, files.Node, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) GetCAR(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) ResolveMutable(ctx context.Context, p ipath.Path) (ImmutablePath, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + panic("i am panicking") +} + +func (api *panicMockAPI) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.Path, error) { + // GetDNSLinkRecord is also called on the WithHostname handler. We have this option + // to disable panicking here so we can test if both the regular gateway handler + // and the hostname handler can handle panics. + if api.panicOnHostnameHandler { + panic("i am panicking") + } + + return nil, errors.New("not implemented") +} + +func (api *panicMockAPI) IsCached(ctx context.Context, p ipath.Path) bool { + panic("i am panicking") +} + +func (api *panicMockAPI) ResolvePath(ctx context.Context, immutablePath ImmutablePath) (ContentPathMetadata, error) { + panic("i am panicking") +} + +func TestGatewayStatusCodeOnPanic(t *testing.T) { + api := &panicMockAPI{} + ts := newTestServer(t, api) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btfs/bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", nil) + assert.Nil(t, err) + + res, err := ts.Client().Do(req) + assert.Nil(t, err) + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) +} + +func TestGatewayStatusCodeOnHostnamePanic(t *testing.T) { + api := &panicMockAPI{panicOnHostnameHandler: true} + ts := newTestServer(t, api) + t.Logf("test server url: %s", ts.URL) + + req, err := http.NewRequest(http.MethodGet, ts.URL+"/btfs/bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", nil) + assert.Nil(t, err) + + res, err := ts.Client().Do(req) + assert.Nil(t, err) + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) +} diff --git a/core/corehttp/gateway/handler_unixfs__redirects.go b/core/corehttp/gateway/handler_unixfs__redirects.go new file mode 100644 index 000000000..7545b7997 --- /dev/null +++ b/core/corehttp/gateway/handler_unixfs__redirects.go @@ -0,0 +1,294 @@ +package gateway + +import ( + "fmt" + "io" + "net/http" + gopath "path" + "strconv" + "strings" + + "go.uber.org/zap" + + files "github.com/bittorrent/go-btfs-files" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + + redirects "github.com/ipfs/go-ipfs-redirects-file" +) + +// Resolving a UnixFS path involves determining if the provided `path.Path` exists and returning the `path.Resolved` +// corresponding to that path. For UnixFS, path resolution is more involved. +// +// When a path under requested CID does not exist, Gateway will check if a `_redirects` file exists +// underneath the root CID of the path, and apply rules defined there. +// See sepcification introduced in: https://github.com/ipfs/specs/pull/290 +// +// Scenario 1: +// If a path exists, we always return the `path.Resolved` corresponding to that path, regardless of the existence of a `_redirects` file. +// +// Scenario 2: +// If a path does not exist, usually we should return a `nil` resolution path and an error indicating that the path +// doesn't exist. However, a `_redirects` file may exist and contain a redirect rule that redirects that path to a different path. +// We need to evaluate the rule and perform the redirect if present. +// +// Scenario 3: +// Another possibility is that the path corresponds to a rewrite rule (i.e. a rule with a status of 200). +// In this case, we don't perform a redirect, but do need to return a `path.Resolved` and `path.Path` corresponding to +// the rewrite destination path. +// +// Note that for security reasons, redirect rules are only processed when the request has origin isolation. +// See https://github.com/ipfs/specs/pull/290 for more information. +func (i *handler) serveRedirectsIfPresent(w http.ResponseWriter, r *http.Request, maybeResolvedImPath, immutableContentPath ImmutablePath, contentPath ipath.Path, logger *zap.SugaredLogger) (newContentPath ImmutablePath, continueProcessing bool, hadMatchingRule bool) { + // contentPath is the full ipfs path to the requested resource, + // regardless of whether path or subdomain resolution is used. + rootPath := getRootPath(immutableContentPath) + redirectsPath := ipath.Join(rootPath, "_redirects") + imRedirectsPath, err := NewImmutablePath(redirectsPath) + if err != nil { + err = fmt.Errorf("trouble processing _redirects path %q: %w", redirectsPath, err) + webError(w, err, http.StatusInternalServerError) + return ImmutablePath{}, false, true + } + + foundRedirect, redirectRules, err := i.getRedirectRules(r, imRedirectsPath) + if err != nil { + err = fmt.Errorf("trouble processing _redirects file at %q: %w", redirectsPath, err) + webError(w, err, http.StatusInternalServerError) + return ImmutablePath{}, false, true + } + + if foundRedirect { + redirected, newPath, err := i.handleRedirectsFileRules(w, r, immutableContentPath, contentPath, redirectRules) + if err != nil { + err = fmt.Errorf("trouble processing _redirects file at %q: %w", redirectsPath, err) + webError(w, err, http.StatusInternalServerError) + return ImmutablePath{}, false, true + } + + if redirected { + return ImmutablePath{}, false, true + } + + // 200 is treated as a rewrite, so update the path and continue + if newPath != "" { + // Reassign contentPath and resolvedPath since the URL was rewritten + p := ipath.New(newPath) + imPath, err := NewImmutablePath(p) + if err != nil { + err = fmt.Errorf("could not use _redirects file to %q: %w", p, err) + webError(w, err, http.StatusInternalServerError) + return ImmutablePath{}, false, true + } + return imPath, true, true + } + } + + // No matching rule, paths remain the same, continue regular processing + return maybeResolvedImPath, true, false +} + +func (i *handler) handleRedirectsFileRules(w http.ResponseWriter, r *http.Request, immutableContentPath ImmutablePath, cPath ipath.Path, redirectRules []redirects.Rule) (redirected bool, newContentPath string, err error) { + // Attempt to match a rule to the URL path, and perform the corresponding redirect or rewrite + pathParts := strings.Split(immutableContentPath.String(), "/") + if len(pathParts) > 3 { + // All paths should start with /ipfs/cid/, so get the path after that + urlPath := "/" + strings.Join(pathParts[3:], "/") + rootPath := strings.Join(pathParts[:3], "/") + // Trim off the trailing / + urlPath = strings.TrimSuffix(urlPath, "/") + + for _, rule := range redirectRules { + // Error right away if the rule is invalid + if !rule.MatchAndExpandPlaceholders(urlPath) { + continue + } + + // We have a match! + + // Rewrite + if rule.Status == 200 { + // Prepend the rootPath + toPath := rootPath + rule.To + return false, toPath, nil + } + + // Or 4xx + if rule.Status == 404 || rule.Status == 410 || rule.Status == 451 { + toPath := rootPath + rule.To + imContent4xxPath, err := NewImmutablePath(ipath.New(toPath)) + if err != nil { + return true, toPath, err + } + + // While we have the immutable path which is enough to fetch the data we need to track mutability for + // headers. + contentPathParts := strings.Split(cPath.String(), "/") + if len(contentPathParts) <= 3 { + // Match behavior as with the immutable path + return false, "", nil + } + // All paths should start with /ip(f|n)s//, so get the path after that + contentRootPath := strings.Join(contentPathParts[:3], "/") + content4xxPath := ipath.New(contentRootPath + rule.To) + err = i.serve4xx(w, r, imContent4xxPath, content4xxPath, rule.Status) + return true, toPath, err + } + + // Or redirect + if rule.Status >= 301 && rule.Status <= 308 { + http.Redirect(w, r, rule.To, rule.Status) + return true, "", nil + } + } + } + + // No redirects matched + return false, "", nil +} + +// getRedirectRules fetches the _redirects file corresponding to a given path and returns the rules +// Returns whether _redirects was found, the rules (if they exist) and if there was an error (other than a missing _redirects) +// If there is an error returns (false, nil, err) +func (i *handler) getRedirectRules(r *http.Request, redirectsPath ImmutablePath) (bool, []redirects.Rule, error) { + // Check for _redirects file. + // Any path resolution failures are ignored and we just assume there's no _redirects file. + // Note that ignoring these errors also ensures that the use of the empty CID (bafkqaaa) in tests doesn't fail. + _, redirectsFileGetResp, err := i.api.Get(r.Context(), redirectsPath) + if err != nil { + if isErrNotFound(err) { + return false, nil, nil + } + return false, nil, err + } + + if redirectsFileGetResp.bytes == nil { + return false, nil, fmt.Errorf(" _redirects is not a file") + } + f := redirectsFileGetResp.bytes + defer f.Close() + + // Parse redirect rules from file + redirectRules, err := redirects.Parse(f) + if err != nil { + return false, nil, fmt.Errorf("could not parse _redirects: %w", err) + } + return true, redirectRules, nil +} + +// Returns the root CID Path for the given path +func getRootPath(path ipath.Path) ipath.Path { + parts := strings.Split(path.String(), "/") + return ipath.New(gopath.Join("/", path.Namespace(), parts[2])) +} + +func (i *handler) serve4xx(w http.ResponseWriter, r *http.Request, content4xxPathImPath ImmutablePath, content4xxPath ipath.Path, status int) error { + pathMetadata, getresp, err := i.api.Get(r.Context(), content4xxPathImPath) + if err != nil { + return err + } + + if getresp.bytes == nil { + return fmt.Errorf("could not convert node for %d page to file", status) + } + content4xxFile := getresp.bytes + defer content4xxFile.Close() + + content4xxCid := pathMetadata.LastSegment.Cid() + + size, err := content4xxFile.Size() + if err != nil { + return fmt.Errorf("could not get size of %d page", status) + } + + log.Debugf("using _redirects: custom %d file at %q", status, content4xxPath) + w.Header().Set("Content-Type", "text/html") + w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) + addCacheControlHeaders(w, r, content4xxPath, content4xxCid) + w.WriteHeader(status) + _, err = io.CopyN(w, content4xxFile, size) + return err +} + +func hasOriginIsolation(r *http.Request) bool { + _, gw := r.Context().Value(GatewayHostnameKey).(string) + _, dnslink := r.Context().Value(DNSLinkHostnameKey).(string) + + if gw || dnslink { + return true + } + + return false +} + +// Deprecated: legacy ipfs-404.html files are superseded by _redirects file +// This is provided only for backward-compatibility, until websites migrate +// to 404s managed via _redirects file (https://github.com/ipfs/specs/pull/290) +func (i *handler) serveLegacy404IfPresent(w http.ResponseWriter, r *http.Request, imPath ImmutablePath) bool { + resolved404File, ctype, err := i.searchUpTreeFor404(r, imPath) + if err != nil { + return false + } + defer resolved404File.Close() + + size, err := resolved404File.Size() + if err != nil { + return false + } + + log.Debugw("using pretty 404 file", "path", imPath) + w.Header().Set("Content-Type", ctype) + w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) + w.WriteHeader(http.StatusNotFound) + _, err = io.CopyN(w, resolved404File, size) + return err == nil +} + +func (i *handler) searchUpTreeFor404(r *http.Request, imPath ImmutablePath) (files.File, string, error) { + filename404, ctype, err := preferred404Filename(r.Header.Values("Accept")) + if err != nil { + return nil, "", err + } + + pathComponents := strings.Split(imPath.String(), "/") + + for idx := len(pathComponents); idx >= 3; idx-- { + pretty404 := gopath.Join(append(pathComponents[0:idx], filename404)...) + parsed404Path := ipath.New("/" + pretty404) + if parsed404Path.IsValid() != nil { + break + } + imparsed404Path, err := NewImmutablePath(parsed404Path) + if err != nil { + break + } + + _, getResp, err := i.api.Get(r.Context(), imparsed404Path) + if err != nil { + continue + } + if getResp.bytes == nil { + return nil, "", fmt.Errorf("found a pretty 404 but it was not a file") + } + return getResp.bytes, ctype, nil + } + + return nil, "", fmt.Errorf("no pretty 404 in any parent folder") +} + +func preferred404Filename(acceptHeaders []string) (string, string, error) { + // If we ever want to offer a 404 file for a different content type + // then this function will need to parse q weightings, but for now + // the presence of anything matching HTML is enough. + for _, acceptHeader := range acceptHeaders { + accepted := strings.Split(acceptHeader, ",") + for _, spec := range accepted { + contentType := strings.SplitN(spec, ";", 1)[0] + switch contentType { + case "*/*", "text/*", "text/html": + return "ipfs-404.html", "text/html", nil + } + } + } + + return "", "", fmt.Errorf("there is no 404 file for the requested content types") +} diff --git a/core/corehttp/gateway/handler_unixfs_dir.go b/core/corehttp/gateway/handler_unixfs_dir.go new file mode 100644 index 000000000..4d6cfa7b5 --- /dev/null +++ b/core/corehttp/gateway/handler_unixfs_dir.go @@ -0,0 +1,224 @@ +package gateway + +import ( + "context" + "fmt" + "net/http" + "net/url" + gopath "path" + "strings" + "time" + + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-btfs/core/corehttp/gateway/assets" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/dustin/go-humanize" + cid "github.com/ipfs/go-cid" + path "github.com/ipfs/go-path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +// serveDirectory returns the best representation of UnixFS directory +// +// It will return index.html if present, or generate directory listing otherwise. +func (i *handler) serveDirectory(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, isHeadRequest bool, directoryMetadata *directoryMetadata, ranges []ByteRange, begin time.Time, logger *zap.SugaredLogger) bool { + ctx, span := spanTrace(ctx, "Handler.ServeDirectory", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) + defer span.End() + + // HostnameOption might have constructed an IPNS/IPFS path using the Host header. + // In this case, we need the original path for constructing redirects + // and links that match the requested URL. + // For example, http://example.net would become /ipns/example.net, and + // the redirects and links would end up as http://example.net/ipns/example.net + requestURI, err := url.ParseRequestURI(r.RequestURI) + if err != nil { + webError(w, fmt.Errorf("failed to parse request path: %w", err), http.StatusInternalServerError) + return false + } + originalURLPath := requestURI.Path + + // Ensure directory paths end with '/' + if originalURLPath[len(originalURLPath)-1] != '/' { + // don't redirect to trailing slash if it's go get + // https://github.com/ipfs/kubo/pull/3963 + goget := r.URL.Query().Get("go-get") == "1" + if !goget { + suffix := "/" + // preserve query parameters + if r.URL.RawQuery != "" { + suffix = suffix + "?" + r.URL.RawQuery + } + // /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar + redirectURL := originalURLPath + suffix + logger.Debugw("directory location moved permanently", "status", http.StatusMovedPermanently) + http.Redirect(w, r, redirectURL, http.StatusMovedPermanently) + return true + } + } + + // Check if directory has index.html, if so, serveFile + idxPath := ipath.Join(contentPath, "index.html") + imIndexPath, err := NewImmutablePath(ipath.Join(resolvedPath, "index.html")) + if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // TODO: could/should this all be skipped to have HEAD requests just return html content type and save the complexity? If so can we skip the above code as well? + var idxFile files.File + if isHeadRequest { + var idx files.Node + _, idx, err = i.api.Head(ctx, imIndexPath) + if err == nil { + f, ok := idx.(files.File) + if !ok { + webError(w, fmt.Errorf("%q could not be read: %w", imIndexPath, files.ErrNotReader), http.StatusUnprocessableEntity) + return false + } + idxFile = f + } + } else { + var getResp *GetResponse + _, getResp, err = i.api.Get(ctx, imIndexPath, ranges...) + if err == nil { + if getResp.bytes == nil { + webError(w, fmt.Errorf("%q could not be read: %w", imIndexPath, files.ErrNotReader), http.StatusUnprocessableEntity) + return false + } + idxFile = getResp.bytes + } + } + + if err == nil { + logger.Debugw("serving index.html file", "path", idxPath) + // write to request + success := i.serveFile(ctx, w, r, resolvedPath, idxPath, idxFile, "text/html", begin) + if success { + i.unixfsDirIndexGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } + return success + } + + if isErrNotFound(err) { + logger.Debugw("no index.html; noop", "path", idxPath) + } else if err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // See statusResponseWriter.WriteHeader + // and https://github.com/ipfs/kubo/issues/7164 + // Note: this needs to occur before listingTemplate.Execute otherwise we get + // superfluous response.WriteHeader call from prometheus/client_golang + if w.Header().Get("Location") != "" { + logger.Debugw("location moved permanently", "status", http.StatusMovedPermanently) + w.WriteHeader(http.StatusMovedPermanently) + return true + } + + // A HTML directory index will be presented, be sure to set the correct + // type instead of relying on autodetection (which may fail). + w.Header().Set("Content-Type", "text/html") + + // Generated dir index requires custom Etag (output may change between go-libipfs versions) + dirEtag := getDirListingEtag(resolvedPath.Cid()) + w.Header().Set("Etag", dirEtag) + + if r.Method == http.MethodHead { + logger.Debug("return as request's HTTP method is HEAD") + return true + } + + var dirListing []assets.DirectoryItem + for l := range directoryMetadata.entries { + if l.Err != nil { + webError(w, l.Err, http.StatusInternalServerError) + return false + } + + name := l.Link.Name + sz := l.Link.Size + linkCid := l.Link.Cid + + hash := linkCid.String() + di := assets.DirectoryItem{ + Size: humanize.Bytes(sz), + Name: name, + Path: gopath.Join(originalURLPath, name), + Hash: hash, + ShortHash: assets.ShortHash(hash), + } + dirListing = append(dirListing, di) + } + + // construct the correct back link + // https://github.com/ipfs/kubo/issues/1365 + backLink := originalURLPath + + // don't go further up than /ipfs/$hash/ + pathSplit := path.SplitList(contentPath.String()) + switch { + // skip backlink when listing a content root + case len(pathSplit) == 3: // url: /ipfs/$hash + backLink = "" + + // skip backlink when listing a content root + case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ + backLink = "" + + // add the correct link depending on whether the path ends with a slash + default: + if strings.HasSuffix(backLink, "/") { + backLink += ".." + } else { + backLink += "/.." + } + } + + size := humanize.Bytes(directoryMetadata.dagSize) + + hash := resolvedPath.Cid().String() + + // Gateway root URL to be used when linking to other rootIDs. + // This will be blank unless subdomain or DNSLink resolution is being used + // for this request. + var gwURL string + + // Get gateway hostname and build gateway URL. + if h, ok := r.Context().Value(GatewayHostnameKey).(string); ok { + gwURL = "//" + h + } else { + gwURL = "" + } + + dnslink := assets.HasDNSLinkOrigin(gwURL, contentPath.String()) + + // See comment above where originalUrlPath is declared. + tplData := assets.DirectoryTemplateData{ + GatewayURL: gwURL, + DNSLink: dnslink, + Listing: dirListing, + Size: size, + Path: contentPath.String(), + Breadcrumbs: assets.Breadcrumbs(contentPath.String(), dnslink), + BackLink: backLink, + Hash: hash, + } + + logger.Debugw("request processed", "tplDataDNSLink", dnslink, "tplDataSize", size, "tplDataBackLink", backLink, "tplDataHash", hash) + + if err := assets.DirectoryTemplate.Execute(w, tplData); err != nil { + webError(w, err, http.StatusInternalServerError) + return false + } + + // Update metrics + i.unixfsGenDirListingGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + return true +} + +func getDirListingEtag(dirCid cid.Cid) string { + return `"DirIndex-` + assets.AssetHash + `_CID-` + dirCid.String() + `"` +} diff --git a/core/corehttp/gateway/handler_unixfs_file.go b/core/corehttp/gateway/handler_unixfs_file.go new file mode 100644 index 000000000..a3f5a1f83 --- /dev/null +++ b/core/corehttp/gateway/handler_unixfs_file.go @@ -0,0 +1,108 @@ +package gateway + +import ( + "context" + "fmt" + "io" + "mime" + "net/http" + gopath "path" + "strings" + "time" + + files "github.com/bittorrent/go-btfs-files" + ipath "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/gabriel-vasile/mimetype" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// serveFile returns data behind a file along with HTTP headers based on +// the file itself, its CID and the contentPath used for accessing it. +func (i *handler) serveFile(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, file files.File, fileContentType string, begin time.Time) bool { + _, span := spanTrace(ctx, "Handler.ServeFile", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) + defer span.End() + + // Set Cache-Control and read optional Last-Modified time + modtime := addCacheControlHeaders(w, r, contentPath, resolvedPath.Cid()) + + // Set Content-Disposition + name := addContentDispositionHeader(w, r, contentPath) + + // Prepare size value for Content-Length HTTP header (set inside of http.ServeContent) + size, err := file.Size() + if err != nil { + http.Error(w, "cannot serve files with unknown sizes", http.StatusBadGateway) + return false + } + + if size == 0 { + // We override null files to 200 to avoid issues with fragment caching reverse proxies. + // Also whatever you are asking for, it's cheaper to just give you the complete file (nothing). + // TODO: remove this if clause once https://github.com/golang/go/issues/54794 is fixed in two latest releases of go + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + return true + } + + // Lazy seeker enables efficient range-requests and HTTP HEAD responses + content := &lazySeeker{ + size: size, + reader: file, + } + + // Calculate deterministic value for Content-Type HTTP header + // (we prefer to do it here, rather than using implicit sniffing in http.ServeContent) + var ctype string + if _, isSymlink := file.(*files.Symlink); isSymlink { + // We should be smarter about resolving symlinks but this is the + // "most correct" we can be without doing that. + ctype = "inode/symlink" + } else { + ctype = mime.TypeByExtension(gopath.Ext(name)) + if ctype == "" { + ctype = fileContentType + } + if ctype == "" { + // uses https://github.com/gabriel-vasile/mimetype library to determine the content type. + // Fixes https://github.com/ipfs/kubo/issues/7252 + mimeType, err := mimetype.DetectReader(content) + if err != nil { + http.Error(w, fmt.Sprintf("cannot detect content-type: %s", err.Error()), http.StatusInternalServerError) + return false + } + + ctype = mimeType.String() + _, err = content.Seek(0, io.SeekStart) + if err != nil { + http.Error(w, "seeker can't seek", http.StatusInternalServerError) + return false + } + } + // Strip the encoding from the HTML Content-Type header and let the + // browser figure it out. + // + // Fixes https://github.com/ipfs/kubo/issues/2203 + if strings.HasPrefix(ctype, "text/html;") { + ctype = "text/html" + } + } + // Setting explicit Content-Type to avoid mime-type sniffing on the client + // (unifies behavior across gateways and web browsers) + w.Header().Set("Content-Type", ctype) + + // special fixup around redirects + w = &statusResponseWriter{w} + + // ServeContent will take care of + // If-None-Match+Etag, Content-Length and range requests + _, dataSent, _ := ServeContent(w, r, name, modtime, content) + + // Was response successful? + if dataSent { + // Update metrics + i.unixfsFileGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) + } + + return dataSent +} diff --git a/core/corehttp/gateway/hostname.go b/core/corehttp/gateway/hostname.go new file mode 100644 index 000000000..b260788b8 --- /dev/null +++ b/core/corehttp/gateway/hostname.go @@ -0,0 +1,594 @@ +package gateway + +import ( + "context" + "fmt" + "net" + "net/http" + "net/url" + "regexp" + "strings" + + cid "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/peer" + dns "github.com/miekg/dns" + + mbase "github.com/multiformats/go-multibase" +) + +// Specification is the specification of an btfs Public Gateway. +type Specification struct { + // Paths is explicit list of path prefixes that should be handled by + // this gateway. Example: `["/btfs", "/btns"]` + // Useful if you only want to support immutable `/btfs`. + Paths []string + + // UseSubdomains indicates whether or not this gateway uses subdomains + // for btfs resources instead of paths. That is: http://CID.btfs.GATEWAY/... + // + // If this flag is set, any /btns/$id and/or /btfs/$id paths in Paths + // will be permanently redirected to http://$id.[btns|btfs].$gateway/. + // + // We do not support using both paths and subdomains for a single domain + // for security reasons (Origin isolation). + UseSubdomains bool + + // NoDNSLink configures this gateway to _not_ resolve DNSLink for the + // specific FQDN provided in `Host` HTTP header. Useful when you want to + // explicitly allow or refuse hosting a single hostname. To refuse all + // DNSLinks in `Host` processing, pass noDNSLink to `WithHostname` instead. + // This flag overrides the global one. + NoDNSLink bool + + // InlineDNSLink configures this gateway to always inline DNSLink names + // (FQDN) into a single DNS label in order to interop with wildcard TLS certs + // and Origin per CID isolation provided by rules like https://publicsuffix.org + // This should be set to true if you use HTTPS. + InlineDNSLink bool +} + +// WithHostname is a middleware that can wrap an http.Handler in order to parse the +// Host header and translating it to the content path. This is useful for Subdomain +// and DNSLink gateways. +// +// publicGateways configures the behavior of known public gateways. Each key is a +// fully qualified domain name (FQDN). +// +// noDNSLink configures the gateway to _not_ perform DNS TXT record lookups in +// response to requests with values in `Host` HTTP header. This flag can be overridden +// per FQDN in publicGateways. +func WithHostname(next http.Handler, api IPFSBackend, publicGateways map[string]*Specification, noDNSLink bool) http.HandlerFunc { + gateways := prepareHostnameGateways(publicGateways) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer panicHandler(w) + + // Unfortunately, many (well, btfs.io) gateways use + // DNSLink so if we blindly rewrite with DNSLink, we'll + // break /btfs links. + // + // We fix this by maintaining a list of known gateways + // and the paths that they serve "gateway" content on. + // That way, we can use DNSLink for everything else. + + // Support X-Forwarded-Host if added by a reverse proxy + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host + host := r.Host + if xHost := r.Header.Get("X-Forwarded-Host"); xHost != "" { + host = xHost + } + + // HTTP Host & Path check: is this one of our "known gateways"? + if gw, ok := gateways.isKnownHostname(host); ok { + // This is a known gateway but request is not using + // the subdomain feature. + + // Does this gateway _handle_ this path? + if hasPrefix(r.URL.Path, gw.Paths...) { + // It does. + + // Should this gateway use subdomains instead of paths? + if gw.UseSubdomains { + // Yes, redirect if applicable + // Example: dweb.link/btfs/{cid} → {cid}.btfs.dweb.link + useInlinedDNSLink := gw.InlineDNSLink + newURL, err := toSubdomainURL(host, r.URL.Path, r, useInlinedDNSLink, api) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if newURL != "" { + // Set "Location" header with redirect destination. + // It is ignored by curl in default mode, but will + // be respected by user agents that follow + // redirects by default, namely web browsers + w.Header().Set("Location", newURL) + + // Note: we continue regular gateway processing: + // HTTP Status Code http.StatusMovedPermanently + // will be set later, in statusResponseWriter + } + } + + // Not a subdomain resource, continue with path processing + // Example: 127.0.0.1:8080/btfs/{CID}, btfs.io/btfs/{CID} etc + next.ServeHTTP(w, r) + return + } + // Not a whitelisted path + + // Try DNSLink, if it was not explicitly disabled for the hostname + if !gw.NoDNSLink && hasDNSLinkRecord(r.Context(), api, host) { + // rewrite path and handle as DNSLink + r.URL.Path = "/btns/" + stripPort(host) + r.URL.Path + next.ServeHTTP(w, withHostnameContext(r, host)) + return + } + + // If not, resource does not exist on the hostname, return 404 + http.NotFound(w, r) + return + } + + // HTTP Host check: is this one of our subdomain-based "known gateways"? + // btfs details extracted from the host: {rootID}.{ns}.{gwHostname} + // /btfs/ example: {cid}.btfs.localhost:8080, {cid}.btfs.dweb.link + // /btns/ example: {libp2p-key}.btns.localhost:8080, {inlined-dnslink-fqdn}.btns.dweb.link + if gw, gwHostname, ns, rootID, ok := gateways.knownSubdomainDetails(host); ok { + // Looks like we're using a known gateway in subdomain mode. + + // Assemble original path prefix. + pathPrefix := "/" + ns + "/" + rootID + + // Retrieve whether or not we should inline DNSLink. + useInlinedDNSLink := gw.InlineDNSLink + + // Does this gateway _handle_ subdomains AND this path? + if !(gw.UseSubdomains && hasPrefix(pathPrefix, gw.Paths...)) { + // If not, resource does not exist, return 404 + http.NotFound(w, r) + return + } + + // Check if rootID is a valid CID + if rootCID, err := cid.Decode(rootID); err == nil { + // Do we need to redirect root CID to a canonical DNS representation? + dnsCID, err := toDNSLabel(rootID, rootCID) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if !strings.HasPrefix(r.Host, dnsCID) { + dnsPrefix := "/" + ns + "/" + dnsCID + newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, useInlinedDNSLink, api) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if newURL != "" { + // Redirect to deterministic CID to ensure CID + // always gets the same Origin on the web + http.Redirect(w, r, newURL, http.StatusMovedPermanently) + return + } + } + + // Do we need to fix multicodec in PeerID represented as CIDv1? + if isPeerIDNamespace(ns) { + if rootCID.Type() != cid.Libp2pKey { + newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, useInlinedDNSLink, api) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if newURL != "" { + // Redirect to CID fixed inside of toSubdomainURL() + http.Redirect(w, r, newURL, http.StatusMovedPermanently) + return + } + } + } + } else { // rootID is not a CID.. + // Check if rootID is a single DNS label with an inlined + // DNSLink FQDN a single DNS label. We support this so + // loading DNSLink names over TLS "just works" on public + // HTTP gateways. + // + // Rationale for doing this can be found under "Option C" + // at: https://github.com/btfs/in-web-browsers/issues/169 + // + // TLDR is: + // https://dweb.link/btns/my.v-long.example.com + // can be loaded from a subdomain gateway with a wildcard + // TLS cert if represented as a single DNS label: + // https://my-v--long-example-com.btns.dweb.link + if ns == "btns" && !strings.Contains(rootID, ".") { + // if there is no TXT recordfor rootID + if !hasDNSLinkRecord(r.Context(), api, rootID) { + // my-v--long-example-com → my.v-long.example.com + dnslinkFQDN := toDNSLinkFQDN(rootID) + if hasDNSLinkRecord(r.Context(), api, dnslinkFQDN) { + // update path prefix to use real FQDN with DNSLink + pathPrefix = "/btns/" + dnslinkFQDN + } + } + } + } + + // Rewrite the path to not use subdomains + r.URL.Path = pathPrefix + r.URL.Path + + // Serve path request + next.ServeHTTP(w, withHostnameContext(r, gwHostname)) + return + } + + // We don't have a known gateway. Fallback on DNSLink lookup + + // Wildcard HTTP Host check: + // 1. is wildcard DNSLink enabled (Gateway.NoDNSLink=false)? + // 2. does Host header include a fully qualified domain name (FQDN)? + // 3. does DNSLink record exist in DNS? + if !noDNSLink && hasDNSLinkRecord(r.Context(), api, host) { + // rewrite path and handle as DNSLink + r.URL.Path = "/btns/" + stripPort(host) + r.URL.Path + ctx := context.WithValue(r.Context(), DNSLinkHostnameKey, host) + next.ServeHTTP(w, withHostnameContext(r.WithContext(ctx), host)) + return + } + + // else, treat it as an old school gateway, I guess. + next.ServeHTTP(w, r) + + }) +} + +// Extends request context to include hostname of a canonical gateway root +// (subdomain root or dnslink fqdn) +func withHostnameContext(r *http.Request, hostname string) *http.Request { + // This is required for links on directory listing pages to work correctly + // on subdomain and dnslink gateways. While DNSlink could read value from + // Host header, subdomain gateways have more comples rules (knownSubdomainDetails) + // More: https://github.com/btfs/dir-index-html/issues/42 + // nolint: staticcheck // non-backward compatible change + ctx := context.WithValue(r.Context(), GatewayHostnameKey, hostname) + return r.WithContext(ctx) +} + +// isDomainNameAndNotPeerID returns bool if string looks like a valid DNS name AND is not a PeerID +func isDomainNameAndNotPeerID(hostname string) bool { + if len(hostname) == 0 { + return false + } + if _, err := peer.Decode(hostname); err == nil { + return false + } + _, ok := dns.IsDomainName(hostname) + return ok +} + +// hasDNSLinkRecord returns if a DNS TXT record exists for the provided host. +func hasDNSLinkRecord(ctx context.Context, api IPFSBackend, host string) bool { + dnslinkName := stripPort(host) + + if !isDomainNameAndNotPeerID(dnslinkName) { + return false + } + + _, err := api.GetDNSLinkRecord(ctx, dnslinkName) + return err == nil +} + +func isSubdomainNamespace(ns string) bool { + switch ns { + case "btfs", "btns", "p2p", "ipld": + // Note: 'p2p' and 'ipld' is only kept here for compatibility with Kubo. + return true + default: + return false + } +} + +func isPeerIDNamespace(ns string) bool { + switch ns { + case "btns", "p2p": + // Note: 'p2p' and 'ipld' is only kept here for compatibility with Kubo. + return true + default: + return false + } +} + +// Label's max length in DNS (https://tools.ietf.org/html/rfc1034#page-7) +const dnsLabelMaxLength int = 63 + +// Converts a CID to DNS-safe representation that fits in 63 characters +func toDNSLabel(rootID string, rootCID cid.Cid) (dnsCID string, err error) { + // Return as-is if things fit + if len(rootID) <= dnsLabelMaxLength { + return rootID, nil + } + + // Convert to Base36 and see if that helped + rootID, err = cid.NewCidV1(rootCID.Type(), rootCID.Hash()).StringOfBase(mbase.Base36) + if err != nil { + return "", err + } + if len(rootID) <= dnsLabelMaxLength { + return rootID, nil + } + + // Can't win with DNS at this point, return error + return "", fmt.Errorf("CID incompatible with DNS label length limit of 63: %s", rootID) +} + +// Returns true if HTTP request involves TLS certificate. +// See https://github.com/btfs/in-web-browsers/issues/169 to understand how it +// impacts DNSLink websites on public gateways. +func isHTTPSRequest(r *http.Request) bool { + // X-Forwarded-Proto if added by a reverse proxy + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto + xproto := r.Header.Get("X-Forwarded-Proto") + // Is request a native TLS (not used atm, but future-proofing) + // or a proxied HTTPS (eg. go-btfs behind nginx at a public gw)? + return r.URL.Scheme == "https" || xproto == "https" +} + +// Converts a FQDN to DNS-safe representation that fits in 63 characters: +// my.v-long.example.com → my-v--long-example-com +func toDNSLinkDNSLabel(fqdn string) (dnsLabel string, err error) { + dnsLabel = strings.ReplaceAll(fqdn, "-", "--") + dnsLabel = strings.ReplaceAll(dnsLabel, ".", "-") + if len(dnsLabel) > dnsLabelMaxLength { + return "", fmt.Errorf("DNSLink representation incompatible with DNS label length limit of 63: %s", dnsLabel) + } + return dnsLabel, nil +} + +// Converts a DNS-safe representation of DNSLink FQDN to real FQDN: +// my-v--long-example-com → my.v-long.example.com +func toDNSLinkFQDN(dnsLabel string) (fqdn string) { + fqdn = strings.ReplaceAll(dnsLabel, "--", "@") // @ placeholder is unused in DNS labels + fqdn = strings.ReplaceAll(fqdn, "-", ".") + fqdn = strings.ReplaceAll(fqdn, "@", "-") + return fqdn +} + +// Converts a hostname/path to a subdomain-based URL, if applicable. +func toSubdomainURL(hostname, path string, r *http.Request, inlineDNSLink bool, api IPFSBackend) (redirURL string, err error) { + var scheme, ns, rootID, rest string + + query := r.URL.RawQuery + parts := strings.SplitN(path, "/", 4) + isHTTPS := isHTTPSRequest(r) + safeRedirectURL := func(in string) (out string, err error) { + safeURI, err := url.ParseRequestURI(in) + if err != nil { + return "", err + } + return safeURI.String(), nil + } + + if isHTTPS { + scheme = "https:" + } else { + scheme = "http:" + } + + switch len(parts) { + case 4: + rest = parts[3] + fallthrough + case 3: + ns = parts[1] + rootID = parts[2] + default: + return "", nil + } + + if !isSubdomainNamespace(ns) { + return "", nil + } + + // add prefix if query is present + if query != "" { + query = "?" + query + } + + // Normalize problematic PeerIDs (eg. ed25519+identity) to CID representation + if isPeerIDNamespace(ns) && !isDomainNameAndNotPeerID(rootID) { + peerID, err := peer.Decode(rootID) + // Note: PeerID CIDv1 with protobuf multicodec will fail, but we fix it + // in the next block + if err == nil { + rootID = peer.ToCid(peerID).String() + } + } + + // If rootID is a CID, ensure it uses DNS-friendly text representation + if rootCID, err := cid.Decode(rootID); err == nil { + multicodec := rootCID.Type() + var base mbase.Encoding = mbase.Base32 + + // Normalizations specific to /btns/{libp2p-key} + if isPeerIDNamespace(ns) { + // Using Base36 for /btns/ for consistency + // Context: https://github.com/ipfs/kubo/pull/7441#discussion_r452372828 + base = mbase.Base36 + + // PeerIDs represented as CIDv1 are expected to have libp2p-key + // multicodec (https://github.com/libp2p/specs/pull/209). + // We ease the transition by fixing multicodec on the fly: + // https://github.com/ipfs/kubo/issues/5287#issuecomment-492163929 + if multicodec != cid.Libp2pKey { + multicodec = cid.Libp2pKey + } + } + + // Ensure CID text representation used in subdomain is compatible + // with the way DNS and URIs are implemented in user agents. + // + // 1. Switch to CIDv1 and enable case-insensitive Base encoding + // to avoid issues when user agent force-lowercases the hostname + // before making the request + // (https://github.com/ipfs/in-web-browsers/issues/89) + rootCID = cid.NewCidV1(multicodec, rootCID.Hash()) + rootID, err = rootCID.StringOfBase(base) + if err != nil { + return "", err + } + // 2. Make sure CID fits in a DNS label, adjust encoding if needed + // (https://github.com/ipfs/kubo/issues/7318) + rootID, err = toDNSLabel(rootID, rootCID) + if err != nil { + return "", err + } + } else { // rootID is not a CID + + // Check if rootID is a FQDN with DNSLink and convert it to TLS-safe + // representation that fits in a single DNS label. We support this so + // loading DNSLink names over TLS "just works" on public HTTP gateways + // that pass 'https' in X-Forwarded-Proto to go-ipfs. + // + // Rationale can be found under "Option C" + // at: https://github.com/ipfs/in-web-browsers/issues/169 + // + // TLDR is: + // /btns/my.v-long.example.com + // can be loaded from a subdomain gateway with a wildcard TLS cert if + // represented as a single DNS label: + // https://my-v--long-example-com.btns.dweb.link + if (inlineDNSLink || isHTTPS) && ns == "btns" && strings.Contains(rootID, ".") { + if hasDNSLinkRecord(r.Context(), api, rootID) { + // my.v-long.example.com → my-v--long-example-com + dnsLabel, err := toDNSLinkDNSLabel(rootID) + if err != nil { + return "", err + } + // update path prefix to use real FQDN with DNSLink + rootID = dnsLabel + } + } + } + + return safeRedirectURL(fmt.Sprintf( + "%s//%s.%s.%s/%s%s", + scheme, + rootID, + ns, + hostname, + rest, + query, + )) +} + +func hasPrefix(path string, prefixes ...string) bool { + for _, prefix := range prefixes { + // Assume people are creative with trailing slashes in Gateway config + p := strings.TrimSuffix(prefix, "/") + // Support for both /version and /btfs/$cid + if p == path || strings.HasPrefix(path, p+"/") { + return true + } + } + return false +} + +func stripPort(hostname string) string { + host, _, err := net.SplitHostPort(hostname) + if err == nil { + return host + } + return hostname +} + +type hostnameGateways struct { + exact map[string]*Specification + wildcard map[*regexp.Regexp]*Specification +} + +// prepareHostnameGateways converts the user given gateways into an internal format +// split between exact and wildcard-based gateway hostnames. +func prepareHostnameGateways(gateways map[string]*Specification) *hostnameGateways { + h := &hostnameGateways{ + exact: map[string]*Specification{}, + wildcard: map[*regexp.Regexp]*Specification{}, + } + + for hostname, gw := range gateways { + if strings.Contains(hostname, "*") { + // from *.domain.tld, construct a regexp that match any direct subdomain + // of .domain.tld. + // + // Regexp will be in the form of ^[^.]+\.domain.tld(?::\d+)?$ + escaped := strings.ReplaceAll(hostname, ".", `\.`) + regexed := strings.ReplaceAll(escaped, "*", "[^.]+") + + re, err := regexp.Compile(fmt.Sprintf(`^%s(?::\d+)?$`, regexed)) + if err != nil { + log.Warn("invalid wildcard gateway hostname \"%s\"", hostname) + } + + h.wildcard[re] = gw + } else { + h.exact[hostname] = gw + } + } + + return h +} + +// isKnownHostname checks the given hostname gateways and returns a matching +// specification with graceful fallback to version without port. +func (gws *hostnameGateways) isKnownHostname(hostname string) (gw *Specification, ok bool) { + // Try hostname (host+optional port - value from Host header as-is) + if gw, ok := gws.exact[hostname]; ok { + return gw, ok + } + // Also test without port + if gw, ok = gws.exact[stripPort(hostname)]; ok { + return gw, ok + } + + // Wildcard support. Test both with and without port. + for re, spec := range gws.wildcard { + if re.MatchString(hostname) { + return spec, true + } + } + + return nil, false +} + +// knownSubdomainDetails parses the Host header and looks for a known gateway matching +// the subdomain host. If found, returns a Specification and the subdomain components +// extracted from Host header: {rootID}.{ns}.{gwHostname}. +// Note: hostname is host + optional port +func (gws *hostnameGateways) knownSubdomainDetails(hostname string) (gw *Specification, gwHostname, ns, rootID string, ok bool) { + labels := strings.Split(hostname, ".") + // Look for FQDN of a known gateway hostname. + // Example: given "dist.btfs.tech.btns.dweb.link": + // 1. Lookup "link" TLD in knownGateways: negative + // 2. Lookup "dweb.link" in knownGateways: positive + // + // Stops when we have 2 or fewer labels left as we need at least a + // rootId and a namespace. + for i := len(labels) - 1; i >= 2; i-- { + fqdn := strings.Join(labels[i:], ".") + gw, ok := gws.isKnownHostname(fqdn) + if !ok { + continue + } + + ns := labels[i-1] + if !isSubdomainNamespace(ns) { + continue + } + + // Merge remaining labels (could be a FQDN with DNSLink) + rootID := strings.Join(labels[:i-1], ".") + return gw, fqdn, ns, rootID, true + } + // no match + return nil, "", "", "", false +} diff --git a/core/corehttp/gateway/hostname_test.go b/core/corehttp/gateway/hostname_test.go new file mode 100644 index 000000000..e48e6eeb7 --- /dev/null +++ b/core/corehttp/gateway/hostname_test.go @@ -0,0 +1,290 @@ +package gateway + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + cid "github.com/ipfs/go-cid" + path "github.com/ipfs/go-path" + "github.com/stretchr/testify/assert" +) + +func TestToSubdomainURL(t *testing.T) { + gwAPI, _ := newMockAPI(t) + testCID, err := cid.Decode("bafkqaglimvwgy3zakrsxg5cun5jxkyten5wwc2lokvjeycq") + assert.Nil(t, err) + + gwAPI.namesys["/btns/dnslink.long-name.example.com"] = path.FromString(testCID.String()) + gwAPI.namesys["/btns/dnslink.too-long.f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o.example.com"] = path.FromString(testCID.String()) + httpRequest := httptest.NewRequest("GET", "http://127.0.0.1:8080", nil) + httpsRequest := httptest.NewRequest("GET", "https://https-request-stub.example.com", nil) + httpsProxiedRequest := httptest.NewRequest("GET", "http://proxied-https-request-stub.example.com", nil) + httpsProxiedRequest.Header.Set("X-Forwarded-Proto", "https") + + for _, test := range []struct { + // in: + request *http.Request + gwHostname string + inlineDNSLink bool + path string + // out: + url string + err error + }{ + + // DNSLink + {httpRequest, "localhost", false, "/btns/dnslink.io", "http://dnslink.io.btns.localhost/", nil}, + // Hostname with port + {httpRequest, "localhost:8080", false, "/btns/dnslink.io", "http://dnslink.io.btns.localhost:8080/", nil}, + // CIDv0 → CIDv1base32 + {httpRequest, "localhost", false, "/btfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.btfs.localhost/", nil}, + // CIDv1 with long sha512 + {httpRequest, "localhost", false, "/btfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, + // PeerID as CIDv1 needs to have libp2p-key multicodec + {httpRequest, "localhost", false, "/btns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.btns.localhost/", nil}, + {httpRequest, "localhost", false, "/btns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.btns.localhost/", nil}, + // PeerID: ed25519+identity multihash → CIDv1Base36 + {httpRequest, "localhost", false, "/btns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.btns.localhost/", nil}, + {httpRequest, "sub.localhost", false, "/btfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.btfs.sub.localhost/", nil}, + // HTTPS requires DNSLink name to fit in a single DNS label – see "Option C" from https://github.com/btfs/in-web-browsers/issues/169 + {httpRequest, "dweb.link", false, "/btns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.btns.dweb.link/", nil}, + {httpsRequest, "dweb.link", false, "/btns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.btns.dweb.link/", nil}, + {httpsProxiedRequest, "dweb.link", false, "/btns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.btns.dweb.link/", nil}, + // HTTP requests can also be converted to fit into a single DNS label - https://github.com/btfs/kubo/issues/9243 + {httpRequest, "localhost", true, "/btns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.btns.localhost/", nil}, + {httpRequest, "dweb.link", true, "/btns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.btns.dweb.link/", nil}, + } { + testName := fmt.Sprintf("%s, %v, %s", test.gwHostname, test.inlineDNSLink, test.path) + t.Run(testName, func(t *testing.T) { + url, err := toSubdomainURL(test.gwHostname, test.path, test.request, test.inlineDNSLink, gwAPI) + assert.Equal(t, test.url, url) + assert.Equal(t, test.err, err) + }) + } +} + +func TestToDNSLinkDNSLabel(t *testing.T) { + for _, test := range []struct { + in string + out string + err error + }{ + {"dnslink.long-name.example.com", "dnslink-long--name-example-com", nil}, + {"dnslink.too-long.f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o.example.com", "", errors.New("DNSLink representation incompatible with DNS label length limit of 63: dnslink-too--long-f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o-example-com")}, + } { + t.Run(test.in, func(t *testing.T) { + out, err := toDNSLinkDNSLabel(test.in) + assert.Equal(t, test.out, out) + assert.Equal(t, test.err, err) + }) + } +} + +func TestToDNSLinkFQDN(t *testing.T) { + for _, test := range []struct { + in string + out string + }{ + {"singlelabel", "singlelabel"}, + {"docs-btfs-tech", "docs.btfs.tech"}, + {"dnslink-long--name-example-com", "dnslink.long-name.example.com"}, + } { + t.Run(test.in, func(t *testing.T) { + out := toDNSLinkFQDN(test.in) + assert.Equal(t, test.out, out) + }) + } +} + +func TestIsHTTPSRequest(t *testing.T) { + httpRequest := httptest.NewRequest("GET", "http://127.0.0.1:8080", nil) + httpsRequest := httptest.NewRequest("GET", "https://https-request-stub.example.com", nil) + httpsProxiedRequest := httptest.NewRequest("GET", "http://proxied-https-request-stub.example.com", nil) + httpsProxiedRequest.Header.Set("X-Forwarded-Proto", "https") + httpProxiedRequest := httptest.NewRequest("GET", "http://proxied-http-request-stub.example.com", nil) + httpProxiedRequest.Header.Set("X-Forwarded-Proto", "http") + oddballRequest := httptest.NewRequest("GET", "foo://127.0.0.1:8080", nil) + for _, test := range []struct { + in *http.Request + out bool + }{ + {httpRequest, false}, + {httpsRequest, true}, + {httpsProxiedRequest, true}, + {httpProxiedRequest, false}, + {oddballRequest, false}, + } { + testName := fmt.Sprintf("%+v", test.in) + t.Run(testName, func(t *testing.T) { + out := isHTTPSRequest(test.in) + assert.Equal(t, test.out, out) + }) + } +} + +func TestHasPrefix(t *testing.T) { + for _, test := range []struct { + prefixes []string + path string + out bool + }{ + {[]string{"/btfs"}, "/btfs/cid", true}, + {[]string{"/btfs/"}, "/btfs/cid", true}, + {[]string{"/version/"}, "/version", true}, + {[]string{"/version"}, "/version", true}, + } { + testName := fmt.Sprintf("%+v, %s", test.prefixes, test.path) + t.Run(testName, func(t *testing.T) { + out := hasPrefix(test.path, test.prefixes...) + assert.Equal(t, test.out, out) + }) + } +} + +func TestIsDomainNameAndNotPeerID(t *testing.T) { + for _, test := range []struct { + hostname string + out bool + }{ + {"", false}, + {"example.com", true}, + {"non-icann.something", true}, + {"..", false}, + {"12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", false}, // valid peerid + {"k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna", false}, // valid peerid + } { + t.Run(test.hostname, func(t *testing.T) { + out := isDomainNameAndNotPeerID(test.hostname) + assert.Equal(t, test.out, out) + }) + } +} + +func TestPortStripping(t *testing.T) { + for _, test := range []struct { + in string + out string + }{ + {"localhost:8080", "localhost"}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost:8080", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost"}, + {"example.com:443", "example.com"}, + {"example.com", "example.com"}, + {"foo-dweb.btfs.pvt.k12.ma.us:8080", "foo-dweb.btfs.pvt.k12.ma.us"}, + {"localhost", "localhost"}, + {"[::1]:8080", "::1"}, + } { + t.Run(test.in, func(t *testing.T) { + out := stripPort(test.in) + assert.Equal(t, test.out, out) + }) + } +} + +func TestToDNSLabel(t *testing.T) { + for _, test := range []struct { + in string + out string + err error + }{ + // <= 63 + {"QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", nil}, + {"bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", nil}, + // > 63 + // PeerID: ed25519+identity multihash → CIDv1Base36 + {"bafzaajaiaejca4syrpdu6gdx4wsdnokxkprgzxf4wrstuc34gxw5k5jrag2so5gk", "k51qzi5uqu5dj16qyiq0tajolkojyl9qdkr254920wxv7ghtuwcz593tp69z9m", nil}, + // CIDv1 with long sha512 → error + {"bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, + } { + t.Run(test.in, func(t *testing.T) { + inCID, _ := cid.Decode(test.in) + out, err := toDNSLabel(test.in, inCID) + assert.Equal(t, test.out, out) + assert.Equal(t, test.err, err) + }) + } +} + +func TestKnownSubdomainDetails(t *testing.T) { + gwLocalhost := &Specification{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} + gwDweb := &Specification{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} + gwLong := &Specification{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} + gwWildcard1 := &Specification{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} + gwWildcard2 := &Specification{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} + + gateways := prepareHostnameGateways(map[string]*Specification{ + "localhost": gwLocalhost, + "dweb.link": gwDweb, + "devgateway.dweb.link": gwDweb, + "dweb.btfs.pvt.k12.ma.us": gwLong, // note the sneaky ".btfs." ;-) + "*.wildcard1.tld": gwWildcard1, + "*.*.wildcard2.tld": gwWildcard2, + }) + + for _, test := range []struct { + // in: + hostHeader string + // out: + gw *Specification + hostname string + ns string + rootID string + ok bool + }{ + // no subdomain + {"127.0.0.1:8080", nil, "", "", "", false}, + {"[::1]:8080", nil, "", "", "", false}, + {"hey.look.example.com", nil, "", "", "", false}, + {"dweb.link", nil, "", "", "", false}, + // malformed Host header + {".....dweb.link", nil, "", "", "", false}, + {"link", nil, "", "", "", false}, + {"8080:dweb.link", nil, "", "", "", false}, + {" ", nil, "", "", "", false}, + {"", nil, "", "", "", false}, + // unknown gateway host + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.unknown.example.com", nil, "", "", "", false}, + // cid in subdomain, known gateway + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost:8080", gwLocalhost, "localhost:8080", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.link", gwDweb, "dweb.link", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.devgateway.dweb.link", gwDweb, "devgateway.dweb.link", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + // capture everything before .btfs. + {"foo.bar.boo-buzz.btfs.dweb.link", gwDweb, "dweb.link", "btfs", "foo.bar.boo-buzz", true}, + // btns + {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, + {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.link", gwDweb, "dweb.link", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, + // edge case check: public gateway under long TLD (see: https://publicsuffix.org) + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, + // dnslink in subdomain + {"en.wikipedia-on-btfs.org.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "en.wikipedia-on-btfs.org", true}, + {"en.wikipedia-on-btfs.org.btns.localhost", gwLocalhost, "localhost", "btns", "en.wikipedia-on-btfs.org", true}, + {"dist.btfs.tech.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "dist.btfs.tech", true}, + {"en.wikipedia-on-btfs.org.btns.dweb.link", gwDweb, "dweb.link", "btns", "en.wikipedia-on-btfs.org", true}, + // edge case check: public gateway under long TLD (see: https://publicsuffix.org) + {"foo.dweb.btfs.pvt.k12.ma.us", nil, "", "", "", false}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, + // other namespaces + {"api.localhost", nil, "", "", "", false}, + {"peerid.p2p.localhost", gwLocalhost, "localhost", "p2p", "peerid", true}, + // wildcards + {"wildcard1.tld", nil, "", "", "", false}, + {".wildcard1.tld", nil, "", "", "", false}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.wildcard1.tld", nil, "", "", "", false}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub.wildcard1.tld", gwWildcard1, "sub.wildcard1.tld", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub1.sub2.wildcard1.tld", nil, "", "", "", false}, + {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub1.sub2.wildcard2.tld", gwWildcard2, "sub1.sub2.wildcard2.tld", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, + } { + t.Run(test.hostHeader, func(t *testing.T) { + gw, hostname, ns, rootID, ok := gateways.knownSubdomainDetails(test.hostHeader) + assert.Equal(t, test.ok, ok) + assert.Equal(t, test.rootID, rootID) + assert.Equal(t, test.ns, ns) + assert.Equal(t, test.hostname, hostname) + assert.Equal(t, test.gw, gw) + }) + } +} diff --git a/core/corehttp/lazyseek.go b/core/corehttp/gateway/lazyseek.go similarity index 98% rename from core/corehttp/lazyseek.go rename to core/corehttp/gateway/lazyseek.go index 2a379dc91..0f4920fad 100644 --- a/core/corehttp/lazyseek.go +++ b/core/corehttp/gateway/lazyseek.go @@ -1,4 +1,4 @@ -package corehttp +package gateway import ( "fmt" diff --git a/core/corehttp/gateway/lazyseek_test.go b/core/corehttp/gateway/lazyseek_test.go new file mode 100644 index 000000000..b10b6a275 --- /dev/null +++ b/core/corehttp/gateway/lazyseek_test.go @@ -0,0 +1,98 @@ +package gateway + +import ( + "fmt" + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +type badSeeker struct { + io.ReadSeeker +} + +var errBadSeek = fmt.Errorf("bad seeker") + +func (bs badSeeker) Seek(offset int64, whence int) (int64, error) { + off, err := bs.ReadSeeker.Seek(0, io.SeekCurrent) + if err != nil { + panic(err) + } + return off, errBadSeek +} + +func TestLazySeekerError(t *testing.T) { + underlyingBuffer := strings.NewReader("fubar") + s := &lazySeeker{ + reader: badSeeker{underlyingBuffer}, + size: underlyingBuffer.Size(), + } + off, err := s.Seek(0, io.SeekEnd) + assert.Nil(t, err) + assert.Equal(t, s.size, off, "expected to seek to the end") + + // shouldn't have actually seeked. + b, err := io.ReadAll(s) + assert.Nil(t, err) + assert.Equal(t, 0, len(b), "expected to read nothing") + + // shouldn't need to actually seek. + off, err = s.Seek(0, io.SeekStart) + assert.Nil(t, err) + assert.Equal(t, int64(0), off, "expected to seek to the start") + + b, err = io.ReadAll(s) + assert.Nil(t, err) + assert.Equal(t, "fubar", string(b), "expected to read string") + + // should fail the second time. + off, err = s.Seek(0, io.SeekStart) + assert.Nil(t, err) + assert.Equal(t, int64(0), off, "expected to seek to the start") + + // right here... + b, err = io.ReadAll(s) + assert.NotNil(t, err) + assert.Equal(t, errBadSeek, err) + assert.Equal(t, 0, len(b), "expected to read nothing") +} + +func TestLazySeeker(t *testing.T) { + underlyingBuffer := strings.NewReader("fubar") + s := &lazySeeker{ + reader: underlyingBuffer, + size: underlyingBuffer.Size(), + } + expectByte := func(b byte) { + t.Helper() + var buf [1]byte + n, err := io.ReadFull(s, buf[:]) + assert.Nil(t, err) + assert.Equal(t, 1, n, "expected to read one byte, read %d", n) + assert.Equal(t, b, buf[0]) + } + expectSeek := func(whence int, off, expOff int64, expErr string) { + t.Helper() + n, err := s.Seek(off, whence) + if expErr == "" { + assert.Nil(t, err) + } else { + assert.EqualError(t, err, expErr) + } + assert.Equal(t, expOff, n) + } + + expectSeek(io.SeekEnd, 0, s.size, "") + b, err := io.ReadAll(s) + assert.Nil(t, err) + assert.Equal(t, 0, len(b), "expected to read nothing") + expectSeek(io.SeekEnd, -1, s.size-1, "") + expectByte('r') + expectSeek(io.SeekStart, 0, 0, "") + expectByte('f') + expectSeek(io.SeekCurrent, 1, 2, "") + expectByte('b') + expectSeek(io.SeekCurrent, -100, 3, "invalid seek offset") +} diff --git a/core/corehttp/gateway/metrics.go b/core/corehttp/gateway/metrics.go new file mode 100644 index 000000000..9c1d01b9e --- /dev/null +++ b/core/corehttp/gateway/metrics.go @@ -0,0 +1,269 @@ +package gateway + +import ( + "context" + "fmt" + "io" + "time" + + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/interface-go-btfs-core/path" + "github.com/ipfs/go-cid" + prometheus "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// Duration histograms measure things like API call execution, how long returning specific +// CID/path, how long CAR fetch form backend took, etc. +// We use fixed definition here, as we don't want to break existing buckets if we need to add more. +var defaultDurationHistogramBuckets = []float64{0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30, 60, 120, 240, 480, 960, 1920} + +type ipfsBackendWithMetrics struct { + api IPFSBackend + apiCallMetric *prometheus.HistogramVec +} + +func newIPFSBackendWithMetrics(api IPFSBackend) *ipfsBackendWithMetrics { + // We can add buckets as a parameter in the future, but for now using static defaults + // suggested in https://github.com/ipfs/kubo/issues/8441 + + apiCallMetric := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "ipfs", + Subsystem: "gw_backend", + Name: "api_call_duration_seconds", + Help: "The time spent in IPFSBackend API calls that returned success.", + Buckets: defaultDurationHistogramBuckets, + }, + []string{"name", "result"}, + ) + + if err := prometheus.Register(apiCallMetric); err != nil { + if are, ok := err.(prometheus.AlreadyRegisteredError); ok { + apiCallMetric = are.ExistingCollector.(*prometheus.HistogramVec) + } else { + log.Errorf("failed to register ipfs_gw_backend_api_call_duration_seconds: %v", err) + } + } + + return &ipfsBackendWithMetrics{api, apiCallMetric} +} + +func (b *ipfsBackendWithMetrics) updateApiCallMetric(name string, err error, begin time.Time) { + end := time.Since(begin).Seconds() + if err == nil { + b.apiCallMetric.WithLabelValues(name, "success").Observe(end) + } else { + b.apiCallMetric.WithLabelValues(name, "failure").Observe(end) + } +} + +func (b *ipfsBackendWithMetrics) Get(ctx context.Context, path ImmutablePath, ranges ...ByteRange) (ContentPathMetadata, *GetResponse, error) { + begin := time.Now() + name := "IPFSBackend.Get" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()), attribute.Int("ranges", len(ranges)))) + defer span.End() + + md, f, err := b.api.Get(ctx, path, ranges...) + + b.updateApiCallMetric(name, err, begin) + return md, f, err +} + +func (b *ipfsBackendWithMetrics) GetAll(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + begin := time.Now() + name := "IPFSBackend.GetAll" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + md, n, err := b.api.GetAll(ctx, path) + + b.updateApiCallMetric(name, err, begin) + return md, n, err +} + +func (b *ipfsBackendWithMetrics) GetBlock(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.File, error) { + begin := time.Now() + name := "IPFSBackend.GetBlock" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + md, n, err := b.api.GetBlock(ctx, path) + + b.updateApiCallMetric(name, err, begin) + return md, n, err +} + +func (b *ipfsBackendWithMetrics) Head(ctx context.Context, path ImmutablePath) (ContentPathMetadata, files.Node, error) { + begin := time.Now() + name := "IPFSBackend.Head" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + md, n, err := b.api.Head(ctx, path) + + b.updateApiCallMetric(name, err, begin) + return md, n, err +} + +func (b *ipfsBackendWithMetrics) ResolvePath(ctx context.Context, path ImmutablePath) (ContentPathMetadata, error) { + begin := time.Now() + name := "IPFSBackend.ResolvePath" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + md, err := b.api.ResolvePath(ctx, path) + + b.updateApiCallMetric(name, err, begin) + return md, err +} + +func (b *ipfsBackendWithMetrics) GetCAR(ctx context.Context, path ImmutablePath) (ContentPathMetadata, io.ReadCloser, <-chan error, error) { + begin := time.Now() + name := "IPFSBackend.GetCAR" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + md, rc, errCh, err := b.api.GetCAR(ctx, path) + + // TODO: handle errCh + b.updateApiCallMetric(name, err, begin) + return md, rc, errCh, err +} + +func (b *ipfsBackendWithMetrics) IsCached(ctx context.Context, path path.Path) bool { + begin := time.Now() + name := "IPFSBackend.IsCached" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + bln := b.api.IsCached(ctx, path) + + b.updateApiCallMetric(name, nil, begin) + return bln +} + +func (b *ipfsBackendWithMetrics) GetIPNSRecord(ctx context.Context, cid cid.Cid) ([]byte, error) { + begin := time.Now() + name := "IPFSBackend.GetIPNSRecord" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("cid", cid.String()))) + defer span.End() + + r, err := b.api.GetIPNSRecord(ctx, cid) + + b.updateApiCallMetric(name, err, begin) + return r, err +} + +func (b *ipfsBackendWithMetrics) ResolveMutable(ctx context.Context, path path.Path) (ImmutablePath, error) { + begin := time.Now() + name := "IPFSBackend.ResolveMutable" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) + defer span.End() + + p, err := b.api.ResolveMutable(ctx, path) + + b.updateApiCallMetric(name, err, begin) + return p, err +} + +func (b *ipfsBackendWithMetrics) GetDNSLinkRecord(ctx context.Context, fqdn string) (path.Path, error) { + begin := time.Now() + name := "IPFSBackend.GetDNSLinkRecord" + ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("fqdn", fqdn))) + defer span.End() + + p, err := b.api.GetDNSLinkRecord(ctx, fqdn) + + b.updateApiCallMetric(name, err, begin) + return p, err +} + +var _ IPFSBackend = (*ipfsBackendWithMetrics)(nil) + +func newHandlerWithMetrics(c Config, api IPFSBackend) *handler { + i := &handler{ + config: c, + api: newIPFSBackendWithMetrics(api), + + // Response-type specific metrics + // ---------------------------- + // Generic: time it takes to execute a successful gateway request (all request types) + getMetric: newHistogramMetric( + "gw_get_duration_seconds", + "The time to GET a successful response to a request (all content types).", + ), + // UnixFS: time it takes to return a file + unixfsFileGetMetric: newHistogramMetric( + "gw_unixfs_file_get_duration_seconds", + "The time to serve an entire UnixFS file from the gateway.", + ), + // UnixFS: time it takes to find and serve an index.html file on behalf of a directory. + unixfsDirIndexGetMetric: newHistogramMetric( + "gw_unixfs_dir_indexhtml_get_duration_seconds", + "The time to serve an index.html file on behalf of a directory from the gateway. This is a subset of gw_unixfs_file_get_duration_seconds.", + ), + // UnixFS: time it takes to generate static HTML with directory listing + unixfsGenDirListingGetMetric: newHistogramMetric( + "gw_unixfs_gen_dir_listing_get_duration_seconds", + "The time to serve a generated UnixFS HTML directory listing from the gateway.", + ), + // CAR: time it takes to return requested CAR stream + carStreamGetMetric: newHistogramMetric( + "gw_car_stream_get_duration_seconds", + "The time to GET an entire CAR stream from the gateway.", + ), + // Block: time it takes to return requested Block + rawBlockGetMetric: newHistogramMetric( + "gw_raw_block_get_duration_seconds", + "The time to GET an entire raw Block from the gateway.", + ), + // TAR: time it takes to return requested TAR stream + tarStreamGetMetric: newHistogramMetric( + "gw_tar_stream_get_duration_seconds", + "The time to GET an entire TAR stream from the gateway.", + ), + // JSON/CBOR: time it takes to return requested DAG-JSON/-CBOR document + jsoncborDocumentGetMetric: newHistogramMetric( + "gw_jsoncbor_get_duration_seconds", + "The time to GET an entire DAG-JSON/CBOR block from the gateway.", + ), + // IPNS Record: time it takes to return IPNS record + ipnsRecordGetMetric: newHistogramMetric( + "gw_ipns_record_get_duration_seconds", + "The time to GET an entire IPNS Record from the gateway.", + ), + } + return i +} + +func newHistogramMetric(name string, help string) *prometheus.HistogramVec { + // We can add buckets as a parameter in the future, but for now using static defaults + // suggested in https://github.com/ipfs/kubo/issues/8441 + histogramMetric := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "ipfs", + Subsystem: "http", + Name: name, + Help: help, + Buckets: defaultDurationHistogramBuckets, + }, + []string{"gateway"}, + ) + if err := prometheus.Register(histogramMetric); err != nil { + if are, ok := err.(prometheus.AlreadyRegisteredError); ok { + histogramMetric = are.ExistingCollector.(*prometheus.HistogramVec) + } else { + log.Errorf("failed to register ipfs_http_%s: %v", name, err) + } + } + return histogramMetric +} + +var tracer = otel.Tracer("btfs/gateway") + +func spanTrace(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return tracer.Start(ctx, fmt.Sprintf("Gateway.%s", spanName), opts...) +} diff --git a/core/corehttp/gateway/testdata/fixtures.car b/core/corehttp/gateway/testdata/fixtures.car new file mode 100644 index 0000000000000000000000000000000000000000..e01ca5c31c26f4c3769396f83455ac80a7037a4d GIT binary patch literal 1688 zcmcColv{nTFhK5L8N?7X0IF7%|s z5h1>i)Z!BN#FEtV#7g(n5(z6I7l_VAgXNw3PS!tlk?UE;vY74HR+%tWT>-5H1}~M% zruTo95RwGx^bGI|_Q)?T$xF;lbxKUm&dJQnE|%zL5^{!^lB?(SISzJFK*AA z7cq_R&}DVeX8*sTFSgxhpJFB?fo@7rYD#8NYI2FhEJm0ogjzg%W*q8%wPV8``7@$_ z=f$j!pZUHvMD@usKMgLPFC0R=AVUI*QcFrIO$?Og}ZSJ9Upyt_IIAcJeP+*#WH1mmR|TL*=OuIy+^m(_>&N8T3&upiUcdjWKCjp zfYncA1UZ6Wn1fOR&=J{fn~MLN{CPMxyTR{&lzq7S+9j_R>C1W^kPEuIy*=7nh$AgC zCsnVcqC|pG$QTk6;m^YDw{kp^?0PRD7=PyMvc5wB>yEBI)Fijq{}@-j#~h%Z{Cs-_ zWgB&gh2Zd0CB`PhZm`1%8ma-t^gtV*HRELW9_7z* zs!N2JlM;(0EWs%r=+7&~aaL+^V(%WA`msB-Chfc3op6rz;Jx#Y*NSadGMHBTKt6@L&RuiM{B!7rgTsT6CS$NF zLhd$d0f!pF%%Kg5?#COBIL-JYBQopvmlHGdcdDqbb9|CAd$P}xsWYW3f_8)oF~&bGQK{{j{mBx@0odU%ol-!wWX+PC1zm zc6rl3=7^tG`)?@jk|`s^mXexUkXj_+BV-P7(VyDLj5}*S-EN)e>A`CDH2d!6^qk_r zH_e9=A`?VcdrJuMW)`Fs>jH}nh@-ebj!FdinV?-z8|HAHoteKX=F$rBiK}Owy{dD~ ze$E7`tu^NE;v6g4yrQ(wZQuvlU<}G!gmk(9{i2XuT3nK!s{nE!NDm?ZIK!0#0LYJ= A8UO$Q literal 0 HcmV?d00001 diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go deleted file mode 100644 index 216eb2fed..000000000 --- a/core/corehttp/gateway_handler.go +++ /dev/null @@ -1,974 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "io" - "mime" - "net/http" - "net/url" - "os" - gopath "path" - "regexp" - "runtime/debug" - "strconv" - "strings" - "time" - - files "github.com/TRON-US/go-btfs-files" - mfs "github.com/TRON-US/go-mfs" - coreiface "github.com/TRON-US/interface-go-btfs-core" - ipath "github.com/TRON-US/interface-go-btfs-core/path" - "github.com/bittorrent/go-btfs/assets" - - "github.com/Workiva/go-datastructures/cache" - humanize "github.com/dustin/go-humanize" - "github.com/gabriel-vasile/mimetype" - "github.com/ipfs/go-cid" - dag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" - routing "github.com/libp2p/go-libp2p/core/routing" -) - -const ( - ipfsPathPrefix = "/btfs/" - ipnsPathPrefix = "/btns/" - GatewayReedSolomonDirectoryCacheCapacity = 10 -) - -// gatewayHandler is a HTTP handler that serves BTFS objects (accessible by default at /btfs/) -// (it serves requests like GET /btfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) -type gatewayHandler struct { - config GatewayConfig - api coreiface.CoreAPI - rsDirs cache.Cache -} - -type ReedSolomonDirectory struct { - rootPath ipath.Resolved - rootDir files.Directory -} - -func (d ReedSolomonDirectory) Size() uint64 { - // Returns one since this Size() is used by the cache.Cache - // to indicate one directory entry in the cache. - return uint64(1) -} - -// StatusResponseWriter enables us to override HTTP Status Code passed to -// WriteHeader function inside of http.ServeContent. Decision is based on -// presence of HTTP Headers such as Location. -type statusResponseWriter struct { - http.ResponseWriter -} - -func (sw *statusResponseWriter) WriteHeader(code int) { - // Check if we need to adjust Status Code to account for scheduled redirect - // This enables us to return payload along with HTTP 301 - // for subdomain redirect in web browsers while also returning body for cli - // tools which do not follow redirects by default (curl, wget). - redirect := sw.ResponseWriter.Header().Get("Location") - if redirect != "" && code == http.StatusOK { - code = http.StatusMovedPermanently - } - sw.ResponseWriter.WriteHeader(code) -} - -func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI, dirs cache.Cache) *gatewayHandler { - i := &gatewayHandler{ - config: c, - api: api, - rsDirs: dirs, - } - return i -} - -func parseIpfsPath(p string) (cid.Cid, string, error) { - rootPath, err := path.ParsePath(p) - if err != nil { - return cid.Cid{}, "", err - } - - // Check the path. - rsegs := rootPath.Segments() - if rsegs[0] != "btfs" { - return cid.Cid{}, "", fmt.Errorf("WritableGateway: only btfs paths supported") - } - - rootCid, err := cid.Decode(rsegs[1]) - if err != nil { - return cid.Cid{}, "", err - } - - return rootCid, path.Join(rsegs[2:]), nil -} - -func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // the hour is a hard fallback, we don't expect it to happen, but just in case - ctx, cancel := context.WithTimeout(r.Context(), time.Hour) - defer cancel() - r = r.WithContext(ctx) - - defer func() { - if r := recover(); r != nil { - log.Error("A panic occurred in the gateway handler!") - log.Error(r) - debug.PrintStack() - } - }() - - if i.config.Writable { - switch r.Method { - case http.MethodPost: - i.postHandler(w, r) - return - case http.MethodPut: - i.putHandler(w, r) - return - case http.MethodDelete: - i.deleteHandler(w, r) - return - } - } - - switch r.Method { - case http.MethodGet, http.MethodHead: - i.getOrHeadHandler(w, r) - return - case http.MethodOptions: - i.optionsHandler(w, r) - return - } - - switch r.Method { - case http.MethodGet, http.MethodHead: - i.getOrHeadHandler(w, r) - return - case http.MethodOptions: - i.optionsHandler(w, r) - return - } - - errmsg := "Method " + r.Method + " not allowed: " - var status int - if !i.config.Writable { - status = http.StatusMethodNotAllowed - errmsg = errmsg + "read only access" - w.Header().Add("Allow", http.MethodGet) - w.Header().Add("Allow", http.MethodHead) - w.Header().Add("Allow", http.MethodOptions) - } else { - status = http.StatusBadRequest - errmsg = errmsg + "bad request for " + r.URL.Path - } - http.Error(w, errmsg, status) -} - -func (i *gatewayHandler) optionsHandler(w http.ResponseWriter, r *http.Request) { - /* - OPTIONS is a noop request that is used by the browsers to check - if server accepts cross-site XMLHttpRequest (indicated by the presence of CORS headers) - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests - */ - i.addUserHeaders(w) // return all custom headers (including CORS ones, if set) -} - -func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { - begin := time.Now() - urlPath := r.URL.Path - escapedURLPath := r.URL.EscapedPath() - - // If the gateway is behind a reverse proxy and mounted at a sub-path, - // the prefix header can be set to signal this sub-path. - // It will be prepended to links in directory listings and the index.html redirect. - prefix := "" - if prfx := r.Header.Get("X-Btfs-Gateway-Prefix"); len(prfx) > 0 { - for _, p := range i.config.PathPrefixes { - if prfx == p || strings.HasPrefix(prfx, p+"/") { - prefix = prfx - break - } - } - } - - // HostnameOption might have constructed an BTNS/BTFS path using the Host header. - // In this case, we need the original path for constructing redirects - // and links that match the requested URL. - // For example, http://example.net would become /btns/example.net, and - // the redirects and links would end up as http://example.net/btns/example.net - requestURI, err := url.ParseRequestURI(r.RequestURI) - if err != nil { - webError(w, "failed to parse request path", err, http.StatusInternalServerError) - return - } - originalUrlPath := prefix + requestURI.Path - - // Service Worker registration request - if r.Header.Get("Service-Worker") == "script" { - // Disallow Service Worker registration on namespace roots - // https://github.com/ipfs/go-ipfs/issues/4025 - matched, _ := regexp.MatchString(`^/bt[fn]s/[^/]+$`, r.URL.Path) - if matched { - err := fmt.Errorf("registration is not allowed for this scope") - webError(w, "navigator.serviceWorker", err, http.StatusBadRequest) - return - } - } - - parsedPath := ipath.New(urlPath) - if err := parsedPath.IsValid(); err != nil { - webError(w, "invalid btfs path", err, http.StatusBadRequest) - return - } - - // Resolve path to the final DAG node for reed solomon directory differrently - // from normal files / directories. - var ( - resolvedPath ipath.Resolved - rootPath string - escapedRootPath string - resolvedRootPath ipath.Resolved - dr files.Node - isReedSolomonSubdirOrFile bool // Is ReedSolomon non-root subdirectory or file? - ) - - top, err := i.isTopLevelEntryPath(w, r, parsedPath.String(), escapedURLPath) - if err != nil { - // helper already handled webError - return - } - - if !top { - // Check if the `parsedPath` is part of Reed-Solomon enconded directory object. - // If yes, put a new entry to the Reed-Solomon directory cache if not exist and - // get the files.Node of the `parsedPath`. - k := strings.SplitN(parsedPath.String(), "/", 4)[2] - v, isPresent, err := i.cacheEntryFor(k) - if err != nil { - internalWebError(w, err) - return - } - var rootDir files.Directory - escapedRootPath, err = getDirRootPath(escapedURLPath) - if err != nil { - webError(w, "invalid btfs path without slashes", err, http.StatusBadRequest) - return - } - if isPresent { - resolvedRootPath, rootDir = v.rootPath, v.rootDir - } else { - rootPath, err = getDirRootPath(parsedPath.String()) - if err != nil { - webError(w, "invalid btfs path without slashes", err, http.StatusBadRequest) - return - } - - var rootDr files.Node - resolvedRootPath, rootDr, err = i.resolveAndGetFilesNode(r.Context(), w, r, ipath.New(rootPath), escapedRootPath) - if err != nil { - // helper already handled webError - return - } - ok := false - rootDir, ok = rootDr.(files.Directory) - if !ok { - log.Warnf("expected directory type for %s", escapedRootPath) - } - } - if isPresent || (rootDir != nil && rootDir.IsReedSolomon()) { - if !isPresent { - // Put the current Reed-Solomon directory entry to the cache - i.rsDirs.Put(k, ReedSolomonDirectory{rootPath: resolvedRootPath, rootDir: rootDir}) - } - // Get the files.Node of the `parsedPath`. - dr, err = findNode(rootDir, parsedPath.String()) - if err != nil { - internalWebError(w, err) - return - } - isReedSolomonSubdirOrFile = true - } - } - - // Normal file/directory case: perform resolve - if !isReedSolomonSubdirOrFile { - // Resolve path to the final DAG node for the ETag - resolvedPath, err = i.api.ResolvePath(r.Context(), parsedPath) - switch err { - case nil: - case coreiface.ErrOffline: - webError(w, "btfs resolve -r "+escapedURLPath, err, http.StatusServiceUnavailable) - return - default: - if i.servePretty404IfPresent(w, r, parsedPath) { - return - } - - webError(w, "btfs resolve -r "+escapedURLPath, err, http.StatusNotFound) - return - } - resolvedPath, dr, err = i.resolveAndGetFilesNode(r.Context(), w, r, parsedPath, escapedURLPath) - if err != nil { - // helper already handled webError - return - } - } - - unixfsGetMetric.WithLabelValues(parsedPath.Namespace()).Observe(time.Since(begin).Seconds()) - - defer dr.Close() - - // Check etag send back to us. - if isReedSolomonSubdirOrFile { - // Set root path prefix for paths for the entity tag. - if !i.setupEntityTag(w, r, resolvedRootPath, rootPath) { - return - } - } else { - if !i.setupEntityTag(w, r, resolvedPath, urlPath) { - return - } - - var responseEtag string - - // we need to figure out whether this is a directory before doing most of the heavy lifting below - _, ok := dr.(files.Directory) - - if ok && assets.BindataVersionHash != "" { - responseEtag = `"DirIndex-` + assets.BindataVersionHash + `_CID-` + resolvedPath.Cid().String() + `"` - } else { - responseEtag = `"` + resolvedPath.Cid().String() + `"` - } - - // Check etag sent back to us - if r.Header.Get("If-None-Match") == responseEtag || r.Header.Get("If-None-Match") == `W/`+responseEtag { - w.WriteHeader(http.StatusNotModified) - return - } - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("X-IPFS-Path", urlPath) - w.Header().Set("Etag", responseEtag) - } - - // set these headers _after_ the error, for we may just not have it - // and don't want the client to cache a 500 response... - // and only if it's /ipfs! - // TODO: break this out when we split /ipfs /ipns routes. - modtime := time.Now() - - if f, ok := dr.(files.File); ok { - if strings.HasPrefix(urlPath, ipfsPathPrefix) { - w.Header().Set("Cache-Control", "public, max-age=29030400, immutable") - - // set modtime to a really long time ago, since files are immutable and should stay cached - modtime = time.Unix(1, 0) - } - - urlFilename := r.URL.Query().Get("filename") - var name string - if urlFilename != "" { - w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename*=UTF-8''%s", url.PathEscape(urlFilename))) - name = urlFilename - } else { - name = getFilename(urlPath) - } - i.serveFile(w, r, name, modtime, f) - return - } - dir, ok := dr.(files.Directory) - if !ok { - internalWebError(w, fmt.Errorf("unsupported file type")) - return - } - - if !isReedSolomonSubdirOrFile { - idx, err := i.api.Unixfs().Get(r.Context(), ipath.Join(resolvedPath, "index.html")) - switch err.(type) { - case nil: - dirwithoutslash := urlPath[len(urlPath)-1] != '/' - goget := r.URL.Query().Get("go-get") == "1" - if dirwithoutslash && !goget { - // See comment above where originalUrlPath is declared. - http.Redirect(w, r, originalUrlPath+"/", http.StatusFound) - return - } - - f, ok := idx.(files.File) - if !ok { - internalWebError(w, files.ErrNotReader) - return - } - - // write to request - i.serveFile(w, r, "index.html", modtime, f) - return - case resolver.ErrNoLink: - // no index.html; noop - default: - internalWebError(w, err) - return - } - } - - // See statusResponseWriter.WriteHeader - // and https://github.com/ipfs/go-ipfs/issues/7164 - // Note: this needs to occur before listingTemplate.Execute otherwise we get - // superfluous response.WriteHeader call from prometheus/client_golang - if w.Header().Get("Location") != "" { - w.WriteHeader(http.StatusMovedPermanently) - return - } - - // A HTML directory index will be presented, be sure to set the correct - // type instead of relying on autodetection (which may fail). - w.Header().Set("Content-Type", "text/html") - if r.Method == http.MethodHead { - return - } - - // storage for directory listing - var dirListing []directoryItem - dirit := dir.Entries() - dirit.BreadthFirstTraversal() - for dirit.Next() { - size := "?" - if s, err := dirit.Node().Size(); err == nil { - // Size may not be defined/supported. Continue anyways. - size = humanize.Bytes(uint64(s)) - } - - hash := "" - if resolvedPath != nil { - if r, err := i.api.ResolvePath(r.Context(), ipath.Join(resolvedPath, dirit.Name())); err == nil { - // Path may not be resolved. Continue anyways. - hash = r.Cid().String() - } - } - - // See comment above where originalUrlPath is declared. - di := directoryItem{ - Size: size, - Name: dirit.Name(), - Path: gopath.Join(originalUrlPath, dirit.Name()), - Hash: hash, - ShortHash: shortHash(hash), - } - dirListing = append(dirListing, di) - } - if dirit.Err() != nil { - internalWebError(w, dirit.Err()) - return - } - - // Put the current Reed-Solomon directory entry to the cache - // if it is Reed-Solomon root directory. - if !isReedSolomonSubdirOrFile && dir.IsReedSolomon() { - dr, err := i.api.Unixfs().Get(r.Context(), resolvedPath) - if err != nil { - webError(w, "btfs cat "+escapedURLPath, err, http.StatusNotFound) - return - } - ok := false - dir, ok = dr.(files.Directory) - if !ok { - internalWebError(w, fmt.Errorf("expected directory type for %s", escapedURLPath)) - return - } - - i.rsDirs.Put(strings.SplitN(parsedPath.String(), "/", 4)[2], - ReedSolomonDirectory{rootPath: resolvedPath, rootDir: dir}) - } - - // construct the correct back link - // https://github.com/ipfs/go-ipfs/issues/1365 - var backLink string = originalUrlPath - - // don't go further up than /btfs/$hash/ - pathSplit := path.SplitList(urlPath) - switch { - // keep backlink - case len(pathSplit) == 3: // url: /btfs/$hash - - // keep backlink - case len(pathSplit) == 4 && pathSplit[3] == "": // url: /btfs/$hash/ - - // add the correct link depending on whether the path ends with a slash - default: - if strings.HasSuffix(backLink, "/") { - backLink += "./.." - } else { - backLink += "/.." - } - } - - size := "?" - if s, err := dir.Size(); err == nil { - // Size may not be defined/supported. Continue anyways. - size = humanize.Bytes(uint64(s)) - } - - hash := "" - if resolvedPath != nil { - hash = resolvedPath.Cid().String() - } - - // Storage for gateway URL to be used when linking to other rootIDs. This - // will be blank unless subdomain resolution is being used for this request. - var gwURL string - - // Get gateway hostname and build gateway URL. - if h, ok := r.Context().Value("gw-hostname").(string); ok { - gwURL = "//" + h - } else { - gwURL = "" - } - - // See comment above where originalUrlPath is declared. - tplData := listingTemplateData{ - GatewayURL: gwURL, - Listing: dirListing, - Size: size, - Path: urlPath, - Breadcrumbs: breadcrumbs(urlPath), - BackLink: backLink, - Hash: hash, - } - - err = listingTemplate.Execute(w, tplData) - if err != nil { - internalWebError(w, err) - return - } -} - -func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, file files.File) { - size, err := file.Size() - if err != nil { - http.Error(w, "cannot serve files with unknown sizes", http.StatusBadGateway) - return - } - - content := &lazySeeker{ - size: size, - reader: file, - } - - var ctype string - if _, isSymlink := file.(*files.Symlink); isSymlink { - // We should be smarter about resolving symlinks but this is the - // "most correct" we can be without doing that. - ctype = "inode/symlink" - } else { - ctype = mime.TypeByExtension(gopath.Ext(name)) - if ctype == "" { - // uses https://github.com/gabriel-vasile/mimetype library to determine the content type. - // Fixes https://github.com/ipfs/go-ipfs/issues/7252 - mimeType, err := mimetype.DetectReader(content) - if err != nil { - http.Error(w, fmt.Sprintf("cannot detect content-type: %s", err.Error()), http.StatusInternalServerError) - return - } - - ctype = mimeType.String() - _, err = content.Seek(0, io.SeekStart) - if err != nil { - http.Error(w, "seeker can't seek", http.StatusInternalServerError) - return - } - } - // Strip the encoding from the HTML Content-Type header and let the - // browser figure it out. - // - // Fixes https://github.com/ipfs/go-ipfs/issues/2203 - if strings.HasPrefix(ctype, "text/html;") { - ctype = "text/html" - } - } - w.Header().Set("Content-Type", ctype) - - w = &statusResponseWriter{w} - http.ServeContent(w, req, name, modtime, content) -} - -func (i *gatewayHandler) servePretty404IfPresent(w http.ResponseWriter, r *http.Request, parsedPath ipath.Path) bool { - resolved404Path, ctype, err := i.searchUpTreeFor404(r, parsedPath) - if err != nil { - return false - } - - dr, err := i.api.Unixfs().Get(r.Context(), resolved404Path) - if err != nil { - return false - } - defer dr.Close() - - f, ok := dr.(files.File) - if !ok { - return false - } - - size, err := f.Size() - if err != nil { - return false - } - - log.Debugf("using pretty 404 file for %s", parsedPath.String()) - w.Header().Set("Content-Type", ctype) - w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) - w.WriteHeader(http.StatusNotFound) - _, err = io.CopyN(w, f, size) - return err == nil -} - -func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { - p, err := i.api.Unixfs().Add(r.Context(), files.NewReaderFile(r.Body)) - if err != nil { - internalWebError(w, err) - return - } - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", p.Cid().String()) - http.Redirect(w, r, p.String(), http.StatusCreated) -} - -func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ds := i.api.Dag() - - // Parse the path - rootCid, newPath, err := parseIpfsPath(r.URL.Path) - if err != nil { - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest) - return - } - if newPath == "" || newPath == "/" { - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest) - return - } - newDirectory, newFileName := gopath.Split(newPath) - - // Resolve the old root. - - rnode, err := ds.Get(ctx, rootCid) - if err != nil { - webError(w, "WritableGateway: Could not create DAG from request", err, http.StatusInternalServerError) - return - } - - pbnd, ok := rnode.(*dag.ProtoNode) - if !ok { - webError(w, "Cannot read non protobuf nodes through gateway", dag.ErrNotProtobuf, http.StatusBadRequest) - return - } - - // Create the new file. - newFilePath, err := i.api.Unixfs().Add(ctx, files.NewReaderFile(r.Body)) - if err != nil { - webError(w, "WritableGateway: could not create DAG from request", err, http.StatusInternalServerError) - return - } - - newFile, err := ds.Get(ctx, newFilePath.Cid()) - if err != nil { - webError(w, "WritableGateway: failed to resolve new file", err, http.StatusInternalServerError) - return - } - - // Patch the new file into the old root. - - root, err := mfs.NewRoot(ctx, ds, pbnd, nil) - if err != nil { - webError(w, "WritableGateway: failed to create MFS root", err, http.StatusBadRequest) - return - } - - if newDirectory != "" { - err := mfs.Mkdir(root, newDirectory, mfs.MkdirOpts{Mkparents: true, Flush: false}) - if err != nil { - webError(w, "WritableGateway: failed to create MFS directory", err, http.StatusInternalServerError) - return - } - } - dirNode, err := mfs.Lookup(root, newDirectory) - if err != nil { - webError(w, "WritableGateway: failed to lookup directory", err, http.StatusInternalServerError) - return - } - dir, ok := dirNode.(*mfs.Directory) - if !ok { - http.Error(w, "WritableGateway: target directory is not a directory", http.StatusBadRequest) - return - } - err = dir.Unlink(newFileName) - switch err { - case os.ErrNotExist, nil: - default: - webError(w, "WritableGateway: failed to replace existing file", err, http.StatusBadRequest) - return - } - err = dir.AddChild(newFileName, newFile) - if err != nil { - webError(w, "WritableGateway: failed to link file into directory", err, http.StatusInternalServerError) - return - } - nnode, err := root.GetDirectory().GetNode() - if err != nil { - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError) - return - } - newcid := nnode.Cid() - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", newcid.String()) - http.Redirect(w, r, gopath.Join(ipfsPathPrefix, newcid.String(), newPath), http.StatusCreated) -} - -func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - // parse the path - - rootCid, newPath, err := parseIpfsPath(r.URL.Path) - if err != nil { - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest) - return - } - if newPath == "" || newPath == "/" { - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest) - return - } - directory, filename := gopath.Split(newPath) - - // lookup the root - - rootNodeIPLD, err := i.api.Dag().Get(ctx, rootCid) - if err != nil { - webError(w, "WritableGateway: failed to resolve root CID", err, http.StatusInternalServerError) - return - } - rootNode, ok := rootNodeIPLD.(*dag.ProtoNode) - if !ok { - http.Error(w, "WritableGateway: empty path", http.StatusInternalServerError) - return - } - - // construct the mfs root - - root, err := mfs.NewRoot(ctx, i.api.Dag(), rootNode, nil) - if err != nil { - webError(w, "WritableGateway: failed to construct the MFS root", err, http.StatusBadRequest) - return - } - - // lookup the parent directory - - parentNode, err := mfs.Lookup(root, directory) - if err != nil { - webError(w, "WritableGateway: failed to look up parent", err, http.StatusInternalServerError) - return - } - - parent, ok := parentNode.(*mfs.Directory) - if !ok { - http.Error(w, "WritableGateway: parent is not a directory", http.StatusInternalServerError) - return - } - - // delete the file - - switch parent.Unlink(filename) { - case nil, os.ErrNotExist: - default: - webError(w, "WritableGateway: failed to remove file", err, http.StatusInternalServerError) - return - } - - nnode, err := root.GetDirectory().GetNode() - if err != nil { - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError) - } - ncid := nnode.Cid() - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", ncid.String()) - // note: StatusCreated is technically correct here as we created a new resource. - http.Redirect(w, r, gopath.Join(ipfsPathPrefix+ncid.String(), directory), http.StatusCreated) -} - -func (i *gatewayHandler) addUserHeaders(w http.ResponseWriter) { - for k, v := range i.config.Headers { - w.Header()[k] = v - } -} - -func (i *gatewayHandler) cacheEntryFor(p string) (*ReedSolomonDirectory, bool, error) { - v := i.rsDirs.Get(p) - if v[0] == nil { - return nil, false, nil - } - d, ok := v[0].(ReedSolomonDirectory) - if !ok { - return nil, false, fmt.Errorf("expected ReedSolomonDirectory cache item type") - } - return &d, true, nil -} - -// resolveAndGetFileNode combines resolution nd getting node on the gateway handler -func (i *gatewayHandler) resolveAndGetFilesNode(ctx context.Context, - w http.ResponseWriter, r *http.Request, pp ipath.Path, escPath string) (ipath.Resolved, files.Node, error) { - resolvedPath, err := i.api.ResolvePath(ctx, pp) - switch err { - case nil: - case coreiface.ErrOffline: - webError(w, "btfs resolve -r "+escPath, err, http.StatusServiceUnavailable) - return nil, nil, err - default: - webError(w, "btfs resolve -r "+escPath, err, http.StatusNotFound) - return nil, nil, err - } - - dr, err := i.api.Unixfs().Get(ctx, resolvedPath) - if err != nil { - webError(w, "btfs cat "+escPath, err, http.StatusNotFound) - return nil, nil, err - } - - return resolvedPath, dr, nil -} - -// isTopLevelEntryPath checks to see if the node being resolved is a root path/directory -func (i *gatewayHandler) isTopLevelEntryPath(w http.ResponseWriter, r *http.Request, - urlPath, escPath string) (bool, error) { - urlPath = gopath.Clean(urlPath) - parts := strings.Split(urlPath, "/") - - var ( - ipnspath *path.Path - err error - ) - switch parts[1] { - case "btfs": - return len(parts) == 3 && parts[2] != "", nil - case "btns": - ipnspath, err = i.api.ResolveIpnsPath(r.Context(), ipath.New(urlPath)) - if err == coreiface.ErrOffline { - webError(w, "btfs resolve -r "+escPath, coreiface.ErrOffline, http.StatusServiceUnavailable) - return false, coreiface.ErrOffline - } - if err != nil { - break - } - return len(ipnspath.Segments()) == 2 && ipnspath.Segments()[1] != "", nil - default: - err = fmt.Errorf("unsupported path namespace: [%s]", parts[1]) - } - webError(w, "btfs resolve -r "+escPath, err, http.StatusNotFound) - return false, err -} - -// setupEntityTag sets the ETag according to resolved path and url path -func (i *gatewayHandler) setupEntityTag(w http.ResponseWriter, r *http.Request, - resolvedPath ipath.Resolved, urlPath string) bool { - // Check etag send back to us - etag := "\"" + resolvedPath.Cid().String() + "\"" - if r.Header.Get("If-None-Match") == etag || r.Header.Get("If-None-Match") == "W/"+etag { - w.WriteHeader(http.StatusNotModified) - return false - } - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("X-BTFS-Path", urlPath) - w.Header().Set("Etag", etag) - - return true -} - -func webError(w http.ResponseWriter, message string, err error, defaultCode int) { - if _, ok := err.(resolver.ErrNoLink); ok { - webErrorWithCode(w, message, err, http.StatusNotFound) - } else if err == routing.ErrNotFound { - webErrorWithCode(w, message, err, http.StatusNotFound) - } else if err == context.DeadlineExceeded { - webErrorWithCode(w, message, err, http.StatusRequestTimeout) - } else { - webErrorWithCode(w, message, err, defaultCode) - } -} - -func webErrorWithCode(w http.ResponseWriter, message string, err error, code int) { - http.Error(w, fmt.Sprintf("%s: %s", message, err), code) - if code >= 500 { - log.Warnf("server error: %s: %s", err) - } -} - -// return a 500 error and log -func internalWebError(w http.ResponseWriter, err error) { - webErrorWithCode(w, "internalWebError", err, http.StatusInternalServerError) -} - -func getFilename(s string) string { - if (strings.HasPrefix(s, ipfsPathPrefix) || strings.HasPrefix(s, ipnsPathPrefix)) && strings.Count(gopath.Clean(s), "/") <= 2 { - // Don't want to treat ipfs.io in /ipns/ipfs.io as a filename. - return "" - } - return gopath.Base(s) -} - -func getDirRootPath(path string) (string, error) { - path = gopath.Clean(path) - idx := 1 - for j := 0; j < 2; j++ { - idx += strings.Index(path[idx:], "/") - if idx < 0 { - return "", fmt.Errorf("root path can not be found from the given string [%s]", path) - } - idx++ - } - return path[:idx-1], nil -} - -func (i *gatewayHandler) searchUpTreeFor404(r *http.Request, parsedPath ipath.Path) (ipath.Resolved, string, error) { - filename404, ctype, err := preferred404Filename(r.Header.Values("Accept")) - if err != nil { - return nil, "", err - } - - pathComponents := strings.Split(parsedPath.String(), "/") - - for idx := len(pathComponents); idx >= 3; idx-- { - pretty404 := gopath.Join(append(pathComponents[0:idx], filename404)...) - parsed404Path := ipath.New("/" + pretty404) - if parsed404Path.IsValid() != nil { - break - } - resolvedPath, err := i.api.ResolvePath(r.Context(), parsed404Path) - if err != nil { - continue - } - return resolvedPath, ctype, nil - } - - return nil, "", fmt.Errorf("no pretty 404 in any parent folder") -} - -func preferred404Filename(acceptHeaders []string) (string, string, error) { - // If we ever want to offer a 404 file for a different content type - // then this function will need to parse q weightings, but for now - // the presence of anything matching HTML is enough. - for _, acceptHeader := range acceptHeaders { - accepted := strings.Split(acceptHeader, ",") - for _, spec := range accepted { - contentType := strings.SplitN(spec, ";", 1)[0] - switch contentType { - case "*/*", "text/*", "text/html": - return "ipfs-404.html", "text/html", nil - } - } - } - - return "", "", fmt.Errorf("there is no 404 file for the requested content types") -} diff --git a/core/corehttp/gateway_indexPage.go b/core/corehttp/gateway_indexPage.go deleted file mode 100644 index ac9ef7037..000000000 --- a/core/corehttp/gateway_indexPage.go +++ /dev/null @@ -1,108 +0,0 @@ -package corehttp - -import ( - "html/template" - "net/url" - "path" - "strings" - - "github.com/bittorrent/go-btfs/assets" - - ipfspath "github.com/ipfs/go-path" -) - -// structs for directory listing -type listingTemplateData struct { - GatewayURL string - Listing []directoryItem - Size string - Path string - Breadcrumbs []breadcrumb - BackLink string - Hash string -} - -type directoryItem struct { - Size string - Name string - Path string - Hash string - ShortHash string -} - -type breadcrumb struct { - Name string - Path string -} - -func breadcrumbs(urlPath string) []breadcrumb { - var ret []breadcrumb - - p, err := ipfspath.ParsePath(urlPath) - if err != nil { - // No breadcrumbs, fallback to bare Path in template - return ret - } - - segs := p.Segments() - for i, seg := range segs { - if i == 0 { - ret = append(ret, breadcrumb{Name: seg}) - } else { - ret = append(ret, breadcrumb{ - Name: seg, - Path: "/" + strings.Join(segs[0:i+1], "/"), - }) - } - } - - return ret -} - -func shortHash(hash string) string { - if len(hash) < 4 { - return hash - } - return (hash[0:4] + "\u2026" + hash[len(hash)-4:]) -} - -var listingTemplate *template.Template - -func init() { - knownIconsBytes, err := assets.Asset("dir-index-html/knownIcons.txt") - if err != nil { - panic(err) - } - knownIcons := make(map[string]struct{}) - for _, ext := range strings.Split(strings.TrimSuffix(string(knownIconsBytes), "\n"), "\n") { - knownIcons[ext] = struct{}{} - } - - // helper to guess the type/icon for it by the extension name - iconFromExt := func(name string) string { - ext := path.Ext(name) - _, ok := knownIcons[ext] - if !ok { - // default blank icon - return "ipfs-_blank" - } - return "ipfs-" + ext[1:] // slice of the first dot - } - - // custom template-escaping function to escape a full path, including '#' and '?' - urlEscape := func(rawUrl string) string { - pathUrl := url.URL{Path: rawUrl} - return pathUrl.String() - } - - // Directory listing template - dirIndexBytes, err := assets.Asset("dir-index-html/dir-index.html") - if err != nil { - panic(err) - } - - listingTemplate = template.Must(template.New("dir").Funcs(template.FuncMap{ - "iconFromExt": iconFromExt, - "urlEscape": urlEscape, - }).Parse(string(dirIndexBytes))) -} diff --git a/core/corehttp/gateway_reedsolomon.go b/core/corehttp/gateway_reedsolomon.go deleted file mode 100644 index b4256ae6c..000000000 --- a/core/corehttp/gateway_reedsolomon.go +++ /dev/null @@ -1,57 +0,0 @@ -package corehttp - -import ( - "fmt" - "strings" - - files "github.com/TRON-US/go-btfs-files" - gopath "path" -) - -func findNode(root files.Node, path string) (files.Node, error) { - // Error/Edge cases - path = gopath.Clean(path) - if path == "" { - return nil, fmt.Errorf("The given path string is empty") - } - - // Define vars - rootDir, ok := root.(files.Directory) - if !ok { - return nil, fmt.Errorf("The given root is not a directory") - } - parts := strings.Split(path, "/") - i := 3 - - // Find the node with part[i] as name and, if not the last, go down. - it := rootDir.Entries() - it.BreadthFirstTraversal() - return visit(it, parts, i, path) -} - -func findHelper(node files.Node, parts []string, i int, path string) (files.Node, error) { - // Define vars - dir, ok := node.(files.Directory) - if !ok { - return nil, fmt.Errorf("The given node is not a directory") - } - - // Find the current part's node and go down. - it := dir.Entries() - it.BreadthFirstTraversal() - return visit(it, parts, i, path) -} - -func visit(it files.DirIterator, parts []string, i int, path string) (files.Node, error) { - // Find the current part's node and go down. - for it.Next() { - if it.Name() == parts[i] { - if len(parts) == i+1 { - return it.Node(), nil - } - i++ - return findHelper(it.Node(), parts, i, path) - } - } - return nil, fmt.Errorf("%s not found from the directory object: %s", path, it.Err()) -} diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index fa53fc4fd..c80228d03 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -3,10 +3,9 @@ package corehttp import ( "context" "errors" - "io/ioutil" + "io" "net/http" "net/http/httptest" - "regexp" "strings" "testing" "time" @@ -17,11 +16,9 @@ import ( namesys "github.com/bittorrent/go-btfs/namesys" repo "github.com/bittorrent/go-btfs/repo" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" - iface "github.com/TRON-US/interface-go-btfs-core" - nsopts "github.com/TRON-US/interface-go-btfs-core/options/namesys" - ipath "github.com/TRON-US/interface-go-btfs-core/path" + config "github.com/bittorrent/go-btfs-config" + iface "github.com/bittorrent/interface-go-btfs-core" + nsopts "github.com/bittorrent/interface-go-btfs-core/options/namesys" datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" path "github.com/ipfs/go-path" @@ -29,9 +26,6 @@ import ( id "github.com/libp2p/go-libp2p/p2p/protocol/identify" ) -// `btfs object new unixfs-dir` -var emptyDir = "/btfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" - type mockNamesys map[string]path.Path func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...nsopts.ResolveOpt) (value path.Path, err error) { @@ -72,7 +66,7 @@ func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Pa return errors.New("not implemented for mockNamesys") } -func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error { +func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { return errors.New("not implemented for mockNamesys") } @@ -126,12 +120,6 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface t.Fatal(err) } - cfg, err := n.Repo.Config() - if err != nil { - t.Fatal(err) - } - cfg.Gateway.PathPrefixes = []string{"/good-prefix"} - // need this variable here since we need to construct handler with // listener, and server with handler. yay cycles. dh := &delegatedHandler{} @@ -141,7 +129,7 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface dh.Handler, err = makeHandler(n, ts.Listener, HostnameOption(), - GatewayOption(false, "/btfs", "/btns"), + GatewayOption("/btfs", "/btns"), VersionOption(), ) if err != nil { @@ -156,499 +144,6 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface return ts, api, n.Context() } -func matchPathOrBreadcrumbs(s string, expected string) bool { - matched, _ := regexp.MatchString("Index of "+regexp.QuoteMeta(expected), s) - return matched -} - -func TestGatewayGet(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - - k, err := api.Unixfs().Add(ctx, files.NewBytesFile([]byte("fnord"))) - if err != nil { - t.Fatal(err) - } - ns["/btns/example.com"] = path.FromString(k.String()) - ns["/btns/working.example.com"] = path.FromString(k.String()) - ns["/btns/double.example.com"] = path.FromString("/btns/working.example.com") - ns["/btns/triple.example.com"] = path.FromString("/btns/double.example.com") - ns["/btns/broken.example.com"] = path.FromString("/btns/" + k.Cid().String()) - // We picked .man because: - // 1. It's a valid TLD. - // 2. Go treats it as the file extension for "man" files (even though - // nobody actually *uses* this extension, AFAIK). - // - // Unfortunately, this may not work on all platforms as file type - // detection is platform dependent. - ns["/btns/example.man"] = path.FromString(k.String()) - - t.Log(ts.URL) - for i, test := range []struct { - host string - path string - status int - text string - }{ - {"127.0.0.1:8080", "/", http.StatusNotFound, "404 page not found\n"}, - {"127.0.0.1:8080", "/" + k.Cid().String(), http.StatusNotFound, "404 page not found\n"}, - {"127.0.0.1:8080", k.String(), http.StatusOK, "fnord"}, - {"127.0.0.1:8080", "/btns/nxdomain.example.com", http.StatusNotFound, "btfs resolve -r /btns/nxdomain.example.com: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"127.0.0.1:8080", "/btns/%0D%0A%0D%0Ahello", http.StatusNotFound, "btfs resolve -r /btns/%0D%0A%0D%0Ahello: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"127.0.0.1:8080", "/btns/example.com", http.StatusOK, "fnord"}, - {"example.com", "/", http.StatusOK, "fnord"}, - - {"working.example.com", "/", http.StatusOK, "fnord"}, - {"double.example.com", "/", http.StatusOK, "fnord"}, - {"triple.example.com", "/", http.StatusOK, "fnord"}, - {"working.example.com", k.String(), http.StatusNotFound, "btfs resolve -r /btns/working.example.com" + k.String() + ": no link named \"btfs\" under " + k.Cid().String() + "\n"}, - {"broken.example.com", "/", http.StatusNotFound, "btfs resolve -r /btns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"broken.example.com", k.String(), http.StatusNotFound, "btfs resolve -r /btns/broken.example.com" + k.String() + ": " + namesys.ErrResolveFailed.Error() + "\n"}, - // This test case ensures we don't treat the TLD as a file extension. - {"example.man", "/", http.StatusOK, "fnord"}, - } { - var c http.Client - r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) - if err != nil { - t.Fatal(err) - } - r.Host = test.host - resp, err := c.Do(r) - - urlstr := "http://" + test.host + test.path - if err != nil { - t.Errorf("error requesting %s: %s", urlstr, err) - continue - } - defer resp.Body.Close() - contentType := resp.Header.Get("Content-Type") - if contentType != "text/plain; charset=utf-8" { - t.Errorf("expected content type to be text/plain, got %s", contentType) - } - body, err := ioutil.ReadAll(resp.Body) - if resp.StatusCode != test.status { - t.Errorf("(%d) got %d, expected %d from %s", i, resp.StatusCode, test.status, urlstr) - t.Errorf("Body: %s", body) - continue - } - if err != nil { - t.Fatalf("error reading response from %s: %s", urlstr, err) - } - if string(body) != test.text { - t.Errorf("unexpected response body from %s: expected %q; got %q", urlstr, test.text, body) - continue - } - } -} - -func TestPretty404(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - - f1 := files.NewMapDirectory(map[string]files.Node{ - "ipfs-404.html": files.NewBytesFile([]byte("Custom 404")), - "deeper": files.NewMapDirectory(map[string]files.Node{ - "ipfs-404.html": files.NewBytesFile([]byte("Deep custom 404")), - }), - }) - - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - host := "example.net" - ns["/btns/"+host] = path.FromString(k.String()) - - for _, test := range []struct { - path string - accept string - status int - text string - }{ - {"/ipfs-404.html", "text/html", http.StatusOK, "Custom 404"}, - {"/nope", "text/html", http.StatusNotFound, "Custom 404"}, - {"/nope", "text/*", http.StatusNotFound, "Custom 404"}, - {"/nope", "*/*", http.StatusNotFound, "Custom 404"}, - {"/nope", "application/json", http.StatusNotFound, "btfs resolve -r /btns/example.net/nope: no link named \"nope\" under QmcmnF7XG5G34RdqYErYDwCKNFQ6jb8oKVR21WAJgubiaj\n"}, - {"/deeper/nope", "text/html", http.StatusNotFound, "Deep custom 404"}, - {"/deeper/", "text/html", http.StatusOK, ""}, - {"/deeper", "text/html", http.StatusOK, ""}, - {"/nope/nope", "text/html", http.StatusNotFound, "Custom 404"}, - } { - var c http.Client - req, err := http.NewRequest("GET", ts.URL+test.path, nil) - if err != nil { - t.Fatal(err) - } - req.Header.Add("Accept", test.accept) - req.Host = host - resp, err := c.Do(req) - - if err != nil { - t.Fatalf("error requesting %s: %s", test.path, err) - } - - defer resp.Body.Close() - if resp.StatusCode != test.status { - t.Fatalf("got %d, expected %d, from %s", resp.StatusCode, test.status, test.path) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("error reading response from %s: %s", test.path, err) - } - - if test.text != "" && string(body) != test.text { - t.Fatalf("unexpected response body from %s: got %q, expected %q", test.path, body, test.text) - } - } -} - -func TestIPNSHostnameRedirect(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - t.Logf("test server url: %s", ts.URL) - - // create /btns/example.net/foo/index.html - - f1 := files.NewMapDirectory(map[string]files.Node{ - "_": files.NewBytesFile([]byte("_")), - "foo": files.NewMapDirectory(map[string]files.Node{ - "index.html": files.NewBytesFile([]byte("_")), - }), - }) - - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - t.Logf("k: %s\n", k) - ns["/btns/example.net"] = path.FromString(k.String()) - - // make request to directory containing index.html - req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect 302 redirect to same path, but with trailing slash - if res.StatusCode != 302 { - t.Errorf("status is %d, expected 302", res.StatusCode) - } - hdr := res.Header["Location"] - if len(hdr) < 1 { - t.Errorf("location header not present") - } else if hdr[0] != "/foo/" { - t.Errorf("location header is %v, expected /foo/", hdr[0]) - } - - // make request with prefix to directory containing index.html - req, err = http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - req.Header.Set("X-Btfs-Gateway-Prefix", "/good-prefix") - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect 302 redirect to same path, but with prefix and trailing slash - if res.StatusCode != 302 { - t.Errorf("status is %d, expected 302", res.StatusCode) - } - hdr = res.Header["Location"] - if len(hdr) < 1 { - t.Errorf("location header not present") - } else if hdr[0] != "/good-prefix/foo/" { - t.Errorf("location header is %v, expected /good-prefix/foo/", hdr[0]) - } - - // make sure /version isn't exposed - req, err = http.NewRequest(http.MethodGet, ts.URL+"/version", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - req.Header.Set("X-Btfs-Gateway-Prefix", "/good-prefix") - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - if res.StatusCode != 404 { - t.Fatalf("expected a 404 error, got: %s", res.Status) - } -} - -func TestIPNSHostnameBacklinks(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - t.Logf("test server url: %s", ts.URL) - - f1 := files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("1")), - "foo? #<'": files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("2")), - "bar": files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("3")), - }), - }), - }) - - // create /btns/example.net/foo/ - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - k2, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'")) - if err != nil { - t.Fatal(err) - } - - k3, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'/bar")) - if err != nil { - t.Fatal(err) - } - - t.Logf("k: %s\n", k) - ns["/btns/example.net"] = path.FromString(k.String()) - - // make request to directory listing - req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo%3F%20%23%3C%27/", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct backlinks - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s := string(body) - t.Logf("body: %s\n", string(body)) - - //FIXME - //if !matchPathOrBreadcrumbs(s, "/btns/example.net/foo? #<'/bar/") { - // t.Fatalf("expected a path in directory listing") - //} - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k2.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } - - // make request to directory listing at root - req, err = http.NewRequest(http.MethodGet, ts.URL, nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct backlinks at root - body, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s = string(body) - t.Logf("body: %s\n", string(body)) - - //TODO - //if !matchPathOrBreadcrumbs(s, "/") { - // t.Fatalf("expected a path in directory listing") - //} - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } - - // make request to directory listing - req, err = http.NewRequest(http.MethodGet, ts.URL+"/foo%3F%20%23%3C%27/bar/", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct backlinks - body, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s = string(body) - t.Logf("body: %s\n", string(body)) - - //if !matchPathOrBreadcrumbs(s, "/btns/example.net/foo? #<'/bar") { - // t.Fatalf("expected a path in directory listing") - //} - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k3.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } - - // make request to directory listing with prefix - req, err = http.NewRequest(http.MethodGet, ts.URL, nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - req.Header.Set("X-Btfs-Gateway-Prefix", "/good-prefix") - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct backlinks with prefix - body, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s = string(body) - t.Logf("body: %s\n", string(body)) - - //if !matchPathOrBreadcrumbs(s, "/btns/example.net") { - // t.Fatalf("expected a path in directory listing") - //} - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } - - // make request to directory listing with illegal prefix - req, err = http.NewRequest(http.MethodGet, ts.URL, nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - req.Header.Set("X-Btfs-Gateway-Prefix", "/bad-prefix") - - // make request to directory listing with evil prefix - req, err = http.NewRequest(http.MethodGet, ts.URL, nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - req.Header.Set("X-Btfs-Gateway-Prefix", "//good-prefix/foo") - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct backlinks without illegal prefix - body, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s = string(body) - t.Logf("body: %s\n", string(body)) - - //TODO - //if !matchPathOrBreadcrumbs(s, "/") { - // t.Fatalf("expected a path in directory listing") - //} - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } -} - -func TestCacheControlImmutable(t *testing.T) { - ts, _, _ := newTestServerAndNode(t, nil) - t.Logf("test server url: %s", ts.URL) - - req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"/", nil) - if err != nil { - t.Fatal(err) - } - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // check the immutable tag isn't set - hdrs, ok := res.Header["Cache-Control"] - if ok { - for _, hdr := range hdrs { - if strings.Contains(hdr, "immutable") { - t.Fatalf("unexpected Cache-Control: immutable on directory listing: %s", hdr) - } - } - } -} - -func TestGoGetSupport(t *testing.T) { - ts, _, _ := newTestServerAndNode(t, nil) - t.Logf("test server url: %s", ts.URL) - - // mimic go-get - req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"?go-get=1", nil) - if err != nil { - t.Fatal(err) - } - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - if res.StatusCode != 200 { - t.Errorf("status is %d, expected 200", res.StatusCode) - } -} - func TestVersion(t *testing.T) { version.CurrentCommit = "theshortcommithash" @@ -665,7 +160,7 @@ func TestVersion(t *testing.T) { if err != nil { t.Fatal(err) } - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) if err != nil { t.Fatalf("error reading response: %s", err) } diff --git a/core/corehttp/hostname.go b/core/corehttp/hostname.go deleted file mode 100644 index 39873f01f..000000000 --- a/core/corehttp/hostname.go +++ /dev/null @@ -1,503 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "net" - "net/http" - "net/url" - "regexp" - "strings" - - core "github.com/bittorrent/go-btfs/core" - coreapi "github.com/bittorrent/go-btfs/core/coreapi" - namesys "github.com/bittorrent/go-btfs/namesys" - - config "github.com/TRON-US/go-btfs-config" - iface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - nsopts "github.com/TRON-US/interface-go-btfs-core/options/namesys" - cid "github.com/ipfs/go-cid" - isd "github.com/jbenet/go-is-domain" - "github.com/libp2p/go-libp2p/core/peer" - mbase "github.com/multiformats/go-multibase" -) - -var defaultPaths = []string{"/btfs/", "/btns/", "/api/", "/p2p/", "/version"} - -var pathGatewaySpec = config.GatewaySpec{ - Paths: defaultPaths, - UseSubdomains: false, -} - -var subdomainGatewaySpec = config.GatewaySpec{ - Paths: defaultPaths, - UseSubdomains: true, -} - -var defaultKnownGateways = map[string]*config.GatewaySpec{ - "localhost": &subdomainGatewaySpec, -} - -// Label's max length in DNS (https://tools.ietf.org/html/rfc1034#page-7) -const dnsLabelMaxLength int = 63 - -// HostnameOption rewrites an incoming request based on the Host header. -func HostnameOption() ServeOption { - return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - childMux := http.NewServeMux() - - coreAPI, err := coreapi.NewCoreAPI(n) - if err != nil { - return nil, err - } - - cfg, err := n.Repo.Config() - if err != nil { - return nil, err - } - - knownGateways := prepareKnownGateways(cfg.Gateway.PublicGateways) - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - // Unfortunately, many (well, ipfs.io) gateways use - // DNSLink so if we blindly rewrite with DNSLink, we'll - // break /ipfs links. - // - // We fix this by maintaining a list of known gateways - // and the paths that they serve "gateway" content on. - // That way, we can use DNSLink for everything else. - - // Support X-Forwarded-Host if added by a reverse proxy - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host - host := r.Host - if xHost := r.Header.Get("X-Forwarded-Host"); xHost != "" { - host = xHost - } - - // HTTP Host & Path check: is this one of our "known gateways"? - if gw, ok := isKnownHostname(host, knownGateways); ok { - // This is a known gateway but request is not using - // the subdomain feature. - - // Does this gateway _handle_ this path? - if hasPrefix(r.URL.Path, gw.Paths...) { - // It does. - - // Should this gateway use subdomains instead of paths? - if gw.UseSubdomains { - // Yes, redirect if applicable - // Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link - newURL, err := toSubdomainURL(host, r.URL.Path, r) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Just to be sure single Origin can't be abused in - // web browsers that ignored the redirect for some - // reason, Clear-Site-Data header clears browsing - // data (cookies, storage etc) associated with - // hostname's root Origin - // Note: we can't use "*" due to bug in Chromium: - // https://bugs.chromium.org/p/chromium/issues/detail?id=898503 - w.Header().Set("Clear-Site-Data", "\"cookies\", \"storage\"") - - // Set "Location" header with redirect destination. - // It is ignored by curl in default mode, but will - // be respected by user agents that follow - // redirects by default, namely web browsers - w.Header().Set("Location", newURL) - - // Note: we continue regular gateway processing: - // HTTP Status Code http.StatusMovedPermanently - // will be set later, in statusResponseWriter - } - } - - // Not a subdomain resource, continue with path processing - // Example: 127.0.0.1:8080/ipfs/{CID}, ipfs.io/ipfs/{CID} etc - childMux.ServeHTTP(w, r) - return - } - // Not a whitelisted path - - // Try DNSLink, if it was not explicitly disabled for the hostname - if !gw.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) { - // rewrite path and handle as DNSLink - r.URL.Path = "/btns/" + stripPort(host) + r.URL.Path - childMux.ServeHTTP(w, r) - return - } - - // If not, resource does not exist on the hostname, return 404 - http.NotFound(w, r) - return - } - - // HTTP Host check: is this one of our subdomain-based "known gateways"? - // Example: {cid}.ipfs.localhost, {cid}.ipfs.dweb.link - if gw, hostname, ns, rootID, ok := knownSubdomainDetails(host, knownGateways); ok { - // Looks like we're using a known gateway in subdomain mode. - - // Add gateway hostname context for linking to other root ids. - // Example: localhost/ipfs/{cid} - ctx := context.WithValue(r.Context(), "gw-hostname", hostname) - - // Assemble original path prefix. - pathPrefix := "/" + ns + "/" + rootID - - // Does this gateway _handle_ subdomains AND this path? - if !(gw.UseSubdomains && hasPrefix(pathPrefix, gw.Paths...)) { - // If not, resource does not exist, return 404 - http.NotFound(w, r) - return - } - - // Check if rootID is a valid CID - if rootCID, err := cid.Decode(rootID); err == nil { - // Do we need to redirect root CID to a canonical DNS representation? - dnsCID, err := toDNSPrefix(rootID, rootCID) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if !strings.HasPrefix(r.Host, dnsCID) { - dnsPrefix := "/" + ns + "/" + dnsCID - newURL, err := toSubdomainURL(hostname, dnsPrefix+r.URL.Path, r) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Redirect to deterministic CID to ensure CID - // always gets the same Origin on the web - http.Redirect(w, r, newURL, http.StatusMovedPermanently) - return - } - } - - // Do we need to fix multicodec in PeerID represented as CIDv1? - if isPeerIDNamespace(ns) { - if rootCID.Type() != cid.Libp2pKey { - newURL, err := toSubdomainURL(hostname, pathPrefix+r.URL.Path, r) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Redirect to CID fixed inside of toSubdomainURL() - http.Redirect(w, r, newURL, http.StatusMovedPermanently) - return - } - } - } - } - - // Rewrite the path to not use subdomains - r.URL.Path = pathPrefix + r.URL.Path - - // Serve path request - childMux.ServeHTTP(w, r.WithContext(ctx)) - return - } - // We don't have a known gateway. Fallback on DNSLink lookup - - // Wildcard HTTP Host check: - // 1. is wildcard DNSLink enabled (Gateway.NoDNSLink=false)? - // 2. does Host header include a fully qualified domain name (FQDN)? - // 3. does DNSLink record exist in DNS? - if !cfg.Gateway.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) { - // rewrite path and handle as DNSLink - r.URL.Path = "/btns/" + stripPort(host) + r.URL.Path - childMux.ServeHTTP(w, r) - return - } - - // else, treat it as an old school gateway, I guess. - childMux.ServeHTTP(w, r) - }) - return childMux, nil - } -} - -type gatewayHosts struct { - exact map[string]*config.GatewaySpec - wildcard []wildcardHost -} - -type wildcardHost struct { - re *regexp.Regexp - spec *config.GatewaySpec -} - -func prepareKnownGateways(publicGateways map[string]*config.GatewaySpec) gatewayHosts { - var hosts gatewayHosts - - hosts.exact = make(map[string]*config.GatewaySpec, len(publicGateways)+len(defaultKnownGateways)) - - // First, implicit defaults such as subdomain gateway on localhost - for hostname, gw := range defaultKnownGateways { - hosts.exact[hostname] = gw - } - - // Then apply values from Gateway.PublicGateways, if present in the config - for hostname, gw := range publicGateways { - if gw == nil { - // Remove any implicit defaults, if present. This is useful when one - // wants to disable subdomain gateway on localhost etc. - delete(hosts.exact, hostname) - continue - } - if strings.Contains(hostname, "*") { - // from *.domain.tld, construct a regexp that match any direct subdomain - // of .domain.tld. - // - // Regexp will be in the form of ^[^.]+\.domain.tld(?::\d+)?$ - - escaped := strings.ReplaceAll(hostname, ".", `\.`) - regexed := strings.ReplaceAll(escaped, "*", "[^.]+") - - re, err := regexp.Compile(fmt.Sprintf(`^%s(?::\d+)?$`, regexed)) - if err != nil { - log.Warn("invalid wildcard gateway hostname \"%s\"", hostname) - } - - hosts.wildcard = append(hosts.wildcard, wildcardHost{re: re, spec: gw}) - } else { - hosts.exact[hostname] = gw - } - } - - return hosts -} - -// isKnownHostname checks Gateway.PublicGateways and returns matching -// GatewaySpec with gracefull fallback to version without port -func isKnownHostname(hostname string, knownGateways gatewayHosts) (gw *config.GatewaySpec, ok bool) { - // Try hostname (host+optional port - value from Host header as-is) - if gw, ok := knownGateways.exact[hostname]; ok { - return gw, ok - } - // Also test without port - if gw, ok = knownGateways.exact[stripPort(hostname)]; ok { - return gw, ok - } - - // Wildcard support. Test both with and without port. - for _, host := range knownGateways.wildcard { - if host.re.MatchString(hostname) { - return host.spec, true - } - } - - return nil, false -} - -// Parses Host header and looks for a known gateway matching subdomain host. -// If found, returns GatewaySpec and subdomain components. -// Note: hostname is host + optional port -func knownSubdomainDetails(hostname string, knownGateways gatewayHosts) (gw *config.GatewaySpec, knownHostname, ns, rootID string, ok bool) { - labels := strings.Split(hostname, ".") - // Look for FQDN of a known gateway hostname. - // Example: given "dist.ipfs.io.ipns.dweb.link": - // 1. Lookup "link" TLD in knownGateways: negative - // 2. Lookup "dweb.link" in knownGateways: positive - // - // Stops when we have 2 or fewer labels left as we need at least a - // rootId and a namespace. - for i := len(labels) - 1; i >= 2; i-- { - fqdn := strings.Join(labels[i:], ".") - gw, ok := isKnownHostname(fqdn, knownGateways) - if !ok { - continue - } - - ns := labels[i-1] - if !isSubdomainNamespace(ns) { - break - } - - // Merge remaining labels (could be a FQDN with DNSLink) - rootID := strings.Join(labels[:i-1], ".") - return gw, fqdn, ns, rootID, true - } - // no match - return nil, "", "", "", false -} - -// isDNSLinkRequest returns bool that indicates if request -// should return data from content path listed in DNSLink record (if exists) -func isDNSLinkRequest(ctx context.Context, ipfs iface.CoreAPI, host string) bool { - fqdn := stripPort(host) - if len(fqdn) == 0 && !isd.IsDomain(fqdn) { - return false - } - name := "/btns/" + fqdn - // check if DNSLink exists - depth := options.Name.ResolveOption(nsopts.Depth(1)) - _, err := ipfs.Name().Resolve(ctx, name, depth) - return err == nil || err == namesys.ErrResolveRecursion -} - -func isSubdomainNamespace(ns string) bool { - switch ns { - case "btfs", "btns", "p2p", "ipld": - return true - default: - return false - } -} - -func isPeerIDNamespace(ns string) bool { - switch ns { - case "btns", "p2p": - return true - default: - return false - } -} - -// Converts an identifier to DNS-safe representation that fits in 63 characters -func toDNSPrefix(rootID string, rootCID cid.Cid) (prefix string, err error) { - // Return as-is if things fit - if len(rootID) <= dnsLabelMaxLength { - return rootID, nil - } - - // Convert to Base36 and see if that helped - rootID, err = cid.NewCidV1(rootCID.Type(), rootCID.Hash()).StringOfBase(mbase.Base36) - if err != nil { - return "", err - } - if len(rootID) <= dnsLabelMaxLength { - return rootID, nil - } - - // Can't win with DNS at this point, return error - return "", fmt.Errorf("CID incompatible with DNS label length limit of 63: %s", rootID) -} - -// Converts a hostname/path to a subdomain-based URL, if applicable. -func toSubdomainURL(hostname, path string, r *http.Request) (redirURL string, err error) { - var scheme, ns, rootID, rest string - - query := r.URL.RawQuery - parts := strings.SplitN(path, "/", 4) - safeRedirectURL := func(in string) (out string, err error) { - safeURI, err := url.ParseRequestURI(in) - if err != nil { - return "", err - } - return safeURI.String(), nil - } - - // Support X-Forwarded-Proto if added by a reverse proxy - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto - xproto := r.Header.Get("X-Forwarded-Proto") - if xproto == "https" { - scheme = "https:" - } else { - scheme = "http:" - } - - switch len(parts) { - case 4: - rest = parts[3] - fallthrough - case 3: - ns = parts[1] - rootID = parts[2] - default: - return "", nil - } - - if !isSubdomainNamespace(ns) { - return "", nil - } - - // add prefix if query is present - if query != "" { - query = "?" + query - } - - // Normalize problematic PeerIDs (eg. ed25519+identity) to CID representation - if isPeerIDNamespace(ns) && !isd.IsDomain(rootID) { - peerID, err := peer.Decode(rootID) - // Note: PeerID CIDv1 with protobuf multicodec will fail, but we fix it - // in the next block - if err == nil { - rootID = peer.ToCid(peerID).String() - } - } - - // If rootID is a CID, ensure it uses DNS-friendly text representation - if rootCID, err := cid.Decode(rootID); err == nil { - multicodec := rootCID.Type() - var base mbase.Encoding = mbase.Base32 - - // Normalizations specific to /ipns/{libp2p-key} - if isPeerIDNamespace(ns) { - // Using Base36 for /ipns/ for consistency - // Context: https://github.com/ipfs/go-ipfs/pull/7441#discussion_r452372828 - base = mbase.Base36 - - // PeerIDs represented as CIDv1 are expected to have libp2p-key - // multicodec (https://github.com/libp2p/specs/pull/209). - // We ease the transition by fixing multicodec on the fly: - // https://github.com/ipfs/go-ipfs/issues/5287#issuecomment-492163929 - if multicodec != cid.Libp2pKey { - multicodec = cid.Libp2pKey - } - } - - // Ensure CID text representation used in subdomain is compatible - // with the way DNS and URIs are implemented in user agents. - // - // 1. Switch to CIDv1 and enable case-insensitive Base encoding - // to avoid issues when user agent force-lowercases the hostname - // before making the request - // (https://github.com/ipfs/in-web-browsers/issues/89) - rootCID = cid.NewCidV1(multicodec, rootCID.Hash()) - rootID, err = rootCID.StringOfBase(base) - if err != nil { - return "", err - } - // 2. Make sure CID fits in a DNS label, adjust encoding if needed - // (https://github.com/ipfs/go-ipfs/issues/7318) - rootID, err = toDNSPrefix(rootID, rootCID) - if err != nil { - return "", err - } - } - - return safeRedirectURL(fmt.Sprintf( - "%s//%s.%s.%s/%s%s", - scheme, - rootID, - ns, - hostname, - rest, - query, - )) -} - -func hasPrefix(path string, prefixes ...string) bool { - for _, prefix := range prefixes { - // Assume people are creative with trailing slashes in Gateway config - p := strings.TrimSuffix(prefix, "/") - // Support for both /version and /ipfs/$cid - if p == path || strings.HasPrefix(path, p+"/") { - return true - } - } - return false -} - -func stripPort(hostname string) string { - host, _, err := net.SplitHostPort(hostname) - if err == nil { - return host - } - return hostname -} diff --git a/core/corehttp/hostname_test.go b/core/corehttp/hostname_test.go deleted file mode 100644 index b3054e7cd..000000000 --- a/core/corehttp/hostname_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package corehttp - -import ( - "errors" - "net/http/httptest" - "testing" - - config "github.com/TRON-US/go-btfs-config" - cid "github.com/ipfs/go-cid" -) - -func TestToSubdomainURL(t *testing.T) { - r := httptest.NewRequest("GET", "http://request-stub.example.com", nil) - for _, test := range []struct { - // in: - hostname string - path string - // out: - url string - err error - }{ - // DNSLink - {"localhost", "/btns/dnslink.io", "http://dnslink.io.btns.localhost/", nil}, - // Hostname with port - {"localhost:8080", "/btns/dnslink.io", "http://dnslink.io.btns.localhost:8080/", nil}, - // CIDv0 → CIDv1base32 - {"localhost", "/btfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.btfs.localhost/", nil}, - // CIDv1 with long sha512 - {"localhost", "/btfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, - // PeerID as CIDv1 needs to have libp2p-key multicodec - {"localhost", "/btns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.btns.localhost/", nil}, - {"localhost", "/btns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.btns.localhost/", nil}, - // PeerID: ed25519+identity multihash → CIDv1Base36 - {"localhost", "/btns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.btns.localhost/", nil}, - {"sub.localhost", "/btfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.btfs.sub.localhost/", nil}, - } { - url, err := toSubdomainURL(test.hostname, test.path, r) - if url != test.url || !equalError(err, test.err) { - t.Errorf("(%s, %s) returned (%s, %v), expected (%s, %v)", test.hostname, test.path, url, err, test.url, test.err) - } - } -} - -func TestHasPrefix(t *testing.T) { - for _, test := range []struct { - prefixes []string - path string - out bool - }{ - {[]string{"/btfs"}, "/btfs/cid", true}, - {[]string{"/btfs/"}, "/btfs/cid", true}, - {[]string{"/version/"}, "/version", true}, - {[]string{"/version"}, "/version", true}, - } { - out := hasPrefix(test.path, test.prefixes...) - if out != test.out { - t.Errorf("(%+v, %s) returned '%t', expected '%t'", test.prefixes, test.path, out, test.out) - } - } -} - -func TestPortStripping(t *testing.T) { - for _, test := range []struct { - in string - out string - }{ - {"localhost:8080", "localhost"}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost:8080", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost"}, - {"example.com:443", "example.com"}, - {"example.com", "example.com"}, - {"foo-dweb.btfs.pvt.k12.ma.us:8080", "foo-dweb.btfs.pvt.k12.ma.us"}, - {"localhost", "localhost"}, - {"[::1]:8080", "::1"}, - } { - out := stripPort(test.in) - if out != test.out { - t.Errorf("(%s): returned '%s', expected '%s'", test.in, out, test.out) - } - } - -} - -func TestDNSPrefix(t *testing.T) { - for _, test := range []struct { - in string - out string - err error - }{ - // <= 63 - {"QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", nil}, - {"bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", nil}, - // > 63 - // PeerID: ed25519+identity multihash → CIDv1Base36 - {"bafzaajaiaejca4syrpdu6gdx4wsdnokxkprgzxf4wrstuc34gxw5k5jrag2so5gk", "k51qzi5uqu5dj16qyiq0tajolkojyl9qdkr254920wxv7ghtuwcz593tp69z9m", nil}, - // CIDv1 with long sha512 → error - {"bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, - } { - inCID, _ := cid.Decode(test.in) - out, err := toDNSPrefix(test.in, inCID) - if out != test.out || !equalError(err, test.err) { - t.Errorf("(%s): returned (%s, %v) expected (%s, %v)", test.in, out, err, test.out, test.err) - } - } - -} - -func TestKnownSubdomainDetails(t *testing.T) { - gwLocalhost := &config.GatewaySpec{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} - gwDweb := &config.GatewaySpec{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} - gwLong := &config.GatewaySpec{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} - gwWildcard1 := &config.GatewaySpec{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} - gwWildcard2 := &config.GatewaySpec{Paths: []string{"/btfs", "/btns", "/api"}, UseSubdomains: true} - - knownGateways := prepareKnownGateways(map[string]*config.GatewaySpec{ - "localhost": gwLocalhost, - "dweb.link": gwDweb, - "dweb.btfs.pvt.k12.ma.us": gwLong, // note the sneaky ".ipfs." ;-) - "*.wildcard1.tld": gwWildcard1, - "*.*.wildcard2.tld": gwWildcard2, - }) - - for _, test := range []struct { - // in: - hostHeader string - // out: - gw *config.GatewaySpec - hostname string - ns string - rootID string - ok bool - }{ - // no subdomain - {"127.0.0.1:8080", nil, "", "", "", false}, - {"[::1]:8080", nil, "", "", "", false}, - {"hey.look.example.com", nil, "", "", "", false}, - {"dweb.link", nil, "", "", "", false}, - // malformed Host header - {".....dweb.link", nil, "", "", "", false}, - {"link", nil, "", "", "", false}, - {"8080:dweb.link", nil, "", "", "", false}, - {" ", nil, "", "", "", false}, - {"", nil, "", "", "", false}, - // unknown gateway host - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.unknown.example.com", nil, "", "", "", false}, - // cid in subdomain, known gateway - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.localhost:8080", gwLocalhost, "localhost:8080", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.link", gwDweb, "dweb.link", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - // capture everything before .btfs. - {"foo.bar.boo-buzz.btfs.dweb.link", gwDweb, "dweb.link", "btfs", "foo.bar.boo-buzz", true}, - // btns - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.link", gwDweb, "dweb.link", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // edge case check: public gateway under long TLD (see: https://publicsuffix.org) - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // dnslink in subdomain - {"en.wikipedia-on-btfs.org.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "en.wikipedia-on-btfs.org", true}, - {"en.wikipedia-on-btfs.org.btns.localhost", gwLocalhost, "localhost", "btns", "en.wikipedia-on-btfs.org", true}, - {"dist.btfs.io.btns.localhost:8080", gwLocalhost, "localhost:8080", "btns", "dist.btfs.io", true}, - {"en.wikipedia-on-btfs.org.btns.dweb.link", gwDweb, "dweb.link", "btns", "en.wikipedia-on-btfs.org", true}, - // edge case check: public gateway under long TLD (see: https://publicsuffix.org) - {"foo.dweb.btfs.pvt.k12.ma.us", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.btns.dweb.btfs.pvt.k12.ma.us", gwLong, "dweb.btfs.pvt.k12.ma.us", "btns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // other namespaces - {"api.localhost", nil, "", "", "", false}, - {"peerid.p2p.localhost", gwLocalhost, "localhost", "p2p", "peerid", true}, - // wildcards - {"wildcard1.tld", nil, "", "", "", false}, - {".wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub.wildcard1.tld", gwWildcard1, "sub.wildcard1.tld", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub1.sub2.wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.btfs.sub1.sub2.wildcard2.tld", gwWildcard2, "sub1.sub2.wildcard2.tld", "btfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - } { - gw, hostname, ns, rootID, ok := knownSubdomainDetails(test.hostHeader, knownGateways) - if ok != test.ok { - t.Errorf("knownSubdomainDetails(%s): ok is %t, expected %t", test.hostHeader, ok, test.ok) - } - if rootID != test.rootID { - t.Errorf("knownSubdomainDetails(%s): rootID is '%s', expected '%s'", test.hostHeader, rootID, test.rootID) - } - if ns != test.ns { - t.Errorf("knownSubdomainDetails(%s): ns is '%s', expected '%s'", test.hostHeader, ns, test.ns) - } - if hostname != test.hostname { - t.Errorf("knownSubdomainDetails(%s): hostname is '%s', expected '%s'", test.hostHeader, hostname, test.hostname) - } - if gw != test.gw { - t.Errorf("knownSubdomainDetails(%s): gw is %+v, expected %+v", test.hostHeader, gw, test.gw) - } - } - -} - -func equalError(a, b error) bool { - return (a == nil && b == nil) || (a != nil && b != nil && a.Error() == b.Error()) -} diff --git a/core/corehttp/lazyseek_test.go b/core/corehttp/lazyseek_test.go deleted file mode 100644 index 144e57d0f..000000000 --- a/core/corehttp/lazyseek_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package corehttp - -import ( - "fmt" - "io" - "io/ioutil" - "strings" - "testing" -) - -type badSeeker struct { - io.ReadSeeker -} - -var badSeekErr = fmt.Errorf("I'm a bad seeker") - -func (bs badSeeker) Seek(offset int64, whence int) (int64, error) { - off, err := bs.ReadSeeker.Seek(0, io.SeekCurrent) - if err != nil { - panic(err) - } - return off, badSeekErr -} - -func TestLazySeekerError(t *testing.T) { - underlyingBuffer := strings.NewReader("fubar") - s := &lazySeeker{ - reader: badSeeker{underlyingBuffer}, - size: underlyingBuffer.Size(), - } - off, err := s.Seek(0, io.SeekEnd) - if err != nil { - t.Fatal(err) - } - if off != s.size { - t.Fatal("expected to seek to the end") - } - - // shouldn't have actually seeked. - b, err := ioutil.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if len(b) != 0 { - t.Fatal("expected to read nothing") - } - - // shouldn't need to actually seek. - off, err = s.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - if off != 0 { - t.Fatal("expected to seek to the start") - } - b, err = ioutil.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if string(b) != "fubar" { - t.Fatal("expected to read string") - } - - // should fail the second time. - off, err = s.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - if off != 0 { - t.Fatal("expected to seek to the start") - } - // right here... - b, err = ioutil.ReadAll(s) - if err == nil { - t.Fatalf("expected an error, got output %s", string(b)) - } - if err != badSeekErr { - t.Fatalf("expected a bad seek error, got %s", err) - } - if len(b) != 0 { - t.Fatalf("expected to read nothing") - } -} - -func TestLazySeeker(t *testing.T) { - underlyingBuffer := strings.NewReader("fubar") - s := &lazySeeker{ - reader: underlyingBuffer, - size: underlyingBuffer.Size(), - } - expectByte := func(b byte) { - t.Helper() - var buf [1]byte - n, err := io.ReadFull(s, buf[:]) - if err != nil { - t.Fatal(err) - } - if n != 1 { - t.Fatalf("expected to read one byte, read %d", n) - } - if buf[0] != b { - t.Fatalf("expected %b, got %b", b, buf[0]) - } - } - expectSeek := func(whence int, off, expOff int64, expErr string) { - t.Helper() - n, err := s.Seek(off, whence) - if expErr == "" { - if err != nil { - t.Fatal("unexpected seek error: ", err) - } - } else { - if err == nil || err.Error() != expErr { - t.Fatalf("expected %s, got %s", err, expErr) - } - } - if n != expOff { - t.Fatalf("expected offset %d, got, %d", expOff, n) - } - } - - expectSeek(io.SeekEnd, 0, s.size, "") - b, err := ioutil.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if len(b) != 0 { - t.Fatal("expected to read nothing") - } - expectSeek(io.SeekEnd, -1, s.size-1, "") - expectByte('r') - expectSeek(io.SeekStart, 0, 0, "") - expectByte('f') - expectSeek(io.SeekCurrent, 1, 2, "") - expectByte('b') - expectSeek(io.SeekCurrent, -100, 3, "invalid seek offset") -} diff --git a/core/corehttp/metrics.go b/core/corehttp/metrics.go index bfcc34e8c..bde4e72bd 100644 --- a/core/corehttp/metrics.go +++ b/core/corehttp/metrics.go @@ -3,11 +3,15 @@ package corehttp import ( "net" "net/http" + "time" + ocprom "contrib.go.opencensus.io/exporter/prometheus" core "github.com/bittorrent/go-btfs/core" prometheus "github.com/prometheus/client_golang/prometheus" promhttp "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opencensus.io/stats/view" + "go.opencensus.io/zpages" ) // This adds the scraping endpoint which Prometheus uses to fetch metrics. @@ -18,6 +22,59 @@ func MetricsScrapingOption(path string) ServeOption { } } +// This adds collection of OpenCensus metrics +func MetricsOpenCensusCollectionOption() ServeOption { + return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + log.Info("Init OpenCensus") + + promRegistry := prometheus.NewRegistry() + pe, err := ocprom.NewExporter(ocprom.Options{ + Namespace: "ipfs_oc", + Registry: promRegistry, + OnError: func(err error) { + log.Errorw("OC ERROR", "error", err) + }, + }) + if err != nil { + return nil, err + } + + // register prometheus with opencensus + view.RegisterExporter(pe) + view.SetReportingPeriod(2 * time.Second) + + // Construct the mux + zpages.Handle(mux, "/debug/metrics/oc/debugz") + mux.Handle("/debug/metrics/oc", pe) + + return mux, nil + } +} + +// MetricsOpenCensusDefaultPrometheusRegistry registers the default prometheus +// registry as an exporter to OpenCensus metrics. This means that OpenCensus +// metrics will show up in the prometheus metrics endpoint +func MetricsOpenCensusDefaultPrometheusRegistry() ServeOption { + return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + log.Info("Init OpenCensus with default prometheus registry") + + pe, err := ocprom.NewExporter(ocprom.Options{ + Registry: prometheus.DefaultRegisterer.(*prometheus.Registry), + OnError: func(err error) { + log.Errorw("OC default registry ERROR", "error", err) + }, + }) + if err != nil { + return nil, err + } + + // register prometheus with opencensus + view.RegisterExporter(pe) + + return mux, nil + } +} + // This adds collection of net/http-related metrics func MetricsCollectionOption(handlerName string) ServeOption { return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { @@ -111,7 +168,7 @@ type IpfsNodeCollector struct { Node *core.IpfsNode } -func (_ IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) { +func (IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) { ch <- peersTotalMetric } @@ -131,9 +188,18 @@ func (c IpfsNodeCollector) PeersTotalValues() map[string]float64 { if c.Node.PeerHost == nil { return vals } - for _, conn := range c.Node.PeerHost.Network().Conns() { + for _, peerID := range c.Node.PeerHost.Network().Peers() { + // Each peer may have more than one connection (see for an explanation + // https://github.com/libp2p/go-libp2p-swarm/commit/0538806), so we grab + // only one, the first (an arbitrary and non-deterministic choice), which + // according to ConnsToPeer is the oldest connection in the list + // (https://github.com/libp2p/go-libp2p-swarm/blob/v0.2.6/swarm.go#L362-L364). + conns := c.Node.PeerHost.Network().ConnsToPeer(peerID) + if len(conns) == 0 { + continue + } tr := "" - for _, proto := range conn.RemoteMultiaddr().Protocols() { + for _, proto := range conns[0].RemoteMultiaddr().Protocols() { tr = tr + "/" + proto.Name } vals[tr] = vals[tr] + 1 diff --git a/core/corehttp/mutex_profile.go b/core/corehttp/mutex_profile.go index 055a803ff..fa01b2147 100644 --- a/core/corehttp/mutex_profile.go +++ b/core/corehttp/mutex_profile.go @@ -41,3 +41,38 @@ func MutexFractionOption(path string) ServeOption { return mux, nil } } + +// BlockProfileRateOption allows to set runtime.SetBlockProfileRate via HTTP +// using POST request with parameter 'rate'. +// The profiler tries to sample 1 event every nanoseconds. +// If rate == 1, then the profiler samples every blocking event. +// To disable, set rate = 0. +func BlockProfileRateOption(path string) ServeOption { + return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "only POST allowed", http.StatusMethodNotAllowed) + return + } + if err := r.ParseForm(); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + rateStr := r.Form.Get("rate") + if len(rateStr) == 0 { + http.Error(w, "parameter 'rate' must be set", http.StatusBadRequest) + return + } + + rate, err := strconv.Atoi(rateStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + log.Infof("Setting BlockProfileRate to %d", rate) + runtime.SetBlockProfileRate(rate) + }) + return mux, nil + } +} diff --git a/core/corehttp/p2p_proxy.go b/core/corehttp/p2p_proxy.go index 8243a1942..cc8d06067 100644 --- a/core/corehttp/p2p_proxy.go +++ b/core/corehttp/p2p_proxy.go @@ -11,6 +11,7 @@ import ( core "github.com/bittorrent/go-btfs/core" p2phttp "github.com/libp2p/go-libp2p-http" + "github.com/libp2p/go-libp2p/core/peer" protocol "github.com/libp2p/go-libp2p/core/protocol" ) @@ -57,7 +58,11 @@ func parseRequest(request *http.Request) (*proxyRequest, error) { split := strings.SplitN(path, "/", 5) if len(split) < 5 { - return nil, fmt.Errorf("Invalid request path '%s'", path) + return nil, fmt.Errorf("invalid request path '%s'", path) + } + + if _, err := peer.Decode(split[2]); err != nil { + return nil, fmt.Errorf("invalid request path '%s'", path) } if split[3] == "http" { @@ -66,7 +71,7 @@ func parseRequest(request *http.Request) (*proxyRequest, error) { split = strings.SplitN(path, "/", 7) if len(split) < 7 || split[3] != "x" || split[5] != "http" { - return nil, fmt.Errorf("Invalid request path '%s'", path) + return nil, fmt.Errorf("invalid request path '%s'", path) } return &proxyRequest{split[2], protocol.ID("/x/" + split[4] + "/http"), split[6]}, nil diff --git a/core/corehttp/redirect.go b/core/corehttp/redirect.go index 033e3dbf6..afceba2a3 100644 --- a/core/corehttp/redirect.go +++ b/core/corehttp/redirect.go @@ -8,8 +8,14 @@ import ( ) func RedirectOption(path string, redirect string) ServeOption { - handler := &redirectHandler{redirect} return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + handler := &redirectHandler{redirect, cfg.API.HTTPHeaders} + if len(path) > 0 { mux.Handle("/"+path+"/", handler) } else { @@ -20,9 +26,14 @@ func RedirectOption(path string, redirect string) ServeOption { } type redirectHandler struct { - path string + path string + headers map[string][]string } func (i *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, i.path, 302) + for k, v := range i.headers { + w.Header()[http.CanonicalHeaderKey(k)] = v + } + + http.Redirect(w, r, i.path, http.StatusFound) } diff --git a/core/corehttp/remote/call.go b/core/corehttp/remote/call.go index a1b1316fc..594aedbf5 100644 --- a/core/corehttp/remote/call.go +++ b/core/corehttp/remote/call.go @@ -3,7 +3,7 @@ package remote import ( "context" - "github.com/TRON-US/go-btfs-api" + "github.com/bittorrent/go-btfs-api" logging "github.com/ipfs/go-log" ) diff --git a/core/corehttp/remote/p2p_call.go b/core/corehttp/remote/p2p_call.go index fe61991ee..64c145b29 100644 --- a/core/corehttp/remote/p2p_call.go +++ b/core/corehttp/remote/p2p_call.go @@ -11,7 +11,7 @@ import ( "github.com/bittorrent/go-btfs/core" - iface "github.com/TRON-US/interface-go-btfs-core" + iface "github.com/bittorrent/interface-go-btfs-core" p2phttp "github.com/libp2p/go-libp2p-http" "github.com/libp2p/go-libp2p/core/peer" diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index 3b6818bd2..559da4f4c 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,10 +1,11 @@ package corehttp -const WebUIPath = "/btfs/QmRdt8SzRBz5px7KfU4hFveJSKzBMFqv73YE4xXJBsVdDJ" // v2.3.1 +const WebUIPath = "/btfs/QmUKCyDc4h9KN93AdZ7ZVqgPProsKs8NAbVJkK3ux9788d" // v2.3.2 // this is a list of all past webUI paths. var WebUIPaths = []string{ WebUIPath, + "/btfs/QmRdt8SzRBz5px7KfU4hFveJSKzBMFqv73YE4xXJBsVdDJ", // v2.3.1 "/btfs/QmbNHqcL9PEhFdT5mXjNnkaAE8SEFkjr2jD7we2ckTL4Li", // v2.3.0 "/btfs/QmZvpBNMribwdjNMrA9gXz27t2gzbae3N2tbCLtjpRTqJn", // v2.2.1.1 "/btfs/QmaK77EYUHxKweLFvRY8gbcMTx2qEb7p4S5aWPN6EHX7T1", // v2.2.1 diff --git a/core/corerepo/gc.go b/core/corerepo/gc.go index 9e2c4d8d4..08b61d396 100644 --- a/core/corerepo/gc.go +++ b/core/corerepo/gc.go @@ -11,7 +11,7 @@ import ( "github.com/bittorrent/go-btfs/gc" "github.com/bittorrent/go-btfs/repo" - mfs "github.com/TRON-US/go-mfs" + mfs "github.com/bittorrent/go-mfs" humanize "github.com/dustin/go-humanize" cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" diff --git a/core/coreunix/add.go b/core/coreunix/add.go index f69066434..8cfdb44c1 100644 --- a/core/coreunix/add.go +++ b/core/coreunix/add.go @@ -9,17 +9,17 @@ import ( gopath "path" "strconv" - chunker "github.com/TRON-US/go-btfs-chunker" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/go-mfs" - "github.com/TRON-US/go-unixfs" - "github.com/TRON-US/go-unixfs/importer/balanced" - ihelper "github.com/TRON-US/go-unixfs/importer/helpers" - "github.com/TRON-US/go-unixfs/importer/trickle" - uio "github.com/TRON-US/go-unixfs/io" - ftutil "github.com/TRON-US/go-unixfs/util" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-mfs" + "github.com/bittorrent/go-unixfs" + "github.com/bittorrent/go-unixfs/importer/balanced" + ihelper "github.com/bittorrent/go-unixfs/importer/helpers" + "github.com/bittorrent/go-unixfs/importer/trickle" + uio "github.com/bittorrent/go-unixfs/io" + ftutil "github.com/bittorrent/go-unixfs/util" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/path" "github.com/ipfs/go-cid" bstore "github.com/ipfs/go-ipfs-blockstore" pin "github.com/ipfs/go-ipfs-pinner" diff --git a/core/coreunix/metadata.go b/core/coreunix/metadata.go index 4ea4a4512..130472d86 100644 --- a/core/coreunix/metadata.go +++ b/core/coreunix/metadata.go @@ -10,15 +10,15 @@ import ( core "github.com/bittorrent/go-btfs/core" - chunker "github.com/TRON-US/go-btfs-chunker" - "github.com/TRON-US/go-mfs" - ft "github.com/TRON-US/go-unixfs" - ihelper "github.com/TRON-US/go-unixfs/importer/helpers" - uio "github.com/TRON-US/go-unixfs/io" - "github.com/TRON-US/go-unixfs/mod" - ftutil "github.com/TRON-US/go-unixfs/util" - coreiface "github.com/TRON-US/interface-go-btfs-core" - ipath "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + "github.com/bittorrent/go-mfs" + ft "github.com/bittorrent/go-unixfs" + ihelper "github.com/bittorrent/go-unixfs/importer/helpers" + uio "github.com/bittorrent/go-unixfs/io" + "github.com/bittorrent/go-unixfs/mod" + ftutil "github.com/bittorrent/go-unixfs/util" + coreiface "github.com/bittorrent/interface-go-btfs-core" + ipath "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" bstore "github.com/ipfs/go-ipfs-blockstore" pin "github.com/ipfs/go-ipfs-pinner" diff --git a/core/coreunix/reed_solomon_add.go b/core/coreunix/reed_solomon_add.go index a7d0e1c0e..0fca75ad9 100644 --- a/core/coreunix/reed_solomon_add.go +++ b/core/coreunix/reed_solomon_add.go @@ -11,9 +11,9 @@ import ( "container/list" "encoding/json" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/go-unixfs" - uio "github.com/TRON-US/go-unixfs/io" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/go-unixfs" + uio "github.com/bittorrent/go-unixfs/io" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" ) diff --git a/core/coreunix/test/add.go b/core/coreunix/test/add.go index e6cbbee9d..713d98b1f 100644 --- a/core/coreunix/test/add.go +++ b/core/coreunix/test/add.go @@ -12,12 +12,12 @@ import ( "github.com/bittorrent/go-btfs/core/coreunix" "github.com/bittorrent/go-btfs/repo" - chunker "github.com/TRON-US/go-btfs-chunker" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" - ftutil "github.com/TRON-US/go-unixfs/util" - coreiface "github.com/TRON-US/interface-go-btfs-core" - "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" + ftutil "github.com/bittorrent/go-unixfs/util" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" diff --git a/core/coreunix/test/add_test.go b/core/coreunix/test/add_test.go index bbbccf1dd..7963f9f41 100644 --- a/core/coreunix/test/add_test.go +++ b/core/coreunix/test/add_test.go @@ -16,9 +16,9 @@ import ( "github.com/bittorrent/go-btfs/gc" "github.com/bittorrent/go-btfs/repo" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" - coreiface "github.com/TRON-US/interface-go-btfs-core" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" + coreiface "github.com/bittorrent/interface-go-btfs-core" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" diff --git a/core/coreunix/test/metadata_test.go b/core/coreunix/test/metadata_test.go index 7b8c77fe4..f589c5edd 100644 --- a/core/coreunix/test/metadata_test.go +++ b/core/coreunix/test/metadata_test.go @@ -12,24 +12,24 @@ import ( "testing" "time" - ft "github.com/TRON-US/go-unixfs" - importer "github.com/TRON-US/go-unixfs/importer" - uio "github.com/TRON-US/go-unixfs/io" - "github.com/TRON-US/go-unixfs/mod" - ftutil "github.com/TRON-US/go-unixfs/util" "github.com/bittorrent/go-btfs/gc" + ft "github.com/bittorrent/go-unixfs" + importer "github.com/bittorrent/go-unixfs/importer" + uio "github.com/bittorrent/go-unixfs/io" + "github.com/bittorrent/go-unixfs/mod" + ftutil "github.com/bittorrent/go-unixfs/util" bserv "github.com/ipfs/go-blockservice" merkledag "github.com/ipfs/go-merkledag" - chunker "github.com/TRON-US/go-btfs-chunker" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" - coreiface "github.com/TRON-US/interface-go-btfs-core" - ipath "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/coreapi" "github.com/bittorrent/go-btfs/core/coreunix" "github.com/bittorrent/go-btfs/repo" + coreiface "github.com/bittorrent/interface-go-btfs-core" + ipath "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" ds "github.com/ipfs/go-datastore" diff --git a/core/hub/settings.go b/core/hub/settings.go index d6ace4cc6..7dd50668d 100644 --- a/core/hub/settings.go +++ b/core/hub/settings.go @@ -4,9 +4,9 @@ import ( "context" "errors" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" - nodepb "github.com/tron-us/go-btfs-common/protos/node" - "github.com/tron-us/go-btfs-common/utils/grpc" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + "github.com/bittorrent/go-btfs-common/utils/grpc" ) func GetHostSettings(ctx context.Context, addr, peerId string) (*nodepb.Node_Settings, error) { diff --git a/core/hub/settings_test.go b/core/hub/settings_test.go index e0558dbae..6747b52a0 100644 --- a/core/hub/settings_test.go +++ b/core/hub/settings_test.go @@ -6,7 +6,7 @@ import ( "reflect" "testing" - nodepb "github.com/tron-us/go-btfs-common/protos/node" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" ) func TestGetSettings(t *testing.T) { diff --git a/core/hub/sync.go b/core/hub/sync.go index ff8cd2f8b..07907cbda 100644 --- a/core/hub/sync.go +++ b/core/hub/sync.go @@ -9,8 +9,8 @@ import ( "github.com/bittorrent/go-btfs/core" - hubpb "github.com/tron-us/go-btfs-common/protos/hub" - "github.com/tron-us/go-btfs-common/utils/grpc" + hubpb "github.com/bittorrent/go-btfs-common/protos/hub" + "github.com/bittorrent/go-btfs-common/utils/grpc" ) const ( diff --git a/core/mock/mock.go b/core/mock/mock.go index b981898cb..932225c86 100644 --- a/core/mock/mock.go +++ b/core/mock/mock.go @@ -10,7 +10,7 @@ import ( libp2p2 "github.com/bittorrent/go-btfs/core/node/libp2p" "github.com/bittorrent/go-btfs/repo" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p" @@ -24,17 +24,24 @@ import ( // NewMockNode constructs an IpfsNode for use in tests. func NewMockNode() (*core.IpfsNode, error) { - ctx := context.Background() - // effectively offline, only peer in its network - return core.NewNode(ctx, &core.BuildCfg{ + return core.NewNode(context.Background(), &core.BuildCfg{ Online: true, Host: MockHostOption(mocknet.New()), }) } func MockHostOption(mn mocknet.Mocknet) libp2p2.HostOption { - return func(ctx context.Context, id peer.ID, ps pstore.Peerstore, _ ...libp2p.Option) (host.Host, error) { + return func(id peer.ID, ps pstore.Peerstore, opts ...libp2p.Option) (host.Host, error) { + var cfg libp2p.Config + if err := cfg.Apply(opts...); err != nil { + return nil, err + } + + // The mocknet does not use the provided libp2p.Option. This options include + // the listening addresses we want our peer listening on. Therefore, we have + // to manually parse the configuration and add them here. + ps.AddAddrs(id, cfg.ListenAddrs, pstore.PermanentAddrTTL) return mn.AddPeerWithPeerstore(id, ps) } } diff --git a/core/node/builder.go b/core/node/builder.go index db9da1b5c..ce2b111e7 100644 --- a/core/node/builder.go +++ b/core/node/builder.go @@ -12,7 +12,7 @@ import ( "github.com/bittorrent/go-btfs/core/node/libp2p" "github.com/bittorrent/go-btfs/repo" - cfg "github.com/TRON-US/go-btfs-config" + cfg "github.com/bittorrent/go-btfs-config" ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" ci "github.com/libp2p/go-libp2p/core/crypto" diff --git a/core/node/core.go b/core/node/core.go index 57a001ed2..fea2a8d3b 100644 --- a/core/node/core.go +++ b/core/node/core.go @@ -7,14 +7,15 @@ import ( "github.com/bittorrent/go-btfs/core/node/helpers" "github.com/bittorrent/go-btfs/repo" irouting "github.com/bittorrent/go-btfs/routing" - - "github.com/TRON-US/go-mfs" - "github.com/TRON-US/go-unixfs" + "github.com/bittorrent/go-mfs" + "github.com/bittorrent/go-unixfs" "github.com/ipfs/go-bitswap" "github.com/ipfs/go-bitswap/network" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" + "github.com/ipfs/go-fetcher" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" "github.com/ipfs/go-filestore" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" @@ -22,6 +23,11 @@ import ( "github.com/ipfs/go-ipfs-pinner/dspinner" format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-unixfsnode" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/schema" "github.com/libp2p/go-libp2p/core/host" "go.uber.org/fx" ) @@ -81,6 +87,26 @@ func (s *syncDagService) Session(ctx context.Context) format.NodeGetter { return merkledag.NewSession(ctx, s.DAGService) } +type fetchersOut struct { + fx.Out + IPLDFetcher fetcher.Factory `name:"ipldFetcher"` + UnixfsFetcher fetcher.Factory `name:"unixfsFetcher"` +} + +// FetcherConfig returns a fetcher config that can build new fetcher instances +func FetcherConfig(bs blockservice.BlockService) fetchersOut { + ipldFetcher := bsfetcher.NewFetcherConfig(bs) + ipldFetcher.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }) + + unixFSFetcher := ipldFetcher.WithReifier(unixfsnode.Reify) + return fetchersOut{IPLDFetcher: ipldFetcher, UnixfsFetcher: unixFSFetcher} +} + // Dag creates new DAGService func Dag(bs blockservice.BlockService) format.DAGService { return merkledag.NewDAGService(bs) diff --git a/core/node/dns.go b/core/node/dns.go index d1205633a..72aa7de31 100644 --- a/core/node/dns.go +++ b/core/node/dns.go @@ -6,7 +6,7 @@ import ( "strings" "time" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" doh "github.com/libp2p/go-doh-resolver" madns "github.com/multiformats/go-multiaddr-dns" diff --git a/core/node/groups.go b/core/node/groups.go index 3c9705c93..45cf0309d 100644 --- a/core/node/groups.go +++ b/core/node/groups.go @@ -7,17 +7,16 @@ import ( "os" "time" + "github.com/bittorrent/go-btfs-common/crypto" "github.com/bittorrent/go-btfs/core/node/libp2p" "github.com/bittorrent/go-btfs/p2p" - "github.com/tron-us/go-btfs-common/crypto" - config "github.com/TRON-US/go-btfs-config" - uio "github.com/TRON-US/go-unixfs/io" + config "github.com/bittorrent/go-btfs-config" + uio "github.com/bittorrent/go-unixfs/io" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" util "github.com/ipfs/go-ipfs-util" log "github.com/ipfs/go-log" - "github.com/ipfs/go-path/resolver" pubsub "github.com/libp2p/go-libp2p-pubsub" peer "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/fx" @@ -334,7 +333,7 @@ func Offline(cfg *config.Config) fx.Option { var Core = fx.Options( fx.Provide(BlockService), fx.Provide(Dag), - fx.Provide(resolver.NewBasicResolver), + fx.Provide(FetcherConfig), fx.Provide(Pinning), fx.Provide(Files), ) diff --git a/core/node/helpers.go b/core/node/helpers.go index 5c27fb9ce..6e6cb2920 100644 --- a/core/node/helpers.go +++ b/core/node/helpers.go @@ -43,7 +43,7 @@ func maybeProvide(opt interface{}, enable bool) fx.Option { return fx.Options() } -//nolint unused +// nolint unused func maybeInvoke(opt interface{}, enable bool) fx.Option { if enable { return fx.Invoke(opt) diff --git a/core/node/ipns.go b/core/node/ipns.go index 774f98b52..5ce13f5d9 100644 --- a/core/node/ipns.go +++ b/core/node/ipns.go @@ -8,8 +8,9 @@ import ( "github.com/bittorrent/go-btfs/namesys/republisher" "github.com/bittorrent/go-btfs/repo" irouting "github.com/bittorrent/go-btfs/routing" + madns "github.com/multiformats/go-multiaddr-dns" - "github.com/TRON-US/go-btns" + "github.com/bittorrent/go-btns" util "github.com/ipfs/go-ipfs-util" record "github.com/libp2p/go-libp2p-record" "github.com/libp2p/go-libp2p/core/crypto" @@ -27,9 +28,18 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator { } // Namesys creates new name system -func Namesys(cacheSize int) func(rt irouting.ProvideManyRouter, repo repo.Repo) (namesys.NameSystem, error) { - return func(rt irouting.ProvideManyRouter, repo repo.Repo) (namesys.NameSystem, error) { - return namesys.NewNameSystem(rt, repo.Datastore(), cacheSize), nil +func Namesys(cacheSize int) func(rt irouting.ProvideManyRouter, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) { + return func(rt irouting.ProvideManyRouter, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) { + opts := []namesys.Option{ + namesys.WithDatastore(repo.Datastore()), + namesys.WithDNSResolver(rslv), + } + + if cacheSize > 0 { + opts = append(opts, namesys.WithCache(cacheSize)) + } + + return namesys.NewNameSystem(rt, opts...) } } diff --git a/core/node/libp2p/host.go b/core/node/libp2p/host.go index e53bf9717..f3f77b045 100644 --- a/core/node/libp2p/host.go +++ b/core/node/libp2p/host.go @@ -63,7 +63,7 @@ func Host(mctx helpers.MetricsCtx, lc fx.Lifecycle, params P2PHostIn) (out P2PHo return r, err })) - out.Host, err = params.HostOption(ctx, params.ID, params.Peerstore, opts...) + out.Host, err = params.HostOption(params.ID, params.Peerstore, opts...) if err != nil { return P2PHostOut{}, err } diff --git a/core/node/libp2p/hostopt.go b/core/node/libp2p/hostopt.go index 22cc89bea..de0b2900e 100644 --- a/core/node/libp2p/hostopt.go +++ b/core/node/libp2p/hostopt.go @@ -1,7 +1,6 @@ package libp2p import ( - "context" "fmt" "github.com/libp2p/go-libp2p" @@ -10,12 +9,12 @@ import ( peerstore "github.com/libp2p/go-libp2p/core/peerstore" ) -type HostOption func(ctx context.Context, id peer.ID, ps peerstore.Peerstore, options ...libp2p.Option) (host.Host, error) +type HostOption func(id peer.ID, ps peerstore.Peerstore, options ...libp2p.Option) (host.Host, error) var DefaultHostOption HostOption = constructPeerHost // isolates the complex initialization steps -func constructPeerHost(ctx context.Context, id peer.ID, ps peerstore.Peerstore, options ...libp2p.Option) (host.Host, error) { +func constructPeerHost(id peer.ID, ps peerstore.Peerstore, options ...libp2p.Option) (host.Host, error) { pkey := ps.PrivKey(id) if pkey == nil { return nil, fmt.Errorf("missing private key for node ID: %s", id.Pretty()) diff --git a/core/node/libp2p/libp2p.go b/core/node/libp2p/libp2p.go index d9eebcc8c..3d2d97220 100644 --- a/core/node/libp2p/libp2p.go +++ b/core/node/libp2p/libp2p.go @@ -7,7 +7,7 @@ import ( version "github.com/bittorrent/go-btfs" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p" diff --git a/core/node/libp2p/nat.go b/core/node/libp2p/nat.go index 6c82ba3dc..19bf323cc 100644 --- a/core/node/libp2p/nat.go +++ b/core/node/libp2p/nat.go @@ -3,7 +3,7 @@ package libp2p import ( "time" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/libp2p/go-libp2p" ) diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go index e07e54420..5988c5709 100644 --- a/core/node/libp2p/rcmgr.go +++ b/core/node/libp2p/rcmgr.go @@ -19,7 +19,7 @@ import ( "go.opencensus.io/stats/view" "go.uber.org/fx" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/core/node/helpers" "github.com/bittorrent/go-btfs/repo" ) diff --git a/core/node/libp2p/rcmgr_defaults.go b/core/node/libp2p/rcmgr_defaults.go index e547994f7..c0062d8c8 100644 --- a/core/node/libp2p/rcmgr_defaults.go +++ b/core/node/libp2p/rcmgr_defaults.go @@ -8,7 +8,7 @@ import ( rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "github.com/pbnjay/memory" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/core/node/libp2p/fd" ) @@ -63,14 +63,14 @@ func createDefaultLimitConfig(cfg config.SwarmConfig) (rcmgr.LimitConfig, error) // By default, we just limit connections on the inbound side. Conns: bigEnough, - ConnsInbound: rcmgr.DefaultLimits.SystemBaseLimit.ConnsInbound, // same as libp2p default, + ConnsInbound: 8 << 10, ConnsOutbound: bigEnough, // We limit streams since they not only take up memory and CPU. // The Memory limit protects us on the memory side, // but a StreamsInbound limit helps protect against unbound CPU consumption from stream processing. Streams: bigEnough, - StreamsInbound: rcmgr.DefaultLimits.SystemBaseLimit.StreamsInbound, + StreamsInbound: bigEnough, StreamsOutbound: bigEnough, }, // Most limits don't see an increase because they're already infinite/bigEnough or at their max value. @@ -84,7 +84,7 @@ func createDefaultLimitConfig(cfg config.SwarmConfig) (rcmgr.LimitConfig, error) ConnsOutbound: 0, Streams: 0, - StreamsInbound: rcmgr.DefaultLimits.SystemLimitIncrease.StreamsInbound, + StreamsInbound: 0, StreamsOutbound: 0, }, diff --git a/core/node/libp2p/relay.go b/core/node/libp2p/relay.go index dcbc4e596..7b1fc0a02 100644 --- a/core/node/libp2p/relay.go +++ b/core/node/libp2p/relay.go @@ -3,7 +3,7 @@ package libp2p import ( "context" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/host/autorelay" diff --git a/core/node/libp2p/routing.go b/core/node/libp2p/routing.go index 2a9dd3845..2ae367c71 100644 --- a/core/node/libp2p/routing.go +++ b/core/node/libp2p/routing.go @@ -10,7 +10,7 @@ import ( "github.com/bittorrent/go-btfs/core/node/helpers" irouting "github.com/bittorrent/go-btfs/routing" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/repo" ds "github.com/ipfs/go-datastore" offroute "github.com/ipfs/go-ipfs-routing/offline" diff --git a/core/node/libp2p/routingopt.go b/core/node/libp2p/routingopt.go index b9fbec2a7..ccbd748c2 100644 --- a/core/node/libp2p/routingopt.go +++ b/core/node/libp2p/routingopt.go @@ -3,7 +3,7 @@ package libp2p import ( "context" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" irouting "github.com/bittorrent/go-btfs/routing" "github.com/ipfs/go-datastore" dht "github.com/libp2p/go-libp2p-kad-dht" diff --git a/core/node/libp2p/sec.go b/core/node/libp2p/sec.go index 7a2058127..7ef4a3c4c 100644 --- a/core/node/libp2p/sec.go +++ b/core/node/libp2p/sec.go @@ -1,7 +1,7 @@ package libp2p import ( - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/p2p/security/noise" tls "github.com/libp2p/go-libp2p/p2p/security/tls" diff --git a/core/node/libp2p/smux.go b/core/node/libp2p/smux.go index 9a5433b00..dce8ebed1 100644 --- a/core/node/libp2p/smux.go +++ b/core/node/libp2p/smux.go @@ -5,7 +5,7 @@ import ( "os" "strings" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/libp2p/go-libp2p" smux "github.com/libp2p/go-libp2p/core/network" mplex "github.com/libp2p/go-libp2p/p2p/muxer/mplex" diff --git a/core/node/libp2p/transport.go b/core/node/libp2p/transport.go index 5a9eb2e64..36e72aff6 100644 --- a/core/node/libp2p/transport.go +++ b/core/node/libp2p/transport.go @@ -3,7 +3,7 @@ package libp2p import ( "fmt" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/metrics" quic "github.com/libp2p/go-libp2p/p2p/transport/quic" diff --git a/core/node/storage.go b/core/node/storage.go index 0e8e301fb..47916177c 100644 --- a/core/node/storage.go +++ b/core/node/storage.go @@ -6,7 +6,7 @@ import ( "github.com/bittorrent/go-btfs/thirdparty/cidv0v1" "github.com/bittorrent/go-btfs/thirdparty/verifbs" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" "github.com/ipfs/go-filestore" blockstore "github.com/ipfs/go-ipfs-blockstore" diff --git a/core/wallet/aes.go b/core/wallet/aes.go deleted file mode 100644 index 2c409d73e..000000000 --- a/core/wallet/aes.go +++ /dev/null @@ -1,73 +0,0 @@ -package wallet - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/md5" - "encoding/base64" - "errors" -) - -var iv = []byte{0x02, 0x00, 0x01, 0x06, 0x00, 0x08, 0x01, 0x04, 0x02, 0x00, 0x01, 0x06, 0x00, 0x08, 0x01, 0x04} - -func EncryptWithAES(key, message string) (string, error) { - hash := md5.New() - hash.Write([]byte(key)) - keyData := hash.Sum(nil) - - block, err := aes.NewCipher(keyData) - if err != nil { - return "", err - } - - enc := cipher.NewCBCEncrypter(block, iv) - content, err := PKCS5Padding([]byte(message), block.BlockSize()) - if err != nil { - return "", err - } - crypted := make([]byte, len(content)) - enc.CryptBlocks(crypted, content) - return base64.StdEncoding.EncodeToString(crypted), nil -} - -func DecryptWithAES(key, message string) (string, error) { - hash := md5.New() - hash.Write([]byte(key)) - keyData := hash.Sum(nil) - - block, err := aes.NewCipher(keyData) - if err != nil { - return "", err - } - - messageData, err := base64.StdEncoding.DecodeString(message) - if err != nil { - return "", err - } - dec := cipher.NewCBCDecrypter(block, iv) - decrypted := make([]byte, len(messageData)) - dec.CryptBlocks(decrypted, messageData) - unpadding, err := PKCS5Unpadding(decrypted) - if err != nil { - return "", err - } - return string(unpadding), nil -} - -func PKCS5Padding(ciphertext []byte, blockSize int) ([]byte, error) { - padding := blockSize - len(ciphertext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...), nil -} - -func PKCS5Unpadding(encrypt []byte) ([]byte, error) { - if len(encrypt) == 0 { - return nil, errors.New("array index out of bound") - } - padding := encrypt[len(encrypt)-1] - if len(encrypt) < int(padding) { - return nil, errors.New("array index out of bound") - } - return encrypt[:len(encrypt)-int(padding)], nil -} diff --git a/core/wallet/helper.go b/core/wallet/helper.go deleted file mode 100644 index fc86f6693..000000000 --- a/core/wallet/helper.go +++ /dev/null @@ -1,16 +0,0 @@ -package wallet - -import ( - "strings" - - config "github.com/TRON-US/go-btfs-config" -) - -func getTokenId(cfg *config.Config) string { - tokenId := TokenId - if strings.Contains(cfg.Services.EscrowDomain, "dev") || - strings.Contains(cfg.Services.EscrowDomain, "staging") { - tokenId = TokenIdDev - } - return tokenId -} diff --git a/core/wallet/import.go b/core/wallet/import.go deleted file mode 100644 index 4abe3e53d..000000000 --- a/core/wallet/import.go +++ /dev/null @@ -1,75 +0,0 @@ -package wallet - -import ( - "encoding/base64" - "encoding/hex" - "fmt" - "os" - "strings" - - "github.com/bittorrent/go-btfs/cmd/btfs/util" - "github.com/bittorrent/go-btfs/core" - - config "github.com/TRON-US/go-btfs-config" -) - -func SetKeys(n *core.IpfsNode, privKey string, mnemonic string) (err error) { - var privK, m string - if mnemonic != "" { - mnemonic = strings.ReplaceAll(mnemonic, " ", ",") - privK, m, err = util.GenerateKey("", "BIP39", mnemonic) - if err != nil { - return err - } - } else if privKey != "" { - privKey, err = privKeyToHex(privKey) - if err != nil { - return err - } - privK, m, err = util.GenerateKey(privKey, "Secp256k1", "") - if err != nil { - return err - } - } - identity, err := config.IdentityConfig(os.Stdout, util.NBitsForKeypairDefault, "Secp256k1", privK, m) - if err != nil { - return err - } - cfg, err := n.Repo.Config() - if err != nil { - return err - } - cfg.Identity = identity - cfg.UI.Wallet.Initialized = false - err = n.Repo.SetConfig(cfg) - if err != nil { - return err - } - return nil -} - -func privKeyToHex(input string) (string, error) { - isHex := true - for _, v := range input { - // 0-9 || A-F || a-f - if !(v >= 48 && v <= 57 || v >= 65 && v <= 70 || v >= 97 && v <= 102) { - isHex = false - break - } - } - if !isHex { - bytes, err := base64.StdEncoding.DecodeString(input) - if err != nil { - return "", err - } - if len(bytes) != 36 || !strings.HasPrefix(input, "CAISI") { - return "", fmt.Errorf("invalid privKey: %s", input) - } - return hex.EncodeToString(bytes[4:]), nil - } - _, err := hex.DecodeString(input) - if err != nil { - return "", err - } - return input, nil -} diff --git a/core/wallet/import_test.go b/core/wallet/import_test.go deleted file mode 100644 index 9d82d004e..000000000 --- a/core/wallet/import_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package wallet - -import ( - "testing" - - coremock "github.com/bittorrent/go-btfs/core/mock" - - "github.com/stretchr/testify/assert" -) - -const ( - expectedPrivKeyBase64 = "CAISIFmxIUg17m/CM3nAeRAsjKHMb7pgkVmCCYfEFyES9Jkx" - expectedPrivKeyHex = "59b1214835ee6fc23379c079102c8ca1cc6fba609159820987c4172112f49931" - expectedMnemonic = "absurd adapt skin skin settle smart other table school toss give reform" -) - -func TestImportPrivateKey(t *testing.T) { - var testCases = []struct { - privKey string // input - mnemonic string // input - expectedPrivKey string // expected - expectedMnemonic string // expected - returnErr bool // expected - }{ - {"CAISIFmxIUg17m/CM3nAeRAsjKHMb7pgkVmCCYfEFyES9Jkx", "", expectedPrivKeyBase64, "", false}, - {"59b1214835ee6fc23379c079102c8ca1cc6fba609159820987c4172112f49931", "", expectedPrivKeyBase64, "", false}, - {"", "absurd adapt skin skin settle smart other table school toss give reform", expectedPrivKeyBase64, expectedMnemonic, false}, - {"CAISIFmxIUg17m/CM3nAeRAsjKHMb7pgkVmCCYfEFyES9Jkx", "absurd adapt skin skin settle smart other table school toss give reform", expectedPrivKeyBase64, expectedMnemonic, false}, - {"errorPrivKey", "", "", "", true}, - {"", "errorMnemonic", "", "", true}, - {"errorPrivKey", "errorMnemonic", "", "", true}, - } - - n, err := coremock.NewMockNode() - if err != nil { - t.Fatal(err) - } - cfg, err := n.Repo.Config() - if err != nil { - t.Fatal(err) - } - for _, tc := range testCases { - err := SetKeys(n, tc.privKey, tc.mnemonic) - assert.Equal(t, tc.returnErr, err != nil) - if err == nil { - assert.Equal(t, tc.expectedPrivKey, cfg.Identity.PrivKey) - assert.Equal(t, tc.expectedMnemonic, cfg.Identity.Mnemonic) - } - } -} diff --git a/core/wallet/ledger.go b/core/wallet/ledger.go deleted file mode 100644 index bfd3f4edd..000000000 --- a/core/wallet/ledger.go +++ /dev/null @@ -1,53 +0,0 @@ -package wallet - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" - walletpb "github.com/bittorrent/go-btfs/protos/wallet" - - ledgerpb "github.com/tron-us/go-btfs-common/protos/ledger" - - "github.com/golang/protobuf/proto" - "github.com/ipfs/go-datastore" -) - -const ( - channelKeyPrefix = "/ledger-channels" - channelKeyTemplate = channelKeyPrefix + "/%d" -) - -func save(ds datastore.Datastore, state *ledgerpb.ChannelState) error { - if state == nil || state.Id == nil { - return errors.New("state or state.Id is nil") - } - return sessions.Save(ds, k(state.Id.Id), &walletpb.ChannelState{State: state, TimeCreate: time.Now()}) -} - -func list(ds datastore.Datastore) ([]*walletpb.ChannelState, error) { - list, err := sessions.List(ds, channelKeyPrefix) - if err != nil { - return nil, err - } - var states []*walletpb.ChannelState - for _, e := range list { - state := &walletpb.ChannelState{} - if err := proto.Unmarshal(e, state); err != nil { - log.Debug(err) - continue - } - states = append(states, state) - } - return states, nil -} - -func rm(ctx context.Context, ds datastore.Datastore, channelId int64) error { - return ds.Delete(ctx, datastore.NewKey(k(channelId))) -} - -func k(channelId int64) string { - return fmt.Sprintf(channelKeyTemplate, channelId) -} diff --git a/core/wallet/signature.go b/core/wallet/signature.go deleted file mode 100644 index 25cae01be..000000000 --- a/core/wallet/signature.go +++ /dev/null @@ -1,113 +0,0 @@ -package wallet - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rand" - "crypto/sha256" - "errors" - "math/big" - "time" - - gbc_crypto "github.com/tron-us/go-btfs-common/crypto" - exPb "github.com/tron-us/go-btfs-common/protos/exchange" - ledgerPb "github.com/tron-us/go-btfs-common/protos/ledger" - corePb "github.com/tron-us/go-btfs-common/protos/protocol/core" - - "github.com/golang/protobuf/proto" -) - -type EcdsaSignature struct { - R, S *big.Int -} - -var ( - ErrTransactionParam = errors.New("transaction is nil") - ErrChannelStateParam = errors.New("channelState is nil") - ErrChannelCommitParam = errors.New("channelCommit is nil") - ErrTypeParam = errors.New("wrong type") -) - -//Sign a Transaction, ChannelState, ChannelCommit in exchange proto or tron proto or ledger proto. -//parameter 'in' can be Transaction, ChannelState, ChannelCommit, return signature. -func Sign(in interface{}, key *ecdsa.PrivateKey) ([]byte, error) { - switch in.(type) { - case *exPb.TronTransaction: - transaction := in.(*exPb.TronTransaction) - if transaction == nil { - return nil, ErrTransactionParam - } - - if transaction.GetRawData().Timestamp == 0 { - transaction.GetRawData().Timestamp = time.Now().UnixNano() / 1000000 - } - - rawData, err := proto.Marshal(transaction.GetRawData()) - if err != nil { - return nil, err - } - return SignTron(rawData, key) - - case *corePb.Transaction: - transaction := in.(*corePb.Transaction) - if transaction == nil { - return nil, ErrTransactionParam - } - - if transaction.GetRawData().Timestamp == 0 { - transaction.GetRawData().Timestamp = time.Now().UnixNano() / 1000000 - } - - rawData, err := proto.Marshal(transaction.GetRawData()) - if err != nil { - return nil, err - } - return SignTron(rawData, key) - - case *ledgerPb.ChannelState: - channelState := in.(*ledgerPb.ChannelState) - if channelState == nil { - return nil, ErrChannelStateParam - } - - raw, err := proto.Marshal(channelState) - if err != nil { - return nil, err - } - return SignChannel(raw, key) - - case *ledgerPb.ChannelCommit: - channelCommit := in.(*ledgerPb.ChannelCommit) - if channelCommit == nil { - return nil, ErrChannelCommitParam - } - - raw, err := proto.Marshal(channelCommit) - if err != nil { - return nil, err - } - return SignChannel(raw, key) - - default: - return nil, ErrTypeParam - } -} - -//Tron' Sign function, return signature and error. -func SignTron(rawData []byte, key *ecdsa.PrivateKey) ([]byte, error) { - signature, err := gbc_crypto.EcdsaSign(key, rawData) - if err != nil { - return nil, err - } - return signature, nil -} - -//Channel' sign function, return signature and error. -func SignChannel(raw []byte, key *ecdsa.PrivateKey) ([]byte, error) { - hash := sha256.Sum256(raw) - signature, err := key.Sign(rand.Reader, hash[:], crypto.SHA256) - if err != nil { - return nil, err - } - return signature, nil -} diff --git a/core/wallet/speed.go b/core/wallet/speed.go deleted file mode 100644 index 355201863..000000000 --- a/core/wallet/speed.go +++ /dev/null @@ -1,80 +0,0 @@ -package wallet - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/tron-us/go-btfs-common/crypto" -) - -const ( - portFileName = "port" - api = "http://127.0.0.1:%d/api" - keyUrl = api + "/private_key?pw=%s&t=%s" - tokenUrl = api + "/token" -) - -var ( - portFile = filepath.Join(portPath, portFileName) -) - -// return speed key in base64 -func DiscoverySpeedKey(password string) (string, error) { - password = url.QueryEscape(password) - if err := validateOs(); err != nil { - return "", err - } - pf, err := os.Open(portFile) - if err != nil { - return "", err - } - port, err := readPort(pf) - if err != nil { - return "", err - } - token, err := get(fmt.Sprintf(tokenUrl, port)) - if err != nil { - return "", err - } - key, err := get(fmt.Sprintf(keyUrl, port, password, token)) - if err != nil { - return "", err - } - if key == "" { - return "", errors.New("invalid private key") - } - base64, err := crypto.Hex64ToBase64(key) - if err != nil { - return "", err - } - return base64, nil -} - -func readPort(r io.Reader) (int64, error) { - bytes, err := ioutil.ReadAll(r) - if err != nil { - return -1, err - } - return strconv.ParseInt(strings.TrimSpace(string(bytes)), 10, 32) -} - -func get(url string) (string, error) { - resp, err := http.Get(url) - if err != nil { - return "", err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - return string(body), nil -} diff --git a/core/wallet/speed_darwin.go b/core/wallet/speed_darwin.go deleted file mode 100644 index e08891e3c..000000000 --- a/core/wallet/speed_darwin.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build darwin -// +build darwin - -package wallet - -import ( - "os" - "path/filepath" -) - -var portPath = func() string { - dir, err := os.UserHomeDir() - if err != nil { - return "~/" - } - return filepath.Join(dir, "Library/Application Support/uTorrent Web/BitTorrentHelper/") -}() - -func validateOs() error { - return nil -} diff --git a/core/wallet/speed_other.go b/core/wallet/speed_other.go deleted file mode 100644 index 35c32023e..000000000 --- a/core/wallet/speed_other.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !windows && !darwin -// +build !windows,!darwin - -package wallet - -import "errors" - -const ( - portPath = "" -) - -func validateOs() error { - return errors.New("not support os type") -} diff --git a/core/wallet/speed_test.go b/core/wallet/speed_test.go deleted file mode 100644 index 21c3e34cb..000000000 --- a/core/wallet/speed_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package wallet - -import ( - "net/http" - "net/http/httptest" - "runtime" - "strings" - "testing" - - "github.com/tron-us/go-btfs-common/crypto" - - "github.com/mitchellh/go-homedir" - "github.com/stretchr/testify/assert" -) - -func TestReadPort(t *testing.T) { - switch runtime.GOOS { - case "darwin": - e, err := homedir.Expand("~/Library/Application Support/uTorrent Web/BitTorrentHelper/") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, e, portPath) - case "windows": - assert.Equal(t, "%AppData%/../Local/BitTorrentHelper/", portPath) - default: - assert.Equal(t, "", portPath) - } - port, err := readPort(strings.NewReader("\n8888\r\n ")) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, int64(8888), port) -} - -func TestGetPlainKey(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - switch r.URL.EscapedPath() { - case "/api/private_key": - w.Write([]byte("c14f99e28b64abfb743a88002939f776b7f9ebab9aeac5cb7340daf7be81c2a1")) - case "/api/token": - w.Write([]byte("token1")) - } - if r.Method != "GET" { - t.Errorf("Expected 'GET' request, got '%s'", r.Method) - } - if r.URL.EscapedPath() != "/api/private_key" && r.URL.EscapedPath() != "/api/token" { - t.Errorf("Expected request to '/api/private_key' or '/api/token', got '%s'", r.URL.EscapedPath()) - } - })) - defer ts.Close() - token, err := get(ts.URL + "/api/token") - assert.Equal(t, "token1", token) - key, err := get(ts.URL + "/api/private_key") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, "c14f99e28b64abfb743a88002939f776b7f9ebab9aeac5cb7340daf7be81c2a1", key) -} - -func TestHexToBase64(t *testing.T) { - base64, err := crypto.Hex64ToBase64("c14f99e28b64abfb743a88002939f776b7f9ebab9aeac5cb7340daf7be81c2a1") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, "CAISIMFPmeKLZKv7dDqIACk593a3+eurmurFy3NA2ve+gcKh", base64) -} diff --git a/core/wallet/speed_windows.go b/core/wallet/speed_windows.go deleted file mode 100644 index f9aa35d58..000000000 --- a/core/wallet/speed_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build windows -// +build windows - -package wallet - -import "os" - -var portPath = func() string { - home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - if home == "" { - home = os.Getenv("USERPROFILE") - } - return home + "\\AppData\\Local\\BitTorrentHelper\\" -}() - -func validateOs() error { - return nil -} diff --git a/core/wallet/transaction.go b/core/wallet/transaction.go deleted file mode 100644 index 6009fbc61..000000000 --- a/core/wallet/transaction.go +++ /dev/null @@ -1,732 +0,0 @@ -package wallet - -import ( - "context" - "crypto/ecdsa" - "encoding/hex" - "errors" - "fmt" - "sort" - "strconv" - "time" - - "github.com/bittorrent/go-btfs/core" - "github.com/bittorrent/go-btfs/core/commands/storage/upload/sessions" - walletpb "github.com/bittorrent/go-btfs/protos/wallet" - - config "github.com/TRON-US/go-btfs-config" - escrowPb "github.com/tron-us/go-btfs-common/protos/escrow" - exPb "github.com/tron-us/go-btfs-common/protos/exchange" - ledgerPb "github.com/tron-us/go-btfs-common/protos/ledger" - tronPb "github.com/tron-us/go-btfs-common/protos/protocol/api" - corePb "github.com/tron-us/go-btfs-common/protos/protocol/core" - "github.com/tron-us/go-btfs-common/utils/grpc" - - "github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-datastore" -) - -var ( - ErrInsufficientExchangeBalanceOnTron = errors.New("exchange balance on Tron network is not sufficient") - ErrInsufficientUserBalanceOnTron = errors.New(fmt.Sprint("User balance on tron network is not sufficient.")) - ErrInsufficientUserBalanceOnLedger = errors.New("rpc error: code = ResourceExhausted desc = NSF") - ErrInsufficientExchangeBalanceOnLedger = errors.New("exchange balance on Private Ledger is not sufficient") -) - -// Do the deposit action, integrate exchange's PrepareDeposit and Deposit API. -func Deposit(ctx context.Context, n *core.IpfsNode, ledgerAddr []byte, amount int64, - privateKey *ecdsa.PrivateKey, runDaemon bool, async bool) (*exPb.PrepareDepositResponse, error) { - log.Debug("Deposit begin!") - //PrepareDeposit - prepareResponse, err := PrepareDeposit(ctx, ledgerAddr, amount) - if err != nil { - return nil, err - } - if prepareResponse.Response.Code != exPb.Response_SUCCESS { - return prepareResponse, errors.New(string(prepareResponse.Response.ReturnMessage)) - } - log.Debug(fmt.Sprintf("PrepareDeposit success, id: [%d]", prepareResponse.GetId())) - - //Do the DepositRequest. - depositResponse, err := DepositRequest(ctx, prepareResponse, privateKey) - if err != nil { - return prepareResponse, err - } - if depositResponse.Response.Code != exPb.Response_SUCCESS { - return prepareResponse, errors.New(string(depositResponse.Response.ReturnMessage)) - } - log.Debug(fmt.Sprintf("Call Deposit API success, id: [%d]", prepareResponse.GetId())) - - err = PersistTx(n.Repo.Datastore(), n.Identity.Pretty(), strconv.FormatInt(prepareResponse.GetId(), 10), - amount, BttWallet, InAppWallet, StatusPending, walletpb.TransactionV1_EXCHANGE) - if err != nil { - return nil, err - } - - if runDaemon && async { - go ConfirmDepositProcess(context.Background(), n, prepareResponse, privateKey) - } else { - err := ConfirmDepositProcess(ctx, n, prepareResponse, privateKey) - if err != nil { - return nil, err - } - } - // Doing confirm deposit. - - log.Debug("Deposit end!") - return prepareResponse, nil -} - -// Call exchange's PrepareDeposit API. -func PrepareDeposit(ctx context.Context, ledgerAddr []byte, amount int64) (*exPb.PrepareDepositResponse, error) { - // Prepare to Deposit. - var err error - var prepareResponse *exPb.PrepareDepositResponse - err = grpc.ExchangeClient(exchangeService).WithContext(ctx, - func(ctx context.Context, client exPb.ExchangeClient) error { - prepareDepositRequest := &exPb.PrepareDepositRequest{Amount: amount, OutTxId: time.Now().UnixNano(), - UserAddress: ledgerAddr} - prepareResponse, err = client.PrepareDeposit(ctx, prepareDepositRequest) - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } - return prepareResponse, nil -} - -// Call exchange's Deposit API -func DepositRequest(ctx context.Context, prepareResponse *exPb.PrepareDepositResponse, privateKey *ecdsa.PrivateKey) (*exPb.DepositResponse, error) { - // Sign Tron Transaction. - tronTransaction := prepareResponse.GetTronTransaction() - for range tronTransaction.GetRawData().GetContract() { - signature, err := Sign(tronTransaction, privateKey) - if err != nil { - return nil, err - } - tronTransaction.Signature = append(tronTransaction.GetSignature(), signature) - } - - var err error - var depositResponse *exPb.DepositResponse - err = grpc.ExchangeClient(exchangeService).WithContext(ctx, - func(ctx context.Context, client exPb.ExchangeClient) error { - depositRequest := &exPb.DepositRequest{Id: prepareResponse.GetId(), SignedTronTransaction: tronTransaction} - - depositResponse, err = client.Deposit(ctx, depositRequest) - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } - return depositResponse, nil -} - -// Continuous call ConfirmDeposit until it responses a FAILED or SUCCESS. -func ConfirmDepositProcess(ctx context.Context, n *core.IpfsNode, prepareResponse *exPb.PrepareDepositResponse, - privateKey *ecdsa.PrivateKey) error { - log.Debug(fmt.Sprintf("[Id:%d] ConfirmDepositProcess begin.", prepareResponse.GetId())) - - // Continuous call ConfirmDeposit until it responses a FAILED or SUCCESS. - now := time.Now().UnixNano() / 1e6 - for time.Now().UnixNano()/1e6 < (now + 7*86400*1000) { - time.Sleep(5 * time.Second) - - // ConfirmDeposit after 1min, because watcher need wait for 1min to confirm tron transaction. - if time.Now().UnixNano()/1e6 < (prepareResponse.GetTronTransaction().GetRawData().GetTimestamp() + 60*1000) { - continue - } - log.Debug(fmt.Sprintf("[Id:%d] ConfirmDeposit begin.", prepareResponse.GetId())) - - confirmDepositResponse, err := ConfirmDeposit(ctx, prepareResponse.GetId()) - if err != nil { - continue - } - if confirmDepositResponse == nil || confirmDepositResponse.SuccessChannelState == nil { - continue - } - err = save(n.Repo.Datastore(), confirmDepositResponse.SuccessChannelState.Channel) - if err != nil { - log.Debug(err) - } - txId := strconv.FormatInt(prepareResponse.GetId(), 10) - if confirmDepositResponse.GetResponse().GetCode() == exPb.Response_TRANSACTION_PENDING { - log.Debug(fmt.Sprintf("[Id:%d]TronTransaction is PENDING.", prepareResponse.GetId())) - - err := UpdateStatus(n.Repo.Datastore(), n.Identity.Pretty(), txId, StatusPending) - if err != nil { - return err - } - continue - } - if confirmDepositResponse.GetResponse().GetCode() == exPb.Response_TRANSACTION_FAILED { - return UpdateStatus(n.Repo.Datastore(), n.Identity.Pretty(), txId, StatusFailed) - } - if confirmDepositResponse.GetResponse().GetCode() == exPb.Response_SUCCESS { - signSuccessChannelState := confirmDepositResponse.GetSuccessChannelState() - if signSuccessChannelState != nil { - toSignature, err := Sign(signSuccessChannelState.GetChannel(), privateKey) - if err != nil { - return err - } - signSuccessChannelState.ToSignature = toSignature - } else { - return fmt.Errorf("[Id:%d] SignSuccessChannelState is nil", prepareResponse.GetId()) - } - - for i := 0; i < 10; i++ { - err = grpc.EscrowClient(escrowService).WithContext(ctx, - func(ctx context.Context, client escrowPb.EscrowServiceClient) error { - _, err = client.CloseChannel(ctx, signSuccessChannelState) - if err != nil { - return err - } - return nil - }) - if err == nil { - break - } - time.Sleep(5 * time.Second) - } - if err != nil { - return err - } - log.Info(fmt.Sprintf("[Id:%d] Close SuccessChannelState succeed.", prepareResponse.GetId())) - err = UpdateStatus(n.Repo.Datastore(), n.Identity.Pretty(), txId, StatusSuccess) - if err != nil { - return err - } - return nil - } - } - if time.Now().UnixNano()/1e6 >= (prepareResponse.GetTronTransaction().GetRawData().GetExpiration() + 240*1000) { - err := fmt.Errorf("[Id:%d] Didn't get the tron transaction results until the expiration time.", - prepareResponse.GetId()) - return err - } - return nil -} - -// Call exchange's ConfirmDeposit API. -func ConfirmDeposit(ctx context.Context, logId int64) (*exPb.ConfirmDepositResponse, error) { - var err error - var confirmDepositResponse *exPb.ConfirmDepositResponse - err = grpc.ExchangeClient(exchangeService).WithContext(ctx, - func(ctx context.Context, client exPb.ExchangeClient) error { - confirmDepositRequest := &exPb.ConfirmDepositRequest{Id: logId} - confirmDepositResponse, err = client.ConfirmDeposit(ctx, confirmDepositRequest) - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } - return confirmDepositResponse, nil -} - -// Do the withdraw action, integrate exchange's PrepareWithdraw and Withdraw API, return channel id and error. -// If Withdraw succeed, with return channel id and logInfo id, error is nil; otherwise will return error, channel id -// and logInfo id is 0. -func Withdraw(ctx context.Context, n *core.IpfsNode, ledgerAddr, externalAddr []byte, amount int64, - privateKey *ecdsa.PrivateKey) (int64, int64, error) { - log.Debug("Withdraw begin!") - outTxId := time.Now().UnixNano() - //PrepareWithdraw - prepareResponse, err := PrepareWithdraw(ctx, ledgerAddr, externalAddr, amount, outTxId) - if err != nil { - return 0, 0, err - } - if prepareResponse.Response.Code != exPb.Response_SUCCESS { - return 0, 0, errors.New(string(prepareResponse.Response.ReturnMessage)) - } - log.Debug(fmt.Sprintf("Prepare withdraw success, id: [%d]", prepareResponse.GetId())) - - channelCommit := &ledgerPb.ChannelCommit{ - Payer: &ledgerPb.PublicKey{Key: ledgerAddr}, - Recipient: &ledgerPb.PublicKey{Key: prepareResponse.GetLedgerExchangeAddress()}, - Amount: amount, - PayerId: time.Now().UnixNano() + prepareResponse.GetId(), - } - //Sign channel commit. - signature, err := Sign(channelCommit, privateKey) - if err != nil { - return 0, 0, err - } - - var channelId *ledgerPb.ChannelID - err = grpc.EscrowClient(escrowService).WithContext(ctx, - func(ctx context.Context, client escrowPb.EscrowServiceClient) error { - channelId, err = client.CreateChannel(ctx, - &ledgerPb.SignedChannelCommit{Channel: channelCommit, Signature: signature}) - if err != nil { - if err.Error() == ErrInsufficientUserBalanceOnLedger.Error() { - return ErrInsufficientUserBalanceOnLedger - } - return err - } - return nil - }) - if err != nil { - return 0, 0, err - } - log.Debug(fmt.Sprintf("CreateChannel success, channelId: [%d]", channelId.GetId())) - - //Do the WithdrawRequest. - withdrawResponse, err := WithdrawRequest(ctx, channelId, ledgerAddr, amount, prepareResponse, privateKey) - if err != nil { - return 0, 0, err - } - - txId := strconv.FormatInt(prepareResponse.GetId(), 10) - err = PersistTx(n.Repo.Datastore(), n.Identity.Pretty(), txId, amount, - InAppWallet, BttWallet, StatusPending, walletpb.TransactionV1_EXCHANGE) - if err != nil { - return 0, 0, err - } - - if withdrawResponse.Response.Code != exPb.Response_SUCCESS { - err := UpdateStatus(n.Repo.Datastore(), n.Identity.Pretty(), txId, StatusFailed) - if err != nil { - return 0, 0, err - } - return 0, 0, errors.New(string(withdrawResponse.Response.ReturnMessage)) - } - log.Debug("Withdraw end!") - err = UpdateStatus(n.Repo.Datastore(), n.Identity.Pretty(), txId, StatusSuccess) - if err != nil { - return 0, 0, err - } - return channelId.Id, prepareResponse.GetId(), nil -} - -// Call exchange's Withdraw API -func PrepareWithdraw(ctx context.Context, ledgerAddr, externalAddr []byte, amount, outTxId int64) ( - *exPb.PrepareWithdrawResponse, error) { - var err error - var prepareResponse *exPb.PrepareWithdrawResponse - err = grpc.ExchangeClient(exchangeService).WithContext(ctx, - func(ctx context.Context, client exPb.ExchangeClient) error { - prepareWithdrawRequest := &exPb.PrepareWithdrawRequest{ - Amount: amount, OutTxId: outTxId, UserAddress: ledgerAddr, UserExternalAddress: externalAddr} - prepareResponse, err = client.PrepareWithdraw(ctx, prepareWithdrawRequest) - if err != nil { - return err - } - log.Debug(prepareResponse) - return nil - }) - if err != nil { - return nil, err - } - - return prepareResponse, nil -} - -// Call exchange's PrepareWithdraw API -func WithdrawRequest(ctx context.Context, channelId *ledgerPb.ChannelID, ledgerAddr []byte, amount int64, - prepareResponse *exPb.PrepareWithdrawResponse, privateKey *ecdsa.PrivateKey) (*exPb.WithdrawResponse, error) { - //make signed success channel state. - successChannelState := &ledgerPb.ChannelState{ - Id: channelId, - Sequence: 1, - From: &ledgerPb.Account{ - Address: &ledgerPb.PublicKey{ - Key: ledgerAddr, - }, - Balance: 0, - }, - To: &ledgerPb.Account{ - Address: &ledgerPb.PublicKey{ - Key: prepareResponse.GetLedgerExchangeAddress(), - }, - Balance: amount, - }, - } - successSignature, err := Sign(successChannelState, privateKey) - if err != nil { - return nil, err - } - successChannelStateSigned := &ledgerPb.SignedChannelState{Channel: successChannelState, FromSignature: successSignature} - - //make signed fail channel state. - failChannelState := &ledgerPb.ChannelState{ - Id: channelId, - Sequence: 1, - From: &ledgerPb.Account{ - Address: &ledgerPb.PublicKey{ - Key: ledgerAddr, - }, - Balance: amount, - }, - To: &ledgerPb.Account{ - Address: &ledgerPb.PublicKey{ - Key: prepareResponse.GetLedgerExchangeAddress(), - }, - Balance: 0, - }, - } - failSignature, err := Sign(failChannelState, privateKey) - if err != nil { - return nil, err - } - - var withdrawResponse *exPb.WithdrawResponse - err = grpc.ExchangeClient(exchangeService).WithContext(ctx, - func(ctx context.Context, client exPb.ExchangeClient) error { - failChannelStateSigned := &ledgerPb.SignedChannelState{Channel: failChannelState, FromSignature: failSignature} - //Post the withdraw request. - withdrawRequest := &exPb.WithdrawRequest{ - Id: prepareResponse.GetId(), - SuccessChannelState: successChannelStateSigned, - FailureChannelState: failChannelStateSigned, - } - withdrawResponse, err = client.Withdraw(ctx, withdrawRequest) - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } - return withdrawResponse, nil -} - -// Get the token balance on tron blockchain -func GetTokenBalance(ctx context.Context, addr []byte, tokenId string) (int64, error) { - var tokenBalance int64 = 0 - err := grpc.SolidityClient(solidityService).WithContext(ctx, - func(ctx context.Context, client tronPb.WalletSolidityClient) error { - account := &corePb.Account{Address: addr} - myAccount, err := client.GetAccount(ctx, account) - if err != nil { - return err - } - tokenMap := myAccount.GetAssetV2() - if tokenMap == nil || len(tokenMap) == 0 { - return nil - } - tokenBalance = tokenMap[tokenId] - return nil - }) - if err != nil { - return 0, err - } - return tokenBalance, nil -} - -var ( - walletTransactionKeyPrefix = "/btfs/%v/wallet/transactions/" - walletTransactionKey = walletTransactionKeyPrefix + "%v/" - - walletTransactionV1KeyPrefix = "/btfs/%v/wallet/v1/transactions/" - walletTransactionV1Key = walletTransactionV1KeyPrefix + "%v/" -) - -func PersistTx(d ds.Datastore, peerId string, txId string, amount int64, - from string, to string, status string, txType walletpb.TransactionV1_Type) error { - return PersistTxWithTime(d, peerId, txId, amount, from, to, status, txType, time.Now()) -} - -func PersistTxWithTime(d ds.Datastore, peerId string, txId string, amount int64, - from string, to string, status string, txType walletpb.TransactionV1_Type, timeCreate time.Time) error { - return sessions.Save(d, fmt.Sprintf(walletTransactionV1Key, peerId, txId), - &walletpb.TransactionV1{ - Id: txId, - TimeCreate: timeCreate, - Amount: amount, - From: from, - To: to, - Status: status, - Type: txType, - }) -} - -func UpdateStatus(d ds.Datastore, peerId string, txId string, status string) error { - key := fmt.Sprintf(walletTransactionV1Key, peerId, txId) - s := &walletpb.TransactionV1{} - err := sessions.Get(d, key, s) - if err != nil { - return err - } - if s.Status != status { - s.Status = status - return sessions.Save(d, key, s) - } - return nil -} - -func GetTransactions(d ds.Datastore, peerId string) ([]*walletpb.TransactionV1, error) { - txs := make(TxSlice, 0) - v0Txs, err := loadV0Txs(d, peerId) - if err != nil || v0Txs == nil { - //ignore, NOP - err = nil - } - txs = append(txs, v0Txs...) - list, err := sessions.List(d, fmt.Sprintf(walletTransactionV1KeyPrefix, peerId)) - if err != nil { - return nil, err - } - for _, bytes := range list { - tx := new(walletpb.TransactionV1) - err := proto.Unmarshal(bytes, tx) - if err != nil { - return nil, err - } - txs = append(txs, tx) - } - sort.Sort(txs) - return txs, nil -} - -type TxSlice []*walletpb.TransactionV1 - -func (p TxSlice) Len() int { - return len(p) -} - -func (p TxSlice) Less(i, j int) bool { - return p[i].TimeCreate.After(p[j].TimeCreate) -} - -func (p TxSlice) Swap(i, j int) { - p[i], p[j] = p[j], p[i] -} - -func loadV0Txs(d ds.Datastore, peerId string) ([]*walletpb.TransactionV1, error) { - list, err := sessions.List(d, fmt.Sprintf(walletTransactionKeyPrefix, peerId)) - if err != nil { - return nil, err - } - txs := make([]*walletpb.TransactionV1, 0) - for _, bytes := range list { - tx := new(walletpb.Transaction) - err := proto.Unmarshal(bytes, tx) - if err != nil { - return nil, err - } - txs = append(txs, &walletpb.TransactionV1{ - Id: strconv.FormatInt(tx.Id, 10), - TimeCreate: tx.TimeCreate, - Amount: tx.Amount, - From: tx.From, - To: tx.To, - Status: tx.Status, - Type: walletpb.TransactionV1_EXCHANGE, - }) - } - return txs, nil -} - -func UpdatePendingTransactions(ctx context.Context, d ds.Datastore, cfg *config.Config, peerId string) (int, int, error) { - scv1, ecv1, err := updateV1Txs(ctx, d, cfg, peerId) - if err != nil { - return 0, 0, err - } - scv0, ecv0, err := updateV0Txs(ctx, d, cfg, peerId) - if err != nil { - return 0, 0, err - } - return scv1 + scv0, ecv1 + ecv0, nil -} - -func CloseLedgerChannel(ctx context.Context, d ds.Datastore, conf *config.Config) error { - if err := Init(ctx, conf); err != nil { - return err - } - states, err := list(d) - if err != nil { - return err - } - if len(states) > 0 { - err = grpc.EscrowClient(escrowService).WithContext(ctx, func(ctx context.Context, c escrowPb.EscrowServiceClient) error { - now := time.Now() - errors := make([]error, 0) - for _, e := range states { - if now.Sub(e.TimeCreate) < 5*time.Minute { - continue - } - sig, err := Sign(e.State, hostWallet.privateKey) - if err != nil { - errors = append(errors, err) - continue - } - _, err = c.CloseChannel(ctx, &ledgerPb.SignedChannelState{ - Channel: e.State, - ToSignature: sig, - }) - if err != nil { - errors = append(errors, err) - continue - } - if err := rm(ctx, d, e.State.Id.Id); err != nil { - errors = append(errors, err) - } - } - if len(errors) > 0 { - return errors[len(errors)-1] - } - return nil - }) - if err != nil { - return err - } - } - return nil -} - -func updateV0Txs(ctx context.Context, d ds.Datastore, cfg *config.Config, peerId string) (int, int, error) { - list, err := sessions.List(d, fmt.Sprintf(walletTransactionKeyPrefix, peerId)) - if err != nil { - return 0, 0, err - } - successCount := 0 - errorCount := 0 - for _, bytes := range list { - tx := new(walletpb.Transaction) - err := proto.Unmarshal(bytes, tx) - if err != nil { - errorCount++ - continue - } - if tx.Status != StatusPending { - continue - } - - txId := strconv.FormatInt(tx.Id, 10) - status, err := getExchangeTxStatus(ctx, cfg, txId) - if err != nil { - errorCount++ - continue - } - if status == StatusPending { - continue - } - err = UpdateStatus(d, peerId, txId, status) - if err != nil { - errorCount++ - continue - } - successCount++ - } - return successCount, errorCount, nil -} - -func updateV1Txs(ctx context.Context, d ds.Datastore, cfg *config.Config, peerId string) (int, int, error) { - list, err := sessions.List(d, fmt.Sprintf(walletTransactionV1KeyPrefix, peerId)) - if err != nil { - return 0, 0, err - } - successCount := 0 - errorCount := 0 - for _, bytes := range list { - tx := new(walletpb.TransactionV1) - err := proto.Unmarshal(bytes, tx) - if err != nil { - errorCount++ - continue - } - if tx.Status != StatusPending { - continue - } - switch tx.Type { - case walletpb.TransactionV1_EXCHANGE: - status, err := getExchangeTxStatus(ctx, cfg, tx.Id) - if err != nil { - errorCount++ - continue - } - if status != StatusPending && status != exPb.Response_TRANSACTION_PENDING.String() { - err := UpdateStatus(d, peerId, tx.Id, status) - if err != nil { - errorCount++ - continue - } - } - case walletpb.TransactionV1_ON_CHAIN: - status, err := getOnChainTxStatus(ctx, d, cfg, peerId, tx.Id) - if err != nil { - errorCount++ - continue - } - if status != StatusPending { - err := UpdateStatus(d, peerId, tx.Id, status) - if err != nil { - errorCount++ - continue - } - } - case walletpb.TransactionV1_OFF_CHAIN: - } - } - successCount++ - return successCount, errorCount, nil -} - -func getExchangeTxStatus(ctx context.Context, cfg *config.Config, txIdStr string) (string, error) { - txId, err := strconv.ParseInt(txIdStr, 10, 64) - if err != nil { - return "", err - } - in := &exPb.QueryTransactionRequest{ - Id: txId, - } - var resp *exPb.QueryTransactionResponse - err = grpc.ExchangeClient(cfg.Services.ExchangeDomain).WithContext(ctx, func(ctx context.Context, client exPb.ExchangeClient) error { - resp, err = client.QueryTransaction(ctx, in) - if err != nil { - return err - } - return nil - }) - if err != nil { - return "", err - } - status := resp.Response.Code.String() - switch status { - case exPb.Response_TRANSACTION_PENDING.String(): - status = StatusPending - case exPb.Response_TRANSACTION_FAILED.String(): - status = StatusFailed - case exPb.Response_SUCCESS.String(): - status = StatusSuccess - } - return status, err -} - -func getOnChainTxStatus(ctx context.Context, d ds.Datastore, cfg *config.Config, peerId string, txId string) (string, error) { - status := StatusPending - err := grpc.WalletClient(cfg.Services.FullnodeDomain).WithContext(ctx, func(ctx context.Context, client tronPb.WalletClient) error { - bytes, err := hex.DecodeString(txId) - if err != nil { - return err - } - in := &tronPb.BytesMessage{ - Value: bytes, - } - resp, err := client.GetTransactionInfoById(ctx, in) - if err != nil { - return err - } - status = resp.Result.String() - return nil - }) - if err != nil { - return "", err - } - if status == corePb.TransactionInfo_SUCESS.String() { - status = StatusSuccess - } else if status == corePb.TransactionInfo_FAILED.String() { - status = StatusFailed - } - return status, nil -} diff --git a/core/wallet/tron.go b/core/wallet/tron.go deleted file mode 100644 index 43eefccd5..000000000 --- a/core/wallet/tron.go +++ /dev/null @@ -1,372 +0,0 @@ -package wallet - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strconv" - "strings" - "time" - - "github.com/thedevsaddam/gojsonq/v2" - - "github.com/bittorrent/go-btfs/core" - walletpb "github.com/bittorrent/go-btfs/protos/wallet" - - config "github.com/TRON-US/go-btfs-config" - "github.com/tron-us/go-btfs-common/crypto" - tronPb "github.com/tron-us/go-btfs-common/protos/protocol/api" - - protocol_core "github.com/tron-us/go-btfs-common/protos/protocol/core" - "github.com/tron-us/go-btfs-common/utils/grpc" - - "github.com/gogo/protobuf/proto" - "github.com/ipfs/go-datastore" - ic "github.com/libp2p/go-libp2p/core/crypto" - "github.com/mr-tron/base58/base58" - "github.com/status-im/keycard-go/hexutils" -) - -var ( - txUrlTemplate = "%s/v1/accounts/%s/transactions?limit=200&only_to=true&order_by=block_timestamp,asc&min_block_timestamp=%d" - curBlockTimestampKey = "/accounts/%s/transactions/current/block_timestamp" - client = http.DefaultClient -) - -func SyncTxFromTronGrid(ctx context.Context, cfg *config.Config, ds datastore.Datastore) ([]*TxData, error) { - keys, err := crypto.FromPrivateKey(cfg.Identity.PrivKey) - if err != nil { - return nil, err - } - url := fmt.Sprintf(txUrlTemplate, cfg.Services.TrongridDomain, keys.Base58Address, 0) - if v, err := ds.Get(ctx, datastore.NewKey(fmt.Sprintf(curBlockTimestampKey, keys.Base58Address))); err == nil { - blockTimestamp, err := strconv.ParseInt(string(v), 10, 64) - url = fmt.Sprintf(txUrlTemplate, cfg.Services.TrongridDomain, keys.Base58Address, blockTimestamp) - if err != nil { - return nil, err - } - } - log.Debug("sync tx called", url) - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) - if err != nil { - return nil, err - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - resp.Body.Close() - jq := gojsonq.New().FromString(string(body)) - if s, ok := jq.Find("success").(bool); !ok || !s { - return nil, errors.New("fail to get latest transactions") - } - i := -1 - tds := make([]*TxData, 0) - var lastBlockTimestamp int64 = 0 - for true { - i++ - pfx := fmt.Sprintf("data.[%d]", i) - if v := jq.Reset().Find(pfx + ".raw_data.contract.[0].parameter.value"); v == nil { - break - } else { - m, ok := v.(map[string]interface{}) - if !ok { - continue - } - if m["asset_name"] != getTokenId(cfg) { - continue - } - from, err := hexToBase58(m["owner_address"].(string)) - if err != nil { - continue - } - to, err := hexToBase58(m["to_address"].(string)) - if err != nil { - continue - } - td := &TxData{ - amount: int64(m["amount"].(float64)), - assetName: m["asset_name"].(string), - from: from, - to: to, - } - if t := jq.Reset().Find(pfx + ".raw_data.timestamp"); t == nil { - continue - } else { - td.timestamp = int64(t.(float64)) - lastBlockTimestamp = td.timestamp - } - if txId := jq.Reset().Find(pfx + ".txID"); txId == nil { - continue - } else { - if err := PersistTxWithTime(ds, cfg.Identity.PeerID, txId.(string), td.amount, td.from, td.to, - StatusSuccess, walletpb.TransactionV1_ON_CHAIN, time.Unix(td.timestamp/1000, td.timestamp%1000)); err != nil { - log.Error(err) - } - } - tds = append(tds, td) - } - } - if len(tds) > 0 { - err := ds.Put(ctx, datastore.NewKey(fmt.Sprintf(curBlockTimestampKey, keys.Base58Address)), - []byte(strconv.FormatInt(lastBlockTimestamp, 10))) - if err != nil { - log.Debug(err) - } - } - return tds, nil -} - -func hexToBase58(h string) (string, error) { - bs, err := hex.DecodeString(h) - if err != nil { - return "", err - } - rs, err := crypto.Encode58Check(bs) - if err != nil { - return "", err - } - return rs, nil -} - -type TxData struct { - amount int64 - assetName string - from string - to string - timestamp int64 -} - -func TransferBTT(ctx context.Context, n *core.IpfsNode, cfg *config.Config, privKey ic.PrivKey, - from string, to string, amount int64) (*TronRet, error) { - return TransferBTTWithMemo(ctx, n, cfg, privKey, from, to, amount, "") -} - -func TransferBTTWithMemo(ctx context.Context, n *core.IpfsNode, cfg *config.Config, privKey ic.PrivKey, - from string, to string, amount int64, memo string) (*TronRet, error) { - var err error - if privKey == nil { - privKey, err = crypto.ToPrivKey(cfg.Identity.PrivKey) - if err != nil { - return nil, err - } - } - if from == "" { - keys, err := crypto.FromIcPrivateKey(privKey) - if err != nil { - return nil, err - } - from = keys.HexAddress - } - tx, err := PrepareTx(ctx, cfg, from, to, amount, memo) - if err != nil { - return nil, err - } - txId := "" - if tx.Txid != nil { - txId = hex.EncodeToString(tx.Txid) - } - raw, err := privKey.Raw() - if err != nil { - return nil, err - } - ecdsa, err := crypto.HexToECDSA(hex.EncodeToString(raw)) - if err != nil { - return nil, err - } - bs, err := proto.Marshal(tx.Transaction.RawData) - if err != nil { - return nil, err - } - sig, err := crypto.EcdsaSign(ecdsa, bs) - if err != nil { - return nil, err - } - rawBytes, err := proto.Marshal(tx.Transaction.RawData) - if err != nil { - return nil, err - } - err = SendRawTransaction(ctx, cfg.Services.FullnodeDomain, rawBytes, sig) - if err != nil { - return nil, err - } - err = PersistTx(n.Repo.Datastore(), n.Identity.String(), txId, amount, - BttWallet, to, StatusPending, walletpb.TransactionV1_ON_CHAIN) - if err != nil { - return nil, err - } - go func() { - // confirmed after 19 * 3 second/block - time.Sleep(1 * time.Minute) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := GetStatus(ctx, cfg.Services.SolidityDomain, txId) - if err != nil { - log.Error(err) - return - } - err = PersistTx(n.Repo.Datastore(), n.Identity.String(), txId, amount, - BttWallet, to, status, walletpb.TransactionV1_ON_CHAIN) - if err != nil { - log.Error(err) - return - } - }() - return &TronRet{ - Message: string(tx.Result.Message), - Result: tx.Result.Result, - Code: tx.Result.Code.String(), - TxId: hex.EncodeToString(tx.Txid), - }, nil -} - -// base58/hex -> hex -func toHex(address string) (string, error) { - if strings.HasPrefix(address, "T") { - bytes, err := base58.Decode(address) - if err != nil { - return "", err - } - if len(bytes) <= 4 { - return "", errors.New("invalid address") - } - address = hexutils.BytesToHex(bytes[:len(bytes)-4]) - } - return address, nil -} - -type TronRet struct { - Message string - Result bool - Code string - TxId string -} - -func PrepareTx(ctx context.Context, cfg *config.Config, from string, to string, amount int64, memo string) (*tronPb.TransactionExtention, error) { - var ( - tx *tronPb.TransactionExtention - err error - ) - from, err = toHex(from) - if err != nil { - return nil, err - } - to, err = toHex(to) - if err != nil { - return nil, err - } - oa, err := hex.DecodeString(from) - if err != nil { - return nil, err - } - ta, err := hex.DecodeString(to) - if err != nil { - return nil, err - } - tokenId := TokenId - if strings.Contains(cfg.Services.EscrowDomain, "dev") || - strings.Contains(cfg.Services.EscrowDomain, "staging") { - tokenId = TokenIdDev - } - err = grpc.WalletClient(cfg.Services.FullnodeDomain).WithContext(ctx, func(ctx context.Context, client tronPb.WalletClient) error { - tx, err = client.TransferAsset2(ctx, &protocol_core.TransferAssetContract{ - AssetName: []byte(tokenId), - OwnerAddress: oa, - ToAddress: ta, - Amount: amount, - }) - if err != nil { - return err - } - if !tx.Result.Result { - return errors.New(string(tx.Result.Message)) - } - return nil - }) - if err != nil { - return nil, err - } - tx.Transaction.RawData.Data = []byte(memo) - bs, err := proto.Marshal(tx.Transaction.RawData) - if err != nil { - return nil, err - } - hashed := sha256.Sum256(bs) - tx.Txid = hashed[:] - return tx, nil -} - -func SendRawTransaction(ctx context.Context, url string, raw []byte, sig []byte) error { - rawMsg := &protocol_core.TransactionRaw{} - err := proto.Unmarshal(raw, rawMsg) - if err != nil { - return err - } - tx := &protocol_core.Transaction{ - RawData: rawMsg, - Signature: [][]byte{sig}, - } - return grpc.WalletClient(url).WithContext(ctx, func(ctx context.Context, client tronPb.WalletClient) error { - _, err = client.BroadcastTransaction(ctx, tx) - return err - }) -} - -func GetStatus(ctx context.Context, url string, txId string) (string, error) { - txIdBytes, err := hex.DecodeString(txId) - if err != nil { - return "", err - } - var info *protocol_core.Transaction - err = grpc.SolidityClient(url).WithContext(ctx, func(ctx context.Context, client tronPb.WalletSolidityClient) error { - info, err = client.GetTransactionById(ctx, &tronPb.BytesMessage{Value: txIdBytes}) - return err - }) - if err != nil { - return StatusFailed, err - } - status := StatusPending - if info != nil && info.Ret != nil && len(info.Ret) > 0 && - info.Ret[0].ContractRet == protocol_core.Transaction_Result_SUCCESS { - status = StatusSuccess - } else { - status = StatusFailed - } - return status, nil -} - -func GetBalanceByWalletAddress(ctx context.Context, solidityUrl string, walletAddress string) (tokenMap map[string]int64, err error) { - address, err := toHex(walletAddress) - if err != nil { - return nil, err - } - - oa, err := hex.DecodeString(address) - if err != nil { - return nil, err - } - - err = grpc.SolidityClient(solidityUrl).WithContext(ctx, func(ctx context.Context, client tronPb.WalletSolidityClient) error { - account := &protocol_core.Account{Address: oa} - myAccount, err := client.GetAccount(ctx, account) - if err != nil { - return err - } - tokenMap = myAccount.GetAssetV2() - if tokenMap == nil { - tokenMap = make(map[string]int64) - } - tokenMap["TRX"] = myAccount.Balance - return nil - }) - - return tokenMap, err -} diff --git a/core/wallet/tron_test.go b/core/wallet/tron_test.go deleted file mode 100644 index 543edbe5c..000000000 --- a/core/wallet/tron_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package wallet - -import ( - "context" - "fmt" - "testing" - "time" - - coremock "github.com/bittorrent/go-btfs/core/mock" - - "github.com/tron-us/go-btfs-common/crypto" - - "github.com/stretchr/testify/assert" -) - -func TestGetHexAddress(t *testing.T) { - keys, err := crypto.FromPrivateKey("CAISILOZbORDZlczUlp5jdonb5y5SMZgaZy6OWp58SkS8jS8") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, "41bc8e7f3e2bb11310b75d6b0b6e8537d069cdb72e", keys.HexAddress) -} - -func TestTransferBTT(t *testing.T) { - node, err := coremock.NewMockNode() - if err != nil { - t.Fatal(err) - } - cfg, err := node.Repo.Config() - if err != nil { - t.Fatal(err) - } - cfg.Services.SolidityDomain = "grpc.shasta.trongrid.io:50052" - cfg.Services.FullnodeDomain = "grpc.shasta.trongrid.io:50051" - cfg.Services.EscrowDomain = "https://escrow-staging.btfs.io" - TokenIdDev = "1000252" - privKey, err := crypto.ToPrivKey("CAISILOZbORDZlczUlp5jdonb5y5SMZgaZy6OWp58SkS8jS8") - if err != nil { - t.Fatal(err) - } - ret, err := TransferBTTWithMemo(context.Background(), node, cfg, privKey, "41BC8E7F3E2BB11310B75D6B0B6E8537D069CDB72E", - "416E2FFC26BDF48B1983CCC9EC2521867F98667760", 1, "Yet another memo") - if err != nil { - t.Fatal(err) - } - assert.True(t, ret.Result) - assert.Equal(t, "SUCCESS", ret.Code) - - ret2, err := TransferBTT(context.Background(), node, cfg, privKey, "TTACjzSeJ9jDHaxRxnho1n3mVK9JASNyr9", - "TX6zrFyDkFGYTeNj7uuJVX2QpRQdURFPFv", 1) - if err != nil { - t.Fatal(err) - } - assert.True(t, ret.Result) - assert.Equal(t, "SUCCESS", ret2.Code) - time.Sleep(2 * time.Minute) -} - -func TestTransactions(t *testing.T) { - node, err := coremock.NewMockNode() - if err != nil { - t.Fatal(err) - } - cfg, err := node.Repo.Config() - if err != nil { - t.Fatal(err) - } - cfg.Services.EscrowDomain = "https://escrow-staging.btfs.io" - cfg.Services.TrongridDomain = "https://api.trongrid.io" - cfg.Identity.PrivKey = "CAISIPxQDgGUHZF20nrEwFUw32MHNtzYmmiKgzxn5C6cqD3m" - _, err = SyncTxFromTronGrid(context.Background(), cfg, node.Repo.Datastore()) - if err != nil { - // FIXME: workaround from jenkins unit test failure. - // it works locally - //t.Fatal(err) - fmt.Println("err", err) - } -} diff --git a/core/wallet/wallet.go b/core/wallet/wallet.go deleted file mode 100644 index 93969a3cd..000000000 --- a/core/wallet/wallet.go +++ /dev/null @@ -1,264 +0,0 @@ -package wallet - -import ( - "context" - "crypto/ecdsa" - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/bittorrent/go-btfs/core" - "github.com/bittorrent/go-btfs/core/commands/storage/upload/escrow" - - config "github.com/TRON-US/go-btfs-config" - "github.com/tron-us/go-btfs-common/crypto" - "github.com/tron-us/go-btfs-common/ledger" - escrowpb "github.com/tron-us/go-btfs-common/protos/escrow" - ledgerpb "github.com/tron-us/go-btfs-common/protos/ledger" - "github.com/tron-us/go-btfs-common/utils/grpc" - "github.com/tron-us/protobuf/proto" - - logging "github.com/ipfs/go-log" - ic "github.com/libp2p/go-libp2p/core/crypto" -) - -var log = logging.Logger("core/wallet") - -var ( - WithdrawMinAmount int64 = 1 - WithdrawMaxAmount int64 = 1000000000000 - DepositMinAmount int64 = 1 - DepositMaxAmount int64 = 1000000000000 - TokenId = "1002000" - TokenIdDev = "1000252" - hostWallet Wallet - - escrowService string - exchangeService string - solidityService string -) - -type Wallet struct { - privKeyIC ic.PrivKey - privateKey *ecdsa.PrivateKey - tronAddress []byte // 41*** - ledgerAddress []byte // address in ledger -} - -// withdraw from ledger to tron -func WalletWithdraw(ctx context.Context, configuration *config.Config, n *core.IpfsNode, amount int64) error { - err := Init(ctx, configuration) - if err != nil { - return err - } - - if hostWallet.privateKey == nil { - log.Error("wallet is not initialized") - return errors.New("wallet is not initialized") - } - - if amount < WithdrawMinAmount || amount > WithdrawMaxAmount { - return errors.New(fmt.Sprintf("withdraw amount should between %d ~ %d", WithdrawMinAmount, WithdrawMaxAmount)) - } - - // get ledger balance before withdraw - ledgerBalance, err := Balance(ctx, configuration) - if err != nil { - return errors.New(fmt.Sprintf("Failed to get ledger balance, reason: %v", err)) - } - log.Info(fmt.Sprintf("Get ledger account success, balance: [%d]", ledgerBalance)) - - if amount > ledgerBalance { - return errors.New(fmt.Sprintf("not enough ledger balance, current balance is %d", ledgerBalance)) - } - - // Doing withdraw request. - channelId, id, err := Withdraw(ctx, n, hostWallet.ledgerAddress, hostWallet.tronAddress, amount, hostWallet.privateKey) - if err != nil { - return err - } - - fmt.Println(fmt.Sprintf("Withdraw submitted! ChannelId: [%d], id [%d]\n", channelId, id)) - return nil -} - -const ( - InAppWallet = "BTFS Wallet" - BttWallet = "BTT Wallet" - - StatusPending = "Pending" - StatusSuccess = "Success" - StatusFailed = "Failed" -) - -func WalletDeposit(ctx context.Context, configuration *config.Config, n *core.IpfsNode, - amount int64, runDaemon bool, async bool) error { - err := Init(ctx, configuration) - if err != nil { - return err - } - - if hostWallet.privateKey == nil { - log.Error("wallet is not initialized") - return errors.New("wallet is not initialized") - } - - if amount < DepositMinAmount || amount > DepositMaxAmount { - return errors.New(fmt.Sprintf("deposit amount should between %d ~ %d", DepositMinAmount, DepositMaxAmount)) - } - - _, err = Balance(ctx, configuration) - if err != nil { - return err - } - - prepareResponse, err := Deposit(ctx, n, hostWallet.ledgerAddress, amount, hostWallet.privateKey, runDaemon, async) - if err != nil { - log.Error("Failed to Deposit, ERR[%v]\n", err) - return err - } - - fmt.Println(fmt.Sprintf("Deposit Submitted: Id [%d]\n", prepareResponse.GetId())) - return nil -} - -// GetBalance both on ledger and Tron. -func GetBalance(ctx context.Context, configuration *config.Config) (int64, int64, error) { - err := Init(ctx, configuration) - if err != nil { - return 0, 0, err - } - - if hostWallet.privateKey == nil { - log.Error("wallet is not initialized") - return 0, 0, errors.New("wallet is not initialized") - } - - // get tron balance - tokenId := TokenId - if strings.Contains(configuration.Services.EscrowDomain, "dev") || - strings.Contains(configuration.Services.EscrowDomain, "staging") { - tokenId = TokenIdDev - } - - tronBalance, err := GetTokenBalance(ctx, hostWallet.tronAddress, tokenId) - if err != nil { - return 0, 0, - errors.New(fmt.Sprintf("Failed to get exchange tron balance, reason: %v", err)) - } - - log.Info(fmt.Sprintf("Get exchange tron account success, balance: [%d]", tronBalance)) - - // get ledger balance from escrow - ledgerBalance, err := Balance(ctx, configuration) - if err != nil { - return 0, 0, - errors.New(fmt.Sprintf("Failed to get ledger balance, reason: %v", err)) - } - - log.Info(fmt.Sprintf("Get ledger account success, balance: [%d]", ledgerBalance)) - - return tronBalance, ledgerBalance, nil -} - -func Init(ctx context.Context, configuration *config.Config) error { - if configuration == nil { - fmt.Println("Init wallet, configuration is nil") - log.Error("init wallet failed, input nil configuration") - return errors.New("init wallet failed") - } - - // get service name - escrowService = configuration.Services.EscrowDomain - exchangeService = configuration.Services.ExchangeDomain - solidityService = configuration.Services.SolidityDomain - - // get key - privKeyIC, err := configuration.Identity.DecodePrivateKey("") - if err != nil { - log.Error("wallet get private key failed") - return err - } - // base64 key - privKeyRaw, err := privKeyIC.Raw() - if err != nil { - log.Error("wallet get private key raw failed") - return err - } - // hex key - hexPrivKey := hex.EncodeToString(privKeyRaw) - // hex key to ecdsa - privateKey, err := crypto.HexToECDSA(hexPrivKey) - if err != nil { - log.Error("error when convent private key to edca, ERR[%v]\n", err) - return err - } - if privateKey == nil { - log.Error("wallet get private key ecdsa failed") - return err - } - hostWallet.privateKey = privateKey - - // tron key 41**** - addr, err := crypto.PublicKeyToAddress(privateKey.PublicKey) - if err != nil { - log.Error("wallet get tron address failed, ERR[%v]\n ", err) - return err - } - addBytes := addr.Bytes() - hostWallet.tronAddress = addBytes - - ledgerAddress, err := ic.MarshalPublicKey(privKeyIC.GetPublic()) - if err != nil { - fmt.Println("get ledger address failed, ERR: \n", err) - return err - } - - hostWallet.ledgerAddress = ledgerAddress - return nil -} - -func Balance(ctx context.Context, configuration *config.Config) (int64, error) { - privKey, err := configuration.Identity.DecodePrivateKey("") - if err != nil { - return 0, err - } - lgSignedPubKey, err := ledger.NewSignedPublicKey(privKey, privKey.GetPublic()) - if err != nil { - return 0, err - } - - return BalanceHelper(ctx, configuration, false, nil, lgSignedPubKey) -} - -func BalanceHelper(ctx context.Context, configuration *config.Config, offsign bool, signedBytes []byte, lgSignedPubKey *ledgerpb.SignedPublicKey) (int64, error) { - if offsign { - var ledgerSignedPubKey ledgerpb.SignedPublicKey - err := proto.Unmarshal(signedBytes, &ledgerSignedPubKey) - if err != nil { - return 0, err - } - lgSignedPubKey = &ledgerSignedPubKey - } - - var balance int64 = 0 - err := grpc.EscrowClient(configuration.Services.EscrowDomain).WithContext(ctx, - func(ctx context.Context, client escrowpb.EscrowServiceClient) error { - res, err := client.BalanceOf(ctx, ledger.NewSignedCreateAccountRequest(lgSignedPubKey.Key, lgSignedPubKey.Signature)) - if err != nil { - return err - } - err = escrow.VerifyEscrowRes(configuration, res.Result, res.EscrowSignature) - if err != nil { - return err - } - balance = res.Result.Balance - log.Debug("balance of account is ", balance) - return nil - }) - if err != nil { - return 0, err - } - return balance, nil -} diff --git a/docs/examples/go-ipfs-as-a-library/main.go b/docs/examples/go-ipfs-as-a-library/main.go index 4b542a97c..9bb1a9ec4 100644 --- a/docs/examples/go-ipfs-as-a-library/main.go +++ b/docs/examples/go-ipfs-as-a-library/main.go @@ -15,11 +15,11 @@ import ( "github.com/bittorrent/go-btfs/plugin/loader" // This package is needed so that all the preloaded plugins are loaded automatically "github.com/bittorrent/go-btfs/repo/fsrepo" - config "github.com/TRON-US/go-btfs-config" - files "github.com/TRON-US/go-btfs-files" - icore "github.com/TRON-US/interface-go-btfs-core" - icorepath "github.com/TRON-US/interface-go-btfs-core/path" + config "github.com/bittorrent/go-btfs-config" + files "github.com/bittorrent/go-btfs-files" libp2p "github.com/bittorrent/go-btfs/core/node/libp2p" + icore "github.com/bittorrent/interface-go-btfs-core" + icorepath "github.com/bittorrent/interface-go-btfs-core/path" "github.com/libp2p/go-libp2p/core/peer" peerstore "github.com/libp2p/go-libp2p/p2p/host/peerstore" ma "github.com/multiformats/go-multiaddr" diff --git a/fuse/ipns/common.go b/fuse/ipns/common.go index 15949d2e1..f4e47176a 100644 --- a/fuse/ipns/common.go +++ b/fuse/ipns/common.go @@ -6,7 +6,7 @@ import ( "github.com/bittorrent/go-btfs/core" nsys "github.com/bittorrent/go-btfs/namesys" - ft "github.com/TRON-US/go-unixfs" + ft "github.com/bittorrent/go-unixfs" path "github.com/ipfs/go-path" ci "github.com/libp2p/go-libp2p/core/crypto" ) diff --git a/fuse/ipns/ipns_unix.go b/fuse/ipns/ipns_unix.go index f3fd8ceff..2117804a5 100644 --- a/fuse/ipns/ipns_unix.go +++ b/fuse/ipns/ipns_unix.go @@ -15,11 +15,11 @@ import ( fuse "bazil.org/fuse" fs "bazil.org/fuse/fs" - mfs "github.com/TRON-US/go-mfs" - ft "github.com/TRON-US/go-unixfs" - iface "github.com/TRON-US/interface-go-btfs-core" - options "github.com/TRON-US/interface-go-btfs-core/options" - path "github.com/TRON-US/interface-go-btfs-core/path" + mfs "github.com/bittorrent/go-mfs" + ft "github.com/bittorrent/go-unixfs" + iface "github.com/bittorrent/interface-go-btfs-core" + options "github.com/bittorrent/interface-go-btfs-core/options" + path "github.com/bittorrent/interface-go-btfs-core/path" cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" dag "github.com/ipfs/go-merkledag" diff --git a/fuse/readonly/ipfs_test.go b/fuse/readonly/ipfs_test.go index 84f32ef7e..ade6d40bc 100644 --- a/fuse/readonly/ipfs_test.go +++ b/fuse/readonly/ipfs_test.go @@ -24,11 +24,11 @@ import ( coremock "github.com/bittorrent/go-btfs/core/mock" fstest "bazil.org/fuse/fs/fstestutil" - chunker "github.com/TRON-US/go-btfs-chunker" - files "github.com/TRON-US/go-btfs-files" - importer "github.com/TRON-US/go-unixfs/importer" - uio "github.com/TRON-US/go-unixfs/io" - ipath "github.com/TRON-US/interface-go-btfs-core/path" + chunker "github.com/bittorrent/go-btfs-chunker" + files "github.com/bittorrent/go-btfs-files" + importer "github.com/bittorrent/go-unixfs/importer" + uio "github.com/bittorrent/go-unixfs/io" + ipath "github.com/bittorrent/interface-go-btfs-core/path" u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go index e4bd87662..7e92aa6bf 100644 --- a/fuse/readonly/readonly_unix.go +++ b/fuse/readonly/readonly_unix.go @@ -11,16 +11,19 @@ import ( "os" "syscall" - ft "github.com/TRON-US/go-unixfs" - uio "github.com/TRON-US/go-unixfs/io" - core "github.com/bittorrent/go-btfs/core" - mdag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/go-path" - fuse "bazil.org/fuse" fs "bazil.org/fuse/fs" + core "github.com/bittorrent/go-btfs/core" + ft "github.com/bittorrent/go-unixfs" + uio "github.com/bittorrent/go-unixfs/io" + "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + mdag "github.com/ipfs/go-merkledag" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-path/resolver" + ipldprime "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" ) var log = logging.Logger("fuse/btfs") @@ -66,20 +69,47 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { return nil, fuse.ENOENT } - nd, err := s.Ipfs.Resolver.ResolvePath(ctx, p) + nd, ndLnk, err := resolver.NewBasicResolver(s.Ipfs.UnixFSFetcherFactory).ResolvePath(ctx, p) if err != nil { // todo: make this error more versatile. return nil, fuse.ENOENT } - switch nd := nd.(type) { - case *mdag.ProtoNode, *mdag.RawNode: - return &Node{Ipfs: s.Ipfs, Nd: nd}, nil + cidLnk, ok := ndLnk.(cidlink.Link) + if !ok { + log.Debugf("non-cidlink returned from ResolvePath: %v", ndLnk) + return nil, fuse.ENOENT + } + + // convert ipld-prime node to universal node + blk, err := s.Ipfs.Blockstore.Get(ctx, cidLnk.Cid) + if err != nil { + log.Debugf("fuse failed to retrieve block: %v: %s", cidLnk, err) + return nil, fuse.ENOENT + } + + var fnd ipld.Node + switch cidLnk.Cid.Prefix().Codec { + case cid.DagProtobuf: + adl, ok := nd.(ipldprime.ADL) + if ok { + substrate := adl.Substrate() + fnd, err = mdag.ProtoNodeConverter(blk, substrate) + } else { + fnd, err = mdag.ProtoNodeConverter(blk, nd) + } + case cid.Raw: + fnd, err = mdag.RawNodeConverter(blk, nd) default: - log.Error("fuse node was not a protobuf node") + log.Error("fuse node was not a supported type") return nil, fuse.ENOTSUP } + if err != nil { + log.Error("could not convert protobuf or raw node") + return nil, fuse.ENOENT + } + return &Node{Ipfs: s.Ipfs, Nd: fnd}, nil } // ReadDirAll reads a particular directory. Disallowed for root. diff --git a/go.mod b/go.mod index 781c6ee14..5417489f9 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,20 @@ go 1.18 require ( bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc - github.com/TRON-US/go-btfs-api v0.4.1-0.20230302072600-2afc74800cfc - github.com/TRON-US/go-btfs-chunker v0.3.0 - github.com/TRON-US/go-btfs-config v0.11.13-0.20230302065423-e10f87f21752 - github.com/TRON-US/go-btfs-files v0.2.0 - github.com/TRON-US/go-btns v0.1.2-0.20230302072552-36250342c5d7 - github.com/TRON-US/go-eccrypto v0.0.1 - github.com/TRON-US/go-mfs v0.3.1 - github.com/TRON-US/go-unixfs v0.6.1 - github.com/TRON-US/interface-go-btfs-core v0.7.1-0.20230322132125-c29558a30a57 - github.com/Workiva/go-datastructures v1.0.52 - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a - github.com/bittorrent/go-btfs-cmds v0.2.14 + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 + github.com/bittorrent/go-btfs-api v0.5.0 + github.com/bittorrent/go-btfs-chunker v0.4.0 + github.com/bittorrent/go-btfs-cmds v0.3.0 + github.com/bittorrent/go-btfs-common v0.9.0 + github.com/bittorrent/go-btfs-config v0.12.3 + github.com/bittorrent/go-btfs-files v0.3.1 + github.com/bittorrent/go-btns v0.2.0 + github.com/bittorrent/go-common/v2 v2.4.0 + github.com/bittorrent/go-eccrypto v0.1.0 + github.com/bittorrent/go-mfs v0.4.0 + github.com/bittorrent/go-unixfs v0.7.0 + github.com/bittorrent/interface-go-btfs-core v0.8.2 + github.com/bittorrent/protobuf v1.4.0 github.com/blang/semver v3.5.1+incompatible github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 github.com/bren2010/proquint v0.0.0-20160323162903-38337c27106d @@ -30,15 +32,15 @@ require ( github.com/gabriel-vasile/mimetype v1.4.1 github.com/go-bindata/go-bindata/v3 v3.1.3 github.com/gogo/protobuf v1.3.2 - github.com/golang/protobuf v1.5.2 + github.com/golang/protobuf v1.5.3 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/ip2location/ip2location-go/v9 v9.0.0 github.com/ipfs/go-bitswap v0.11.0 - github.com/ipfs/go-block-format v0.0.3 + github.com/ipfs/go-block-format v0.1.2 github.com/ipfs/go-blockservice v0.5.0 - github.com/ipfs/go-cid v0.3.2 + github.com/ipfs/go-cid v0.4.0 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-delegated-routing v0.7.0 @@ -67,15 +69,17 @@ require ( github.com/ipfs/go-merkledag v0.8.1 github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 - github.com/ipfs/go-path v0.3.0 + github.com/ipfs/go-path v0.3.1 + github.com/ipfs/go-unixfsnode v1.4.0 github.com/ipfs/go-verifcid v0.0.2 github.com/ipld/go-car v0.4.0 github.com/ipld/go-car/v2 v2.4.0 + github.com/ipld/go-codec-dagpb v1.4.1 github.com/jbenet/go-is-domain v1.0.5 github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c github.com/jbenet/go-temp-err-catcher v0.1.0 github.com/jbenet/goprocess v0.1.4 - github.com/klauspost/reedsolomon v1.9.9 + github.com/klauspost/reedsolomon v1.9.14 github.com/libp2p/go-libp2p v0.24.2 github.com/libp2p/go-libp2p-http v0.4.0 github.com/libp2p/go-libp2p-kad-dht v0.20.0 @@ -92,7 +96,6 @@ require ( github.com/markbates/pkger v0.17.0 github.com/mholt/archiver/v3 v3.3.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multiaddr v0.8.0 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.1.1 @@ -103,28 +106,26 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.15.1 github.com/shirou/gopsutil/v3 v3.20.12 github.com/status-im/keycard-go v0.2.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/thedevsaddam/gojsonq/v2 v2.5.2 - github.com/tron-us/go-btfs-common v0.8.14-0.20230322132332-b16546817ed8 - github.com/tron-us/go-common/v2 v2.3.2 - github.com/tron-us/protobuf v1.3.7 - github.com/tyler-smith/go-bip32 v0.0.0-20170922074101-2c9cfd177564 + github.com/tyler-smith/go-bip32 v1.0.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b + go.opentelemetry.io/otel v1.15.1 + go.opentelemetry.io/otel/trace v1.15.1 go.uber.org/fx v1.18.2 go.uber.org/zap v1.24.0 go4.org v0.0.0-20200411211856-f5505b9728dd golang.org/x/crypto v0.6.0 golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.5.0 + golang.org/x/sys v0.6.0 gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.4.0 @@ -161,20 +162,23 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect github.com/huandu/xstrings v1.3.2 // indirect + github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect - github.com/ipfs/go-unixfsnode v1.4.0 // indirect github.com/ipld/edelweiss v0.2.0 // indirect - github.com/ipld/go-codec-dagpb v1.4.1 // indirect github.com/libp2p/go-libp2p-core v0.20.1 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.0 // indirect @@ -185,9 +189,11 @@ require ( github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/marten-seemann/webtransport-go v0.4.3 // indirect github.com/mattn/go-pointer v0.0.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/onsi/ginkgo/v2 v2.5.1 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect github.com/pion/datachannel v1.5.2 // indirect github.com/pion/dtls/v2 v2.1.5 // indirect github.com/pion/ice/v2 v2.2.6 // indirect @@ -205,18 +211,21 @@ require ( github.com/pion/turn/v2 v2.0.8 // indirect github.com/pion/udp v0.1.1 // indirect github.com/pion/webrtc/v3 v3.1.42 // indirect + github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/tidwall/btree v1.3.1 // indirect + github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect + github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect go.etcd.io/bbolt v1.3.6 // indirect - go.opentelemetry.io/otel v1.8.0 // indirect - go.opentelemetry.io/otel/trace v1.8.0 // indirect + go.opentelemetry.io/otel/metric v0.38.1 // indirect golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) require ( + contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect @@ -230,10 +239,9 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash v1.1.0 github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cheggaaa/pb v1.0.29 - github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/codemodus/kace v0.5.1 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/cskr/pubsub v1.0.2 // indirect @@ -267,6 +275,7 @@ require ( github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect github.com/ipfs/go-ipfs-pq v0.0.2 // indirect + github.com/ipfs/go-ipfs-redirects-file v0.1.1 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-peertaskqueue v0.8.0 // indirect github.com/ipld/go-ipld-prime v0.19.0 @@ -275,7 +284,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kisielk/errcheck v1.5.0 // indirect github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid v1.2.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/pgzip v1.2.1 // indirect github.com/koron/go-ssdp v0.0.3 // indirect @@ -302,7 +310,6 @@ require ( github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.0 // indirect - github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect @@ -315,7 +322,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/segmentio/encoding v0.3.6 // indirect @@ -335,9 +342,10 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect go.opencensus.io v0.24.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1 go.uber.org/atomic v1.10.0 // indirect go.uber.org/dig v1.15.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + go.uber.org/multierr v1.9.0 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.7.0 // indirect golang.org/x/term v0.5.0 // indirect @@ -348,21 +356,16 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect mellium.im/sasl v0.3.1 // indirect ) -replace github.com/ipfs/go-path => github.com/TRON-US/go-path v0.2.0 +replace github.com/ipfs/go-path => github.com/bittorrent/go-path v0.4.1 replace github.com/libp2p/go-libp2p-yamux => github.com/libp2p/go-libp2p-yamux v0.2.8 replace github.com/libp2p/go-libp2p-mplex => github.com/libp2p/go-libp2p-mplex v0.2.4 exclude github.com/anacrolix/dht/v2 v2.15.2-0.20220123034220-0538803801cb - -// replace github.com/tron-us/go-btfs-common => /Users/shawn.huang/github/Shwan-Huang-Tron/go-btfs-common - -// replace github.com/TRON-US/interface-go-btfs-core => /Users/shawn.huang/github/Shwan-Huang-Tron/interface-go-btfs-core diff --git a/go.sum b/go.sum index 0092e49f6..3b0cea235 100644 --- a/go.sum +++ b/go.sum @@ -13,17 +13,30 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= +contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797 h1:yDf7ARQc637HoxDho7xjqdvO5ZA2Yb+xzv/fOnnvZzw= crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= @@ -42,20 +55,13 @@ github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOv github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= @@ -72,9 +78,7 @@ github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETF github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= @@ -87,32 +91,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo= github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/TRON-US/go-btfs-api v0.4.1-0.20230302072600-2afc74800cfc h1:tzpKInjURcW0tYRh603CqJV28fIal9j+Nra8gLAL61U= -github.com/TRON-US/go-btfs-api v0.4.1-0.20230302072600-2afc74800cfc/go.mod h1:QIly9ank50fKWlSG0hDFfh3nUW/EW1v+MZMdkKDkuEs= -github.com/TRON-US/go-btfs-chunker v0.3.0 h1:06/rAYKtC3BQRYVxMtEKvqFFHhSB+XKcqt3JZ0CjD4o= -github.com/TRON-US/go-btfs-chunker v0.3.0/go.mod h1:m0xvt42kqLskWsLF6SQ51AA9cqPzWoweydOcDgSDX/U= -github.com/TRON-US/go-btfs-config v0.11.13-0.20230302065423-e10f87f21752 h1:7x1fsjYkrYaIshiPlIpC/2JHkAAVjHjjqxzb9uQkbvg= -github.com/TRON-US/go-btfs-config v0.11.13-0.20230302065423-e10f87f21752/go.mod h1:lQltFKuuOu01erg5beHmRghAySunxMEO7D9ospdXwsw= -github.com/TRON-US/go-btfs-files v0.1.1/go.mod h1:tD2vOKLcLCDNMn9rrA27n2VbNpHdKewGzEguIFY+EJ0= -github.com/TRON-US/go-btfs-files v0.2.0 h1:JZ+F0gX8iPmUf1OlrdOdsA8GMGxCHhwQ03jEWWEgVLE= -github.com/TRON-US/go-btfs-files v0.2.0/go.mod h1:Qx+rTOIC0xl3ZkosGcEoB4hqExZmTONErPys8K5suEc= -github.com/TRON-US/go-btns v0.1.2-0.20230302072552-36250342c5d7 h1:baAVrkm+7KXJVHdR1UwGnbjf0SCzlyg5eUMikijnFLY= -github.com/TRON-US/go-btns v0.1.2-0.20230302072552-36250342c5d7/go.mod h1:5pSqYTtOje5Y3caewck20nlMQLrcmzePD77BYGmiea8= -github.com/TRON-US/go-eccrypto v0.0.1 h1:+/5Uid61UGysbxv6Cv6gx4ru1gEiJOlir/P7ElAe7A0= -github.com/TRON-US/go-eccrypto v0.0.1/go.mod h1:QZqTUSKP9MdYh+0LPsnVKvXV/Q2f9Qb6V4ejvUmHVvI= -github.com/TRON-US/go-mfs v0.3.1 h1:5foDPPlIcF4bPXZ18Qd+lHv3WPBQTJlAHTLSAeFK/rY= -github.com/TRON-US/go-mfs v0.3.1/go.mod h1:hXLxeLnJp50uu+Ibg7Tf7BzaC49m8RSTRA/eDl0wx1s= -github.com/TRON-US/go-path v0.2.0 h1:aGz3nG1Apt6wqJ7JlZyxxCvWQ0A2M+0qKYxJFx/Sj+g= -github.com/TRON-US/go-path v0.2.0/go.mod h1:VpDkSBKQ9EFQOUgi54Tq/O/tGi8n1RfYNks13M3DEs8= -github.com/TRON-US/go-unixfs v0.6.0/go.mod h1:U3+FopU5+8rwrr05MJOwDB1E9vAwKGsb/GII0LkXZ8k= -github.com/TRON-US/go-unixfs v0.6.1 h1:7KFhJdt+XsapVSmxEq+mfUFOEPS8SyoaWJkkrr59N4A= -github.com/TRON-US/go-unixfs v0.6.1/go.mod h1:U3+FopU5+8rwrr05MJOwDB1E9vAwKGsb/GII0LkXZ8k= -github.com/TRON-US/interface-go-btfs-core v0.7.1-0.20230322132125-c29558a30a57 h1:DaZeNjipM2TAR/NUoWgjpAph+/S2H9ZI7r2GNk72RVw= -github.com/TRON-US/interface-go-btfs-core v0.7.1-0.20230322132125-c29558a30a57/go.mod h1:qR28xsYp7x29rscfQKtZs8hLmAK0Z2Wyp/oO3ZhJQts= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0= @@ -120,14 +100,16 @@ github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk= github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8= github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI= +github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= github.com/anacrolix/chansync v0.3.0 h1:lRu9tbeuw3wl+PhMu/r+JJCRu5ArFXIluOgdF0ao6/U= @@ -216,8 +198,35 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk= github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bittorrent/go-btfs-cmds v0.2.14 h1:0cy2Jh4/bEw6Kf5FEgpo7umpnOC86BjRnTctHDCnQ5c= -github.com/bittorrent/go-btfs-cmds v0.2.14/go.mod h1:wfgWxf016bZPTztF9BeEKSkvEhcjC1MbufhXFnoetSU= +github.com/bittorrent/go-btfs-api v0.5.0 h1:DTQNE0vOm9LxCWAxToi6AsH/lFvWse/GOKYGI/nqSCU= +github.com/bittorrent/go-btfs-api v0.5.0/go.mod h1:JnVWIMvRK3Rhv8WsW5y0KkJUbw/Vgt247Y8C8Tn/SPE= +github.com/bittorrent/go-btfs-chunker v0.4.0 h1:ruX0vPwJdj0KP4SAtxNzZm593MUA+A3LLV+l9j2w09c= +github.com/bittorrent/go-btfs-chunker v0.4.0/go.mod h1:1xf90c9gOKrHf2tyFIfB5GgFoTkEd1r/5m73ts+WW9A= +github.com/bittorrent/go-btfs-cmds v0.3.0 h1:xpCBgk3zIm84Ne6EjeJgi8WLB5YJJUIFMjK9L9RfL5k= +github.com/bittorrent/go-btfs-cmds v0.3.0/go.mod h1:Fbac/Rou32G0jpoa6wLrNNDxcGOZbGfk+GiG0r3uEIU= +github.com/bittorrent/go-btfs-common v0.9.0 h1:jHcFvYQmvmA4IdvVtkI5d/S/HW65Qz21C6oxeyK812w= +github.com/bittorrent/go-btfs-common v0.9.0/go.mod h1:OG1n3DfcTxQYfLd5zco54LfL3IiDDaw3s7Igahu0Rj0= +github.com/bittorrent/go-btfs-config v0.12.3 h1:Zi/GTwHo/PJV+90+w45P7axkWsUpOB/XFhgvNk+TwRs= +github.com/bittorrent/go-btfs-config v0.12.3/go.mod h1:DNaHVC9wU84KLKoC4HkvdoFJKVZ7TF530qzfYu30fCI= +github.com/bittorrent/go-btfs-files v0.3.0/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= +github.com/bittorrent/go-btfs-files v0.3.1 h1:esq3j+6FtZ+SlaxKjVtiYgvXk/SWUiTcv0Q1MeJoPnQ= +github.com/bittorrent/go-btfs-files v0.3.1/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= +github.com/bittorrent/go-btns v0.2.0 h1:OMpxUiRbtb/PRTK/z/flxcwOfTvNKMsTLOubYFhKy1s= +github.com/bittorrent/go-btns v0.2.0/go.mod h1:+Cinr/1Jl7V/Pqgz+vbOdHXkLVFbMqjypmbAv8QiQPs= +github.com/bittorrent/go-common/v2 v2.4.0 h1:u0jldKnQteTPQDNKj5GUBOUj2Tswn0+GfWN7yq2QAaY= +github.com/bittorrent/go-common/v2 v2.4.0/go.mod h1:DVJCWPoehldR7u0K1n9UeKKsQL28mYiY7XMShjGfB3I= +github.com/bittorrent/go-eccrypto v0.1.0 h1:sNosO+VGuh8IRQvrm9BJ4FeEatRp8ToMfpRTYaNqe7g= +github.com/bittorrent/go-eccrypto v0.1.0/go.mod h1:1kX5RLI52B+1l0VwwBtv+6h28Gu8XojZUu0wc/Iw6GU= +github.com/bittorrent/go-mfs v0.4.0 h1:xb7Bxp65LQP8yhflx47ZMuXzIMSSo9ZrasVhroCvRxs= +github.com/bittorrent/go-mfs v0.4.0/go.mod h1:w7XQuaSCDsL0MhcMP02ViFJQHYg2tLf+/v0w/m7wMfM= +github.com/bittorrent/go-path v0.4.1 h1:9qJe6V2/O3n8Z3tqgN3wgbYcXrcwAv1U3de5xiyYodg= +github.com/bittorrent/go-path v0.4.1/go.mod h1:eNLsxJEEMxn/CDzUJ6wuNl+6No6tEUhOZcPKsZsYX0E= +github.com/bittorrent/go-unixfs v0.7.0 h1:2SPuQcAmubJUl+zuKoGWdculoZRn7D0zkDnTZ9pupqo= +github.com/bittorrent/go-unixfs v0.7.0/go.mod h1:0UNGV0k5MFsMGOeNjOJFtURcXDFz8bjtyfhcom+vW7A= +github.com/bittorrent/interface-go-btfs-core v0.8.2 h1:iTStlXLoandcKyFruq4U0uVSR3CQU7ey9Lwf8Mu3jw0= +github.com/bittorrent/interface-go-btfs-core v0.8.2/go.mod h1:tQ3d3uI2gH+AO7ikbBwlulRgff0/dzobz9H3SL00yYo= +github.com/bittorrent/protobuf v1.4.0 h1:3AW4SZUud3/8/orb8O/957CdspwxWjX/qprvF49aQ70= +github.com/bittorrent/protobuf v1.4.0/go.mod h1:k2fZczatqZOyvWUezE02Xt5uFcVqdUd1tNeZwXjELCk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -262,6 +271,7 @@ github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= @@ -292,7 +302,6 @@ github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -313,7 +322,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -360,7 +368,6 @@ github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 h1:QV0ZrfBLpFc2KDk+a4LJefDczXnonRwrYrQJY/9L4dA= github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302/go.mod h1:qBlWZqWeVx9BjvqBsnC/8RUlAYpIFmPvgROcw0n1scE= @@ -375,13 +382,14 @@ github.com/ethereum/go-ethereum v1.11.1 h1:EMymmWFzpS7G9l9NvVN8G73cgdUIqDPNRf2YT github.com/ethereum/go-ethereum v1.11.1/go.mod h1:DuefStAgaxoaYGLR0FueVcVbehmn5n9QUcVrMCuOvuc= github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g= github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= @@ -425,50 +433,49 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-pg/migrations/v7 v7.1.9/go.mod h1:ycN6RqhOqa3km5KVLvRyESYP+lvqhrGYZxAIQ5HPPMM= github.com/go-pg/migrations/v7 v7.1.11 h1:59sWcsENzTrY2OUvG/yfkTgz92F3AyT5/wwmgnw6MoA= github.com/go-pg/migrations/v7 v7.1.11/go.mod h1:v/v7SfckdB2IGmUyopKyASTzjmN30HnDucLLZcCvBWU= github.com/go-pg/pg/v9 v9.0.0-beta.14/go.mod h1:T2Sr6bpTCOr2lUqOUMiXLMJqZHSUBKk1LdgSqjwhZfA= github.com/go-pg/pg/v9 v9.0.0/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= github.com/go-pg/pg/v9 v9.0.3/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= -github.com/go-pg/pg/v9 v9.1.5/go.mod h1:QM13HBLkdml4zcKOfUfGLymM6hb72aKTJLrmaH8rsFg= github.com/go-pg/pg/v9 v9.1.6/go.mod h1:QM13HBLkdml4zcKOfUfGLymM6hb72aKTJLrmaH8rsFg= github.com/go-pg/pg/v9 v9.2.0/go.mod h1:fG8qbL+ei4e/fCZLHK+Z+/7b9B+pliZtbpaucG4/YNQ= github.com/go-pg/pg/v9 v9.2.1 h1:4rWNJkj+aPuDFqgieTzNhHBuYaXREh3yaB9NlBerFys= github.com/go-pg/pg/v9 v9.2.1/go.mod h1:fG8qbL+ei4e/fCZLHK+Z+/7b9B+pliZtbpaucG4/YNQ= github.com/go-pg/urlstruct v0.1.0/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= -github.com/go-pg/urlstruct v0.1.4/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= github.com/go-pg/urlstruct v0.2.6/go.mod h1:dxENwVISWSOX+k87hDt0ueEJadD+gZWv3tHzwfmZPu8= github.com/go-pg/urlstruct v0.3.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/urlstruct v0.4.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/zerochecker v0.1.1/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU= github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= -github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= @@ -488,7 +495,6 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -500,18 +506,19 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -528,8 +535,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -543,6 +551,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -551,22 +560,25 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -580,9 +592,7 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -600,7 +610,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -662,32 +671,28 @@ github.com/hypnoglow/go-pg-monitor/gopgv9 v0.1.0 h1:IcRPj0qujrS96YaSL/qDKxI67eKr github.com/hypnoglow/go-pg-monitor/gopgv9 v0.1.0/go.mod h1:0Mj+MFtASobV/5qHb68nxBdoGjr1QXTDU/9ZKPi8UF0= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ip2location/ip2location-go v8.2.0+incompatible/go.mod h1:3JUY1TBjTx1GdA7oRT7Zeqfc0bg3lMMuU5lXmzdpuME= github.com/ip2location/ip2location-go/v9 v9.0.0 h1:7Yc2txYtbnwIUSP+YIUPO1lEgcPchx0jKohBbvbJuHw= github.com/ip2location/ip2location-go/v9 v9.0.0/go.mod h1:s5SV6YZL10TpfPpXw//7fEJC65G/yH7Oh+Tjq9JcQEQ= github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ= github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitswap v0.0.9/go.mod h1:kAPf5qgn2W2DrgAcscZ3HrM9qh4pH+X8Fkk3UPrwvis= +github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= +github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.1.3/go.mod h1:YEQlFy0kkxops5Vy+OxWdRSEZIoS7I7KDIwoa5Chkps= github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So= +github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= +github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.1.1/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I= github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= github.com/ipfs/go-blockservice v0.5.0 h1:B2mwhhhVQl2ntW2EIpaWPwSCxSuqr5fFA93Ms4bYLEY= @@ -700,14 +705,16 @@ github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67Fexh github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA= +github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= +github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= @@ -745,6 +752,7 @@ github.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEc github.com/ipfs/go-graphsync v0.14.0 h1:f5KYkc8GpwwE1BrjBOWxIkRivXIw7fVqGZlnILpvbSc= github.com/ipfs/go-graphsync v0.14.0/go.mod h1:1LDVVnNHjit8ddJOtw3Jq9epP792xWFXXL3dJWIBIkM= github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= +github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= @@ -780,7 +788,8 @@ github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= github.com/ipfs/go-ipfs-provider v0.8.0 h1:4YTe9IdX99NUZEEzOsooPNxQozI+lY5x6SDWjUYhPiM= github.com/ipfs/go-ipfs-provider v0.8.0/go.mod h1:qCpwpoohIRVXvNzkygzsM3qdqP/sXlrogtA5I45tClc= -github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs= +github.com/ipfs/go-ipfs-redirects-file v0.1.1 h1:Io++k0Vf/wK+tfnhEh63Yte1oQK5VGT2hIEYpD0Rzx8= +github.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk= github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= @@ -789,6 +798,7 @@ github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyB github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= +github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8= github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= @@ -818,8 +828,8 @@ github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Ax github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= +github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= github.com/ipfs/go-merkledag v0.8.1 h1:N3yrqSre/ffvdwtHL4MXy0n7XH+VzN8DlzDrJySPa94= @@ -828,14 +838,14 @@ github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fG github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZaGVF1CUVdE+s= github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= -github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ= github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= +github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= github.com/ipfs/go-unixfs v0.3.1 h1:LrfED0OGfG98ZEegO4/xiprx2O+yS+krCMQSp7zLVv8= github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= +github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA= github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= @@ -886,12 +896,12 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -913,19 +923,16 @@ github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4= -github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= -github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= -github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= +github.com/klauspost/reedsolomon v1.9.14 h1:vkPCIhFMn2VdktLUcugqsU4vcLXN3dAhVd1uWA+TDD8= +github.com/klauspost/reedsolomon v1.9.14/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -938,6 +945,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -952,8 +960,6 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= -github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk= -github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= @@ -965,7 +971,6 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A= github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= @@ -977,18 +982,15 @@ github.com/libp2p/go-libp2p v0.24.2 h1:iMViPIcLY0D6zr/f+1Yq9EavCZu2i7eDstsr1nEwS github.com/libp2p/go-libp2p v0.24.2/go.mod h1:WuxtL2V8yGjam03D93ZBC19tvOUiPpewYv1xdFGWu1k= github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc= github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.0.9/go.mod h1:uU+IBvEQzCu953/ps7bYzC/D/R0Ho2A9LfKVVCatlqU= github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= @@ -1017,47 +1019,29 @@ github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJB github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.20.1 h1:fQz4BJyIFmSZAiTbKV8qoYhEH5Dtv/cVhZbG3Ib/+Cw= github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= -github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= -github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.0.5/go.mod h1:YtF20GUxjgoKZ4zmXj8j3Nb2TUSBHFlOCetzYdbZL5I= github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= github.com/libp2p/go-libp2p-gostream v0.5.0 h1:niNGTUrFoUDP/8jxMgu97zngMO+UGYBpVpbCKwIJBls= github.com/libp2p/go-libp2p-gostream v0.5.0/go.mod h1:rXrb0CqfcRRxa7m3RSKORQiKiWgk3IPeXWda66ZXKsA= -github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go= -github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8= github.com/libp2p/go-libp2p-http v0.4.0 h1:V+f9Rhe/8GkColmXoyJyA0NVsN9F3TCLZgW2hwjoX5w= github.com/libp2p/go-libp2p-http v0.4.0/go.mod h1:92tmLGrlBliQFDlZRpBXT3BJM7rGFONy0vsNrG/bMPg= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.4/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.5/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k= github.com/libp2p/go-libp2p-kad-dht v0.20.0 h1:1bcMa74JFwExCHZMFEmjtHzxX5DovhJ07EtR6UOTEpc= github.com/libp2p/go-libp2p-kad-dht v0.20.0/go.mod h1:qPIXdiZsLczhV4/+4EO1jE8ae0YCW4ZOogc4WVIyTEU= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= -github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg= github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08= github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmgoXK8H331ui39CE= github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= -github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= -github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q= github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= -github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= -github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es= github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= -github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= @@ -1067,33 +1051,26 @@ github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRj github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= -github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= -github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys= github.com/libp2p/go-libp2p-routing-helpers v0.4.0 h1:b7y4aixQ7AwbqYfcOQ6wTw8DQvuRZeTAA0Od3YYN5yc= github.com/libp2p/go-libp2p-routing-helpers v0.4.0/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= -github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0= github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= -github.com/libp2p/go-libp2p-swarm v0.0.6/go.mod h1:s5GZvzg9xXe8sbeESuFpjt8CJPTCa8mhEusweJqyFy8= github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= -github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= @@ -1104,9 +1081,6 @@ github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotl github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= -github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk= -github.com/libp2p/go-libp2p-transport v0.0.5/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A= -github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc= github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= @@ -1114,11 +1088,9 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIW github.com/libp2p/go-libp2p-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA= github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= -github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= @@ -1137,6 +1109,7 @@ github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdm github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= +github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= @@ -1158,25 +1131,18 @@ github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2L github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-socket-activation v0.1.0 h1:OImQPhtbGlCNaF/KSTl6pBBy+chA5eBt5i9uMJNtEdY= github.com/libp2p/go-socket-activation v0.1.0/go.mod h1:gzda2dNkMG5Ti2OfWNNwW0FDIbj0g/aJJU320FcLfhk= -github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= -github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ= -github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw= github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19K427vCzQ+xHKH/o= github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= -github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I= github.com/libp2p/go-testutil v0.1.0 h1:4QhjaWGO89udplblLVpgGDOQjzFlRavZOjuEnz2rLMc= github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU= github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= -github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= @@ -1194,7 +1160,6 @@ github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYg github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1276,12 +1241,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 h1:UCU8+cLbbvyxi0sQ9fSeoEhZgvrrD9HKMtX6Gmc1vk8= -github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= @@ -1351,8 +1313,6 @@ github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJ github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= @@ -1489,7 +1449,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= @@ -1506,10 +1465,14 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1525,10 +1488,13 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1537,10 +1503,13 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= +github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= @@ -1550,6 +1519,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v8ObDPR0dzr2a6sXTB1Fq7IHs= @@ -1651,7 +1621,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1661,39 +1631,35 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= -github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= -github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= github.com/tidwall/btree v1.3.1 h1:636+tdVDs8Hjcf35Di260W2xCW4KuoXOKyk9QWOvCpA= github.com/tidwall/btree v1.3.1/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tron-us/go-btfs-common v0.8.14-0.20230322132332-b16546817ed8 h1:D9IeCieIsvYLX7MMhug6ysc1wtGrlANrix4KEF58ARw= -github.com/tron-us/go-btfs-common v0.8.14-0.20230322132332-b16546817ed8/go.mod h1:d3rlu0zXMVOlRC5ZyvHIJQa3/7FsELGwa3fkbBcnj/o= -github.com/tron-us/go-common/v2 v2.1.9/go.mod h1:YIEJZF9Ph79g0zZWOvfNDtJhvO5OqSNPAb/TM1i+KvQ= -github.com/tron-us/go-common/v2 v2.3.2 h1:5mYTPdcdq5Hi6DBVSIZr6+NcKv3w8koboduLoZx+xPE= -github.com/tron-us/go-common/v2 v2.3.2/go.mod h1:/ktTJfsQWnrtSsoAvT3ybJR1nw7qMSEX+dcDxcv0xro= -github.com/tron-us/protobuf v1.3.7 h1:nYnRqyiyEHK5YzQT0DScL8W65X6py+F9xDnMZx63qaY= -github.com/tron-us/protobuf v1.3.7/go.mod h1:INMJF54ZV6c8ZMc3imHsMl1kqIpe4VnbCUK4zYcVHqE= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip32 v0.0.0-20170922074101-2c9cfd177564 h1:NXXyQVeRVLK8Xu27/hkkjwVOZLk5v4ZBEvvMtqMqznM= -github.com/tyler-smith/go-bip32 v0.0.0-20170922074101-2c9cfd177564/go.mod h1:0/YuQQF676+d4CMNclTqGUam1EDwz0B8o03K9pQqA3c= +github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ= +github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= @@ -1751,14 +1717,18 @@ github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1: github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -1775,16 +1745,22 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1 h1:pX+lppB8PArapyhS6nBStyQmkaDUPWdQf0UmEGRCQ54= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1/go.mod h1:2FmkXne0k9nkp27LD/m+uoh8dNlstsiCJ7PLc/S72aI= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg= -go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8= +go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/metric v0.38.1 h1:2MM7m6wPw9B8Qv8iHygoAgkbejed59uUR6ezR5T3X2s= +go.opentelemetry.io/otel/metric v0.38.1/go.mod h1:FwqNHD3I/5iX9pfrRGZIlYICrJv0rHEUl2Ln5vdIVnQ= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY= -go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY= +go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1798,12 +1774,14 @@ go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= @@ -1813,13 +1791,14 @@ go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= -golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1828,7 +1807,6 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1839,9 +1817,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1858,9 +1834,11 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1904,9 +1882,9 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hM golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1934,11 +1912,11 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1949,7 +1927,11 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1963,14 +1945,19 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1980,6 +1967,9 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1988,12 +1978,14 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2005,7 +1997,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2019,7 +2010,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2050,17 +2040,22 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2077,23 +2072,29 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2114,7 +2115,6 @@ golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2154,15 +2154,25 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2184,7 +2194,13 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2217,9 +2233,19 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -2237,7 +2263,10 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= @@ -2251,12 +2280,15 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2304,26 +2336,15 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= -k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= -k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200318093247-d1ab8797c558/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= @@ -2334,10 +2355,8 @@ mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/namesys/base.go b/namesys/base.go index 937938cb2..699de01eb 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" path "github.com/ipfs/go-path" ) diff --git a/namesys/dns.go b/namesys/dns.go index 866820370..45944e9db 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -3,10 +3,9 @@ package namesys import ( "context" "errors" - "net" "strings" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" path "github.com/ipfs/go-path" isd "github.com/jbenet/go-is-domain" ) @@ -14,7 +13,7 @@ import ( const ethTLD = "eth" const linkTLD = "link" -type LookupTXTFunc func(name string) (txt []string, err error) +type LookupTXTFunc func(ctx context.Context, name string) (txt []string, err error) // DNSResolver implements a Resolver on DNS domains type DNSResolver struct { @@ -24,8 +23,8 @@ type DNSResolver struct { } // NewDNSResolver constructs a name resolver using DNS TXT records. -func NewDNSResolver() *DNSResolver { - return &DNSResolver{lookupTXT: net.LookupTXT} +func NewDNSResolver(lookup LookupTXTFunc) *DNSResolver { + return &DNSResolver{lookupTXT: lookup} } // Resolve implements Resolver. @@ -72,10 +71,10 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } rootChan := make(chan lookupRes, 1) - go workDomain(r, fqdn, rootChan) + go workDomain(ctx, r, fqdn, rootChan) subChan := make(chan lookupRes, 1) - go workDomain(r, "_dnslink."+fqdn, subChan) + go workDomain(ctx, r, "_dnslink."+fqdn, subChan) appendPath := func(p path.Path) (path.Path, error) { if len(segments) > 1 { @@ -119,10 +118,10 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options return out } -func workDomain(r *DNSResolver, name string, res chan lookupRes) { +func workDomain(ctx context.Context, r *DNSResolver, name string, res chan lookupRes) { defer close(res) - txt, err := r.lookupTXT(name) + txt, err := r.lookupTXT(ctx, name) if err != nil { // Error is != nil res <- lookupRes{"", err} diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 97d9f1f3a..39b904780 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -1,17 +1,18 @@ package namesys import ( + "context" "fmt" "testing" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" ) type mockDNS struct { entries map[string][]string } -func (m *mockDNS) lookupTXT(name string) (txt []string, err error) { +func (m *mockDNS) lookupTXT(ctx context.Context, name string) (txt []string, err error) { txt, ok := m.entries[name] if !ok { return nil, fmt.Errorf("no TXT entry for %s", name) diff --git a/namesys/interface.go b/namesys/interface.go index 22cc1dfd9..a1dbb1fdd 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" path "github.com/ipfs/go-path" ci "github.com/libp2p/go-libp2p/core/crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 8e745c3e7..94cce4bfc 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - btns "github.com/TRON-US/go-btns" - pb "github.com/TRON-US/go-btns/pb" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + btns "github.com/bittorrent/go-btns" + pb "github.com/bittorrent/go-btns/pb" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index e6032b554..78a2bf41c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,15 +7,17 @@ import ( "strings" "time" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" lru "github.com/hashicorp/golang-lru" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" path "github.com/ipfs/go-path" isd "github.com/jbenet/go-is-domain" ci "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" routing "github.com/libp2p/go-libp2p/core/routing" + madns "github.com/multiformats/go-multiaddr-dns" ) // mpns (a multi-protocol NameSystem) implements generic BTFS naming. @@ -27,22 +29,53 @@ import ( // // It can only publish to: (a) BTFS routing naming. type mpns struct { + ds ds.Datastore + dnsResolver, proquintResolver, ipnsResolver resolver ipnsPublisher Publisher staticMap map[string]path.Path cache *lru.Cache } +type Option func(*mpns) error -// NewNameSystem will construct the BTFS naming system based on Routing -func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { - var ( - cache *lru.Cache - staticMap map[string]path.Path - ) - if cachesize > 0 { - cache, _ = lru.New(cachesize) +// WithCache is an option that instructs the name system to use a (LRU) cache of the given size. +func WithCache(size int) Option { + return func(ns *mpns) error { + if size <= 0 { + return fmt.Errorf("invalid cache size %d; must be > 0", size) + } + + cache, err := lru.New(size) + if err != nil { + return err + } + + ns.cache = cache + return nil + } +} + +// WithDNSResolver is an option that supplies a custom DNS resolver to use instead of the system +// default. +func WithDNSResolver(rslv madns.BasicResolver) Option { + return func(ns *mpns) error { + ns.dnsResolver = NewDNSResolver(rslv.LookupTXT) + return nil + } +} + +// WithDatastore is an option that supplies a datastore to use instead of an in-memory map datastore. The datastore is used to store published IPNS records and make them available for querying. +func WithDatastore(ds ds.Datastore) Option { + return func(ns *mpns) error { + ns.ds = ds + return nil } +} + +// NewNameSystem will construct the BTFS naming system based on Routing +func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { + var staticMap map[string]path.Path // Prewarm namesys cache with static records for deterministic tests and debugging. // Useful for testing things like DNSLink without real DNS lookup. @@ -57,15 +90,37 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys staticMap[key] = value } } + ns := &mpns{ + staticMap: staticMap, + } - return &mpns{ - dnsResolver: NewDNSResolver(), - proquintResolver: new(ProquintResolver), - ipnsResolver: NewIpnsResolver(r), - ipnsPublisher: NewIpnsPublisher(r, ds), - staticMap: staticMap, - cache: cache, + for _, opt := range opts { + err := opt(ns) + if err != nil { + return nil, err + } } + + if ns.ds == nil { + ns.ds = dssync.MutexWrap(ds.NewMapDatastore()) + } + + if ns.dnsResolver == nil { + ns.dnsResolver = NewDNSResolver(madns.DefaultResolver.LookupTXT) + } + + ns.ipnsResolver = NewIpnsResolver(r) + ns.ipnsPublisher = NewIpnsPublisher(r, ns.ds) + ns.proquintResolver = new(ProquintResolver) + return ns, nil + // return &mpns{ + // dnsResolver: NewDNSResolver(), + // proquintResolver: new(ProquintResolver), + // ipnsResolver: NewIpnsResolver(r), + // ipnsPublisher: NewIpnsPublisher(r, ds), + // staticMap: staticMap, + // cache: cache, + // } } // DefaultResolverCacheTTL defines max ttl of a record placed in namesys cache. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 78c43a270..93407b14e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - btns "github.com/TRON-US/go-btns" - unixfs "github.com/TRON-US/go-unixfs" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + btns "github.com/bittorrent/go-btns" + unixfs "github.com/bittorrent/go-unixfs" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" offroute "github.com/ipfs/go-ipfs-routing/offline" @@ -112,7 +112,10 @@ func TestPublishWithCache0(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, 0) + nsys, err := NewNameSystem(routing, WithDatastore(dst)) + if err != nil { + t.Fatal(err) + } p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) if err != nil { t.Fatal(err) @@ -147,7 +150,11 @@ func TestPublishWithTTL(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, 128) + nsys, err := NewNameSystem(routing, WithDatastore(dst), WithCache(128)) + if err != nil { + t.Fatal(err) + } + p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) if err != nil { t.Fatal(err) diff --git a/namesys/proquint.go b/namesys/proquint.go index bebc9cfe2..a9ae00f5a 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" proquint "github.com/bren2010/proquint" path "github.com/ipfs/go-path" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 24fa0790d..177aff034 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,9 +6,9 @@ import ( "sync" "time" - ipns "github.com/TRON-US/go-btns" - pb "github.com/TRON-US/go-btns/pb" - ft "github.com/TRON-US/go-unixfs" + ipns "github.com/bittorrent/go-btns" + pb "github.com/bittorrent/go-btns/pb" + ft "github.com/bittorrent/go-unixfs" proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" dsquery "github.com/ipfs/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 2b90852fa..5ea8dc9f1 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - ipns "github.com/TRON-US/go-btns" + ipns "github.com/bittorrent/go-btns" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index d4da426d5..4e3c66ad0 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -8,8 +8,8 @@ import ( keystore "github.com/bittorrent/go-btfs/keystore" namesys "github.com/bittorrent/go-btfs/namesys" - btns "github.com/TRON-US/go-btns" - pb "github.com/TRON-US/go-btns/pb" + btns "github.com/bittorrent/go-btns" + pb "github.com/bittorrent/go-btns/pb" proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 39785e14e..9b14ef197 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,8 +12,8 @@ import ( "github.com/bittorrent/go-btfs/namesys" . "github.com/bittorrent/go-btfs/namesys/republisher" - "github.com/TRON-US/go-btns" - pb "github.com/TRON-US/go-btns/pb" + "github.com/bittorrent/go-btns" + pb "github.com/bittorrent/go-btns/pb" "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" @@ -39,8 +39,10 @@ func TestRepublish(t *testing.T) { t.Fatal(err) } - nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) - + nd.Namesys, err = namesys.NewNameSystem(nd.Routing, namesys.WithDatastore(nd.Repo.Datastore())) + if err != nil { + t.Fatal(err) + } nodes = append(nodes, nd) } @@ -134,7 +136,10 @@ func TestLongEOLRepublish(t *testing.T) { t.Fatal(err) } - nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) + nd.Namesys, err = namesys.NewNameSystem(nd.Routing, namesys.WithDatastore(nd.Repo.Datastore())) + if err != nil { + t.Fatal(err) + } nodes = append(nodes, nd) } diff --git a/namesys/resolve/pathresolver_test.go b/namesys/resolve/pathresolver_test.go deleted file mode 100644 index dc2dbbee4..000000000 --- a/namesys/resolve/pathresolver_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package resolve_test - -import ( - "testing" - - coremock "github.com/bittorrent/go-btfs/core/mock" - "github.com/bittorrent/go-btfs/namesys/resolve" - - path "github.com/ipfs/go-path" -) - -func TestResolveNoComponents(t *testing.T) { - n, err := coremock.NewMockNode() - if n == nil || err != nil { - t.Fatal("Should have constructed a mock node", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/btns/")) - if err.Error() != "invalid path \"/btns/\": btns path missing BTNS ID" { - t.Error("Should error with no components (/ipns/).", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/btfs/")) - if err.Error() != "invalid path \"/btfs/\": not enough path components" { - t.Error("Should error with no components (/ipfs/).", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/../..")) - if err.Error() != "invalid path \"/../..\": unknown namespace \"..\"" { - t.Error("Should error with invalid path.", err) - } -} diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index 729241cbd..606e60c02 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -8,10 +8,8 @@ import ( "github.com/bittorrent/go-btfs/namesys" - "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" ) var log = logging.Logger("nsresolv") @@ -68,7 +66,7 @@ func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (pat // Resolve resolves the given path by parsing out protocol-specific // entries (e.g. /btns/) and then going through the /btfs/ // entries and returning the final node. -func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (format.Node, error) { +/* func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (format.Node, error) { p, err := ResolveIPNS(ctx, nsys, p) if err != nil { return nil, err @@ -77,3 +75,4 @@ func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, // ok, we have an BTFS path now (or what we'll treat as one) return r.ResolvePath(ctx, p) } +*/ diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index decba9e2b..9f3cbec97 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - ipns "github.com/TRON-US/go-btns" + ipns "github.com/bittorrent/go-btns" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" mockrouting "github.com/ipfs/go-ipfs-routing/mock" diff --git a/namesys/routing.go b/namesys/routing.go index 07fa63ec7..9116642f0 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,9 +5,9 @@ import ( "strings" "time" - ipns "github.com/TRON-US/go-btns" - pb "github.com/TRON-US/go-btns/pb" - opts "github.com/TRON-US/interface-go-btfs-core/options/namesys" + ipns "github.com/bittorrent/go-btns" + pb "github.com/bittorrent/go-btns/pb" + opts "github.com/bittorrent/interface-go-btfs-core/options/namesys" proto "github.com/gogo/protobuf/proto" cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" diff --git a/plugin/daemon.go b/plugin/daemon.go index 3d0f65003..91d75027c 100644 --- a/plugin/daemon.go +++ b/plugin/daemon.go @@ -1,7 +1,7 @@ package plugin import ( - coreiface "github.com/TRON-US/interface-go-btfs-core" + coreiface "github.com/bittorrent/interface-go-btfs-core" ) // PluginDaemon is an interface for daemon plugins. These plugins will be run on diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go index ef88a513b..5f377305a 100644 --- a/plugin/loader/loader.go +++ b/plugin/loader/loader.go @@ -14,8 +14,8 @@ import ( fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" "github.com/ipld/go-ipld-prime/multicodec" - config "github.com/TRON-US/go-btfs-config" - cserialize "github.com/TRON-US/go-btfs-config/serialize" + config "github.com/bittorrent/go-btfs-config" + cserialize "github.com/bittorrent/go-btfs-config/serialize" logging "github.com/ipfs/go-log" opentracing "github.com/opentracing/opentracing-go" ) diff --git a/protos/contracts/contracts.pb.go b/protos/contracts/contracts.pb.go index 61ab8891f..cc2ba62db 100644 --- a/protos/contracts/contracts.pb.go +++ b/protos/contracts/contracts.pb.go @@ -5,10 +5,10 @@ package contractspb import ( fmt "fmt" + node "github.com/bittorrent/go-btfs-common/protos/node" + _ "github.com/bittorrent/protobuf/gogoproto" + proto "github.com/bittorrent/protobuf/proto" golang_proto "github.com/golang/protobuf/proto" - node "github.com/tron-us/go-btfs-common/protos/node" - _ "github.com/tron-us/protobuf/gogoproto" - proto "github.com/tron-us/protobuf/proto" io "io" math "math" math_bits "math/bits" diff --git a/protos/renter/renters.pb.go b/protos/renter/renters.pb.go index 228e552e1..1cb6d62dd 100644 --- a/protos/renter/renters.pb.go +++ b/protos/renter/renters.pb.go @@ -5,11 +5,11 @@ package renterpb import ( fmt "fmt" + _ "github.com/bittorrent/protobuf/gogoproto" + proto "github.com/bittorrent/protobuf/proto" + github_com_tron_us_protobuf_types "github.com/bittorrent/protobuf/types" _ "github.com/gogo/protobuf/types" golang_proto "github.com/golang/protobuf/proto" - _ "github.com/tron-us/protobuf/gogoproto" - proto "github.com/tron-us/protobuf/proto" - github_com_tron_us_protobuf_types "github.com/tron-us/protobuf/types" io "io" math "math" math_bits "math/bits" diff --git a/protos/session/session.pb.go b/protos/session/session.pb.go index c26fc76e3..3348b511e 100644 --- a/protos/session/session.pb.go +++ b/protos/session/session.pb.go @@ -5,11 +5,11 @@ package sessionpb import ( fmt "fmt" + _ "github.com/bittorrent/protobuf/gogoproto" + proto "github.com/bittorrent/protobuf/proto" + github_com_tron_us_protobuf_types "github.com/bittorrent/protobuf/types" _ "github.com/gogo/protobuf/types" golang_proto "github.com/golang/protobuf/proto" - _ "github.com/tron-us/protobuf/gogoproto" - proto "github.com/tron-us/protobuf/proto" - github_com_tron_us_protobuf_types "github.com/tron-us/protobuf/types" io "io" math "math" math_bits "math/bits" diff --git a/protos/shard/shard.pb.go b/protos/shard/shard.pb.go index 088a4433d..ad52a7529 100644 --- a/protos/shard/shard.pb.go +++ b/protos/shard/shard.pb.go @@ -5,10 +5,10 @@ package shardpb import ( fmt "fmt" + guard "github.com/bittorrent/go-btfs-common/protos/guard" + _ "github.com/bittorrent/protobuf/gogoproto" + proto "github.com/bittorrent/protobuf/proto" golang_proto "github.com/golang/protobuf/proto" - guard "github.com/tron-us/go-btfs-common/protos/guard" - _ "github.com/tron-us/protobuf/gogoproto" - proto "github.com/tron-us/protobuf/proto" io "io" math "math" math_bits "math/bits" diff --git a/protos/wallet/wallet.pb.go b/protos/wallet/wallet.pb.go index ba88a7cc6..01d821632 100644 --- a/protos/wallet/wallet.pb.go +++ b/protos/wallet/wallet.pb.go @@ -5,12 +5,12 @@ package walletpb import ( fmt "fmt" + ledger "github.com/bittorrent/go-btfs-common/protos/ledger" + _ "github.com/bittorrent/protobuf/gogoproto" + proto "github.com/bittorrent/protobuf/proto" + github_com_tron_us_protobuf_types "github.com/bittorrent/protobuf/types" _ "github.com/gogo/protobuf/types" golang_proto "github.com/golang/protobuf/proto" - ledger "github.com/tron-us/go-btfs-common/protos/ledger" - _ "github.com/tron-us/protobuf/gogoproto" - proto "github.com/tron-us/protobuf/proto" - github_com_tron_us_protobuf_types "github.com/tron-us/protobuf/types" io "io" math "math" math_bits "math/bits" diff --git a/repo/fsrepo/config_test.go b/repo/fsrepo/config_test.go index e9359874b..540047c7a 100644 --- a/repo/fsrepo/config_test.go +++ b/repo/fsrepo/config_test.go @@ -10,7 +10,7 @@ import ( "github.com/bittorrent/go-btfs/plugin/loader" "github.com/bittorrent/go-btfs/repo/fsrepo" - "github.com/TRON-US/go-btfs-config" + "github.com/bittorrent/go-btfs-config" ) // note: to test sorting of the mountpoints in the disk spec they are diff --git a/repo/fsrepo/doc.go b/repo/fsrepo/doc.go index 50d52855a..327afcf09 100644 --- a/repo/fsrepo/doc.go +++ b/repo/fsrepo/doc.go @@ -2,19 +2,19 @@ // // TODO explain the package roadmap... // -// .ipfs/ -// ├── client/ -// | ├── client.lock <------ protects client/ + signals its own pid -// │ ├── ipfs-client.cpuprof -// │ └── ipfs-client.memprof -// ├── config -// ├── daemon/ -// │ ├── daemon.lock <------ protects daemon/ + signals its own address -// │ ├── ipfs-daemon.cpuprof -// │ └── ipfs-daemon.memprof -// ├── datastore/ -// ├── repo.lock <------ protects datastore/ and config -// └── version +// .ipfs/ +// ├── client/ +// | ├── client.lock <------ protects client/ + signals its own pid +// │ ├── ipfs-client.cpuprof +// │ └── ipfs-client.memprof +// ├── config +// ├── daemon/ +// │ ├── daemon.lock <------ protects daemon/ + signals its own address +// │ ├── ipfs-daemon.cpuprof +// │ └── ipfs-daemon.memprof +// ├── datastore/ +// ├── repo.lock <------ protects datastore/ and config +// └── version package fsrepo // TODO prevent multiple daemons from running diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go index 970a40832..a154304e5 100644 --- a/repo/fsrepo/fsrepo.go +++ b/repo/fsrepo/fsrepo.go @@ -17,8 +17,8 @@ import ( mfsr "github.com/bittorrent/go-btfs/repo/fsrepo/migrations" dir "github.com/bittorrent/go-btfs/thirdparty/dir" - config "github.com/TRON-US/go-btfs-config" - serialize "github.com/TRON-US/go-btfs-config/serialize" + config "github.com/bittorrent/go-btfs-config" + serialize "github.com/bittorrent/go-btfs-config/serialize" ds "github.com/ipfs/go-datastore" measure "github.com/ipfs/go-ds-measure" filestore "github.com/ipfs/go-filestore" diff --git a/repo/fsrepo/fsrepo_test.go b/repo/fsrepo/fsrepo_test.go index 1b0255f1a..e664f9994 100644 --- a/repo/fsrepo/fsrepo_test.go +++ b/repo/fsrepo/fsrepo_test.go @@ -10,7 +10,7 @@ import ( "github.com/bittorrent/go-btfs/thirdparty/assert" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" datastore "github.com/ipfs/go-datastore" ) diff --git a/repo/fsrepo/migrations/migrations.go b/repo/fsrepo/migrations/migrations.go index 2e4781f2d..31b733b60 100644 --- a/repo/fsrepo/migrations/migrations.go +++ b/repo/fsrepo/migrations/migrations.go @@ -15,7 +15,7 @@ import ( "strings" ) -var DistPath = "https://raw.githubusercontent.com/TRON-US/btfs-distributions/master" +var DistPath = "https://raw.githubusercontent.com/bittorrent/btfs-distributions/master" func init() { // Possible for the download path to be overwritten from command line diff --git a/repo/mock.go b/repo/mock.go index e8f57a6f9..b3b06c0cd 100644 --- a/repo/mock.go +++ b/repo/mock.go @@ -5,7 +5,7 @@ import ( keystore "github.com/bittorrent/go-btfs/keystore" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" filestore "github.com/ipfs/go-filestore" ma "github.com/multiformats/go-multiaddr" ) diff --git a/repo/onlyone.go b/repo/onlyone.go index f24717c2b..738274d0c 100644 --- a/repo/onlyone.go +++ b/repo/onlyone.go @@ -19,8 +19,8 @@ type OnlyOne struct { // that are unique across different concrete Repo implementations, // e.g. by creating a local type: // -// type repoKey string -// r, err := o.Open(repoKey(path), open) +// type repoKey string +// r, err := o.Open(repoKey(path), open) // // Call Repo.Close when done. func (o *OnlyOne) Open(key interface{}, open func() (Repo, error)) (Repo, error) { diff --git a/repo/pbstore.go b/repo/pbstore.go index b9ff66f85..d0b8e06e9 100644 --- a/repo/pbstore.go +++ b/repo/pbstore.go @@ -3,7 +3,7 @@ package repo import ( "context" - "github.com/tron-us/protobuf/proto" + "github.com/bittorrent/protobuf/proto" "github.com/ipfs/go-datastore" ) diff --git a/repo/pbstore_test.go b/repo/pbstore_test.go index d8880e8f3..207c15f2b 100644 --- a/repo/pbstore_test.go +++ b/repo/pbstore_test.go @@ -3,7 +3,7 @@ package repo import ( "testing" - "github.com/tron-us/go-btfs-common/protos/hub" + "github.com/bittorrent/go-btfs-common/protos/hub" "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" diff --git a/repo/repo.go b/repo/repo.go index 5940b6ec4..83b57c77f 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -6,7 +6,7 @@ import ( keystore "github.com/bittorrent/go-btfs/keystore" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" ds "github.com/ipfs/go-datastore" filestore "github.com/ipfs/go-filestore" ma "github.com/multiformats/go-multiaddr" diff --git a/reportstatus/reportstatus.go b/reportstatus/reportstatus.go index 1306deab6..205ca8563 100644 --- a/reportstatus/reportstatus.go +++ b/reportstatus/reportstatus.go @@ -8,12 +8,12 @@ import ( "strings" "time" - config "github.com/TRON-US/go-btfs-config" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/reportstatus/abi" "github.com/bittorrent/go-btfs/transaction" "github.com/ethereum/go-ethereum/common" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" "github.com/cenkalti/backoff/v4" logging "github.com/ipfs/go-log" diff --git a/routing/delegated.go b/routing/delegated.go index 3e6fed73d..2245b91a4 100644 --- a/routing/delegated.go +++ b/routing/delegated.go @@ -7,7 +7,7 @@ import ( "fmt" "net/http" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" "github.com/ipfs/go-datastore" drc "github.com/ipfs/go-delegated-routing/client" drp "github.com/ipfs/go-delegated-routing/gen/proto" diff --git a/routing/delegated_test.go b/routing/delegated_test.go index 5f9e4b0a9..068c60a69 100644 --- a/routing/delegated_test.go +++ b/routing/delegated_test.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "testing" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" crypto "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" diff --git a/routing/error.go b/routing/error.go index b4def535e..8cd0e6fa5 100644 --- a/routing/error.go +++ b/routing/error.go @@ -3,7 +3,7 @@ package routing import ( "fmt" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" ) type ParamNeededError struct { diff --git a/settlement/swap/swapprotocol/pb/swap.pb.go b/settlement/swap/swapprotocol/pb/swap.pb.go index ce6c5312b..d8301d756 100644 --- a/settlement/swap/swapprotocol/pb/swap.pb.go +++ b/settlement/swap/swapprotocol/pb/swap.pb.go @@ -5,7 +5,7 @@ package pb import ( fmt "fmt" - proto "github.com/tron-us/protobuf/proto" + proto "github.com/bittorrent/protobuf/proto" io "io" math "math" math_bits "math/bits" diff --git a/settlement/swap/swapprotocol/swapprotocol.go b/settlement/swap/swapprotocol/swapprotocol.go index 2289f6b5f..7f5648dcc 100644 --- a/settlement/swap/swapprotocol/swapprotocol.go +++ b/settlement/swap/swapprotocol/swapprotocol.go @@ -12,13 +12,13 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" - coreiface "github.com/TRON-US/interface-go-btfs-core" "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/corehttp/remote" "github.com/bittorrent/go-btfs/settlement/swap/priceoracle" "github.com/bittorrent/go-btfs/settlement/swap/swapprotocol/pb" "github.com/bittorrent/go-btfs/settlement/swap/vault" + coreiface "github.com/bittorrent/interface-go-btfs-core" "github.com/ethereum/go-ethereum/common" logging "github.com/ipfs/go-log" diff --git a/settlement/swap/vault/chequestore.go b/settlement/swap/vault/chequestore.go index 47ea8521b..53bb9245a 100644 --- a/settlement/swap/vault/chequestore.go +++ b/settlement/swap/vault/chequestore.go @@ -4,12 +4,13 @@ import ( "context" "errors" "fmt" - "github.com/bittorrent/go-btfs/chain/tokencfg" "math/big" "strings" "sync" "time" + "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/statestore" "github.com/bittorrent/go-btfs/transaction" "github.com/bittorrent/go-btfs/transaction/crypto" @@ -257,8 +258,8 @@ func (s *chequeStore) ReceivedChequeRecordsByPeer(vault common.Address) ([]Chequ return records, nil } -//store cheque record -//Beneficiary common.Address +// store cheque record +// Beneficiary common.Address func (s *chequeStore) storeChequeRecord(vault common.Address, amount *big.Int, token common.Address) error { var indexRange IndexRange err := s.store.Get(historyReceivedChequeIndexKey(vault), &indexRange) @@ -524,8 +525,8 @@ func historySendChequeKey(beneficiary common.Address, index uint64) string { return fmt.Sprintf("%s_%x", beneficiaryStr, index) } -//store cheque record -//Beneficiary common.Address +// store cheque record +// Beneficiary common.Address func (s *chequeStore) StoreSendChequeRecord(vault, beneficiary common.Address, amount *big.Int, token common.Address) error { var indexRange IndexRange err := s.store.Get(historySendChequeIndexKey(beneficiary), &indexRange) diff --git a/spin/analytics.go b/spin/analytics.go index 209694a1a..fd7e46248 100644 --- a/spin/analytics.go +++ b/spin/analytics.go @@ -9,16 +9,16 @@ import ( "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/storage/helper" - config "github.com/TRON-US/go-btfs-config" - iface "github.com/TRON-US/interface-go-btfs-core" "github.com/alecthomas/units" + nodepb "github.com/bittorrent/go-btfs-common/protos/node" + pb "github.com/bittorrent/go-btfs-common/protos/status" + config "github.com/bittorrent/go-btfs-config" + iface "github.com/bittorrent/interface-go-btfs-core" "github.com/gogo/protobuf/proto" "github.com/ipfs/go-bitswap" logging "github.com/ipfs/go-log" ic "github.com/libp2p/go-libp2p/core/crypto" "github.com/shirou/gopsutil/v3/cpu" - nodepb "github.com/tron-us/go-btfs-common/protos/node" - pb "github.com/tron-us/go-btfs-common/protos/status" ) type dcWrap struct { @@ -130,7 +130,6 @@ func Analytics(api iface.CoreAPI, cfgRoot string, node *core.IpfsNode, BTFSVersi } dc.setRoles() - //go dc.collectionAgent(node) go dc.collectionAgentOnline(node) go dc.collectionAgentOnlineDaily(node) } diff --git a/spin/analytics_online.go b/spin/analytics_online.go index 33f0b624f..2e4fc7410 100644 --- a/spin/analytics_online.go +++ b/spin/analytics_online.go @@ -6,11 +6,11 @@ import ( "strings" "time" - config "github.com/TRON-US/go-btfs-config" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" + cgrpc "github.com/bittorrent/go-btfs-common/utils/grpc" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/core" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" - cgrpc "github.com/tron-us/go-btfs-common/utils/grpc" "github.com/cenkalti/backoff/v4" "github.com/gogo/protobuf/proto" diff --git a/spin/analytics_online_daily.go b/spin/analytics_online_daily.go index c3ab2b4c7..caf48d5f0 100644 --- a/spin/analytics_online_daily.go +++ b/spin/analytics_online_daily.go @@ -7,11 +7,11 @@ import ( "strings" "time" - config "github.com/TRON-US/go-btfs-config" + onlinePb "github.com/bittorrent/go-btfs-common/protos/online" + cgrpc "github.com/bittorrent/go-btfs-common/utils/grpc" + config "github.com/bittorrent/go-btfs-config" "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/core" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" - cgrpc "github.com/tron-us/go-btfs-common/utils/grpc" "github.com/cenkalti/backoff/v4" ) diff --git a/tar/format.go b/tar/format.go index 4696247ca..14ea6c76d 100644 --- a/tar/format.go +++ b/tar/format.go @@ -8,9 +8,9 @@ import ( "io" "strings" - chunker "github.com/TRON-US/go-btfs-chunker" - importer "github.com/TRON-US/go-unixfs/importer" - uio "github.com/TRON-US/go-unixfs/io" + chunker "github.com/bittorrent/go-btfs-chunker" + importer "github.com/bittorrent/go-unixfs/importer" + uio "github.com/bittorrent/go-unixfs/io" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" dag "github.com/ipfs/go-merkledag" diff --git a/test/bench/bench_cli_ipfs_add/main.go b/test/bench/bench_cli_ipfs_add/main.go index 31ecd9f0f..ad787a69c 100644 --- a/test/bench/bench_cli_ipfs_add/main.go +++ b/test/bench/bench_cli_ipfs_add/main.go @@ -12,7 +12,7 @@ import ( "github.com/bittorrent/go-btfs/thirdparty/unit" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" random "github.com/jbenet/go-random" ) diff --git a/test/bench/offline_add/main.go b/test/bench/offline_add/main.go index 4b8d03488..b03e8c783 100644 --- a/test/bench/offline_add/main.go +++ b/test/bench/offline_add/main.go @@ -11,7 +11,7 @@ import ( "github.com/bittorrent/go-btfs/thirdparty/unit" - config "github.com/TRON-US/go-btfs-config" + config "github.com/bittorrent/go-btfs-config" random "github.com/jbenet/go-random" ) diff --git a/test/dependencies/graphsync-get/graphsync-get.go b/test/dependencies/graphsync-get/graphsync-get.go index 46f2b4960..fbace6044 100644 --- a/test/dependencies/graphsync-get/graphsync-get.go +++ b/test/dependencies/graphsync-get/graphsync-get.go @@ -7,27 +7,19 @@ import ( "log" "os" - uio "github.com/TRON-US/go-unixfs/io" - "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" + uio "github.com/bittorrent/go-unixfs/io" dssync "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-graphsync" gsimpl "github.com/ipfs/go-graphsync/impl" "github.com/ipfs/go-graphsync/network" "github.com/ipfs/go-graphsync/storeutil" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" - "github.com/ipfs/go-merkledag" - "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" ipldselector "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" - "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" ) func newGraphsync(ctx context.Context, p2p host.Host, bs blockstore.Blockstore) (graphsync.GraphExchange, error) { diff --git a/test/integration/addcat_test.go b/test/integration/addcat_test.go index 613353eb1..7f65a7ca8 100644 --- a/test/integration/addcat_test.go +++ b/test/integration/addcat_test.go @@ -17,9 +17,9 @@ import ( mock "github.com/bittorrent/go-btfs/core/mock" "github.com/bittorrent/go-btfs/thirdparty/unit" - files "github.com/TRON-US/go-btfs-files" - "github.com/TRON-US/interface-go-btfs-core/options" - "github.com/TRON-US/interface-go-btfs-core/path" + files "github.com/bittorrent/go-btfs-files" + "github.com/bittorrent/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/path" logging "github.com/ipfs/go-log" random "github.com/jbenet/go-random" diff --git a/test/integration/bench_cat_test.go b/test/integration/bench_cat_test.go index 24e598556..88272a999 100644 --- a/test/integration/bench_cat_test.go +++ b/test/integration/bench_cat_test.go @@ -14,7 +14,7 @@ import ( mock "github.com/bittorrent/go-btfs/core/mock" "github.com/bittorrent/go-btfs/thirdparty/unit" - files "github.com/TRON-US/go-btfs-files" + files "github.com/bittorrent/go-btfs-files" testutil "github.com/libp2p/go-libp2p-testing/net" peer "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" diff --git a/test/integration/rs_addcat_test.go b/test/integration/rs_addcat_test.go index 449c8a6fc..6b0e1f900 100644 --- a/test/integration/rs_addcat_test.go +++ b/test/integration/rs_addcat_test.go @@ -3,11 +3,11 @@ package integrationtest import ( "github.com/bittorrent/go-btfs/thirdparty/unit" - files "github.com/TRON-US/go-btfs-files" + files "github.com/bittorrent/go-btfs-files" "testing" - "github.com/TRON-US/interface-go-btfs-core/options" + "github.com/bittorrent/interface-go-btfs-core/options" "github.com/stretchr/testify/assert" testutil "github.com/libp2p/go-libp2p-testing/net" diff --git a/test/integration/three_legged_cat_test.go b/test/integration/three_legged_cat_test.go index 2e705300e..935d0cfaf 100644 --- a/test/integration/three_legged_cat_test.go +++ b/test/integration/three_legged_cat_test.go @@ -14,7 +14,7 @@ import ( mock "github.com/bittorrent/go-btfs/core/mock" "github.com/bittorrent/go-btfs/thirdparty/unit" - files "github.com/TRON-US/go-btfs-files" + files "github.com/bittorrent/go-btfs-files" testutil "github.com/libp2p/go-libp2p-testing/net" peer "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" diff --git a/test/integration/wan_lan_dht_test.go b/test/integration/wan_lan_dht_test.go index 0cf94c72f..83eb88ce3 100644 --- a/test/integration/wan_lan_dht_test.go +++ b/test/integration/wan_lan_dht_test.go @@ -150,14 +150,14 @@ StartupWait: for { select { case err := <-testPeer.DHT.LAN.RefreshRoutingTable(): - if err != nil { - fmt.Printf("Error refreshing routing table: %v\n", err) - } + // if err != nil { + // fmt.Printf("Error refreshing routing table: %v\n", err) + // } if testPeer.DHT.LAN.RoutingTable() == nil || testPeer.DHT.LAN.RoutingTable().Size() == 0 || err != nil { //delay the sleep time so that the LAN can find all each other - time.Sleep(3 * time.Second) + time.Sleep(100 * time.Millisecond) continue } break StartupWait @@ -169,29 +169,29 @@ StartupWait: startupCancel() // choose a lan peer and validate lan DHT is functioning. - i := rand.Intn(len(lanPeers)) - if testPeer.PeerHost.Network().Connectedness(lanPeers[i].Identity) == corenet.Connected { - i = (i + 1) % len(lanPeers) - if testPeer.PeerHost.Network().Connectedness(lanPeers[i].Identity) == corenet.Connected { - _ = testPeer.PeerHost.Network().ClosePeer(lanPeers[i].Identity) - testPeer.PeerHost.Peerstore().ClearAddrs(lanPeers[i].Identity) - } - } - // That peer will provide a new CID, and we'll validate the test node can find it. + // i := rand.Intn(len(lanPeers)) + // if testPeer.PeerHost.Network().Connectedness(lanPeers[i].Identity) == corenet.Connected { + // i = (i + 1) % len(lanPeers) + // if testPeer.PeerHost.Network().Connectedness(lanPeers[i].Identity) == corenet.Connected { + // _ = testPeer.PeerHost.Network().ClosePeer(lanPeers[i].Identity) + // testPeer.PeerHost.Peerstore().ClearAddrs(lanPeers[i].Identity) + // } + // } + // // That peer will provide a new CID, and we'll validate the test node can find it. provideCid := cid.NewCidV1(cid.Raw, []byte("Lan Provide Record")) - provideCtx, cancel := context.WithTimeout(ctx, 3*time.Second) - defer cancel() - if err := lanPeers[i].DHT.Provide(provideCtx, provideCid, true); err != nil { - return err - } - provChan := testPeer.DHT.FindProvidersAsync(provideCtx, provideCid, 0) - prov, ok := <-provChan - if !ok || prov.ID == "" { - return fmt.Errorf("Expected provider. stream closed early") - } - if prov.ID != lanPeers[i].Identity { - return fmt.Errorf("Unexpected lan peer provided record") - } + // provideCtx, cancel := context.WithTimeout(ctx, time.Second) + // defer cancel() + // if err := lanPeers[i].DHT.Provide(provideCtx, provideCid, true); err != nil { + // return err + // } + // provChan := testPeer.DHT.FindProvidersAsync(provideCtx, provideCid, 0) + // prov, ok := <-provChan + // if !ok || prov.ID == "" { + // return fmt.Errorf("Expected provider. stream closed early, ok is: %t, prov is %+v", ok, prov) + // } + // if prov.ID != lanPeers[i].Identity { + // return fmt.Errorf("Unexpected lan peer provided record") + // } // Now, connect with a wan peer. for _, p := range wanPeers { @@ -208,8 +208,6 @@ StartupWait: startupCtx, startupCancel = context.WithTimeout(ctx, time.Second*60) WanStartupWait: for { - //delay the sleep time so that the LAN can find all each other - time.Sleep(3 * time.Second) select { case err := <-testPeer.DHT.WAN.RefreshRoutingTable(): //if err != nil { @@ -229,7 +227,7 @@ WanStartupWait: startupCancel() // choose a wan peer and validate wan DHT is functioning. - i = rand.Intn(len(wanPeers)) + i := rand.Intn(len(wanPeers)) if testPeer.PeerHost.Network().Connectedness(wanPeers[i].Identity) == corenet.Connected { i = (i + 1) % len(wanPeers) if testPeer.PeerHost.Network().Connectedness(wanPeers[i].Identity) == corenet.Connected { @@ -240,13 +238,13 @@ WanStartupWait: // That peer will provide a new CID, and we'll validate the test node can find it. wanCid := cid.NewCidV1(cid.Raw, []byte("Wan Provide Record")) - wanProvideCtx, cancel := context.WithTimeout(ctx, 3*time.Second) + wanProvideCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := wanPeers[i].DHT.Provide(wanProvideCtx, wanCid, true); err != nil { return err } - provChan = testPeer.DHT.FindProvidersAsync(wanProvideCtx, wanCid, 0) - prov, ok = <-provChan + provChan := testPeer.DHT.FindProvidersAsync(wanProvideCtx, wanCid, 0) + prov, ok := <-provChan if !ok || prov.ID == "" { return fmt.Errorf("Expected one provider, closed early") } @@ -261,7 +259,7 @@ WanStartupWait: testPeer.PeerHost.Peerstore().ClearAddrs(wanPeers[i].Identity) } - provideCtx, cancel = context.WithTimeout(ctx, 3*time.Second) + provideCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := wanPeers[i].DHT.Provide(provideCtx, provideCid, true); err != nil { return err @@ -271,10 +269,10 @@ WanStartupWait: if !ok { return fmt.Errorf("Expected two providers, got 0") } - prov, ok = <-provChan - if !ok { - return fmt.Errorf("Expected two providers, got 1") - } + // prov, ok = <-provChan + // if !ok { + // return fmt.Errorf("Expected two providers, got 1") + // } return nil } diff --git a/test_pkgs.txt b/test_pkgs.txt index 29323e8bd..efb134f9d 100644 --- a/test_pkgs.txt +++ b/test_pkgs.txt @@ -16,8 +16,8 @@ github.com/bittorrent/go-btfs/core/commands/storage/upload/upload github.com/bittorrent/go-btfs/core/coreapi github.com/bittorrent/go-btfs/core/coreapi/test github.com/bittorrent/go-btfs/core/corehttp +github.com/bittorrent/go-btfs/core/corehttp/gateway github.com/bittorrent/go-btfs/core/hub -github.com/bittorrent/go-btfs/core/wallet github.com/bittorrent/go-btfs/keystore github.com/bittorrent/go-btfs/namesys github.com/bittorrent/go-btfs/namesys/republisher diff --git a/tests_coverage.html b/tests_coverage.html new file mode 100644 index 000000000..51c29d80d --- /dev/null +++ b/tests_coverage.html @@ -0,0 +1,38514 @@ + + + + + + assets: Go Coverage Report + + + +
+ +
+ not tracked + + not covered + covered + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/thirdparty/notifier/notifier.go b/thirdparty/notifier/notifier.go index 60bf6da3a..bb8860702 100644 --- a/thirdparty/notifier/notifier.go +++ b/thirdparty/notifier/notifier.go @@ -14,23 +14,21 @@ import ( // their own Notifiee interfaces to ensure type-safety // of notifications: // -// type RocketNotifiee interface{ -// Countdown(r Rocket, countdown time.Duration) -// LiftedOff(Rocket) -// ReachedOrbit(Rocket) -// Detached(Rocket, Capsule) -// Landed(Rocket) -// } -// +// type RocketNotifiee interface{ +// Countdown(r Rocket, countdown time.Duration) +// LiftedOff(Rocket) +// ReachedOrbit(Rocket) +// Detached(Rocket, Capsule) +// Landed(Rocket) +// } type Notifiee interface{} // Notifier is a notification dispatcher. It's meant // to be composed, and its zero-value is ready to be used. // -// type Rocket struct { -// notifier notifier.Notifier -// } -// +// type Rocket struct { +// notifier notifier.Notifier +// } type Notifier struct { mu sync.RWMutex // guards notifiees nots map[Notifiee]struct{} @@ -51,17 +49,16 @@ func RateLimited(limit int) *Notifier { // Notify signs up Notifiee e for notifications. This function // is meant to be called behind your own type-safe function(s): // -// // generic function for pattern-following -// func (r *Rocket) Notify(n Notifiee) { -// r.notifier.Notify(n) -// } -// -// // or as part of other functions -// func (r *Rocket) Onboard(a Astronaut) { -// r.astronauts = append(r.austronauts, a) -// r.notifier.Notify(a) -// } +// // generic function for pattern-following +// func (r *Rocket) Notify(n Notifiee) { +// r.notifier.Notify(n) +// } // +// // or as part of other functions +// func (r *Rocket) Onboard(a Astronaut) { +// r.astronauts = append(r.austronauts, a) +// r.notifier.Notify(a) +// } func (n *Notifier) Notify(e Notifiee) { n.mu.Lock() if n.nots == nil { // so that zero-value is ready to be used. @@ -74,17 +71,16 @@ func (n *Notifier) Notify(e Notifiee) { // StopNotify stops notifying Notifiee e. This function // is meant to be called behind your own type-safe function(s): // -// // generic function for pattern-following -// func (r *Rocket) StopNotify(n Notifiee) { -// r.notifier.StopNotify(n) -// } -// -// // or as part of other functions -// func (r *Rocket) Detach(c Capsule) { -// r.notifier.StopNotify(c) -// r.capsule = nil -// } +// // generic function for pattern-following +// func (r *Rocket) StopNotify(n Notifiee) { +// r.notifier.StopNotify(n) +// } // +// // or as part of other functions +// func (r *Rocket) Detach(c Capsule) { +// r.notifier.StopNotify(c) +// r.capsule = nil +// } func (n *Notifier) StopNotify(e Notifiee) { n.mu.Lock() if n.nots != nil { // so that zero-value is ready to be used. @@ -97,22 +93,22 @@ func (n *Notifier) StopNotify(e Notifiee) { // This is done by calling the given function with each notifiee. It is // meant to be called with your own type-safe notification functions: // -// func (r *Rocket) Launch() { -// r.notifyAll(func(n Notifiee) { -// n.Launched(r) -// }) -// } +// func (r *Rocket) Launch() { +// r.notifyAll(func(n Notifiee) { +// n.Launched(r) +// }) +// } // -// // make it private so only you can use it. This function is necessary -// // to make sure you only up-cast in one place. You control who you added -// // to be a notifiee. If Go adds generics, maybe we can get rid of this -// // method but for now it is like wrapping a type-less container with -// // a type safe interface. -// func (r *Rocket) notifyAll(notify func(Notifiee)) { -// r.notifier.NotifyAll(func(n notifier.Notifiee) { -// notify(n.(Notifiee)) -// }) -// } +// // make it private so only you can use it. This function is necessary +// // to make sure you only up-cast in one place. You control who you added +// // to be a notifiee. If Go adds generics, maybe we can get rid of this +// // method but for now it is like wrapping a type-less container with +// // a type safe interface. +// func (r *Rocket) notifyAll(notify func(Notifiee)) { +// r.notifier.NotifyAll(func(n notifier.Notifiee) { +// notify(n.(Notifiee)) +// }) +// } // // Note well: each notification is launched in its own goroutine, so they // can be processed concurrently, and so that whatever the notification does diff --git a/utils/common.go b/utils/common.go new file mode 100644 index 000000000..ba3c3295e --- /dev/null +++ b/utils/common.go @@ -0,0 +1,22 @@ +package utils + +import ( + "errors" + cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" +) + +func CheckSimpleMode(env cmds.Environment) error { + conf, err := cmdenv.GetConfig(env) + if err != nil { + return err + } + + //fmt.Println("CheckSimpleMode ... ", conf.SimpleMode) + + if conf.SimpleMode { + return errors.New("this api is not support in simple mode, please check the node's simple mode! ") + } + + return nil +} diff --git a/version.go b/version.go index ac22baa34..e41ac97b7 100644 --- a/version.go +++ b/version.go @@ -4,7 +4,7 @@ package btfs var CurrentCommit string // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "2.3.1" +const CurrentVersionNumber = "2.3.2" const ApiVersion = "/go-btfs/" + CurrentVersionNumber + "/" From 3395b69c7c10fa30ac2200804fe42ae165a0702b Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Fri, 22 Sep 2023 19:40:49 +0800 Subject: [PATCH 02/16] feat: add multibase commands (#354) * fix: return an explicit error instead of panic and optimize the error log (#339) * feat: accesss key * feat: access-key module & access-key cmds * fix: remove redunt error condition * opt: handle wrapper error and remove unused bucket type define in access-key module * fix: return an explicit error instead of panic and optimize the error log (#339) (#341) * feat: add multibase commands * feat: add daemon check before execute accesskey commands * optmize: access-key store prefix * optmize: not found error * feat: add s3 signature * chore: * chore: * chore: * chore: * chore: * feat: add store * chore: * feat: s3 access-key, server, handlers, statestore, filestore * chore: * optmize: code structure * style: s3 code structure * style: code structure * optmize: code structure * feat: add multiple context lock * feat: check auth * chore: * chore: * feat: add bucket service * chore: * mod: update bucket lock * chore: * chore: * chore: * del s3d * chore: s3 req & rsp structure * chore: * feat: add pubBucket api * feat: add more bucket api * chore: * chore: * feat: add request and response * chore: * feat: server build * chore: check acl * chore * chore: adjust bucket url *  * feat: add auth middleware * feat: adjust code structure * optmize: code structure & auth bug * feat: put object * chore: * mod: mod bucket parse req * optmize: adjust place of response error * chore: mig sig 01 * chore: clear sig * chore: mig sig 02 * chore: mig sig 03 * chore: mig sig 04 * optmize: refractor codes * optmize: rename auth to sign * optmize: code structure & h.name * fix: h.name * feat: put-object * feat: multipart * fix: nslock key * chore: rename s3 constructor file name * fix: tidy example go-ipfs-as-a-library go mod * chore: change default s3 server address to local * mod: add object api * feat: s3-compatible-api - 1. add start option and configure; 2. optmize providers interfaces and implements; 3. rewrite the server construct function * merge: object * chore: add object lock * chore: of delete objs * chore: * fix: list objects bug * chore: rename ListObjetV1Handler to ListObjectHandler, rename BTFS-Hash to CID * refractor: bucket service * refactor: object service * refractor: refract object service * refractor: handlers * refractor: bucket handler * refractor: bucket handler * refractor: response * refractor: response func * refractor: response * refractor: object * refractor: object * refractor: objects * refractor: btf api add timeout & add cid refs to enable referred cid can not be deleted * ref: fix delete object remove body * ref: format code * fix: routers * fix: add cors header * fix: router option * feat: add delete objects handler * refractor: multipart * ref: multipart * fix: multipart etag calculation * chore: add min part size todo * chore: upgrade 'github.com/anacrolix/torrent' from v1.47.0 to v1.52.5 * opt: comment and amz header * opt: code * opt: preflight cache max age * feat: bucket response add acl header * opt: change cid-list header to cid * fix: required check exlude unknow location * fix: get object acl * ref: requests * ref: complete refractor * fix: args parse * fix: get object unlock * fix: object acl writer * fix: delete objects error * fix: Sign handler name * fix: object name escape * fix: copy source validate * opt: s3 log * opt: s3 api log * fix: allow Cache-Control header in PutObject and CopyObject Action * opt: add access-key command taglines * opt: add accesskey command description --------- Co-authored-by: Steve Co-authored-by: fish <920886811@163.com> --- blocks/blockstoreutil/remove.go | 4 +- chain/chain.go | 5 +- cmd/btfs/daemon.go | 31 + core/commands/accesskey.go | 175 + core/commands/cid.go | 2 +- core/commands/cmdenv/cidbase.go | 2 +- core/commands/commands.go | 2 +- core/commands/commands_test.go | 5 + core/commands/files.go | 6 +- core/commands/filestore.go | 4 +- core/commands/multibase.go | 171 + core/commands/object/object.go | 10 +- core/commands/object/patch.go | 4 +- core/commands/refs.go | 2 +- core/commands/root.go | 2 + core/commands/storage/upload/upload/upload.go | 17 +- core/corehttp/gateway/gateway.go | 2 +- core/corehttp/gateway/gateway_test.go | 6 +- docs/examples/go-ipfs-as-a-library/go.mod | 18 +- docs/examples/go-ipfs-as-a-library/go.sum | 2849 +++++++++++++---- fuse/ipns/ipns_test.go | 8 +- fuse/readonly/readonly_unix.go | 2 +- go.mod | 34 +- go.sum | 70 +- s3/action/action.go | 150 + s3/api/contexts/contexts.go | 26 + s3/api/contexts/contexts_access_key.go | 16 + s3/api/contexts/contexts_handle_inf.go | 25 + s3/api/handlers/handlers.go | 153 + s3/api/handlers/handlers_bucket.go | 147 + s3/api/handlers/handlers_middlewares.go | 85 + s3/api/handlers/handlers_multipart.go | 101 + s3/api/handlers/handlers_object.go | 224 ++ s3/api/handlers/options.go | 69 + s3/api/handlers/proto.go | 41 + s3/api/providers/btfs_api.go | 97 + s3/api/providers/btfs_api_options.go | 29 + s3/api/providers/proto.go | 31 + s3/api/providers/providers.go | 27 + s3/api/providers/providers_options.go | 3 + s3/api/providers/storage_state_store_proxy.go | 42 + s3/api/requests/errors.go | 103 + s3/api/requests/parsers.go | 286 ++ s3/api/requests/parsers_bucket.go | 122 + s3/api/requests/parsers_multipart.go | 184 ++ s3/api/requests/parsers_object.go | 368 +++ s3/api/requests/validates.go | 389 +++ s3/api/responses/errors.go | 1039 ++++++ s3/api/responses/writers.go | 316 ++ s3/api/responses/writers_bucket.go | 82 + s3/api/responses/writers_multipart.go | 37 + s3/api/responses/writers_object.go | 182 ++ s3/api/routers/options.go | 3 + s3/api/routers/proto.go | 7 + s3/api/routers/routers.go | 96 + s3/api/server/options.go | 13 + s3/api/server/server.go | 72 + s3/api/services/accesskey/instance.go | 48 + s3/api/services/accesskey/options.go | 40 + s3/api/services/accesskey/proto.go | 27 + s3/api/services/accesskey/service.go | 170 + s3/api/services/object/clean_read_closer.go | 41 + s3/api/services/object/options.go | 68 + s3/api/services/object/proto.go | 279 ++ s3/api/services/object/service.go | 191 ++ s3/api/services/object/service_bucket.go | 328 ++ s3/api/services/object/service_multipart.go | 490 +++ s3/api/services/object/service_object.go | 768 +++++ s3/api/services/sign/options.go | 3 + s3/api/services/sign/proto.go | 11 + s3/api/services/sign/service.go | 49 + s3/api/services/sign/signature-type.go | 96 + s3/api/services/sign/signature-v4-parser.go | 190 ++ .../services/sign/signature-v4-streaming.go | 421 +++ s3/api/services/sign/signature-v4-utils.go | 276 ++ s3/api/services/sign/signature-v4.go | 119 + s3/consts/consts.go | 95 + s3/ctxmu/ctx_rwlock.go | 67 + s3/ctxmu/multi_ctx_rwlock.go | 137 + s3/ctxmu/multi_ctx_rwlock_test.go | 109 + s3/etag/etag.go | 194 ++ s3/etag/etag_test.go | 210 ++ s3/etag/reader.go | 138 + s3/hash/errors.go | 33 + s3/hash/reader.go | 211 ++ s3/policy/policy.go | 68 + s3/s3.go | 67 + s3/set/match.go | 48 + s3/set/match_test.go | 529 +++ s3/set/stringset.go | 198 ++ s3/set/stringset_test.go | 359 +++ s3/utils/coalesce.go | 11 + s3/utils/encode.go | 89 + settlement/swap/vault/cashout.go | 4 +- settlement/swap/vault/factory_test.go | 4 +- settlement/swap/vault/vault.go | 2 +- statestore/leveldb/leveldb.go | 4 +- utils/random.go | 20 + 98 files changed, 13561 insertions(+), 647 deletions(-) create mode 100644 core/commands/accesskey.go create mode 100644 core/commands/multibase.go create mode 100644 s3/action/action.go create mode 100644 s3/api/contexts/contexts.go create mode 100644 s3/api/contexts/contexts_access_key.go create mode 100644 s3/api/contexts/contexts_handle_inf.go create mode 100644 s3/api/handlers/handlers.go create mode 100644 s3/api/handlers/handlers_bucket.go create mode 100644 s3/api/handlers/handlers_middlewares.go create mode 100644 s3/api/handlers/handlers_multipart.go create mode 100644 s3/api/handlers/handlers_object.go create mode 100644 s3/api/handlers/options.go create mode 100644 s3/api/handlers/proto.go create mode 100644 s3/api/providers/btfs_api.go create mode 100644 s3/api/providers/btfs_api_options.go create mode 100644 s3/api/providers/proto.go create mode 100644 s3/api/providers/providers.go create mode 100644 s3/api/providers/providers_options.go create mode 100644 s3/api/providers/storage_state_store_proxy.go create mode 100644 s3/api/requests/errors.go create mode 100644 s3/api/requests/parsers.go create mode 100644 s3/api/requests/parsers_bucket.go create mode 100644 s3/api/requests/parsers_multipart.go create mode 100644 s3/api/requests/parsers_object.go create mode 100644 s3/api/requests/validates.go create mode 100644 s3/api/responses/errors.go create mode 100644 s3/api/responses/writers.go create mode 100644 s3/api/responses/writers_bucket.go create mode 100644 s3/api/responses/writers_multipart.go create mode 100644 s3/api/responses/writers_object.go create mode 100644 s3/api/routers/options.go create mode 100644 s3/api/routers/proto.go create mode 100644 s3/api/routers/routers.go create mode 100644 s3/api/server/options.go create mode 100644 s3/api/server/server.go create mode 100644 s3/api/services/accesskey/instance.go create mode 100644 s3/api/services/accesskey/options.go create mode 100644 s3/api/services/accesskey/proto.go create mode 100644 s3/api/services/accesskey/service.go create mode 100644 s3/api/services/object/clean_read_closer.go create mode 100644 s3/api/services/object/options.go create mode 100644 s3/api/services/object/proto.go create mode 100644 s3/api/services/object/service.go create mode 100644 s3/api/services/object/service_bucket.go create mode 100644 s3/api/services/object/service_multipart.go create mode 100644 s3/api/services/object/service_object.go create mode 100644 s3/api/services/sign/options.go create mode 100644 s3/api/services/sign/proto.go create mode 100644 s3/api/services/sign/service.go create mode 100644 s3/api/services/sign/signature-type.go create mode 100644 s3/api/services/sign/signature-v4-parser.go create mode 100644 s3/api/services/sign/signature-v4-streaming.go create mode 100644 s3/api/services/sign/signature-v4-utils.go create mode 100644 s3/api/services/sign/signature-v4.go create mode 100644 s3/consts/consts.go create mode 100644 s3/ctxmu/ctx_rwlock.go create mode 100644 s3/ctxmu/multi_ctx_rwlock.go create mode 100644 s3/ctxmu/multi_ctx_rwlock_test.go create mode 100644 s3/etag/etag.go create mode 100644 s3/etag/etag_test.go create mode 100644 s3/etag/reader.go create mode 100644 s3/hash/errors.go create mode 100644 s3/hash/reader.go create mode 100644 s3/policy/policy.go create mode 100644 s3/s3.go create mode 100644 s3/set/match.go create mode 100644 s3/set/match_test.go create mode 100644 s3/set/stringset.go create mode 100644 s3/set/stringset_test.go create mode 100644 s3/utils/coalesce.go create mode 100644 s3/utils/encode.go create mode 100644 utils/random.go diff --git a/blocks/blockstoreutil/remove.go b/blocks/blockstoreutil/remove.go index e08a95d47..81780c124 100644 --- a/blocks/blockstoreutil/remove.go +++ b/blocks/blockstoreutil/remove.go @@ -71,8 +71,8 @@ func RmBlocks(ctx context.Context, blocks bs.GCBlockstore, pins pin.Pinner, cids } // FilterPinned takes a slice of Cids and returns it with the pinned Cids -// removed. If a Cid is pinned, it will place RemovedBlock objects in the given -// out channel, with an error which indicates that the Cid is pinned. +// removed. If a CID is pinned, it will place RemovedBlock objects in the given +// out channel, with an error which indicates that the CID is pinned. // This function is used in RmBlocks to filter out any blocks which are not // to be removed (because they are pinned). func FilterPinned(ctx context.Context, pins pin.Pinner, out chan<- interface{}, cids []cid.Cid) []cid.Cid { diff --git a/chain/chain.go b/chain/chain.go index 6f4c8df3e..a81ecfade 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -5,12 +5,13 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/bittorrent/go-btfs/chain/tokencfg" "io" "math/big" "strings" "time" + "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/accounting" "github.com/bittorrent/go-btfs/chain/config" "github.com/bittorrent/go-btfs/settlement" @@ -348,7 +349,7 @@ func initSwap( priceOracle := priceoracle.New(currentPriceOracleAddress, transactionService) _, err := priceOracle.CheckNewPrice(tokencfg.GetWbttToken()) // CheckNewPrice when node starts if err != nil { - return nil, nil, errors.New("CheckNewPrice " + err.Error()) + return nil, nil, errors.New("CheckNewPrice error, it may happens when contract call failed if bttc chain rpc is down, please try again") } swapProtocol := swapprotocol.New(overlayEthAddress, priceOracle) diff --git a/cmd/btfs/daemon.go b/cmd/btfs/daemon.go index 889e5d984..91f14ce38 100644 --- a/cmd/btfs/daemon.go +++ b/cmd/btfs/daemon.go @@ -7,6 +7,8 @@ import ( "errors" _ "expvar" "fmt" + "github.com/bittorrent/go-btfs/s3" + "github.com/bittorrent/go-btfs/s3/api/services/accesskey" "io/ioutil" "math/rand" "net" @@ -100,6 +102,7 @@ const ( chainID = "chain-id" // apiAddrKwd = "address-api" // swarmAddrKwd = "address-swarm" + enableS3CompatibleAPIKwd = "s3-compatible-api" ) // BTFS daemon test exit error code @@ -227,6 +230,7 @@ Headers. // TODO: add way to override addresses. tricky part: updating the config if also --init. // cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"), // cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"), + cmds.BoolOption(enableS3CompatibleAPIKwd, "Enable s3-compatible-api server"), }, Subcommands: map[string]*cmds.Command{}, NoRemote: true, @@ -713,6 +717,33 @@ If the user need to start multiple nodes on the same machine, the configuration functest(cfg.Services.OnlineServerDomain, cfg.Identity.PeerID, hValue) } + // Init s3 providers + err = s3.InitProviders(statestore) + if err != nil { + return err + } + + // Init access-key + accesskey.InitService(s3.GetProviders()) + + // Start s3-compatible-api server + s3OptEnable, s3Opt := req.Options[enableS3CompatibleAPIKwd].(bool) + if s3OptEnable || (!s3Opt && cfg.S3CompatibleAPI.Enable) { + s3Server := s3.NewServer(cfg.S3CompatibleAPI) + err = s3Server.Start() + if err != nil { + fmt.Printf("S3-Compatible-API server: %v\n", err) + return + } + fmt.Printf("S3-Compatible-API server started, endpoint-url: http://%s\n", cfg.S3CompatibleAPI.Address) + defer func() { + err = s3Server.Stop() + if err != nil { + fmt.Printf("S3-Compatible-API server: %v\n", err) + } + }() + } + if SimpleMode == false { // set Analytics flag if specified if dc, ok := req.Options[enableDataCollection]; ok == true { diff --git a/core/commands/accesskey.go b/core/commands/accesskey.go new file mode 100644 index 000000000..3e82dacf9 --- /dev/null +++ b/core/commands/accesskey.go @@ -0,0 +1,175 @@ +package commands + +import ( + "errors" + cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" + "github.com/bittorrent/go-btfs/s3/api/services/accesskey" +) + +var AccessKeyCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Manage S3-Compatible-API access-keys.", + ShortDescription: "Commands for generate, update, get and list access-keys stored in this node.", + }, + Subcommands: map[string]*cmds.Command{ + "generate": accessKeyGenerateCmd, + "enable": accessKeyEnableCmd, + "disable": accessKeyDisableCmd, + "reset": accessKeyResetCmd, + "delete": accessKeyDeleteCmd, + "get": accessKeyGetCmd, + "list": accessKeyListCmd, + }, + NoLocal: true, +} + +func checkDaemon(env cmds.Environment) (err error) { + node, err := cmdenv.GetNode(env) + if err != nil { + return + } + if !node.IsDaemon { + err = errors.New("please start the node first") + } + return +} + +var accessKeyGenerateCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Generate a new access-key record.", + ShortDescription: "Outputs the new created access-key record.", + }, + Arguments: []cmds.Argument{}, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + ack, err := accesskey.Generate() + if err != nil { + return + } + err = cmds.EmitOnce(res, ack) + return + }, +} + +var accessKeyEnableCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Set status of the specified access-key to enable.", + ShortDescription: "Outputs empty if the access-key has been set to enable or it was already enabled.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, true, "The key").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + key := req.Arguments[0] + err = accesskey.Enable(key) + return + }, +} + +var accessKeyDisableCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Set status of the specified access-key to enable.", + ShortDescription: "Outputs empty if the access-key has been set to disable or it was already disabled.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, true, "The key").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + key := req.Arguments[0] + err = accesskey.Disable(key) + return + }, +} + +var accessKeyResetCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Rest secret of the specified access-key.", + ShortDescription: "Outputs the updated access-key record if it's secret has been reset.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, true, "The key").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + key := req.Arguments[0] + err = accesskey.Reset(key) + return + }, +} + +var accessKeyDeleteCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Delete the specified access-key", + ShortDescription: "Outputs empty if access-key record has been deleted.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, true, "The key").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + key := req.Arguments[0] + err = accesskey.Delete(key) + return + }, +} + +var accessKeyGetCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Get an access-key detail info.", + ShortDescription: "Outputs access-key record for the specified key.", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, true, "The key").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + key := req.Arguments[0] + ack, err := accesskey.Get(key) + if err != nil { + return + } + err = cmds.EmitOnce(res, ack) + return + }, +} + +var accessKeyListCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "List all access-keys.", + ShortDescription: "Outputs all non-deleted access-keys stored in current node.", + }, + Arguments: []cmds.Argument{}, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) { + err = checkDaemon(env) + if err != nil { + return + } + list, err := accesskey.List() + if err != nil { + return + } + err = cmds.EmitOnce(res, list) + return + }, +} diff --git a/core/commands/cid.go b/core/commands/cid.go index 0bbc3ce26..16d972985 100644 --- a/core/commands/cid.go +++ b/core/commands/cid.go @@ -117,7 +117,7 @@ The optional format string is a printf style format string: } type CidFormatRes struct { - CidStr string // Original Cid String passed in + CidStr string // Original CID String passed in Formatted string // Formatted Result ErrorMsg string // Error } diff --git a/core/commands/cmdenv/cidbase.go b/core/commands/cmdenv/cidbase.go index 0e0822af1..445705d28 100644 --- a/core/commands/cmdenv/cidbase.go +++ b/core/commands/cmdenv/cidbase.go @@ -58,7 +58,7 @@ func CidBaseDefined(req *cmds.Request) bool { } // CidEncoderFromPath creates a new encoder that is influenced from -// the encoded Cid in a Path. For CidV0 the multibase from the base +// the encoded CID in a Path. For CidV0 the multibase from the base // encoder is used and automatic upgrades are disabled. For CidV1 the // multibase from the CID is used and upgrades are enabled. // diff --git a/core/commands/commands.go b/core/commands/commands.go index 4f92bd252..3d1d6ecc1 100644 --- a/core/commands/commands.go +++ b/core/commands/commands.go @@ -12,7 +12,7 @@ import ( "sort" "strings" - "github.com/bittorrent/go-btfs-cmds" + cmds "github.com/bittorrent/go-btfs-cmds" ) type commandEncoder struct { diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 1982d51b9..1366d2963 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -342,6 +342,11 @@ func TestCommands(t *testing.T) { "/bittorrent/scrape", "/bittorrent/metainfo", "/bittorrent/bencode", + "/multibase", + "/multibase/encode", + "/multibase/decode", + "/multibase/transcode", + "/multibase/list", } cmdSet := make(map[string]struct{}) diff --git a/core/commands/files.go b/core/commands/files.go index 1b9768bf1..ae9272310 100644 --- a/core/commands/files.go +++ b/core/commands/files.go @@ -87,8 +87,8 @@ const ( filesHashOptionName = "hash" ) -var cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "Cid version to use. (experimental)") -var hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set Cid version to 1 if used. (experimental)") +var cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "CID version to use. (experimental)") +var hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set CID version to 1 if used. (experimental)") var errFormat = errors.New("format was set by multiple options. Only one format option is allowed") @@ -735,7 +735,7 @@ stat' on the file or any of its ancestors. }, Arguments: []cmds.Argument{ cmds.StringArg("path", true, false, "Path to write to."), - cmds.FileArg("data", true, false, "Data to write.").EnableStdin(), + cmds.FileArg("data", true, false, "data to write.").EnableStdin(), }, Options: []cmds.Option{ cmds.Int64Option(filesOffsetOptionName, "o", "Byte offset to begin writing at."), diff --git a/core/commands/filestore.go b/core/commands/filestore.go index e6520984e..808bf2d9d 100644 --- a/core/commands/filestore.go +++ b/core/commands/filestore.go @@ -45,7 +45,7 @@ The output is: `, }, Arguments: []cmds.Argument{ - cmds.StringArg("obj", false, true, "Cid of objects to list."), + cmds.StringArg("obj", false, true, "CID of objects to list."), }, Options: []cmds.Option{ cmds.BoolOption(fileOrderOptionName, "sort the results based on the path of the backing file"), @@ -122,7 +122,7 @@ For ERROR entries the error will also be printed to stderr. `, }, Arguments: []cmds.Argument{ - cmds.StringArg("obj", false, true, "Cid of objects to verify."), + cmds.StringArg("obj", false, true, "CID of objects to verify."), }, Options: []cmds.Option{ cmds.BoolOption(fileOrderOptionName, "verify the objects based on the order of the backing file"), diff --git a/core/commands/multibase.go b/core/commands/multibase.go new file mode 100644 index 000000000..bdba34b0d --- /dev/null +++ b/core/commands/multibase.go @@ -0,0 +1,171 @@ +package commands + +import ( + "bytes" + "fmt" + "io" + "strings" + + cmds "github.com/bittorrent/go-btfs-cmds" + cmdenv "github.com/bittorrent/go-btfs/core/commands/cmdenv" + mbase "github.com/multiformats/go-multibase" +) + +var MbaseCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Encode and decode files or stdin with multibase format", + }, + Subcommands: map[string]*cmds.Command{ + "encode": mbaseEncodeCmd, + "decode": mbaseDecodeCmd, + "transcode": mbaseTranscodeCmd, + "list": basesCmd, + }, + Extra: CreateCmdExtras(SetDoesNotUseRepo(true)), +} + +const ( + mbaseOptionName = "b" +) + +var mbaseEncodeCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Encode data into multibase string", + LongDescription: ` +This command expects a file name or data provided via stdin. + +By default it will use URL-safe base64url encoding, +but one can customize used base with -b: + + > echo hello | btfs multibase encode -b base16 > output_file + > cat output_file + f68656c6c6f0a + + > echo hello > input_file + > btfs multibase encode -b base16 input_file + f68656c6c6f0a + `, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("file", true, false, "data to encode").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.StringOption(mbaseOptionName, "multibase encoding").WithDefault("base64url"), + }, + Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + if err := req.ParseBodyArgs(); err != nil { + return err + } + encoderName, _ := req.Options[mbaseOptionName].(string) + encoder, err := mbase.EncoderByName(encoderName) + if err != nil { + return err + } + files := req.Files.Entries() + file, err := cmdenv.GetFileArg(files) + if err != nil { + return fmt.Errorf("failed to access file: %w", err) + } + buf, err := io.ReadAll(file) + if err != nil { + return fmt.Errorf("failed to read file contents: %w", err) + } + encoded := encoder.Encode(buf) + reader := strings.NewReader(encoded) + return resp.Emit(reader) + }, +} + +var mbaseDecodeCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Decode multibase string", + LongDescription: ` +This command expects multibase inside of a file or via stdin: + + > echo -n hello | btfs multibase encode -b base16 > file + > cat file + f68656c6c6f + + > btfs multibase decode file + hello + + > cat file | btfs multibase decode + hello +`, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("encoded_file", true, false, "encoded data to decode").EnableStdin(), + }, + Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + if err := req.ParseBodyArgs(); err != nil { + return err + } + files := req.Files.Entries() + file, err := cmdenv.GetFileArg(files) + if err != nil { + return fmt.Errorf("failed to access file: %w", err) + } + encodedData, err := io.ReadAll(file) + if err != nil { + return fmt.Errorf("failed to read file contents: %w", err) + } + _, data, err := mbase.Decode(string(encodedData)) + if err != nil { + return fmt.Errorf("failed to decode multibase: %w", err) + } + reader := bytes.NewReader(data) + return resp.Emit(reader) + }, +} + +var mbaseTranscodeCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Transcode multibase string between bases", + LongDescription: ` +This command expects multibase inside of a file or via stdin. + +By default it will use URL-safe base64url encoding, +but one can customize used base with -b: + + > echo -n hello | btfs multibase encode > file + > cat file + uaGVsbG8 + + > btfs multibase transcode file -b base16 > transcoded_file + > cat transcoded_file + f68656c6c6f +`, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("encoded_file", true, false, "encoded data to decode").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.StringOption(mbaseOptionName, "multibase encoding").WithDefault("base64url"), + }, + Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + if err := req.ParseBodyArgs(); err != nil { + return err + } + encoderName, _ := req.Options[mbaseOptionName].(string) + encoder, err := mbase.EncoderByName(encoderName) + if err != nil { + return err + } + files := req.Files.Entries() + file, err := cmdenv.GetFileArg(files) + if err != nil { + return fmt.Errorf("failed to access file: %w", err) + } + encodedData, err := io.ReadAll(file) + if err != nil { + return fmt.Errorf("failed to read file contents: %w", err) + } + _, data, err := mbase.Decode(string(encodedData)) + if err != nil { + return fmt.Errorf("failed to decode multibase: %w", err) + } + encoded := encoder.Encode(data) + reader := strings.NewReader(encoded) + return resp.Emit(reader) + }, +} diff --git a/core/commands/object/object.go b/core/commands/object/object.go index 524e490f0..3a9d4c28b 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -270,7 +270,7 @@ Supported values are: Type: Node{}, Encoders: cmds.EncoderMap{ cmds.Protobuf: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Node) error { - // deserialize the Data field as text as this was the standard behaviour + // deserialize the data field as text as this was the standard behaviour object, err := deserializeNode(out, "text") if err != nil { return nil @@ -371,20 +371,20 @@ It reads from stdin, and the output is a base58 encoded multihash. 'btfs object put' is a plumbing command for storing DAG nodes. It reads from stdin, and the output is a base58 encoded multihash. -Data should be in the format specified by the --inputenc flag. +data should be in the format specified by the --inputenc flag. --inputenc may be one of the following: * "protobuf" * "json" (default) Examples: - $ echo '{ "Data": "abc" }' | btfs object put + $ echo '{ "data": "abc" }' | btfs object put This creates a node with the data 'abc' and no links. For an object with links, create a file named 'node.json' with the contents: { - "Data": "another", + "data": "another", "Links": [ { "Name": "some link", "Hash": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", @@ -399,7 +399,7 @@ And then run: }, Arguments: []cmds.Argument{ - cmds.FileArg("data", true, false, "Data to be stored as a DAG object.").EnableStdin(), + cmds.FileArg("data", true, false, "data to be stored as a DAG object.").EnableStdin(), }, Options: []cmds.Option{ cmds.StringOption(inputencOptionName, "Encoding type of input data. One of: {\"protobuf\", \"json\"}.").WithDefault("json"), diff --git a/core/commands/object/patch.go b/core/commands/object/patch.go index f2eb0dc4e..b196738b2 100644 --- a/core/commands/object/patch.go +++ b/core/commands/object/patch.go @@ -40,13 +40,13 @@ Example: $ echo "hello" | btfs object patch $HASH append-data NOTE: This does not append data to a file - it modifies the actual raw -data within an object. Objects have a max size of 1MB and objects larger than +data within an object. ToDeleteObjects have a max size of 1MB and objects larger than the limit will not be respected by the network. `, }, Arguments: []cmds.Argument{ cmds.StringArg("root", true, false, "The hash of the node to modify."), - cmds.FileArg("data", true, false, "Data to append.").EnableStdin(), + cmds.FileArg("data", true, false, "data to append.").EnableStdin(), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) diff --git a/core/commands/refs.go b/core/commands/refs.go index 9f564297c..bda6b2c73 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -296,7 +296,7 @@ func (rw *RefWriter) visit(c cid.Cid, depth int) (bool, bool) { // Unique == true && depth < MaxDepth (or unlimited) from this point // Branch pruning cases: - // - We saw the Cid before and either: + // - We saw the CID before and either: // - Depth is unlimited (MaxDepth = -1) // - We saw it higher (smaller depth) in the DAG (means we must have // explored deep enough before) diff --git a/core/commands/root.go b/core/commands/root.go index 7a9c9ed21..c6a3fc5c6 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -179,6 +179,8 @@ var rootSubcommands = map[string]*cmds.Command{ "network": NetworkCmd, "statuscontract": StatusContractCmd, "bittorrent": bittorrentCmd, + "multibase": MbaseCmd, + "accesskey": AccessKeyCmd, } // RootRO is the readonly version of Root diff --git a/core/commands/storage/upload/upload/upload.go b/core/commands/storage/upload/upload/upload.go index 7a8c7b248..390b3e41e 100644 --- a/core/commands/storage/upload/upload/upload.go +++ b/core/commands/storage/upload/upload/upload.go @@ -4,15 +4,18 @@ import ( "context" "errors" "fmt" - "github.com/bittorrent/go-btfs/chain/tokencfg" - "github.com/bittorrent/go-btfs/utils" "strconv" "strings" "time" + "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" + coreiface "github.com/bittorrent/interface-go-btfs-core" + "github.com/bittorrent/go-btfs/settlement/swap/swapprotocol" "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/hosts" "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" "github.com/bittorrent/go-btfs/core/commands/storage/upload/offline" @@ -111,7 +114,15 @@ Use status command to check for completion: }, RunTimeout: 15 * time.Minute, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - err := utils.CheckSimpleMode(env) + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + if !nd.IsOnline { + return coreiface.ErrOffline + } + err = utils.CheckSimpleMode(env) if err != nil { return err } diff --git a/core/corehttp/gateway/gateway.go b/core/corehttp/gateway/gateway.go index 1b9423f72..9e8e946e6 100644 --- a/core/corehttp/gateway/gateway.go +++ b/core/corehttp/gateway/gateway.go @@ -92,7 +92,7 @@ type IPFSBackend interface { // Get returns a GetResponse with UnixFS file, directory or a block in IPLD // format e.g., (DAG-)CBOR/JSON. // - // Returned Directories are preferably a minimum info required for enumeration: Name, Size, and Cid. + // Returned Directories are preferably a minimum info required for enumeration: Name, Size, and CID. // // Optional ranges follow [HTTP Byte Ranges] notation and can be used for // pre-fetching specific sections of a file or a block. diff --git a/core/corehttp/gateway/gateway_test.go b/core/corehttp/gateway/gateway_test.go index 304051a95..96d47180a 100644 --- a/core/corehttp/gateway/gateway_test.go +++ b/core/corehttp/gateway/gateway_test.go @@ -418,7 +418,7 @@ func TestIPNSHostnameRedirect(t *testing.T) { // assert.Contains(t, s, "
", "expected backlink in directory listing") // assert.Contains(t, s, "", "expected file in directory listing") -// assert.Contains(t, s, s, k2.Cid().String(), "expected hash in directory listing") +// assert.Contains(t, s, s, k2.CID().String(), "expected hash in directory listing") // // make request to directory listing at root // req, err = http.NewRequest(http.MethodGet, ts.URL, nil) @@ -440,7 +440,7 @@ func TestIPNSHostnameRedirect(t *testing.T) { // assert.Contains(t, s, "", "expected file in directory listing") // // https://github.com/btfs/dir-index-html/issues/42 // assert.Contains(t, s, "example.net/foo? #<'/bar"), "expected a path in directory listing") // assert.Contains(t, s, "", "expected backlink in directory listing") // assert.Contains(t, s, "", "expected file in directory listing") -// assert.Contains(t, s, k3.Cid().String(), "expected hash in directory listing") +// assert.Contains(t, s, k3.CID().String(), "expected hash in directory listing") // } func TestPretty404(t *testing.T) { diff --git a/docs/examples/go-ipfs-as-a-library/go.mod b/docs/examples/go-ipfs-as-a-library/go.mod index dbb7f3e2c..0e83c1ea3 100644 --- a/docs/examples/go-ipfs-as-a-library/go.mod +++ b/docs/examples/go-ipfs-as-a-library/go.mod @@ -3,17 +3,13 @@ module github.com/ipfs/go-ipfs/examples/go-ipfs-as-a-library go 1.14 require ( - github.com/TRON-US/go-btfs v1.5.0 - github.com/TRON-US/go-btfs-config v0.11.5 - github.com/TRON-US/go-btfs-files v0.2.0 - github.com/TRON-US/interface-go-btfs-core v0.7.0 - github.com/ipfs/go-ipfs v0.7.0 - github.com/ipfs/go-ipfs-config v0.9.0 - github.com/ipfs/go-ipfs-files v0.0.8 - github.com/TRON-US/interface-go-btfs-core v0.4.0 - github.com/libp2p/go-libp2p-core v0.9.0 - github.com/libp2p/go-libp2p-peerstore v0.2.7 - github.com/multiformats/go-multiaddr v0.3.3 + github.com/bittorrent/go-btfs v0.0.0-20230626064024-58978cbfe949 + github.com/bittorrent/go-btfs-config v0.12.3 + github.com/bittorrent/go-btfs-files v0.3.1 + github.com/bittorrent/interface-go-btfs-core v0.8.2 + github.com/klauspost/cpuid v1.2.4 // indirect + github.com/libp2p/go-libp2p v0.24.2 + github.com/multiformats/go-multiaddr v0.8.0 ) replace github.com/ipfs/go-ipfs => ./../../.. diff --git a/docs/examples/go-ipfs-as-a-library/go.sum b/docs/examples/go-ipfs-as-a-library/go.sum index 887738c6c..d1726045d 100644 --- a/docs/examples/go-ipfs-as-a-library/go.sum +++ b/docs/examples/go-ipfs-as-a-library/go.sum @@ -1,5 +1,7 @@ -bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= +bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -8,6 +10,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -15,32 +18,409 @@ cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gc cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= +crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= +crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= +crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -61,77 +441,233 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= +github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= +github.com/RoaringBitmap/roaring v0.4.18/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= +github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= +github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= +github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk= +github.com/RoaringBitmap/roaring v0.6.0/go.mod h1:WZ83fjBF/7uBHi6QoFyfGL4+xuV4Qn+xFkm4+vSzrhE= +github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= +github.com/RoaringBitmap/roaring v1.0.1-0.20220510143707-3f418c4f42a4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= +github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/Stebalien/go-bitfield v0.0.0-20180330043415-076a62f9ce6e/go.mod h1:3oM7gXIttpYDAJXpVNnSCiUMYBLIZ6cb1t+Ip982MRo= github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo= github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/TRON-US/go-btfs v1.5.0 h1:XrvhGK9+imGikGf/sSWdhyST4nV6FKZpwJl3SxGDUGw= -github.com/TRON-US/go-btfs v1.5.0/go.mod h1:x/uFzBwyxNs+2Srn5cH3BzEzC9EV0MRsivK0/pU8B0A= -github.com/TRON-US/go-btfs-api v0.3.0/go.mod h1:surmr8ztnpbVY7y2H7dbb7npNXfdaV0UH6cFwhtJPw0= -github.com/TRON-US/go-btfs-chunker v0.3.0/go.mod h1:m0xvt42kqLskWsLF6SQ51AA9cqPzWoweydOcDgSDX/U= -github.com/TRON-US/go-btfs-cmds v0.2.7/go.mod h1:GLPyQ0EX9JiL41IZd6yRr42RlxNHovp4V4gXZNE2Rfk= -github.com/TRON-US/go-btfs-cmds v0.2.13/go.mod h1:GLPyQ0EX9JiL41IZd6yRr42RlxNHovp4V4gXZNE2Rfk= -github.com/TRON-US/go-btfs-config v0.6.0/go.mod h1:82nKCMRhsgY0I8DCasIUpSr6ZP9iHLsZJSMUxytMpEw= -github.com/TRON-US/go-btfs-config v0.7.0 h1:v1O6ggE71k3a6KuwfUgbqFFPMJkZymvyORXDquQTKl8= -github.com/TRON-US/go-btfs-config v0.7.0/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= -github.com/TRON-US/go-btfs-config v0.11.2 h1:3rDK/jyeEj5hKAlj+iMBy7SB4wuy8ti5aDjaG8cxAt4= -github.com/TRON-US/go-btfs-config v0.11.2/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= -github.com/TRON-US/go-btfs-config v0.11.5 h1:Gc1TPaG3xTDojwBRsyumOv3le0NrYiWCBmB9No++Axg= -github.com/TRON-US/go-btfs-config v0.11.5/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= -github.com/TRON-US/go-btfs-files v0.1.1/go.mod h1:tD2vOKLcLCDNMn9rrA27n2VbNpHdKewGzEguIFY+EJ0= -github.com/TRON-US/go-btfs-files v0.2.0/go.mod h1:Qx+rTOIC0xl3ZkosGcEoB4hqExZmTONErPys8K5suEc= -github.com/ipfs/go-ipfs-pinner v0.1.1/go.mod h1:4CVxKEQNNTbuW92BIYwiH9/W63eDNtlt7bK0fCnXSag= -github.com/TRON-US/go-btns v0.1.1 h1:19rUEc9QK5aIz5Z278lQh6omXbQ3Ha40ecQX1DnGnT8= -github.com/TRON-US/go-btns v0.1.1/go.mod h1:PWfgUinn65Xzar61xNunkadza7h+v+cYFCOXpuVsixY= -github.com/TRON-US/go-eccrypto v0.0.1/go.mod h1:QZqTUSKP9MdYh+0LPsnVKvXV/Q2f9Qb6V4ejvUmHVvI= -github.com/TRON-US/go-mfs v0.3.1 h1:5foDPPlIcF4bPXZ18Qd+lHv3WPBQTJlAHTLSAeFK/rY= -github.com/TRON-US/go-mfs v0.3.1/go.mod h1:hXLxeLnJp50uu+Ibg7Tf7BzaC49m8RSTRA/eDl0wx1s= -github.com/TRON-US/go-unixfs v0.6.0/go.mod h1:U3+FopU5+8rwrr05MJOwDB1E9vAwKGsb/GII0LkXZ8k= -github.com/TRON-US/go-unixfs v0.6.1 h1:7KFhJdt+XsapVSmxEq+mfUFOEPS8SyoaWJkkrr59N4A= -github.com/TRON-US/go-unixfs v0.6.1/go.mod h1:U3+FopU5+8rwrr05MJOwDB1E9vAwKGsb/GII0LkXZ8k= -github.com/TRON-US/interface-go-btfs-core v0.7.0 h1:IV72YgoLbd86df6lXdUum4wO+msB6+NC0504vtD69gs= -github.com/TRON-US/interface-go-btfs-core v0.7.0/go.mod h1:cpdcK3UWZGA78cte11zoUn8kpoilKKVMIPXKk8e75Z8= github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= +github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k= +github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= +github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o= +github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI= +github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= +github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= +github.com/alexflint/go-arg v1.1.0/go.mod h1:3Rj4baqzWaGGmZA2+bVTV8zQOZEjBQAPBnL5xLT+ftY= +github.com/alexflint/go-arg v1.2.0/go.mod h1:3Rj4baqzWaGGmZA2+bVTV8zQOZEjBQAPBnL5xLT+ftY= +github.com/alexflint/go-arg v1.3.0/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= +github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= +github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA= +github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= +github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/anacrolix/args v0.3.0/go.mod h1:41JBnF8sKExNVLHPkCdL74jkZc3dSxAkGsk1TuKOUFI= +github.com/anacrolix/args v0.4.1-0.20211104085705-59f0fe94eb8f/go.mod h1:41JBnF8sKExNVLHPkCdL74jkZc3dSxAkGsk1TuKOUFI= +github.com/anacrolix/args v0.5.0/go.mod h1:Fj/N2PehEwTBE5t/V/9xgTcxDkuYQ+5IBoFw/8gkldI= +github.com/anacrolix/args v0.5.1-0.20220509024600-c3b77d0b61ac/go.mod h1:Fj/N2PehEwTBE5t/V/9xgTcxDkuYQ+5IBoFw/8gkldI= +github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a/go.mod h1:9xUiZbkh+94FbiIAL1HXpAIBa832f3Mp07rRPl5c5RQ= +github.com/anacrolix/chansync v0.0.0-20210524073341-a336ebc2de92/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/chansync v0.1.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/chansync v0.2.1-0.20210910114620-14955c95ded9/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/chansync v0.3.0-0.0.20211007004133-3f72684c4a93/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/chansync v0.3.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/confluence v1.7.1-0.20210221224747-9cb14aa2c53a/go.mod h1:T0JHvSaf9UfoiUdCtCOUuRroHm/tauUJTbLc6/vd5YA= +github.com/anacrolix/confluence v1.7.1-0.20210221225853-90405640e928/go.mod h1:NoLcfoRet+kYttjLXJRmh4qBVrylJsfIItik5GGj21A= +github.com/anacrolix/confluence v1.7.1-0.20210311004351-d642adb8546c/go.mod h1:KCZ3eObqKECNeZg0ekAoJVakHMP3gAdR8i0bQ26IkzM= +github.com/anacrolix/confluence v1.8.0/go.mod h1:GsPP6ikA8h/CU7ExbuMOswpzZpPdf1efDPu4rVXL43g= +github.com/anacrolix/confluence v1.9.0/go.mod h1:O5uS+WVgip+3SOcV1K7E/jE3m4DtK7Jk6QJTnU2VS5s= +github.com/anacrolix/dht v0.0.0-20180412060941-24cbf25b72a4/go.mod h1:hQfX2BrtuQsLQMYQwsypFAab/GvHg8qxwVi4OJdR1WI= +github.com/anacrolix/dht/v2 v2.0.1/go.mod h1:GbTT8BaEtfqab/LPd5tY41f3GvYeii3mmDUK300Ycyo= +github.com/anacrolix/dht/v2 v2.2.1-0.20191103020011-1dba080fb358/go.mod h1:d7ARx3WpELh9uOEEr0+8wvQeVTOkPse4UU6dKpv4q0E= +github.com/anacrolix/dht/v2 v2.3.2-0.20200103043204-8dce00767ebd/go.mod h1:cgjKyErDnKS6Mej5D1fEqBKg3KwFF2kpFZJp3L6/fGI= +github.com/anacrolix/dht/v2 v2.5.1-0.20200317023935-129f05e9b752/go.mod h1:7RLvyOjm+ZPA7vgFRP+1eRjFzrh27p/nF0VCk5LcjoU= +github.com/anacrolix/dht/v2 v2.8.0/go.mod h1:RjeKbveVwjnaVj5os4y/NQwqEoDWHigo5rdge9MP52k= +github.com/anacrolix/dht/v2 v2.8.1-0.20210221225335-7a6713a749f9/go.mod h1:p7fLHxqc1mtrFGXfJ226Fo2akG3Pv8ngCTnYAzVJXa4= +github.com/anacrolix/dht/v2 v2.8.1-0.20210311003418-13622df072ae/go.mod h1:wLmYr78fBu4KfUUkFZyGFFwDPDw9EHL5x8c632XCZzs= +github.com/anacrolix/dht/v2 v2.9.1/go.mod h1:ZyYcIQinN/TE3oKONCchQOLjhYR786Jaxz3jsBtih4A= +github.com/anacrolix/dht/v2 v2.10.0/go.mod h1:KC51tqylRYBu82RM5pEYf+g1n7db+F0tOJqSbCjjZWc= +github.com/anacrolix/dht/v2 v2.10.5-0.20210902001729-06cc4fe90e53/go.mod h1:zHjijcebN+L7JbzxW0mOraHis+I81EIgsJAAtiw8bQ8= +github.com/anacrolix/dht/v2 v2.10.6-0.20211007004332-99263ec9c1c8/go.mod h1:WID4DexLrucfnwzv1OV8REzgoCpyVDwEczxIOrUeFrY= +github.com/anacrolix/dht/v2 v2.14.1-0.20211220010335-4062f7927abf/go.mod h1:zJgaiAU2yhzmchZE2mY8WyZ64LK/F/D9MAeN0ct73qQ= +github.com/anacrolix/dht/v2 v2.15.2-0.20220123034220-0538803801cb/go.mod h1:GCylVI6WTvbxvhY7pBoHiE5dmjfDWkhqbobDpjND01A= +github.com/anacrolix/dht/v2 v2.16.2-0.20220311024416-dd658f18fd51/go.mod h1:osiyaNrMLG9dw7wUtVMaII/NdCjlXeHjUcYzXnmop68= +github.com/anacrolix/dht/v2 v2.19.0/go.mod h1:0h83KnnAQ2AUYhpQ/CkoZP45K41pjDAlPR9zGHgFjQE= +github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/envpprof v1.0.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= +github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= +github.com/anacrolix/envpprof v1.1.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= +github.com/anacrolix/envpprof v1.2.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= +github.com/anacrolix/fuse v0.2.0/go.mod h1:Kfu02xBwnySDpH3N23BmrP3MDfwAQGRLUCj6XyeOvBQ= +github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= +github.com/anacrolix/go-libutp v0.0.0-20180522111405-6baeb806518d/go.mod h1:beQSaSxwH2d9Eeu5ijrEnHei5Qhk+J6cDm1QkWFru4E= +github.com/anacrolix/go-libutp v1.0.2/go.mod h1:uIH0A72V++j0D1nnmTjjZUiH/ujPkFxYWkxQ02+7S0U= +github.com/anacrolix/go-libutp v1.0.4/go.mod h1:8vSGX5g0b4eebsDBNVQHUXSCwYaN18Lnkse0hUW8/5w= +github.com/anacrolix/go-libutp v1.1.0/go.mod h1:so9zroOUhFPGnIkddyflaCCl+xdTsRSq97/AOQ2/Hjk= +github.com/anacrolix/go-libutp v1.2.0/go.mod h1:RrJ3KcaDcf9Jqp33YL5V/5CBEc6xMc7aJL8wXfuWL50= +github.com/anacrolix/log v0.0.0-20180412014343-2323884b361d/go.mod h1:sf/7c2aTldL6sRQj/4UKyjgVZBu2+M2z9wf7MmwPiew= +github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.3.1-0.20190913000754-831e4ffe0174/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.3.1-0.20191001111012-13cede988bcd/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.4.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.5.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.6.1-0.20200416071330-f58a030e6149/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= +github.com/anacrolix/log v0.7.1-0.20200604014615-c244de44fd2d/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= +github.com/anacrolix/log v0.8.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= +github.com/anacrolix/log v0.9.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= +github.com/anacrolix/log v0.10.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= +github.com/anacrolix/log v0.10.1-0.20220123034749-3920702c17f8/go.mod h1:GmnE2c0nvz8pOIPUSC9Rawgefy1sDXqposC2wgtBZE4= +github.com/anacrolix/log v0.11.0/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.13.2-0.20220426014722-7b7d13a55d55/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= +github.com/anacrolix/missinggo v0.0.0-20180522035225-b4a5853e62ff/go.mod h1:b0p+7cn+rWMIphK1gDH2hrDuwGOcbB6V4VXeSsEfHVk= +github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= +github.com/anacrolix/missinggo v0.2.1-0.20190310234110-9fbdc9f242a8/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= +github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc= +github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= +github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= +github.com/anacrolix/missinggo/v2 v2.2.1-0.20191103010835-12360f38ced0/go.mod h1:ZzG3/cc3t+5zcYWAgYrJW0MBsSwNwOkTlNquBbP51Bc= +github.com/anacrolix/missinggo/v2 v2.3.0/go.mod h1:ZzG3/cc3t+5zcYWAgYrJW0MBsSwNwOkTlNquBbP51Bc= +github.com/anacrolix/missinggo/v2 v2.3.1/go.mod h1:3XNH0OEmyMUZuvXmYdl+FDfXd0vvSZhvOLy8CFx8tLg= +github.com/anacrolix/missinggo/v2 v2.4.1-0.20200227072623-f02f6484f997/go.mod h1:KY+ij+mWvwGuqSuecLjjPv5LFw5ICUc1UvRems3VAZE= +github.com/anacrolix/missinggo/v2 v2.5.0/go.mod h1:HYuCbwvJXY3XbcmcIcTgZXHleoDXawxPWx/YiPzFzV0= +github.com/anacrolix/missinggo/v2 v2.5.1-0.20210520011502-b3d95d6b1d02/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= +github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= +github.com/anacrolix/missinggo/v2 v2.5.2-0.20210623112532-e21e4ddc477d/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= +github.com/anacrolix/missinggo/v2 v2.5.2/go.mod h1:yNvsLrtZYRYCOI+KRH/JM8TodHjtIE/bjOGhQaLOWIE= +github.com/anacrolix/missinggo/v2 v2.7.0/go.mod h1:2IZIvmRTizALNYFYXsPR7ofXPzJgyBpKZ4kMqMEICkI= +github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw= +github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc= +github.com/anacrolix/multiless v0.0.0-20191223025854-070b7994e841/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.0.0-20200413040533-acfd16f65d5d/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.0.0-20210222022749-ef43011a77ec/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.1.1-0.20210520040635-10ee7b5f3cff/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.1.1-0.20210529082330-de2f6cf29619/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.2.0/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.2.1-0.20211218050420-533661eef5dc/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/multiless v0.3.0/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= +github.com/anacrolix/publicip v0.2.0/go.mod h1:67G1lVkLo8UjdEcJkwScWVTvlJ35OCDsRJoWXl/wi4g= +github.com/anacrolix/squirrel v0.1.0/go.mod h1:YzgVvikMdFD441oTWlNG189bpKabO9Sbf3uCSVgca04= +github.com/anacrolix/squirrel v0.1.1-0.20210914065657-81bc5ecdc43a/go.mod h1:YzgVvikMdFD441oTWlNG189bpKabO9Sbf3uCSVgca04= +github.com/anacrolix/squirrel v0.2.1-0.20211119092713-2efaee06d169/go.mod h1:dJyE7VefQvX0KAKMkOQDGOVEs91a+LvXQ2BjEKl/DIw= +github.com/anacrolix/squirrel v0.4.0/go.mod h1:dJyE7VefQvX0KAKMkOQDGOVEs91a+LvXQ2BjEKl/DIw= +github.com/anacrolix/squirrel v0.4.1-0.20220122230132-14b040773bac/go.mod h1:YzgVvikMdFD441oTWlNG189bpKabO9Sbf3uCSVgca04= +github.com/anacrolix/stm v0.1.0/go.mod h1:ZKz7e7ERWvP0KgL7WXfRjBXHNRhlVRlbBQecqFtPq+A= +github.com/anacrolix/stm v0.1.1-0.20191106051447-e749ba3531cf/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= +github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= +github.com/anacrolix/stm v0.2.1-0.20201002073511-c35a2c748c6a/go.mod h1:spImf/rXwiAUoYYJK1YCZeWkpaHZ3kzjGFjwK5OStfU= +github.com/anacrolix/stm v0.2.1-0.20210310231625-45c211559de6/go.mod h1:spImf/rXwiAUoYYJK1YCZeWkpaHZ3kzjGFjwK5OStfU= +github.com/anacrolix/stm v0.3.0-alpha/go.mod h1:spImf/rXwiAUoYYJK1YCZeWkpaHZ3kzjGFjwK5OStfU= +github.com/anacrolix/stm v0.3.0/go.mod h1:spImf/rXwiAUoYYJK1YCZeWkpaHZ3kzjGFjwK5OStfU= +github.com/anacrolix/stm v0.4.0/go.mod h1:GCkwqWoAsP7RfLW+jw+Z0ovrt2OO7wRzcTtFYMYY5t8= +github.com/anacrolix/sync v0.0.0-20171108081538-eee974e4f8c1/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w= +github.com/anacrolix/sync v0.0.0-20180611022320-3c4cb11f5a01/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w= +github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk= +github.com/anacrolix/sync v0.2.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= +github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= +github.com/anacrolix/sync v0.4.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= +github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v0.0.0-20180605133421-f477c8c2f14c/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v0.0.0-20180803105420-3a8ff5428f76/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v1.0.1/go.mod h1:gb0fiMQ02qU1djCSqaxGmruMvZGrMwSReidMB0zjdxo= +github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= +github.com/anacrolix/tagflag v1.1.1-0.20200411025953-9bb5209d56c2/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= +github.com/anacrolix/tagflag v1.2.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= +github.com/anacrolix/tagflag v1.3.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= +github.com/anacrolix/torrent v0.0.0-20180622074351-fefeef4ee9eb/go.mod h1:3vcFVxgOASslNXHdivT8spyMRBanMCenHRpe0u5vpBs= +github.com/anacrolix/torrent v1.7.1/go.mod h1:uvOcdpOjjrAq3uMP/u1Ide35f6MJ/o8kMnFG8LV3y6g= +github.com/anacrolix/torrent v1.9.0/go.mod h1:jJJ6lsd2LD1eLHkUwFOhy7I0FcLYH0tHKw2K7ZYMHCs= +github.com/anacrolix/torrent v1.11.0/go.mod h1:FwBai7SyOFlflvfEOaM88ag/jjcBWxTOqD6dVU/lKKA= +github.com/anacrolix/torrent v1.15.0/go.mod h1:MFc6KcbpAyfwGqOyRkdarUK9QnKA/FkVg0usFk1OQxU= +github.com/anacrolix/torrent v1.22.0/go.mod h1:GWTwQkOAilf0LR3C6A74XEkWPg0ejfFD9GcEIe57ess= +github.com/anacrolix/torrent v1.23.0/go.mod h1:737rU+al1LBWEs3IHBystZvsbg24iSP+8Gb25Vc/s5U= +github.com/anacrolix/torrent v1.25.1-0.20210221061757-051093ca31f5/go.mod h1:737rU+al1LBWEs3IHBystZvsbg24iSP+8Gb25Vc/s5U= +github.com/anacrolix/torrent v1.25.1-0.20210224024805-693c30dd889e/go.mod h1:d4V6QqkInfQidWVk8b8hMv8mtciswNitI1A2BiRSQV0= +github.com/anacrolix/torrent v1.28.1-0.20210622065255-582f0ccd48a0/go.mod h1:15VRIA5/DwqbqETbKo3fzlC4aSbB0iMoo10ng3mzAbE= +github.com/anacrolix/torrent v1.29.0/go.mod h1:40Hf2bWxFqTbTWbrdig57JnmYLCjShbWWjdbB3VN5n4= +github.com/anacrolix/torrent v1.30.2/go.mod h1:vbNxKfaGiNq8edcCaQI1oSNJwh4GMqtMUMF9qOdZ6C0= +github.com/anacrolix/torrent v1.31.1-0.20210910222643-d957502528e0/go.mod h1:akZJHHFN8aWH2lcPZQ0I3etujnenwYpUvj36HV9uvAI= +github.com/anacrolix/torrent v1.35.1-0.20211104090255-eaeb38b18c6a/go.mod h1:97nxJW8NIeUyGdBvMOAl9cmcxi8xPez3nlE0RwSZcL0= +github.com/anacrolix/torrent v1.40.0/go.mod h1:ejOLXT7fZE8ONHsoIYD7WS4/l6SEwtRGxcsP3dEu/Fk= +github.com/anacrolix/torrent v1.41.1-0.20220309095723-02b6ee995497/go.mod h1:GZlCftqtWLz7EGJ3Oo+JP35jrU5zENxgvTCJ0w+SUSU= +github.com/anacrolix/torrent v1.46.0/go.mod h1:3DE+VA4AgyfKDPjZcIo70D3VFZRo3bfdEBn70CGjca4= +github.com/anacrolix/torrent v1.47.0/go.mod h1:SYPxEUjMwqhDr3kWGzyQLkFMuAb1bgJ57JRMpuD3ZzE= +github.com/anacrolix/upnp v0.1.1/go.mod h1:LXsbsp5h+WGN7YR+0A7iVXm5BL1LYryDev1zuJMWYQo= +github.com/anacrolix/upnp v0.1.2-0.20200416075019-5e9378ed1425/go.mod h1:Pz94W3kl8rf+wxH3IbCa9Sq+DTJr8OSbV2Q3/y51vYs= +github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96/go.mod h1:Wa6n8cYIdaG35x15aH3Zy6d03f7P728QfdcDeD/IEOs= +github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= +github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/arl/statsviz v0.4.0/go.mod h1:+5inUy/dxy11x/KSmicG3ZrEEy0Yr81AFm3dn4QC04M= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -151,20 +687,59 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/benbjohnson/clock v1.0.1/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.0.2 h1:Z0CN0Yb4ig9sGPXkvAQcGJfnrrMQ5QYLCMPRi9iD7YE= -github.com/benbjohnson/clock v1.0.2/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= +github.com/benbjohnson/immutable v0.3.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bittorrent/go-btfs v0.0.0-20230626064024-58978cbfe949 h1:1FBiTP+5+D6zATxnCZjnVU6+zukuGkuSCgBM7/2w/Xg= +github.com/bittorrent/go-btfs v0.0.0-20230626064024-58978cbfe949/go.mod h1:h5PjtZkd2YY0X5/vkaBm7yDQbLzyzMV3v5O2QggxSSA= +github.com/bittorrent/go-btfs-api v0.5.0/go.mod h1:JnVWIMvRK3Rhv8WsW5y0KkJUbw/Vgt247Y8C8Tn/SPE= +github.com/bittorrent/go-btfs-chunker v0.4.0 h1:ruX0vPwJdj0KP4SAtxNzZm593MUA+A3LLV+l9j2w09c= +github.com/bittorrent/go-btfs-chunker v0.4.0/go.mod h1:1xf90c9gOKrHf2tyFIfB5GgFoTkEd1r/5m73ts+WW9A= +github.com/bittorrent/go-btfs-cmds v0.3.0 h1:xpCBgk3zIm84Ne6EjeJgi8WLB5YJJUIFMjK9L9RfL5k= +github.com/bittorrent/go-btfs-cmds v0.3.0/go.mod h1:Fbac/Rou32G0jpoa6wLrNNDxcGOZbGfk+GiG0r3uEIU= +github.com/bittorrent/go-btfs-common v0.9.0 h1:jHcFvYQmvmA4IdvVtkI5d/S/HW65Qz21C6oxeyK812w= +github.com/bittorrent/go-btfs-common v0.9.0/go.mod h1:OG1n3DfcTxQYfLd5zco54LfL3IiDDaw3s7Igahu0Rj0= +github.com/bittorrent/go-btfs-config v0.12.0/go.mod h1:DNaHVC9wU84KLKoC4HkvdoFJKVZ7TF530qzfYu30fCI= +github.com/bittorrent/go-btfs-config v0.12.3 h1:Zi/GTwHo/PJV+90+w45P7axkWsUpOB/XFhgvNk+TwRs= +github.com/bittorrent/go-btfs-config v0.12.3/go.mod h1:DNaHVC9wU84KLKoC4HkvdoFJKVZ7TF530qzfYu30fCI= +github.com/bittorrent/go-btfs-files v0.3.0/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= +github.com/bittorrent/go-btfs-files v0.3.1 h1:esq3j+6FtZ+SlaxKjVtiYgvXk/SWUiTcv0Q1MeJoPnQ= +github.com/bittorrent/go-btfs-files v0.3.1/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= +github.com/bittorrent/go-btns v0.2.0 h1:OMpxUiRbtb/PRTK/z/flxcwOfTvNKMsTLOubYFhKy1s= +github.com/bittorrent/go-btns v0.2.0/go.mod h1:+Cinr/1Jl7V/Pqgz+vbOdHXkLVFbMqjypmbAv8QiQPs= +github.com/bittorrent/go-common/v2 v2.4.0 h1:u0jldKnQteTPQDNKj5GUBOUj2Tswn0+GfWN7yq2QAaY= +github.com/bittorrent/go-common/v2 v2.4.0/go.mod h1:DVJCWPoehldR7u0K1n9UeKKsQL28mYiY7XMShjGfB3I= +github.com/bittorrent/go-eccrypto v0.1.0 h1:sNosO+VGuh8IRQvrm9BJ4FeEatRp8ToMfpRTYaNqe7g= +github.com/bittorrent/go-eccrypto v0.1.0/go.mod h1:1kX5RLI52B+1l0VwwBtv+6h28Gu8XojZUu0wc/Iw6GU= +github.com/bittorrent/go-mfs v0.4.0 h1:xb7Bxp65LQP8yhflx47ZMuXzIMSSo9ZrasVhroCvRxs= +github.com/bittorrent/go-mfs v0.4.0/go.mod h1:w7XQuaSCDsL0MhcMP02ViFJQHYg2tLf+/v0w/m7wMfM= +github.com/bittorrent/go-unixfs v0.7.0 h1:2SPuQcAmubJUl+zuKoGWdculoZRn7D0zkDnTZ9pupqo= +github.com/bittorrent/go-unixfs v0.7.0/go.mod h1:0UNGV0k5MFsMGOeNjOJFtURcXDFz8bjtyfhcom+vW7A= +github.com/bittorrent/interface-go-btfs-core v0.8.2 h1:iTStlXLoandcKyFruq4U0uVSR3CQU7ey9Lwf8Mu3jw0= +github.com/bittorrent/interface-go-btfs-core v0.8.2/go.mod h1:tQ3d3uI2gH+AO7ikbBwlulRgff0/dzobz9H3SL00yYo= +github.com/bittorrent/protobuf v1.4.0 h1:3AW4SZUud3/8/orb8O/957CdspwxWjX/qprvF49aQ70= +github.com/bittorrent/protobuf v1.4.0/go.mod h1:k2fZczatqZOyvWUezE02Xt5uFcVqdUd1tNeZwXjELCk= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= github.com/bren2010/proquint v0.0.0-20160323162903-38337c27106d h1:QgeLLoPD3kRVmeu/1al9iIpIANMi9O1zXFm8BnYGCJg= github.com/bren2010/proquint v0.0.0-20160323162903-38337c27106d/go.mod h1:Jbj8eKecMNwf0KFI75skSUZqMB4UCRcndUScVBTWyUI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= @@ -172,13 +747,22 @@ github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dm github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= @@ -192,18 +776,29 @@ github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOC github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= @@ -211,63 +806,128 @@ github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3h github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f h1:BOaYiTvg8p9vBUXpklC22XSK/mifLF7lG9jtmYYi3Tc= -github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= -github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= +github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= +github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= @@ -276,58 +936,108 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302/go.mod h1:qBlWZqWeVx9BjvqBsnC/8RUlAYpIFmPvgROcw0n1scE= +github.com/elliotchance/orderedmap v1.2.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw= +github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw= +github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.9.17/go.mod h1:kihoiSg74VC4dZAXMkmoWp70oQabz48BJg1tuzricFc= -github.com/ethereum/go-ethereum v1.9.24 h1:6AK+ORt3EMDO+FTjzXy/AQwHMbu52J2nYHIjyQX9azQ= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM= -github.com/ethereum/go-ethereum v1.10.3/go.mod h1:99onQmSd1GRGOziyGldI41YQb7EESX3Q4H41IfJgIQQ= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.11.1 h1:EMymmWFzpS7G9l9NvVN8G73cgdUIqDPNRf2YTSGBXlk= +github.com/ethereum/go-ethereum v1.11.1/go.mod h1:DuefStAgaxoaYGLR0FueVcVbehmn5n9QUcVrMCuOvuc= +github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fomichev/secp256k1 v0.0.0-20180413221153-00116ff8c62f h1:YbIfHI+s+tauzUAdNw+lF8a9o056PoIPaLGodgh5D7g= github.com/fomichev/secp256k1 v0.0.0-20180413221153-00116ff8c62f/go.mod h1:X4BmRxczPduAy11nSLYwnR11VuvnbG7ozOTDKLHhx70= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gabriel-vasile/mimetype v1.1.0/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= -github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= -github.com/gabriel-vasile/mimetype v1.1.2/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732/go.mod h1:o/XfIXWi4/GqbQirfRm5uTbXMG5NpqxkxblnbZ+QM9I= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/go-unsnap-stream v0.0.0-20210130063903-47dfef350d96/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -335,191 +1045,279 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-pg/migrations/v7 v7.1.6/go.mod h1:ycN6RqhOqa3km5KVLvRyESYP+lvqhrGYZxAIQ5HPPMM= -github.com/go-pg/migrations/v7 v7.1.9/go.mod h1:ycN6RqhOqa3km5KVLvRyESYP+lvqhrGYZxAIQ5HPPMM= github.com/go-pg/migrations/v7 v7.1.11/go.mod h1:v/v7SfckdB2IGmUyopKyASTzjmN30HnDucLLZcCvBWU= github.com/go-pg/pg/v9 v9.0.0-beta.14/go.mod h1:T2Sr6bpTCOr2lUqOUMiXLMJqZHSUBKk1LdgSqjwhZfA= github.com/go-pg/pg/v9 v9.0.0/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= -github.com/go-pg/pg/v9 v9.0.1/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= github.com/go-pg/pg/v9 v9.0.3/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= -github.com/go-pg/pg/v9 v9.1.5/go.mod h1:QM13HBLkdml4zcKOfUfGLymM6hb72aKTJLrmaH8rsFg= github.com/go-pg/pg/v9 v9.1.6/go.mod h1:QM13HBLkdml4zcKOfUfGLymM6hb72aKTJLrmaH8rsFg= github.com/go-pg/pg/v9 v9.2.0/go.mod h1:fG8qbL+ei4e/fCZLHK+Z+/7b9B+pliZtbpaucG4/YNQ= +github.com/go-pg/pg/v9 v9.2.1/go.mod h1:fG8qbL+ei4e/fCZLHK+Z+/7b9B+pliZtbpaucG4/YNQ= github.com/go-pg/urlstruct v0.1.0/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= -github.com/go-pg/urlstruct v0.1.4/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= github.com/go-pg/urlstruct v0.2.6/go.mod h1:dxENwVISWSOX+k87hDt0ueEJadD+gZWv3tHzwfmZPu8= github.com/go-pg/urlstruct v0.3.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/urlstruct v0.4.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/zerochecker v0.1.1/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= -github.com/go-redis/redis/v7 v7.0.0-beta.4/go.mod h1:xhhSbUMTsleRPur+Vgx9sUHtyN33bdjxY+9/0n9Ig8s= -github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/google/gopacket v1.1.18 h1:lum7VRA9kdlvBi7/v2p7/zcbkduHaCH/SVVyurs7OpY= -github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= +github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= -github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= +github.com/gosuri/uilive v0.0.3/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= +github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= +github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= -github.com/gxed/pubsub v0.0.0-20180201040156-26ebdf44f824/go.mod h1:OiEWyHgK+CWrmOlVquHaIK1vhpUJydC9m0Je6mhaiNE= github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1/go.mod h1:jvfsLIxk0fY/2BKSQ1xf2406AKA5dwMmKKv0ADcOfN8= github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -530,90 +1328,116 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.1.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.2.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= -github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/hypnoglow/go-pg-monitor v0.1.0/go.mod h1:qe/oofabOXAvIn2iv/eLrtSUaHgNBeR0Dwbgf62fgbQ= github.com/hypnoglow/go-pg-monitor/gopgv9 v0.1.0/go.mod h1:0Mj+MFtASobV/5qHb68nxBdoGjr1QXTDU/9ZKPi8UF0= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/ip2location/ip2location-go v8.2.0+incompatible/go.mod h1:3JUY1TBjTx1GdA7oRT7Zeqfc0bg3lMMuU5lXmzdpuME= github.com/ip2location/ip2location-go/v9 v9.0.0/go.mod h1:s5SV6YZL10TpfPpXw//7fEJC65G/yH7Oh+Tjq9JcQEQ= github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/go-bitswap v0.0.3/go.mod h1:jadAZYsP/tcRMl47ZhFxhaNuDQoXawT8iHMg+iFoQbg= +github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= +github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= +github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-bitswap v0.0.9/go.mod h1:kAPf5qgn2W2DrgAcscZ3HrM9qh4pH+X8Fkk3UPrwvis= github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.1.3/go.mod h1:YEQlFy0kkxops5Vy+OxWdRSEZIoS7I7KDIwoa5Chkps= github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMiCSA4MzM= -github.com/ipfs/go-bitswap v0.2.19 h1:EhgRz8gqWQIBADY9gpqJOrfs5E1MtVfQFy1Vq8Z+Fq8= -github.com/ipfs/go-bitswap v0.2.19/go.mod h1:C7TwBgHnu89Q8sHsTJP7IhUqF9XYLe71P4tT5adgmYo= -github.com/ipfs/go-bitswap v0.2.20 h1:Zfi5jDUoqxDThORUznqdeL77DdGniAzlccNJ4vr+Itc= -github.com/ipfs/go-bitswap v0.2.20/go.mod h1:C7TwBgHnu89Q8sHsTJP7IhUqF9XYLe71P4tT5adgmYo= +github.com/ipfs/go-bitswap v0.3.4/go.mod h1:4T7fvNv/LmOys+21tnLzGKncMeeXUYUd1nUiJ2teMvI= +github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= +github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= +github.com/ipfs/go-bitswap v0.8.0/go.mod h1:/h8sBij8UVEaNWl8ABzpLRA5Y1cttdNUnpeGo2AA/LQ= +github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= +github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRMtYNGrlxZ8KuI= +github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= +github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= +github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So= github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.1.1/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I= -github.com/ipfs/go-blockservice v0.1.2/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I= -github.com/ipfs/go-blockservice v0.1.3 h1:9XgsPMwwWJSC9uVr2pMDsW2qFTBSkxpGMhmna8mIjPM= -github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= +github.com/ipfs/go-blockservice v0.1.4/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= +github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= +github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= +github.com/ipfs/go-blockservice v0.4.0/go.mod h1:kRjO3wlGW9mS1aKuiCeGhx9K1DagQ10ACpVO59qgAx4= +github.com/ipfs/go-blockservice v0.5.0 h1:B2mwhhhVQl2ntW2EIpaWPwSCxSuqr5fFA93Ms4bYLEY= +github.com/ipfs/go-blockservice v0.5.0/go.mod h1:W6brZ5k20AehbmERplmERn8o2Ni3ZZubvAxaIUeaT6w= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= -github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.6 h1:go0y+GcDOGeJIV01FeBsta4FHngoA4Wz7KMeLkXAhMs= github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cidutil v0.0.2 h1:CNOboQf1t7Qp0nuNh8QMmhJs0+Q//bRL1axtCnIB1Yo= -github.com/ipfs/go-cidutil v0.0.2/go.mod h1:ewllrvrxG6AMYStla3GD7Cqn+XYSLqjK0vc+086tB6s= +github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= +github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= +github.com/ipfs/go-cid v0.3.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= +github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA= +github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= +github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.3.0/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8= github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg= github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= +github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= +github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= +github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= +github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= +github.com/ipfs/go-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU= +github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= @@ -621,148 +1445,171 @@ github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaH github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-badger v0.2.4 h1:UPGB0y7luFHk+mY/tUZrif/272M8o+hFsW+avLUeWrM= -github.com/ipfs/go-ds-badger v0.2.4/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-flatfs v0.4.4 h1:DmGZ4qOYQLNgu8Mltuz1DtUHpm+BjWMcVN3F3H3VJzQ= -github.com/ipfs/go-ds-flatfs v0.4.4/go.mod h1:e4TesLyZoA8k1gV/yCuBTnt2PJtypn4XUlB5n8KQMZY= -github.com/ipfs/go-ds-flatfs v0.4.5 h1:4QceuKEbH+HVZ2ZommstJMi3o3II+dWS3IhLaD7IGHs= -github.com/ipfs/go-ds-flatfs v0.4.5/go.mod h1:e4TesLyZoA8k1gV/yCuBTnt2PJtypn4XUlB5n8KQMZY= +github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= +github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= +github.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4= +github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4= github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.4.2 h1:QmQoAJ9WkPMUfBLnu1sBVy0xWWlJPg0m4kRAiJL9iaw= github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-measure v0.1.0 h1:vE4TyY4aeLeVgnnPBC5QzKIjKrqzha0NCujTfgvVbVQ= -github.com/ipfs/go-ds-measure v0.1.0/go.mod h1:1nDiFrhLlwArTME1Ees2XaBOl49OoCgd2A3f8EchMSY= -github.com/ipfs/go-filestore v0.0.3 h1:MhZ1jT5K3NewZwim6rS/akcJLm1xM+r6nz6foeB9EwE= -github.com/ipfs/go-filestore v0.0.3/go.mod h1:dvXRykFzyyXN2CdNlRGzDAkXMDPyI+D7JE066SiKLSE= -github.com/ipfs/go-fs-lock v0.0.5 h1:nlKE27N7hlvsTXT3CSDkM6KRqgXpnaDjvyCHjAiZyK8= -github.com/ipfs/go-fs-lock v0.0.5/go.mod h1:fq8gXFwbi1on9KScveHuVJ2wjuqo5jaDgCtZdKLuCO8= -github.com/ipfs/go-fs-lock v0.0.6 h1:sn3TWwNVQqSeNjlWy6zQ1uUGAZrV3hPOyEA6y1/N2a0= -github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM= -github.com/ipfs/go-graphsync v0.1.1 h1:bFDAYS0Z48yd8ROPI6f/zIVmJxaDLA6m8cVuJPKC5fE= -github.com/ipfs/go-graphsync v0.1.1/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE= -github.com/ipfs/go-graphsync v0.2.0 h1:x94MvHLNuRwBlZzVal7tR1RYK7T7H6bqQLPopxDbIF0= -github.com/ipfs/go-graphsync v0.2.0/go.mod h1:gEBvJUNelzMkaRPJTpg/jaKN4AQW/7wDWu0K92D8o10= +github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= +github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= +github.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjApYyQ= +github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE= +github.com/ipfs/go-fetcher v1.5.0/go.mod h1:5pDZ0393oRF/fHiLmtFZtpMNBQfHOYNPtryWedVuSWE= +github.com/ipfs/go-fetcher v1.6.1 h1:UFuRVYX5AIllTiRhi5uK/iZkfhSpBCGX7L70nSZEmK8= +github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= +github.com/ipfs/go-filestore v1.2.0 h1:O2wg7wdibwxkEDcl7xkuQsPvJFRBVgVSsOJ/GP6z3yU= +github.com/ipfs/go-filestore v1.2.0/go.mod h1:HLJrCxRXquTeEEpde4lTLMaE/MYJZD7WHLkp9z6+FF8= +github.com/ipfs/go-fs-lock v0.0.7 h1:6BR3dajORFrFTkb5EpCUFIAypsoxpGpDSVUdFwzgL9U= +github.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEcvYrHhhc= +github.com/ipfs/go-graphsync v0.14.0 h1:f5KYkc8GpwwE1BrjBOWxIkRivXIw7fVqGZlnILpvbSc= +github.com/ipfs/go-graphsync v0.14.0/go.mod h1:1LDVVnNHjit8ddJOtw3Jq9epP792xWFXXL3dJWIBIkM= github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.1.4 h1:2SGI6U1B44aODevza8Rde3+dY30Pb+lbcObe1LETxOQ= github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ= +github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= +github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= +github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= +github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= -github.com/ipfs/go-ipfs-cmds v0.3.0 h1:mi9oYrSCox5aBhutqAYqw6/9crlyGbw4E/aJtwS4zI4= -github.com/ipfs/go-ipfs-cmds v0.3.0/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk= -github.com/ipfs/go-ipfs-config v0.9.0 h1:qTXJ9CyOyQv1LFJUMysxz8fi6RxxnP9QqcmiobuANvw= -github.com/ipfs/go-ipfs-config v0.9.0/go.mod h1:GQUxqb0NfkZmEU92PxqqqLVVFTLpoGGUlBaTyDaAqrE= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1 h1:IW/bXGeaAZV2VH0Kuok+Ohva/zHkHmeLFBxC1k7mNPc= github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1 h1:LJXIo9W7CAmugqI+uofioIpRb6rY30GUu7G6LUfpMvM= +github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= +github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1 h1:P56jYKZF7lDDOLx5SotVh5KFxoY6C81I1NSHW1FxGew= +github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= +github.com/ipfs/go-ipfs-exchange-interface v0.2.0 h1:8lMSJmKogZYNo2jjhUs0izT+dck05pqUw4mWNW9Pw6Y= +github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-files v0.0.2/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= +github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= +github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= +github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= +github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg= github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= -github.com/ipfs/go-ipfs-flags v0.0.1/go.mod h1:RnXBb9WV53GSfTrSDVK61NLTFKvWc60n+K9EgCDh+rA= -github.com/ipfs/go-ipfs-pinner v0.0.4 h1:EmxhS3vDsCK/rZrsgxX0Le9m2drBcGlUd7ah/VyFYVE= -github.com/ipfs/go-ipfs-pinner v0.0.4/go.mod h1:s4kFZWLWGDudN8Jyd/GTpt222A12C2snA2+OTdy/7p8= +github.com/ipfs/go-ipfs-pinner v0.2.1 h1:kw9hiqh2p8TatILYZ3WAfQQABby7SQARdrdA+5Z5QfY= +github.com/ipfs/go-ipfs-pinner v0.2.1/go.mod h1:l1AtLL5bovb7opnG77sh4Y10waINz3Y1ni6CvTzx7oo= github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-provider v0.4.3 h1:k54OHXZcFBkhL6l3GnPS9PfpaLeLqZjVASG1bgfBdfQ= -github.com/ipfs/go-ipfs-provider v0.4.3/go.mod h1:rcQBVqfblDQRk5LaCtf2uxuKxMJxvKmF5pLS0pO4au4= +github.com/ipfs/go-ipfs-provider v0.8.0 h1:4YTe9IdX99NUZEEzOsooPNxQozI+lY5x6SDWjUYhPiM= +github.com/ipfs/go-ipfs-provider v0.8.0/go.mod h1:qCpwpoohIRVXvNzkygzsM3qdqP/sXlrogtA5I45tClc= +github.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk= github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs= -github.com/ipfs/go-ipfs-routing v0.1.0 h1:gAJTT1cEeeLj6/DlLX6t+NxD9fQe2ymTO6qWRDI/HQQ= github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= +github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= +github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= +github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipld-cbor v0.0.1/go.mod h1:RXHr8s4k0NE0TKhnrxqZC9M888QfsBN9rhS5NjfKzY8= github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.4 h1:Aw3KPOKXjvrm6VjwJvFf1F1ekR/BH3jdof3Bk7OTiSA= -github.com/ipfs/go-ipld-cbor v0.0.4/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= +github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8= +github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0 h1:xGlJKkArkmBvowr+GMCX0FEZtkro71K1AwiKnL37mwA= github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-git v0.0.3 h1:/YjkjCyo5KYRpW+suby8Xh9Cm/iH9dAgGV6qyZ1dGus= -github.com/ipfs/go-ipld-git v0.0.3/go.mod h1:RuvMXa9qtJpDbqngyICCU/d+cmLFXxLsbIclmD0Lcr0= -github.com/ipfs/go-ipns v0.0.2 h1:oq4ErrV4hNQ2Eim257RTYRgfOSV/s8BDaf9iIl4NwFs= -github.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U= +github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= +github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= +github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= +github.com/ipfs/go-ipld-git v0.1.1 h1:TWGnZjS0htmEmlMFEkA3ogrNCqWjIxwr16x1OsdhG+Y= +github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYDpKUkJubI= +github.com/ipfs/go-ipld-legacy v0.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= +github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= +github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= +github.com/ipfs/go-ipns v0.2.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= +github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= +github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= -github.com/ipfs/go-log v1.0.3 h1:Gg7SUYSZ7BrqaKMwM+hRgcAkKv4QLfzP4XPQt5Sx/OI= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= +github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.3 h1:Q2gXcBoCALyLN/pUQlz1qgu0x3uFV6FzP9oXhpfyJpc= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.0.8 h1:3b3YNopMHlj4AvyhWAx0pDxqSQWYi4/WuWO7yRV6/Qg= -github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= -github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= +github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g= +github.com/ipfs/go-log/v2 v2.5.0/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= -github.com/ipfs/go-merkledag v0.1.0/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.0/go.mod h1:4pymaZLhSLNVuiCITYrpViD6vmfZ/Ws4n/L9tfNv3S4= -github.com/ipfs/go-merkledag v0.3.1 h1:3UqWINBEr3/N+r6OwgFXAddDP/8zpQX/8J7IGVOCqRQ= -github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1xY= github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= +github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= +github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= +github.com/ipfs/go-merkledag v0.8.1 h1:N3yrqSre/ffvdwtHL4MXy0n7XH+VzN8DlzDrJySPa94= +github.com/ipfs/go-merkledag v0.8.1/go.mod h1:uYUlWE34GhbcTjGuUDEcdPzsEtOdnOupL64NgSRjmWI= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= -github.com/ipfs/go-mfs v0.1.2 h1:DlelNSmH+yz/Riy0RjPKlooPg0KML4lXGdLw7uZkfAg= -github.com/ipfs/go-mfs v0.1.2/go.mod h1:T1QBiZPEpkPLzDqEJLNnbK55BVKVlNi2a+gVm4diFo0= -github.com/ipfs/go-path v0.0.3/go.mod h1:zIRQUez3LuQIU25zFjC2hpBTHimWx7VK5bjZgRLbbdo= -github.com/ipfs/go-path v0.0.7 h1:H06hKMquQ0aYtHiHryOMLpQC1qC3QwXwkahcEVD51Ho= github.com/ipfs/go-path v0.0.7/go.mod h1:6KTKmeRnBXgqrTvzFrPV3CamxcgvXX/4z79tfAd2Sno= -github.com/ipfs/go-path v0.0.8 h1:R0k6t9x/pa+g8qzl5apQIPurJFozXhopks3iw3MX+jU= -github.com/ipfs/go-path v0.0.8/go.mod h1:VpDkSBKQ9EFQOUgi54Tq/O/tGi8n1RfYNks13M3DEs8= +github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= +github.com/ipfs/go-path v0.3.1 h1:wkeaCWE/NTuuPGlEkLTsED5UkzfKYZpxaFFPgk8ZVLE= +github.com/ipfs/go-path v0.3.1/go.mod h1:eNLsxJEEMxn/CDzUJ6wuNl+6No6tEUhOZcPKsZsYX0E= github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ= github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.2.0 h1:2cSr7exUGKYyDeUyQ7P/nHPs9P7Ht/B+ROrpN1EJOjc= github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUnr+P7jivavs9lY= -github.com/ipfs/go-unixfs v0.0.4/go.mod h1:eIo/p9ADu/MFOuyxzwU+Th8D6xoxU//r590vUpWyfz8= -github.com/ipfs/go-unixfs v0.1.0/go.mod h1:lysk5ELhOso8+Fed9U1QTGey2ocsfaZ18h0NCO2Fj9s= -github.com/ipfs/go-unixfs v0.2.4 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo= +github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= +github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= +github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E= +github.com/ipfs/go-unixfs v0.3.1 h1:LrfED0OGfG98ZEegO4/xiprx2O+yS+krCMQSp7zLVv8= +github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= +github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= +github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA= +github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= -github.com/TRON-US/interface-go-btfs-core v0.3.0/go.mod h1:Tihp8zxGpUeE3Tokr94L6zWZZdkRQvG5TL6i9MuNE+s= -github.com/TRON-US/interface-go-btfs-core v0.4.0 h1:+mUiamyHIwedqP8ZgbCIwpy40oX7QcXUbo4CZOeJVJg= -github.com/TRON-US/interface-go-btfs-core v0.4.0/go.mod h1:UJBcU6iNennuI05amq3FQ7g0JHUkibHFAfhfUIy927o= -github.com/ipld/go-car v0.1.1-0.20200429200904-c222d793c339/go.mod h1:eajxljm6I8o3LitnFeVEmucwZmz7+yLSiKce9yYMefg= -github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e h1:ZISbJlM0urTANR9KRfRaqlBmyOj5uUtxs2r4Up9IXsA= -github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8= -github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f h1:XpOuNQ5GbXxUcSukbQcW9jkE7REpaFGJU2/T00fo9kA= -github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM= -github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1 h1:K1Ysr7kgIlo7YQkPqdkA6H7BVdIugvuAz7OQUTJxLdE= -github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs= -github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6 h1:6Mq+tZGSEMEoJJ1NbJRhddeelkXZcU8yfH/ZRYUo/Es= -github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0= -github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= +github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= +github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= +github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= +github.com/ipld/go-car v0.4.0 h1:U6W7F1aKF/OJMHovnOVdst2cpQE5GhmHibQkAixgNcQ= +github.com/ipld/go-car v0.4.0/go.mod h1:Uslcn4O9cBKK9wqHm/cLTFacg6RAPv6LZx2mxd2Ypl4= +github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= +github.com/ipld/go-car/v2 v2.4.0 h1:8jI6/iKlyLqRZzLz31jFWTqKvslaVzFsin305sOuqNQ= +github.com/ipld/go-car/v2 v2.4.0/go.mod h1:zjpRf0Jew9gHqSvjsKVyoq9OY9SWoEKdYCQUKVaaPT0= +github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= +github.com/ipld/go-codec-dagpb v1.3.1/go.mod h1:ErNNglIi5KMur/MfFE/svtgQthzVvf+43MrzLbpcIZY= +github.com/ipld/go-codec-dagpb v1.4.1 h1:CUQJaOPRgSZ27OUPgUWtvdvvd2d17/IGGAIMOo4yYp0= +github.com/ipld/go-codec-dagpb v1.4.1/go.mod h1:XdXTO/TUD/ra9RcK/NfmwBfr1JpFxM2uRKaB9oe4LxE= +github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= +github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= +github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= +github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs= +github.com/ipld/go-ipld-prime v0.17.1-0.20220624062450-534ccf82237d/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs= +github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE= +github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= +github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= +github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ= +github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= @@ -786,33 +1633,60 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1C github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kataras/blocks v0.0.6/go.mod h1:UK+Iwk0Oxpc0GdoJja7sEildotAUKK1LYeYcVF0COWc= +github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/iris/v12 v12.2.0-beta5/go.mod h1:q26aoWJ0Knx/00iPKg5iizDK7oQQSPjbD8np0XDh6dc= +github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/neffos v0.0.20/go.mod h1:srdvC/Uo8mgrApWW0AYtiiLgMbyNPf69qPsd2FhE6MQ= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= +github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -820,121 +1694,127 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4= github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= -github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= +github.com/klauspost/reedsolomon v1.9.14 h1:vkPCIhFMn2VdktLUcugqsU4vcLXN3dAhVd1uWA+TDD8= +github.com/klauspost/reedsolomon v1.9.14/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= +github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= +github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk= -github.com/libp2p/go-conn-security-multistream v0.0.1/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= -github.com/libp2p/go-conn-security-multistream v0.1.0 h1:aqGmto+ttL/uJgX0JtQI0tD21CIEy5eYd1Hlp0juHY0= github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= -github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ= +github.com/libp2p/go-doh-resolver v0.4.0 h1:gUBa1f1XsPwtpE1du0O+nnZCUqtG7oYi7Bb+0S7FQqw= +github.com/libp2p/go-doh-resolver v0.4.0/go.mod h1:v1/jwsFusgsWIGX/c6vCRrnJ60x7bhTiq/fs2qt0cAg= github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p v0.0.2/go.mod h1:Qu8bWqFXiocPloabFGUcVG4kk94fLvfC8mWTDdFC9wE= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= github.com/libp2p/go-libp2p v0.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A= github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -github.com/libp2p/go-libp2p v0.6.0/go.mod h1:mfKWI7Soz3ABX+XEBR61lGbg+ewyMtJHVt043oWeqwg= github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= -github.com/libp2p/go-libp2p v0.7.4 h1:xVj1oSlN0C+FlxqiLuHC8WruMvq24xxfeVxmNhTG0r0= github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= -github.com/libp2p/go-libp2p v0.8.2/go.mod h1:NQDA/F/qArMHGe0J7sDScaKjW8Jh4y/ozQqBbYJ+BnA= -github.com/libp2p/go-libp2p v0.8.3 h1:IFWeNzxkBaNO1N8stN9ayFGdC6RmVuSsKd5bou7qpK0= -github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El8cTaefiM= -github.com/libp2p/go-libp2p v0.10.0 h1:7ooOvK1wi8eLpyTppy8TeH43UHy5uI75GAHGJxenUi0= -github.com/libp2p/go-libp2p v0.10.0/go.mod h1:yBJNpb+mGJdgrwbKAKrhPU0u3ogyNFTfjJ6bdM+Q/G8= -github.com/libp2p/go-libp2p v0.11.0 h1:jb5mqdqYEBAybTEhD8io43Cz5LzVKuWxOK7znSN69jE= -github.com/libp2p/go-libp2p v0.11.0/go.mod h1:3/ogJDXsbbepEfqtZKBR/DedzxJXCeK17t2Z9RE9bEE= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 h1:BM7aaOF7RpmNn9+9g6uTjGJ0cTzWr5j9i9IKeun2M8U= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= -github.com/libp2p/go-libp2p-autonat v0.0.2/go.mod h1:fs71q5Xk+pdnKU014o2iq1RhMs9/PMaG5zXRFNnIIT4= +github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo= +github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= +github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= +github.com/libp2p/go-libp2p v0.23.2/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI= +github.com/libp2p/go-libp2p v0.23.4/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI= +github.com/libp2p/go-libp2p v0.24.2 h1:iMViPIcLY0D6zr/f+1Yq9EavCZu2i7eDstsr1nEwSAk= +github.com/libp2p/go-libp2p v0.24.2/go.mod h1:WuxtL2V8yGjam03D93ZBC19tvOUiPpewYv1xdFGWu1k= +github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= +github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= -github.com/libp2p/go-libp2p-autonat v0.2.1 h1:T0CRQhrvTBKfBSYw6Xo2K3ixtNpAnRCraxof3AAfgQA= github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= -github.com/libp2p/go-libp2p-autonat v0.2.2 h1:4dlgcEEugTFWSvdG2UIFxhnOMpX76QaZSRAtXmYB8n4= github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= -github.com/libp2p/go-libp2p-autonat v0.2.3 h1:w46bKK3KTOUWDe5mDYMRjJu1uryqBp8HCNDp/TWMqKw= -github.com/libp2p/go-libp2p-autonat v0.2.3/go.mod h1:2U6bNWCNsAG9LEbwccBDQbjzQ8Krdjge1jLTE9rdoMM= -github.com/libp2p/go-libp2p-autonat v0.3.2 h1:OhDSwVVaq7liTaRIsFFYvsaPp0pn2yi0WazejZ4DUmo= -github.com/libp2p/go-libp2p-autonat v0.3.2/go.mod h1:0OzOi1/cVc7UcxfOddemYD5vzEqi4fwRbnZcJGLi68U= +github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= +github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc= github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUADzkJacgZLvk= github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-blankhost v0.1.6 h1:CkPp1/zaCrCnBo0AdsQA0O1VkUYoUOtyHOnoa8gKIcE= -github.com/libp2p/go-libp2p-blankhost v0.1.6/go.mod h1:jONCAJqEP+Z8T6EQviGL4JsQcLx1LgTGtVqFNY8EMfQ= -github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk= github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.0.1/go.mod h1:Dqm0s/BiV63j8EEAs8hr1H5HudqvCAeXxDyic59lCwE= github.com/libp2p/go-libp2p-circuit v0.0.9/go.mod h1:uU+IBvEQzCu953/ps7bYzC/D/R0Ho2A9LfKVVCatlqU= github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= -github.com/libp2p/go-libp2p-circuit v0.2.1 h1:BDiBcQxX/ZJJ/yDl3sqZt1bjj4PkZCEi7IEpwxXr13k= github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= -github.com/libp2p/go-libp2p-circuit v0.2.2 h1:87RLabJ9lrhoiSDDZyCJ80ZlI5TLJMwfyoGAaWXzWqA= -github.com/libp2p/go-libp2p-circuit v0.2.2/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4= -github.com/libp2p/go-libp2p-circuit v0.2.3 h1:3Uw1fPHWrp1tgIhBz0vSOxRUmnKL8L/NGUyEd5WfSGM= -github.com/libp2p/go-libp2p-circuit v0.2.3/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4= -github.com/libp2p/go-libp2p-circuit v0.3.1 h1:69ENDoGnNN45BNDnBd+8SXSetDuw0eJFcGmOvvtOgBw= -github.com/libp2p/go-libp2p-circuit v0.3.1/go.mod h1:8RMIlivu1+RxhebipJwFDA45DasLx+kkrp4IlJj53F4= github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-connmgr v0.2.3/go.mod h1:Gqjg29zI8CwXX21zRxy6gOg8VYu3zVerJRt2KyktzH4= -github.com/libp2p/go-libp2p-connmgr v0.2.4 h1:TMS0vc0TCBomtQJyWr7fYxcVYYhx+q/2gF++G5Jkl/w= -github.com/libp2p/go-libp2p-connmgr v0.2.4/go.mod h1:YV0b/RIm8NGPnnNWM7hG9Q38OeQiQfKhHCCs1++ufn0= github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= -github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE= github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= @@ -943,88 +1823,61 @@ github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.1 h1:6Cu7WljPQtGY2krBlMoD8L/zH3tMUsCbqNFH7cZwCoI= github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.2 h1:hevsCcdLiazurKBoeNn64aPYTVOPdY4phaEGeLtHOAs= -github.com/libp2p/go-libp2p-core v0.5.2/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.6.0 h1:u03qofNYTBN+yVg08PuAKylZogVf0xcTEeM8skGf+ak= github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.6.1 h1:XS+Goh+QegCDojUZp00CaPMfiEADCrLjNZskWE7pvqs= -github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.9.0 h1:t97Mv0LIBZlP2FXVRNKKVzHJCIjbIWGxYptGId4+htU= -github.com/libp2p/go-libp2p-core v0.9.0/go.mod h1:ESsbz31oC3C1AvMJoGx26RTuCkNhmkSRCqZ0kQtJ2/8= +github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= +github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= -github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.0.1/go.mod h1:ZkkF9xIFRLA1xCc7bstYFkd80gBGK8Fc1JqGoU2i+zI= github.com/libp2p/go-libp2p-discovery v0.0.5/go.mod h1:YtF20GUxjgoKZ4zmXj8j3Nb2TUSBHFlOCetzYdbZL5I= github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= -github.com/libp2p/go-libp2p-discovery v0.3.0 h1:+JnYBRLzZQtRq0mK3xhyjBwHytLmJXMTZkQfbw+UrGA= github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= -github.com/libp2p/go-libp2p-discovery v0.4.0 h1:dK78UhopBk48mlHtRCzbdLm3q/81g77FahEBTjcqQT8= -github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4= -github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ= github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-discovery v0.5.1/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-gostream v0.2.1/go.mod h1:1Mjp3LDmkqICe5tH9yLVNCqFaRTy6OwBvuJV6j1b9Nk= +github.com/libp2p/go-libp2p-gostream v0.5.0/go.mod h1:rXrb0CqfcRRxa7m3RSKORQiKiWgk3IPeXWda66ZXKsA= github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go= github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8= -github.com/libp2p/go-libp2p-http v0.1.5/go.mod h1:2YfPjsQxUlBGFQl2u461unkQ7ukwiSs7NX2eSslOJiU= +github.com/libp2p/go-libp2p-http v0.4.0/go.mod h1:92tmLGrlBliQFDlZRpBXT3BJM7rGFONy0vsNrG/bMPg= github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= github.com/libp2p/go-libp2p-interface-connmgr v0.0.4/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= github.com/libp2p/go-libp2p-interface-connmgr v0.0.5/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k= -github.com/libp2p/go-libp2p-kad-dht v0.8.2 h1:s7y38B+hdj1AkNR3PCTpvNqBsZHxOf7hoUy7+fNlSZQ= -github.com/libp2p/go-libp2p-kad-dht v0.8.2/go.mod h1:u3rbYbp3CSraAHD5s81CJ3hHozKTud/UOXfAgh93Gek= -github.com/libp2p/go-libp2p-kad-dht v0.9.0 h1:AKeFYZvfAa/32Sgm0LrPDxGXB62AUtU8MRqqMobBfUM= -github.com/libp2p/go-libp2p-kad-dht v0.9.0/go.mod h1:LEKcCFHxnvypOPaqZ0m6h0fLQ9Y8t1iZMOg7a0aQDD4= -github.com/libp2p/go-libp2p-kbucket v0.4.2 h1:wg+VPpCtY61bCasGRexCuXOmEmdKjN+k1w+JtTwu9gA= -github.com/libp2p/go-libp2p-kbucket v0.4.2/go.mod h1:7sCeZx2GkNK1S6lQnGUW5JYZCFPnXzAZCCBBS70lytY= -github.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70= -github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk= +github.com/libp2p/go-libp2p-kad-dht v0.20.0 h1:1bcMa74JFwExCHZMFEmjtHzxX5DovhJ07EtR6UOTEpc= +github.com/libp2p/go-libp2p-kad-dht v0.20.0/go.mod h1:qPIXdiZsLczhV4/+4EO1jE8ae0YCW4ZOogc4WVIyTEU= +github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= +github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= +github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg= -github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08= -github.com/libp2p/go-libp2p-metrics v0.1.0/go.mod h1:rpoJmXWFxnj7qs5sJ02sxSzrhaZvpqBn8GCG6Sx6E1k= github.com/libp2p/go-libp2p-mplex v0.1.1/go.mod h1:KUQWpGkCzfV7UIpi8SKsAVxyBgz1c9R5EvxgnwLsb/I= github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= -github.com/libp2p/go-libp2p-mplex v0.2.3 h1:2zijwaJvpdesST2MXpI5w9wWFRgYtMcpRX7rrw0jmOo= github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= -github.com/libp2p/go-libp2p-mplex v0.2.4 h1:XFFXaN4jhqnIuJVjYOR3k6bnRj0mFfJOlIuDVww+4Zo= -github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmgoXK8H331ui39CE= github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ= github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU= github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q= -github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ= github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es= -github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY= github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= @@ -1034,260 +1887,287 @@ github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVd github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.3 h1:MofRq2l3c15vQpEygTetV+zRRrncz+ktiXW7H2EKoEQ= -github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw= -github.com/libp2p/go-libp2p-peerstore v0.2.4/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U= github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= -github.com/libp2p/go-libp2p-pubsub v0.3.0/go.mod h1:TxPOBuo1FPdsTjFnv+FGZbNbWYsp74Culx+4ViQpato= -github.com/libp2p/go-libp2p-pubsub v0.3.1 h1:7Hyv2d8BK/x1HGRJTZ8X++VQEP+WqDTSwpUSZGTVLYA= -github.com/libp2p/go-libp2p-pubsub v0.3.1/go.mod h1:TxPOBuo1FPdsTjFnv+FGZbNbWYsp74Culx+4ViQpato= -github.com/libp2p/go-libp2p-pubsub v0.3.2/go.mod h1:Uss7/Cfz872KggNb+doCVPHeCDmXB7z500m/R8DaAUk= -github.com/libp2p/go-libp2p-pubsub v0.3.5 h1:iF75GWpcxKEUQU8tTkgLy69qIQvfhL+t6U6ndQrB6ho= -github.com/libp2p/go-libp2p-pubsub v0.3.5/go.mod h1:DTMSVmZZfXodB/pvdTGrY2eHPZ9W2ev7hzTH83OKHrI= -github.com/libp2p/go-libp2p-pubsub-router v0.3.0 h1:ghpHApTMXN+aZ+InYvpJa/ckBW4orypzNI0aWQDth3s= -github.com/libp2p/go-libp2p-pubsub-router v0.3.0/go.mod h1:6kZb1gGV1yGzXTfyNsi4p+hyt1JnA1OMGHeExTOJR3A= -github.com/libp2p/go-libp2p-pubsub-router v0.3.2 h1:BGC4irCUXlwmlCSxnA2DVDNY8JqhfAUUaiq3CZvcddw= -github.com/libp2p/go-libp2p-pubsub-router v0.3.2/go.mod h1:G4MAvYzPxhoR0LEBluS9Ow+Nnr/8iDalUN+RNwVgNkY= -github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M= -github.com/libp2p/go-libp2p-quic-transport v0.7.1 h1:X6Ond9GANspXpgwJlSR9yxcMMD6SLBnGKRtwjBG5awc= -github.com/libp2p/go-libp2p-quic-transport v0.7.1/go.mod h1:TD31to4E5exogR/GWHClXCfkktigjAl5rXSt7HoxNvY= -github.com/libp2p/go-libp2p-quic-transport v0.8.0 h1:mHA94K2+TD0e9XtjWx/P5jGGZn0GdQ4OFYwNllagv4E= -github.com/libp2p/go-libp2p-quic-transport v0.8.0/go.mod h1:F2FG/6Bzz0U6essUVxDzE0s9CrY4XGLbl7QEmDNvU7A= +github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= +github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= +github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= -github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg= -github.com/libp2p/go-libp2p-record v0.1.2 h1:M50VKzWnmUrk/M5/Dz99qO9Xh4vs8ijsK+7HkJvRP+0= github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= -github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0= -github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= +github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= +github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= -github.com/libp2p/go-libp2p-secio v0.0.1/go.mod h1:IdG6iQybdcYmbTzxp4J5dwtUEDTOvZrT0opIDVNPrJs= +github.com/libp2p/go-libp2p-routing-helpers v0.4.0 h1:b7y4aixQ7AwbqYfcOQ6wTw8DQvuRZeTAA0Od3YYN5yc= +github.com/libp2p/go-libp2p-routing-helpers v0.4.0/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0= github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= -github.com/libp2p/go-libp2p-secio v0.2.2 h1:rLLPvShPQAcY6eNurKNZq3eZjPWfU9kXF2eI9jIYdrg= github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= -github.com/libp2p/go-libp2p-swarm v0.0.1/go.mod h1:mh+KZxkbd3lQnveQ3j2q60BM1Cw2mX36XXQqwfPOShs= github.com/libp2p/go-libp2p-swarm v0.0.6/go.mod h1:s5GZvzg9xXe8sbeESuFpjt8CJPTCa8mhEusweJqyFy8= github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= -github.com/libp2p/go-libp2p-swarm v0.2.3 h1:uVkCb8Blfg7HQ/f30TyHn1g/uCwXsAET7pU0U59gx/A= github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= -github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h/GGZes8Wku/M5Y= -github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA= -github.com/libp2p/go-libp2p-swarm v0.2.8 h1:cIUUvytBzNQmGSjnXFlI6UpoBGsaud82mJPIJVfkDlg= github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= +github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw= github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.1 h1:U03z3HnGI7Ni8Xx6ONVZvUFOAzWYmolWf5W5jAOPNmU= github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= -github.com/libp2p/go-libp2p-testing v0.2.0/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= -github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM= +github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk= -github.com/libp2p/go-libp2p-transport v0.0.4/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A= github.com/libp2p/go-libp2p-transport v0.0.5/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A= -github.com/libp2p/go-libp2p-transport-upgrader v0.0.1/go.mod h1:NJpUAgQab/8K6K0m+JmZCe5RUXG10UMEx4kWe9Ipj5c= github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc= github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0 h1:5EhPgQhXZNyfL22ERZTUoVp9UVVbNowWNVtELQaKCHk= github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 h1:q3ULhsknEQ34eVDhv4YwKS8iet69ffs9+Fir6a7weN4= github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s= github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= +github.com/libp2p/go-libp2p-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA= +github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8= github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4= github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-libp2p-yamux v0.2.7 h1:vzKu0NVtxvEIDGCv6mjKRcK0gipSgaXmJZ6jFv0d/dk= github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= -github.com/libp2p/go-libp2p-yamux v0.2.8 h1:0s3ELSLu2O7hWKfX1YjzudBKCP0kZ+m9e2+0veXzkn4= github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= +github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= github.com/libp2p/go-libp2p-yamux v0.5.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg= github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.0.1/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.1.2 h1:qOg1s+WdGLlpkrczDqmhYzyk3vCfsQ8+RxRTQjOZWwI= github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-msgio v0.0.1/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= +github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA= github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= +github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= +github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q= github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-netroute v0.1.2 h1:UHhB35chwgvcRI392znJA3RCBtZ3MpE3ahNCN5MR4Xg= +github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= +github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig= github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= +github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= +github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.5 h1:pQkejVhF0xp08D4CQUcw8t+BFJeXowja6RVcb5p++EA= github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= +github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= +github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport-transport v0.0.1/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= +github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= -github.com/libp2p/go-reuseport-transport v0.0.3 h1:zzOeXnTooCkRvoH+bSXEfXhn76+LAiwoneM0gnXjF2M= github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= -github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM= github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-sockaddr v0.0.2 h1:tCuXfpA9rq7llM/v834RKc/Xvovy/AqM9kHvTV/jY/Q= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0= github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-socket-activation v0.0.2/go.mod h1:KP44C+yZ7gA8sTxavgaD0b8vXVFJwam2CEW0s7+f094= +github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-socket-activation v0.1.0/go.mod h1:gzda2dNkMG5Ti2OfWNNwW0FDIbj0g/aJJU320FcLfhk= github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ= github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw= -github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROmAFwUHpeRidG+q7LTQOg= github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= -github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-tcp-transport v0.0.1/go.mod h1:mnjg0o0O5TmXUaUIanYPUqkW4+u6mK0en8rlpA6BBTs= github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19K427vCzQ+xHKH/o= github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= -github.com/libp2p/go-tcp-transport v0.2.0 h1:YoThc549fzmNJIh7XjHVtMIFaEDRtIrtWciG5LyYAPo= github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= -github.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns= github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I= github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.0.1/go.mod h1:p3bKjDWHEgtuKKj+2OdPYs5dAPIjtpQGHF2tJfGz7Ww= github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU= github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= -github.com/libp2p/go-ws-transport v0.3.0 h1:mjo6pL5aVR9rCjl9wNq3DupbaQlyR61pzoOT2MdtxaA= github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.3.1 h1:ZX5rWB8nhRRJVaPO6tmkGI/Xx8XNboYX20PW5hXIscw= -github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.5 h1:ibuz4naPAully0pN6J/kmUARiqLpnDQIzI/8GCOrljg= github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.7 h1:v40A1eSPJDIZwz2AvrV3cxpTZEGDP11QJbukmEhYyQI= github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= +github.com/libp2p/go-yamux/v3 v3.1.2/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= +github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= +github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= +github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lispad/go-generics-tools v1.0.0/go.mod h1:stn7X24ZIyFvaSyttafq3VlJzGJJJkUtLYdbgi/gopM= +github.com/lispad/go-generics-tools v1.1.0/go.mod h1:2csd1EJljo/gy5qG4khXol7ivCPptNjG5Uv2X8MgK84= github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= -github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= -github.com/lucas-clemente/quic-go v0.17.3 h1:jMX/MmDNCljfisgMmPGUcBJ+zUh9w3d3ia4YJjYS3TM= -github.com/lucas-clemente/quic-go v0.17.3/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= -github.com/lucas-clemente/quic-go v0.18.0 h1:JhQDdqxdwdmGdKsKgXi1+coHRoGhvU6z0rNzOJqZ/4o= +github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= +github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= +github.com/lucas-clemente/quic-go v0.29.1/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE= +github.com/lucas-clemente/quic-go v0.31.0/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= +github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4= +github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236/go.mod h1:wTLsd5FC9rts7GkMpsPGk64CIuea+03yaLAp19Jmlg8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/pkger v0.17.0/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.9.1 h1:O0YKQxNVPaiFgMng0suWEOY2Sb4LT2sRn9Qimq3Z1IQ= -github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk= -github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= +github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= +github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= +github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= +github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= +github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= +github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= +github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/marten-seemann/webtransport-go v0.1.1/go.mod h1:kBEh5+RSvOA4troP1vyOVBWK4MIMzDICXVrvCPrYcrM= +github.com/marten-seemann/webtransport-go v0.4.3 h1:vkt5o/Ci+luknRteWdYGYH1KcB7ziup+J+1PzZJIvmg= +github.com/marten-seemann/webtransport-go v0.4.3/go.mod h1:4xcfySgZMLP4aG5GBGj1egP7NlpfwgYJ1WJMvPPiVMU= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.28 h1:gQhy5bsJa8zTlVI8lywCTZp1lguor+xevFoYlzeCTQY= github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= -github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1298,41 +2178,54 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.2.1 h1:SgG/cw5vqyB5QQe5FPe2TqggU9WtrA9X4nZw7LlVqOI= github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2 h1:XZLDTszBIJe6m0zF6ITBrEcZR73OPUhCBBS9rYAuUzI= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= +github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc= +github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= +github.com/multiformats/go-multiaddr v0.6.0/go.mod h1:F4IpaKZuPP360tOMn2Tpyu0At8w23aRyVqeK0DbFeGM= +github.com/multiformats/go-multiaddr v0.7.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= +github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr-dns v0.3.0/go.mod h1:mNzQ4eTGDg0ll1N9jKPOUogZPoJ30W8a7zk66FQPpdQ= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -1342,42 +2235,49 @@ github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJV github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.4 h1:g6gwydsfADqFvrHoMkS0n9Ok9CG6F7ytOH/bJDkhIOY= github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.5 h1:QoRKvu0xHN1FCFJcMQLbG/yQE2z441L5urvG3+qyz7g= github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk= github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.2 h1:2pAgScmS1g9XjH7EtAfNhTuyrWYEWcxy0G5Wo85hWDA= -github.com/multiformats/go-multibase v0.0.2/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= +github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= +github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= +github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= +github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= +github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= +github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= +github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= +github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= -github.com/multiformats/go-multihash v0.0.7/go.mod h1:XuKXPp8VHcTygube3OWZC+aZrA+H1IhmjoCDtJc7PXM= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= +github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= +github.com/multiformats/go-multihash v0.2.0/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= +github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= +github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-multistream v0.1.2 h1:knyamLYMPFPngQjGQ0lhnlys3jtVR/3xV6TREUJr+fE= -github.com/multiformats/go-multistream v0.1.2/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= +github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= +github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1386,16 +2286,27 @@ github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1408,28 +2319,53 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= +github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -1441,103 +2377,247 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= +github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg= +github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= +github.com/pion/dtls/v2 v2.0.1/go.mod h1:uMQkz2W0cSqY00xav7WByQ4Hb+18xeQh2oH2fRezr5U= +github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I= +github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI= +github.com/pion/dtls/v2 v2.0.7/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10= +github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= +github.com/pion/dtls/v2 v2.1.1/go.mod h1:qG3gA7ZPZemBqpEFqRKyURYdKEwFZQCGb7gv9T3ON3Y= +github.com/pion/dtls/v2 v2.1.2/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= +github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= +github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= +github.com/pion/ice v0.7.18/go.mod h1:+Bvnm3nYC6Nnp7VV6glUkuOfToB/AtMRZpOU8ihuf4c= +github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI= +github.com/pion/ice/v2 v2.1.7/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= +github.com/pion/ice/v2 v2.1.10/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= +github.com/pion/ice/v2 v2.1.12/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= +github.com/pion/ice/v2 v2.1.20/go.mod h1:hEAldRzBhTtAfvlU1V/2/nLCMvveQWFKPNCop+63/Iw= +github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE= +github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c= +github.com/pion/interceptor v0.0.12/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4= +github.com/pion/interceptor v0.0.13/go.mod h1:svsW2QoLHLoGLUr4pDoSopGBEWk8FZwlfxId/OKRKzo= +github.com/pion/interceptor v0.0.15/go.mod h1:pg3J253eGi5bqyKzA74+ej5Y19ez2jkWANVnF+Z9Dfk= +github.com/pion/interceptor v0.1.7/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U= +github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0= +github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= +github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k= +github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk= +github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I= +github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI= +github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.6.5/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= +github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= +github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E= +github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk= +github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= +github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA= +github.com/pion/srtp v1.5.2/go.mod h1:NiBff/MSxUwMUwx/fRNyD/xGE+dVvf8BOCeXhjCXZ9U= +github.com/pion/srtp/v2 v2.0.1/go.mod h1:c8NWHhhkFf/drmHTAblkdu8++lsISEBBdAuiyxgqIsE= +github.com/pion/srtp/v2 v2.0.2/go.mod h1:VEyLv4CuxrwGY8cxM+Ng3bmVy8ckz/1t6A0q/msKOw0= +github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A= +github.com/pion/srtp/v2 v2.0.9/go.mod h1:5TtM9yw6lsH0ppNCehB/EjEUli7VkUgKSPJqWVqbhQ4= +github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= +github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE= +github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= +github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= +github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= +github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= +github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= +github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog= +github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw= +github.com/pion/turn/v2 v2.0.6/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= +github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= +github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/webrtc/v2 v2.2.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc= +github.com/pion/webrtc/v3 v3.0.11/go.mod h1:WEvXneGTeqNmiR59v5jTsxMc4yXQyOQcRsrdAbNwSEU= +github.com/pion/webrtc/v3 v3.0.27/go.mod h1:QpLDmsU5a/a05n230gRtxZRvfHhFzn9ukGUL2x4G5ic= +github.com/pion/webrtc/v3 v3.0.32/go.mod h1:wX3V5dQQUGCifhT1mYftC2kCrDQX6ZJ3B7Yad0R9JK0= +github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38/go.mod h1:L5S/oAhL0Fzt/rnftVQRrP80/j5jygY7XRZzWwFx6P4= +github.com/pion/webrtc/v3 v3.1.42/go.mod h1:ffD9DulDrPxyWvDPUIPAOSAWx9GUlOExiJPf7cCcMLA= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14 h1:2m16U/rLwVaRdz7ANkHtHTodP3zTP3N451MADg64x5k= github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a h1:hjZfReYVLbqFkAtr2us7vdy04YWz3LVAirzP7reh8+M= -github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= +github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.2/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/dnscache v0.0.0-20190621150935-06bb5526f76b/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= +github.com/rs/dnscache v0.0.0-20210201191234-295bba877686/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= +github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/segmentio/encoding v0.1.11/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/segmentio/encoding v0.1.15/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= +github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.20.12/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4= +github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= @@ -1547,6 +2627,7 @@ github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9A github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= @@ -1556,21 +2637,27 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= +github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= +github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= -github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= @@ -1584,77 +2671,111 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= +github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g= +github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk= +github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= -github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= +github.com/tidwall/btree v1.3.1/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.1/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tron-us/go-btfs-common v0.2.11/go.mod h1:9ND33JahGMg52sCC2/gO5DakLsd1Pg2lVe2CihW7lBE= -github.com/tron-us/go-btfs-common v0.3.7/go.mod h1:FbYoo6ZrtnJH3TKdyJTGQrUP2rbwNVATQpxplwaYQ/c= -github.com/tron-us/go-btfs-common v0.7.0/go.mod h1:0T4AmMiAWKkT6qgi/QSONy/KlX2OeKiAcYOFMwVo97I= -github.com/tron-us/go-btfs-common v0.7.10 h1:3w9Ui44rNs6uiofZn0jFc4hkClGbaRqPU1KAlQArm4Q= -github.com/tron-us/go-btfs-common v0.7.10/go.mod h1:xnIFfbMRS5VsF948fBHPcYIeYGZkQgaJ6NIEGIPfYUs= -github.com/tron-us/go-btfs-common v0.7.13 h1:LGppZsnJRsiZZy9wiqwbzjc4igz3HNWdmQVaijCUGO4= -github.com/tron-us/go-btfs-common v0.7.13/go.mod h1:xnIFfbMRS5VsF948fBHPcYIeYGZkQgaJ6NIEGIPfYUs= -github.com/tron-us/go-common/v2 v2.0.5/go.mod h1:GiKX9noBLHotkZAU+7ET4h7N0DYWnm3OcGHOFJg1Q68= -github.com/tron-us/go-common/v2 v2.1.1/go.mod h1:YIEJZF9Ph79g0zZWOvfNDtJhvO5OqSNPAb/TM1i+KvQ= -github.com/tron-us/go-common/v2 v2.1.9/go.mod h1:YIEJZF9Ph79g0zZWOvfNDtJhvO5OqSNPAb/TM1i+KvQ= -github.com/tron-us/go-common/v2 v2.2.1/go.mod h1:YIEJZF9Ph79g0zZWOvfNDtJhvO5OqSNPAb/TM1i+KvQ= -github.com/tron-us/go-common/v2 v2.3.0/go.mod h1:/ktTJfsQWnrtSsoAvT3ybJR1nw7qMSEX+dcDxcv0xro= -github.com/tron-us/protobuf v1.3.4/go.mod h1:INMJF54ZV6c8ZMc3imHsMl1kqIpe4VnbCUK4zYcVHqE= -github.com/tron-us/protobuf v1.3.7 h1:nYnRqyiyEHK5YzQT0DScL8W65X6py+F9xDnMZx63qaY= -github.com/tron-us/protobuf v1.3.7/go.mod h1:INMJF54ZV6c8ZMc3imHsMl1kqIpe4VnbCUK4zYcVHqE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip32 v0.0.0-20170922074101-2c9cfd177564/go.mod h1:0/YuQQF676+d4CMNclTqGUam1EDwz0B8o03K9pQqA3c= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/bufpool v0.1.5/go.mod h1:fL9i/PRTuS7AELqAHwSU1Zf1c70xhkhGe/cD5ud9pJk= @@ -1663,22 +2784,27 @@ github.com/vmihailenco/msgpack/v4 v4.3.5/go.mod h1:DuaveEe48abshDmz5UBKyZ+yDugva github.com/vmihailenco/msgpack/v4 v4.3.7/go.mod h1:Ii+PksJlvFT5ZRcB/4YLAInMIp6a0WOCm0L3BU0aNG4= github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser v0.1.0/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= +github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= +github.com/warpfork/go-testmark v0.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= +github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830 h1:8kxMKmKzXXL4Ru1nyhvdms/JjWt+3YLpvRb/bAjO/y0= github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4= github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= -github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 h1:WXhVOwj2USAXB5oMDwRl3piOux2XMV9TANaYxXHdkoE= +github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= +github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20200402171437-3d27c146c105 h1:Sh6UG5dW5xW8Ek2CtRGq4ipdEvvx9hOyBJjEGyTYDl0= -github.com/whyrusleeping/cbor-gen v0.0.0-20200402171437-3d27c146c105/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d h1:wSxKhvbN7kUoP0sfRS+w2tWr45qlU8409i94hHLOT8w= github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 h1:bsUlNhdmbtlfdLVXAVfuvKQ01RnWAM09TVrJkI7NZs4= +github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= @@ -1686,34 +2812,54 @@ github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDbeKFZInPUrAG+bjuJmUXONGdEFW7XL0SpTY1y4= -github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible/go.mod h1:dRWHHvc4HDQSHh9gbKEBbUZ+f2Q8iZTPG3UOGYODxSQ= -github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI= -github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI= github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1/go.mod h1:tKH72zYNt/exx6/5IQO6L9LoQ0rEjd5SbbWaDTs9Zso= -github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA= github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 h1:Y1/FEOpaCpD21WxrmfeIYCFPuVPRCY2XZTWzTNHGw30= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs= +github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b/go.mod h1:xT1Y5p2JR2PfSZihE0s4mjdJaRGp1waCTf5JzhQLBck= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= -github.com/whyrusleeping/yamux v1.1.5/go.mod h1:E8LnQQ8HKx5KD29HZFUwM1PxCOdPRzGwur1mcYhXcD8= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/willf/bloom v0.0.0-20170505221640-54e3b963ee16/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= +github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1721,51 +2867,80 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1/go.mod h1:2FmkXne0k9nkp27LD/m+uoh8dNlstsiCJ7PLc/S72aI= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= +go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8= +go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.8.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.8.0/go.mod h1:w8aZL87GMOvOBa2lU/JlVXE1q4chk/0FX+8ai4513bw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0/go.mod h1:twhIvtDQW2sWP1O2cT1N8nkSBgKCRZv2z6COTTBrf8Q= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/metric v0.38.1/go.mod h1:FwqNHD3I/5iX9pfrRGZIlYICrJv0rHEUl2Ln5vdIVnQ= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= +go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk= +go.opentelemetry.io/otel/sdk v1.8.0/go.mod h1:uPSfc+yfDH2StDM/Rm35WE8gXSNdvCg023J6HeGNO0c= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= +go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY= +go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.18.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/dig v1.9.0 h1:pJTDXKEhRqBI8W7rU7kwT5EgyRZuSMVSFcZolOvKK9U= -go.uber.org/dig v1.9.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= -go.uber.org/dig v1.10.0 h1:yLmDDj9/zuDjv3gz8GQGviXMs9TfysIUMUilCpgzUJY= -go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= -go.uber.org/fx v1.12.0 h1:+1+3Cz9M0dFMPy9SW9XUIUHye8bnPUm7q7DroNGWYG4= -go.uber.org/fx v1.12.0/go.mod h1:egT3Kyg1JFYQkvKLZ3EsykxkNrZxgXS+gKoKo7abERY= -go.uber.org/fx v1.13.1 h1:CFNTr1oin5OJ0VCZ8EycL3wzF29Jz2g0xe55RFsf2a4= -go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w= -go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= -go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= +go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= +go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= +go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= -golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1777,6 +2952,7 @@ golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1788,25 +2964,48 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1822,6 +3021,18 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= +golang.org/x/exp v0.0.0-20220328175248-053ad81199eb/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20220609121020-a51bd0440498/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1833,24 +3044,35 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1867,9 +3089,10 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1879,12 +3102,13 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1895,21 +3119,75 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210427231257-85d9c07bbe3a/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1917,21 +3195,48 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1942,7 +3247,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1963,16 +3268,23 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191126131656-8a8471f7e56d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1980,72 +3292,176 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -2059,17 +3475,15 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2082,23 +3496,56 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375 h1:SjQ2+AKWgZLc1xej6WSzL+Dfs5Uyd5xcZH1mGC411IA= -golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -2115,7 +3562,51 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2124,6 +3615,8 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -2148,10 +3641,108 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= +google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -2167,38 +3758,76 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= @@ -2214,15 +3843,18 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2230,40 +3862,143 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= -k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= -k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200318093247-d1ab8797c558/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.3/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.10.0/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU= +modernc.org/sqlite v1.13.0/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg= +modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f/go.mod h1:YT5XFRKOueohjppHO4cHb54eQlnaUGsZMHoryaCpNo4= +modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc= +modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE= +modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js= +modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +zombiezen.com/go/sqlite v0.2.0/go.mod h1:VyBqNtpcF4vdvYgdwTSHJlwxyvTYCDQAZM9/qmGPyLg= +zombiezen.com/go/sqlite v0.8.0/go.mod h1:EMNzBZwTS5Yg6nwujgJdEo0brNm2a6f8Y4zoGiWZ5RU= diff --git a/fuse/ipns/ipns_test.go b/fuse/ipns/ipns_test.go index 276f6a0dc..9ac110d60 100644 --- a/fuse/ipns/ipns_test.go +++ b/fuse/ipns/ipns_test.go @@ -66,10 +66,10 @@ func verifyFile(t *testing.T, path string, wantData []byte) { t.Fatal(err) } if len(isData) != len(wantData) { - t.Fatal("Data not equal - length check failed") + t.Fatal("data not equal - length check failed") } if !bytes.Equal(isData, wantData) { - t.Fatal("Data not equal") + t.Fatal("data not equal") } } @@ -328,7 +328,7 @@ func TestAppendFile(t *testing.T) { t.Fatal(err) } if !bytes.Equal(rbuf, data) { - t.Fatal("Data inconsistent!") + t.Fatal("data inconsistent!") } } @@ -458,7 +458,7 @@ func TestFSThrash(t *testing.T) { } if !bytes.Equal(data, out) { - t.Errorf("Data didn't match in %s: expected %v, got %v", name, data, out) + t.Errorf("data didn't match in %s: expected %v, got %v", name, data, out) } } } diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go index 7e92aa6bf..dc7451d42 100644 --- a/fuse/readonly/readonly_unix.go +++ b/fuse/readonly/readonly_unix.go @@ -272,7 +272,7 @@ func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadR if err != nil { return err } - // Data has a capacity of Size + // data has a capacity of Size buf := resp.Data[:int(req.Size)] n, err := io.ReadFull(r, buf) resp.Data = buf[:n] diff --git a/go.mod b/go.mod index 5417489f9..fbc52759f 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,12 @@ go 1.18 require ( bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 + github.com/aws/aws-sdk-go v1.45.2 github.com/bittorrent/go-btfs-api v0.5.0 github.com/bittorrent/go-btfs-chunker v0.4.0 github.com/bittorrent/go-btfs-cmds v0.3.0 github.com/bittorrent/go-btfs-common v0.9.0 - github.com/bittorrent/go-btfs-config v0.12.3 + github.com/bittorrent/go-btfs-config v0.13.0-pre2 github.com/bittorrent/go-btfs-files v0.3.1 github.com/bittorrent/go-btns v0.2.0 github.com/bittorrent/go-common/v2 v2.4.0 @@ -34,6 +35,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.3 github.com/google/uuid v1.3.0 + github.com/gorilla/mux v1.7.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/ip2location/ip2location-go/v9 v9.0.0 @@ -133,20 +135,20 @@ require ( ) require ( - crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 // indirect + crawshaw.io/sqlite v0.3.3-0.20220618202545-d1964889ea3c // indirect github.com/BurntSushi/toml v1.2.0 // indirect - github.com/RoaringBitmap/roaring v1.2.1 // indirect + github.com/RoaringBitmap/roaring v1.2.3 // indirect github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect github.com/alecthomas/atomic v0.1.0-alpha2 // indirect github.com/anacrolix/chansync v0.3.0 // indirect - github.com/anacrolix/dht/v2 v2.19.0 // indirect + github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444 // indirect github.com/anacrolix/envpprof v1.2.1 // indirect - github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 // indirect - github.com/anacrolix/go-libutp v1.2.0 // indirect - github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 // indirect + github.com/anacrolix/generics v0.0.0-20230428105757-683593396d68 // indirect + github.com/anacrolix/go-libutp v1.3.1 // indirect + github.com/anacrolix/log v0.14.0 // indirect github.com/anacrolix/missinggo v1.3.0 // indirect github.com/anacrolix/missinggo/perf v1.0.0 // indirect - github.com/anacrolix/missinggo/v2 v2.7.0 // indirect + github.com/anacrolix/missinggo/v2 v2.7.2-0.20230527121029-a582b4f397b9 // indirect github.com/anacrolix/mmsg v1.0.0 // indirect github.com/anacrolix/multiless v0.3.0 // indirect github.com/anacrolix/stm v0.4.0 // indirect @@ -179,6 +181,7 @@ require ( github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect github.com/ipld/edelweiss v0.2.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/libp2p/go-libp2p-core v0.20.1 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.0 // indirect @@ -195,7 +198,7 @@ require ( github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect github.com/pion/datachannel v1.5.2 // indirect - github.com/pion/dtls/v2 v2.1.5 // indirect + github.com/pion/dtls/v2 v2.2.4 // indirect github.com/pion/ice/v2 v2.2.6 // indirect github.com/pion/interceptor v0.1.11 // indirect github.com/pion/logging v0.2.2 // indirect @@ -208,14 +211,15 @@ require ( github.com/pion/srtp/v2 v2.0.9 // indirect github.com/pion/stun v0.3.5 // indirect github.com/pion/transport v0.13.1 // indirect + github.com/pion/transport/v2 v2.0.0 // indirect github.com/pion/turn/v2 v2.0.8 // indirect - github.com/pion/udp v0.1.1 // indirect + github.com/pion/udp v0.1.4 // indirect github.com/pion/webrtc/v3 v3.1.42 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/tidwall/btree v1.3.1 // indirect + github.com/tidwall/btree v1.6.0 // indirect github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect go.etcd.io/bbolt v1.3.6 // indirect @@ -233,7 +237,7 @@ require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/Stebalien/go-bitfield v0.0.1 // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect - github.com/anacrolix/torrent v1.47.0 + github.com/anacrolix/torrent v1.52.5 github.com/andybalholm/brotli v1.0.4 // indirect github.com/benbjohnson/clock v1.3.0 github.com/beorn7/perks v1.0.1 // indirect @@ -281,7 +285,7 @@ require ( github.com/ipld/go-ipld-prime v0.19.0 github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect + github.com/json-iterator/go v1.1.12 github.com/kisielk/errcheck v1.5.0 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect @@ -324,7 +328,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/rs/cors v1.7.0 // indirect + github.com/rs/cors v1.7.0 github.com/segmentio/encoding v0.3.6 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect @@ -334,7 +338,7 @@ require ( github.com/tklauser/numcpus v0.2.2 // indirect github.com/ulikunitz/xz v0.5.6 // indirect github.com/vmihailenco/bufpool v0.1.11 // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect + github.com/vmihailenco/msgpack/v4 v4.3.12 github.com/vmihailenco/tagparser v0.1.2 // indirect github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect diff --git a/go.sum b/go.sum index 3b0cea235..1229c7e8e 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797 h1:yDf7ARQc637HoxDho7xjqdvO5ZA2Yb+xzv/fOnnvZzw= crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= -crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 h1:fILCBBFnjnrQ0whVJlGhfv1E/QiaFDNtGFBObEVRnYg= -crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= +crawshaw.io/sqlite v0.3.3-0.20220618202545-d1964889ea3c h1:wvzox0eLO6CKQAMcOqz7oH3UFqMpMmK7kwmwV+22HIs= +crawshaw.io/sqlite v0.3.3-0.20220618202545-d1964889ea3c/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -83,8 +83,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= -github.com/RoaringBitmap/roaring v1.2.1 h1:58/LJlg/81wfEHd5L9qsHduznOIhyv4qb1yWcSvVq9A= -github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= +github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY= +github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= @@ -114,23 +114,23 @@ github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5 github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= github.com/anacrolix/chansync v0.3.0 h1:lRu9tbeuw3wl+PhMu/r+JJCRu5ArFXIluOgdF0ao6/U= github.com/anacrolix/chansync v0.3.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= -github.com/anacrolix/dht/v2 v2.19.0 h1:A9oMHWRGbLmCyx1JlYzg79bDrur8V60+0ts8ZwEVYt4= -github.com/anacrolix/dht/v2 v2.19.0/go.mod h1:0h83KnnAQ2AUYhpQ/CkoZP45K41pjDAlPR9zGHgFjQE= +github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444 h1:8V0K09lrGoeT2KRJNOtspA7q+OMxGwQqK/Ug0IiaaRE= +github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444/go.mod h1:MctKM1HS5YYDb3F30NGJxLE+QPuqWoT5ReW/4jt8xew= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= github.com/anacrolix/envpprof v1.2.1 h1:25TJe6t/i0AfzzldiGFKCpD+s+dk8lONBcacJZB2rdE= github.com/anacrolix/envpprof v1.2.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= -github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 h1:k4/h2B1gGF+PJGyGHxs8nmHHt1pzWXZWBj6jn4OBlRc= -github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= -github.com/anacrolix/go-libutp v1.2.0 h1:sjxoB+/ARiKUR7IK/6wLWyADIBqGmu1fm0xo+8Yy7u0= -github.com/anacrolix/go-libutp v1.2.0/go.mod h1:RrJ3KcaDcf9Jqp33YL5V/5CBEc6xMc7aJL8wXfuWL50= +github.com/anacrolix/generics v0.0.0-20230428105757-683593396d68 h1:fyXlBfnlFzZSFckJ8QLb2lfmWfY++4RiUnae7ZMuv0A= +github.com/anacrolix/generics v0.0.0-20230428105757-683593396d68/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= +github.com/anacrolix/go-libutp v1.3.1 h1:idJzreNLl+hNjGC3ZnUOjujEaryeOGgkwHLqSGoige0= +github.com/anacrolix/go-libutp v1.3.1/go.mod h1:heF41EC8kN0qCLMokLBVkB8NXiLwx3t8R8810MTNI5o= github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.10.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= github.com/anacrolix/log v0.10.1-0.20220123034749-3920702c17f8/go.mod h1:GmnE2c0nvz8pOIPUSC9Rawgefy1sDXqposC2wgtBZE4= -github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 h1:bAgFzUxN1K3U8KwOzqCOhiygOr5NqYO3kNlV9tvp2Rc= -github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.14.0 h1:mYhTSemILe/Z8tIxbGdTIWWpPspI8W/fhZHpoFbDaL0= +github.com/anacrolix/log v0.14.0/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= @@ -144,8 +144,8 @@ github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5ur github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= github.com/anacrolix/missinggo/v2 v2.5.2/go.mod h1:yNvsLrtZYRYCOI+KRH/JM8TodHjtIE/bjOGhQaLOWIE= -github.com/anacrolix/missinggo/v2 v2.7.0 h1:4fzOAAn/VCvfWGviLmh64MPMttrlYew81JdPO7nSHvI= -github.com/anacrolix/missinggo/v2 v2.7.0/go.mod h1:2IZIvmRTizALNYFYXsPR7ofXPzJgyBpKZ4kMqMEICkI= +github.com/anacrolix/missinggo/v2 v2.7.2-0.20230527121029-a582b4f397b9 h1:W/oGeHhYwxueeiDjQfmK9G+X9M2xJgfTtow62v0TWAs= +github.com/anacrolix/missinggo/v2 v2.7.2-0.20230527121029-a582b4f397b9/go.mod h1:mIEtp9pgaXqt8VQ3NQxFOod/eQ1H0D1XsZzKUQfwtac= github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw= github.com/anacrolix/mmsg v1.0.0 h1:btC7YLjOn29aTUAExJiVUhQOuf/8rhm+/nWCMAnL3Hg= github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc= @@ -161,8 +161,8 @@ github.com/anacrolix/sync v0.4.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DC github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= -github.com/anacrolix/torrent v1.47.0 h1:aDUnhQZ8+kfStLICHiXOGGYVFgDENK+kz4q96linyRg= -github.com/anacrolix/torrent v1.47.0/go.mod h1:SYPxEUjMwqhDr3kWGzyQLkFMuAb1bgJ57JRMpuD3ZzE= +github.com/anacrolix/torrent v1.52.5 h1:jWowdx+EU6zFVfBwmnL0d3H4J6vTFEGOrHI35YdfIT8= +github.com/anacrolix/torrent v1.52.5/go.mod h1:CcM8oPMYye5J42cSqJrmUpqwRFgSsJQ1jCEHwygqnqQ= github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96 h1:QAVZ3pN/J4/UziniAhJR2OZ9Ox5kOY2053tBbbqUPYA= github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96/go.mod h1:Wa6n8cYIdaG35x15aH3Zy6d03f7P728QfdcDeD/IEOs= github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4= @@ -181,6 +181,8 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= +github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= @@ -206,8 +208,8 @@ github.com/bittorrent/go-btfs-cmds v0.3.0 h1:xpCBgk3zIm84Ne6EjeJgi8WLB5YJJUIFMjK github.com/bittorrent/go-btfs-cmds v0.3.0/go.mod h1:Fbac/Rou32G0jpoa6wLrNNDxcGOZbGfk+GiG0r3uEIU= github.com/bittorrent/go-btfs-common v0.9.0 h1:jHcFvYQmvmA4IdvVtkI5d/S/HW65Qz21C6oxeyK812w= github.com/bittorrent/go-btfs-common v0.9.0/go.mod h1:OG1n3DfcTxQYfLd5zco54LfL3IiDDaw3s7Igahu0Rj0= -github.com/bittorrent/go-btfs-config v0.12.3 h1:Zi/GTwHo/PJV+90+w45P7axkWsUpOB/XFhgvNk+TwRs= -github.com/bittorrent/go-btfs-config v0.12.3/go.mod h1:DNaHVC9wU84KLKoC4HkvdoFJKVZ7TF530qzfYu30fCI= +github.com/bittorrent/go-btfs-config v0.13.0-pre2 h1:sneJ4a5bA15ST9WRUR4G+1FuGUVmszSEuihCeKzeyNk= +github.com/bittorrent/go-btfs-config v0.13.0-pre2/go.mod h1:DNaHVC9wU84KLKoC4HkvdoFJKVZ7TF530qzfYu30fCI= github.com/bittorrent/go-btfs-files v0.3.0/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= github.com/bittorrent/go-btfs-files v0.3.1 h1:esq3j+6FtZ+SlaxKjVtiYgvXk/SWUiTcv0Q1MeJoPnQ= github.com/bittorrent/go-btfs-files v0.3.1/go.mod h1:ylMf73m6oK94hL7VPblY1ZZpePsr6XbPV4BaNUwGZR0= @@ -405,7 +407,7 @@ github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -601,6 +603,7 @@ github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKp github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -893,6 +896,10 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -1407,8 +1414,9 @@ github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= -github.com/pion/dtls/v2 v2.1.5 h1:jlh2vtIyUBShchoTDqpCCqiYCyRFJ/lvf/gQ8TALs+c= github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= +github.com/pion/dtls/v2 v2.2.4 h1:YSfYwDQgrxMYXLBc/m7PFY5BVtWlNm/DN4qoU2CbcWg= +github.com/pion/dtls/v2 v2.2.4/go.mod h1:WGKfxqhrddne4Kg3p11FUMJrynkOY4lb25zHNO49wuw= github.com/pion/ice/v2 v2.2.6 h1:R/vaLlI1J2gCx141L5PEwtuGAGcyS6e7E0hDeJFq5Ig= github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE= github.com/pion/interceptor v0.1.11 h1:00U6OlqxA3FFB50HSg25J/8cWi7P6FbSzw4eFn24Bvs= @@ -1437,10 +1445,13 @@ github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZ github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA= github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= +github.com/pion/transport/v2 v2.0.0 h1:bsMYyqHCbkvHwj+eNCFBuxtlKndKfyGI2vaQmM3fIE4= +github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc= github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw= github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= -github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/udp v0.1.4 h1:OowsTmu1Od3sD6i3fQUJxJn2fEvJO6L1TidgadtbTI8= +github.com/pion/udp v0.1.4/go.mod h1:G8LDo56HsFwC24LIcnT4YIDU5qcB6NepqqjP0keL2us= github.com/pion/webrtc/v3 v3.1.42 h1:wJEQFIXVanptnQcHOLTuIo4AtGB2+mG2x4OhIhnITOA= github.com/pion/webrtc/v3 v3.1.42/go.mod h1:ffD9DulDrPxyWvDPUIPAOSAWx9GUlOExiJPf7cCcMLA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1641,8 +1652,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= -github.com/tidwall/btree v1.3.1 h1:636+tdVDs8Hjcf35Di260W2xCW4KuoXOKyk9QWOvCpA= -github.com/tidwall/btree v1.3.1/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= +github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -1839,6 +1850,7 @@ golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1885,7 +1897,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1957,6 +1968,8 @@ golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -2086,13 +2099,18 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2103,6 +2121,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/s3/action/action.go b/s3/action/action.go new file mode 100644 index 000000000..c5d6ad30c --- /dev/null +++ b/s3/action/action.go @@ -0,0 +1,150 @@ +package action + +import ( + "github.com/bittorrent/go-btfs/s3/set" +) + +type Action string + +// ActionSet - set of actions. +// https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html#amazons3-actions-as-permissions +const ( + //--- bucket + + // CreateBucketAction - CreateBucket Rest API action. + CreateBucketAction = "s3:CreateBucket" + + // HeadBucketAction - HeadBucket Rest API action. + HeadBucketAction = "s3:HeadBucket" + + // ListBucketAction - ListBucket Rest API action. + ListBucketAction = "s3:ListBucket" + + // DeleteBucketAction - DeleteBucket Rest API action. + DeleteBucketAction = "s3:DeleteBucket" + + // PutBucketAclAction - PutBucketACL Rest API action. + PutBucketAclAction = "s3:PutBucketACL" + + // GetBucketAclAction - GetBucketACL Rest API action. + GetBucketAclAction = "s3:GetBucketACL" + + //--- object + + // ListObjectsAction - ListObjects Rest API action. + ListObjectsAction = "s3:ListObjects" + + // ListObjectsV2Action - ListObjectsV2 Rest API action. + ListObjectsV2Action = "s3:ListObjectsV2" + + // HeadObjectAction - HeadObject Rest API action. + HeadObjectAction = "s3:HeadObject" + + // PutObjectAction - PutObject Rest API action. + PutObjectAction = "s3:PutObject" + + // GetObjectAction - GetObject Rest API action. + GetObjectAction = "s3:GetObject" + + // CopyObjectAction - CopyObject Rest API action. + CopyObjectAction = "s3:CopyObject" + + // DeleteObjectAction - DeleteObject Rest API action. + DeleteObjectAction = "s3:DeleteObject" + + // DeleteObjectsAction - DeleteObjects Rest API action. + DeleteObjectsAction = "s3:DeleteObjects" + + //--- multipart upload + + // CreateMultipartUploadAction - CreateMultipartUpload Rest API action. + CreateMultipartUploadAction Action = "s3:CreateMultipartUpload" + + // AbortMultipartUploadAction - AbortMultipartUpload Rest API action. + AbortMultipartUploadAction Action = "s3:AbortMultipartUpload" + + // CompleteMultipartUploadAction - CompleteMultipartUpload Rest API action. + CompleteMultipartUploadAction Action = "s3:CompleteMultipartUpload" + + // UploadPartAction - UploadPartUpload Rest API action. + UploadPartAction Action = "s3:UploadPartUpload" +) + +// SupportedActions List of all supported actions. +var SupportedActions = map[Action]struct{}{ + CreateBucketAction: {}, + HeadBucketAction: {}, + ListBucketAction: {}, + DeleteBucketAction: {}, + PutBucketAclAction: {}, + GetBucketAclAction: {}, + + ListObjectsAction: {}, + ListObjectsV2Action: {}, + HeadObjectAction: {}, + PutObjectAction: {}, + GetObjectAction: {}, + CopyObjectAction: {}, + DeleteObjectAction: {}, + DeleteObjectsAction: {}, + + CreateMultipartUploadAction: {}, + AbortMultipartUploadAction: {}, + CompleteMultipartUploadAction: {}, + UploadPartAction: {}, +} + +// IsValid - checks if action is valid or not. +func (action Action) IsValid() bool { + for supAction := range SupportedActions { + if action.Match(supAction) { + return true + } + } + return false +} + +// Match - matches action name with action patter. +func (action Action) Match(a Action) bool { + return set.Match(string(action), string(a)) + //return true +} + +// List of all supported object actions. +var supportedBucketActions = map[Action]struct{}{ + CreateBucketAction: {}, + HeadBucketAction: {}, + ListBucketAction: {}, + DeleteBucketAction: {}, + PutBucketAclAction: {}, + GetBucketAclAction: {}, +} + +// IsBucketAction - returns whether action is bucket type or not. +func (action Action) IsBucketAction() bool { + _, ok := supportedBucketActions[action] + return ok +} + +// List of all supported object actions. +var supportedObjectActions = map[Action]struct{}{ + ListObjectsAction: {}, + ListObjectsV2Action: {}, + HeadObjectAction: {}, + PutObjectAction: {}, + GetObjectAction: {}, + CopyObjectAction: {}, + DeleteObjectAction: {}, + DeleteObjectsAction: {}, + + CreateMultipartUploadAction: {}, + AbortMultipartUploadAction: {}, + CompleteMultipartUploadAction: {}, + UploadPartAction: {}, +} + +// IsObjectAction - returns whether action is object type or not. +func (action Action) IsObjectAction() bool { + _, ok := supportedObjectActions[action] + return ok +} diff --git a/s3/api/contexts/contexts.go b/s3/api/contexts/contexts.go new file mode 100644 index 000000000..a475de203 --- /dev/null +++ b/s3/api/contexts/contexts.go @@ -0,0 +1,26 @@ +package contexts + +import ( + "context" + "net/http" +) + +type key string + +const ( + keyOfAccessKey key = "ctx-access-key" + keyOfHandleInf key = "ctx-handle-inf" + keyOfRequestArgs key = "ctx-request-args" +) + +func set(r *http.Request, k key, v any) { + ctx := context.WithValue(r.Context(), k, v) + nr := r.WithContext(ctx) + *r = *nr + return +} + +func get(r *http.Request, k key) (v any) { + v = r.Context().Value(k) + return +} diff --git a/s3/api/contexts/contexts_access_key.go b/s3/api/contexts/contexts_access_key.go new file mode 100644 index 000000000..cc4b6103f --- /dev/null +++ b/s3/api/contexts/contexts_access_key.go @@ -0,0 +1,16 @@ +package contexts + +import ( + "net/http" +) + +func SetAccessKey(r *http.Request, ack string) { + set(r, keyOfAccessKey, ack) + return +} + +func GetAccessKey(r *http.Request) (ack string) { + v := get(r, keyOfAccessKey) + ack, _ = v.(string) + return +} diff --git a/s3/api/contexts/contexts_handle_inf.go b/s3/api/contexts/contexts_handle_inf.go new file mode 100644 index 000000000..5204fee19 --- /dev/null +++ b/s3/api/contexts/contexts_handle_inf.go @@ -0,0 +1,25 @@ +package contexts + +import ( + "net/http" +) + +type handleInfo struct { + name string + args interface{} + err error +} + +func SetHandleInf(r *http.Request, name string, args interface{}, err error) { + set(r, keyOfHandleInf, handleInfo{name, args, err}) + return +} + +func GetHandleInf(r *http.Request) (name string, args interface{}, err error) { + v := get(r, keyOfHandleInf) + inf, _ := v.(handleInfo) + name = inf.name + err = inf.err + args = inf.args + return +} diff --git a/s3/api/handlers/handlers.go b/s3/api/handlers/handlers.go new file mode 100644 index 000000000..e1e3c2a2a --- /dev/null +++ b/s3/api/handlers/handlers.go @@ -0,0 +1,153 @@ +// Package handlers is an implementation of Handlerser +package handlers + +import ( + "github.com/bittorrent/go-btfs/s3/api/requests" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/api/services/accesskey" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/api/services/sign" + "github.com/bittorrent/go-btfs/s3/hash" + "net/http" + "runtime" + "strings" +) + +var _ Handlerser = (*Handlers)(nil) + +type Handlers struct { + headers http.Header + acksvc accesskey.Service + sigsvc sign.Service + objsvc object.Service +} + +func NewHandlers( + acksvc accesskey.Service, sigsvc sign.Service, objsvc object.Service, + options ...Option) (handlers *Handlers) { + handlers = &Handlers{ + headers: defaultHeaders, + acksvc: acksvc, + sigsvc: sigsvc, + objsvc: objsvc, + } + for _, option := range options { + option(handlers) + } + return +} + +// name returns name of the handler function +func (h *Handlers) name() string { + pc := make([]uintptr, 1) + runtime.Callers(3, pc) + f := runtime.FuncForPC(pc[0]) + ps := strings.Split(f.Name(), ".") + if len(ps) > 0 { + return ps[len(ps)-1] + } + return "UnknownHandler" +} + +// toResponseErr convert internal error to response error +func (h *Handlers) toResponseErr(err error) (rerr *responses.Error) { + switch err { + // Errors from requests + case requests.ErrBucketNameInvalid: + rerr = responses.ErrInvalidBucketName + case requests.ErrObjectNameInvalid: + rerr = responses.ErrInvalidObjectName + case requests.ErrObjectNameTooLong: + rerr = responses.ErrKeyTooLongError + case requests.ErrObjectNamePrefixSlash: + rerr = responses.ErrInvalidObjectNamePrefixSlash + case requests.ErrRegionUnsupported: + rerr = responses.ErrInvalidRegion + case requests.ErrACLUnsupported: + rerr = responses.ErrMalformedACLError + case requests.ErrContentMd5Invalid: + rerr = responses.ErrInvalidDigest + case requests.ErrChecksumSha256Invalid: + rerr = responses.ErrContentSHA256Mismatch + case requests.ErrContentLengthMissing: + rerr = responses.ErrMissingContentLength + case requests.ErrContentLengthTooSmall: + rerr = responses.ErrEntityTooSmall + case requests.ErrContentLengthTooLarge: + rerr = responses.ErrEntityTooLarge + case requests.ErrCopySrcInvalid: + rerr = responses.ErrInvalidCopySource + case requests.ErrCopyDestInvalid: + rerr = responses.ErrInvalidCopyDest + case requests.ErrDeletesCountInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrMaxKeysInvalid: + rerr = responses.ErrInvalidMaxKeys + case requests.ErrPrefixInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrMarkerInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrMarkerPrefixCombinationInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrContinuationTokenInvalid: + rerr = responses.ErrIncorrectContinuationToken + case requests.ErrStartAfterInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrPartNumberInvalid: + rerr = responses.ErrInvalidPartNumber + case requests.ErrPartsCountInvalid: + rerr = responses.ErrInvalidRequest + case requests.ErrPartInvalid: + rerr = responses.ErrInvalidPart + case requests.ErrPartOrderInvalid: + rerr = responses.ErrInvalidPartOrder + // Errors from Object service + case object.ErrBucketNotFound: + rerr = responses.ErrNoSuchBucket + case object.ErrBucketNotEmpty: + rerr = responses.ErrBucketNotEmpty + case object.ErrObjectNotFound: + rerr = responses.ErrNoSuchKey + case object.ErrUploadNotFound: + rerr = responses.ErrNoSuchUpload + case object.ErrBucketAlreadyExists: + rerr = responses.ErrBucketAlreadyExists + case object.ErrNotAllowed: + rerr = responses.ErrAccessDenied + case object.ErrPartNotExists: + rerr = responses.ErrInvalidPart + case object.ErrPartETagNotMatch: + rerr = responses.ErrInvalidPart + case object.ErrPartTooSmall: + rerr = responses.ErrEntityTooSmall + case object.ErrCanceled: + rerr = responses.ErrClientDisconnected + case object.ErrTimout: + rerr = responses.ErrOperationTimedOut + // Others + default: + switch nerr := err.(type) { + case requests.ErrFailedParseValue: + rerr = responses.ErrInvalidRequest + case requests.ErrFailedDecodeXML: + rerr = responses.ErrMalformedXML + case requests.ErrMissingRequiredParam: + rerr = responses.ErrInvalidRequest + case requests.ErrWithUnsupportedParam: + rerr = responses.ErrNotImplemented + case hash.SHA256Mismatch: + rerr = responses.ErrContentSHA256Mismatch + case hash.BadDigest: + rerr = responses.ErrBadDigest + case hash.ErrSizeMismatch: + if nerr.Got < nerr.Want { + rerr = responses.ErrIncompleteBody + } else { + rerr = responses.ErrMissingContentLength + } + default: + rerr = responses.ErrInternalError + } + } + return +} diff --git a/s3/api/handlers/handlers_bucket.go b/s3/api/handlers/handlers_bucket.go new file mode 100644 index 000000000..4389d0e92 --- /dev/null +++ b/s3/api/handlers/handlers_bucket.go @@ -0,0 +1,147 @@ +package handlers + +import ( + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/requests" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "net/http" +) + +func (h *Handlers) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { + var args *object.CreateBucketArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseCreateBucketRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + buc, err := h.objsvc.CreateBucket(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteCreateBucketResponse(w, r, buc) + return +} + +func (h *Handlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { + var args *object.GetBucketArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseHeadBucketRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + buc, err := h.objsvc.GetBucket(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteHeadBucketResponse(w, r, buc) + return +} + +func (h *Handlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { + var args *object.DeleteBucketArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseDeleteBucketRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + err = h.objsvc.DeleteBucket(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteDeleteBucketResponse(w) + return +} + +func (h *Handlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { + var args *object.ListBucketsArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseListBucketsRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + list, err := h.objsvc.ListBuckets(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteListBucketsResponse(w, r, list) + return +} + +func (h *Handlers) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) { + var args *object.PutBucketACLArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParsePutBucketAclRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + err = h.objsvc.PutBucketACL(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WritePutBucketAclResponse(w, r) + return +} + +func (h *Handlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { + var args *object.GetBucketACLArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseGetBucketACLRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + acl, err := h.objsvc.GetBucketACL(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteGetBucketACLResponse(w, r, acl) + return +} diff --git a/s3/api/handlers/handlers_middlewares.go b/s3/api/handlers/handlers_middlewares.go new file mode 100644 index 000000000..a62b68c15 --- /dev/null +++ b/s3/api/handlers/handlers_middlewares.go @@ -0,0 +1,85 @@ +package handlers + +import ( + "errors" + "fmt" + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/api/services/accesskey" + "github.com/bittorrent/go-btfs/s3/consts" + rscors "github.com/rs/cors" + "net/http" + "strconv" + "time" +) + +func (h *Handlers) Cors(handler http.Handler) http.Handler { + headers := h.headers + cred := headers.Get(consts.AccessControlAllowCredentials) == "true" + maxAge, _ := strconv.Atoi(headers.Get(consts.AccessControlMaxAge)) + ch := rscors.New(rscors.Options{ + AllowedOrigins: headers.Values(consts.AccessControlAllowOrigin), + AllowedMethods: headers.Values(consts.AccessControlAllowMethods), + AllowedHeaders: headers.Values(consts.AccessControlAllowHeaders), + ExposedHeaders: headers.Values(consts.AccessControlExposeHeaders), + MaxAge: maxAge, + AllowCredentials: cred, + }) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // add all user headers + for k, v := range h.headers { + w.Header()[k] = v + } + // next + ch.Handler(handler).ServeHTTP(w, r) + }) +} + +func (h *Handlers) Log(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + handler.ServeHTTP(w, r) + hname, args, herr := contexts.GetHandleInf(r) + end := time.Now() + ela := end.Sub(start) + fmt.Printf("s3-api: | %s | <%-4s> | %s | %s | %+v | %v | %s \n", end.Format(time.RFC3339), r.Method, r.URL, hname, args, herr, ela) + }) +} + +func (h *Handlers) Sign(handler http.Handler) http.Handler { + h.sigsvc.SetSecretGetter(func(key string) (secret string, exists, enable bool, err error) { + ack, err := h.acksvc.Get(key) + if errors.Is(err, accesskey.ErrNotFound) { + exists = false + enable = true + err = nil + return + } + if err != nil { + return + } + exists = true + secret = ack.Secret + enable = ack.Enable + return + }) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var err *responses.Error + defer func() { + if err != nil { + contexts.SetHandleInf(r, "Sign", err, nil) + } + }() + + ack, err := h.sigsvc.VerifyRequestSignature(r) + if err != nil { + responses.WriteErrorResponse(w, r, err) + return + } + + contexts.SetAccessKey(r, ack) + + handler.ServeHTTP(w, r) + }) +} diff --git a/s3/api/handlers/handlers_multipart.go b/s3/api/handlers/handlers_multipart.go new file mode 100644 index 000000000..c88c5f802 --- /dev/null +++ b/s3/api/handlers/handlers_multipart.go @@ -0,0 +1,101 @@ +package handlers + +import ( + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/requests" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "net/http" +) + +func (h *Handlers) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { + var args *object.CreateMultipartUploadArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseCreateMultipartUploadRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + multipart, err := h.objsvc.CreateMultipartUpload(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteCreateMultipartUploadResponse(w, r, multipart) + return +} + +func (h *Handlers) UploadPartHandler(w http.ResponseWriter, r *http.Request) { + var args *object.UploadPartArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseUploadPartRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + part, err := h.objsvc.UploadPart(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteUploadPartResponse(w, r, part) + return +} + +func (h *Handlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { + var args *object.AbortMultipartUploadArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseAbortMultipartUploadRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + err = h.objsvc.AbortMultipartUpload(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteAbortMultipartUploadResponse(w, r) + return +} + +func (h *Handlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { + var args *object.CompleteMultipartUploadArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseCompleteMultipartUploadRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + obj, err := h.objsvc.CompleteMultiPartUpload(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteCompleteMultipartUploadResponse(w, r, obj) + return +} diff --git a/s3/api/handlers/handlers_object.go b/s3/api/handlers/handlers_object.go new file mode 100644 index 000000000..eb4abd0cd --- /dev/null +++ b/s3/api/handlers/handlers_object.go @@ -0,0 +1,224 @@ +package handlers + +import ( + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/requests" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "net/http" +) + +// PutObjectHandler . +func (h *Handlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) { + var args *object.PutObjectArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParsePutObjectRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + obj, err := h.objsvc.PutObject(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WritePutObjectResponse(w, r, obj) + return +} + +// CopyObjectHandler . +func (h *Handlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { + var args *object.CopyObjectArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseCopyObjectRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + obj, err := h.objsvc.CopyObject(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteCopyObjectResponse(w, r, obj) + return +} + +// HeadObjectHandler . +func (h *Handlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { + var args *object.GetObjectArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseHeadObjectRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + obj, _, err := h.objsvc.GetObject(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteHeadObjectResponse(w, r, obj) + return +} + +// GetObjectHandler . +func (h *Handlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) { + var args *object.GetObjectArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseGetObjectRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + obj, body, err := h.objsvc.GetObject(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteGetObjectResponse(w, r, obj, body) + return +} + +// DeleteObjectHandler . +func (h *Handlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { + var args *object.DeleteObjectArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseDeleteObjectRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + err = h.objsvc.DeleteObject(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteDeleteObjectResponse(w, r, nil) + return +} + +// DeleteObjectsHandler . +func (h *Handlers) DeleteObjectsHandler(w http.ResponseWriter, r *http.Request) { + var args *object.DeleteObjectsArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseDeleteObjectsRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + deletes, err := h.objsvc.DeleteObjects(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteDeleteObjectsResponse(w, r, h.toResponseErr, deletes) + return +} + +// ListObjectsHandler . +func (h *Handlers) ListObjectsHandler(w http.ResponseWriter, r *http.Request) { + var args *object.ListObjectsArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseListObjectsRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + list, err := h.objsvc.ListObjects(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteListObjectsResponse(w, r, list) + return +} + +// ListObjectsV2Handler . +func (h *Handlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { + var args *object.ListObjectsV2Args + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseListObjectsV2Request(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + list, err := h.objsvc.ListObjectsV2(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteListObjectsV2Response(w, r, list) + return +} + +// GetObjectACLHandler - GET Object ACL +func (h *Handlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { + var args *object.GetObjectACLArgs + var err error + defer func() { + contexts.SetHandleInf(r, h.name(), args, err) + }() + + args, err = requests.ParseGetObjectACLRequest(r) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + acl, err := h.objsvc.GetObjectACL(r.Context(), args) + if err != nil { + responses.WriteErrorResponse(w, r, h.toResponseErr(err)) + return + } + + responses.WriteGetObjectACLResponse(w, r, acl) + return +} diff --git a/s3/api/handlers/options.go b/s3/api/handlers/options.go new file mode 100644 index 000000000..0abda6c36 --- /dev/null +++ b/s3/api/handlers/options.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" +) + +var defaultCorsMethods = []string{ + http.MethodGet, + http.MethodPut, + http.MethodHead, + http.MethodPost, + http.MethodDelete, + http.MethodOptions, + http.MethodPatch, +} + +var defaultCorsHeaders = []string{ + consts.Date, + consts.ETag, + consts.ServerInfo, + consts.Connection, + consts.AcceptRanges, + consts.ContentRange, + consts.ContentEncoding, + consts.ContentLength, + consts.ContentType, + consts.ContentMD5, + consts.ContentDisposition, + consts.LastModified, + consts.ContentLanguage, + consts.CacheControl, + consts.Location, + consts.RetryAfter, + consts.AmzBucketRegion, + consts.Expires, + consts.Authorization, + consts.Action, + consts.XRequestWith, + consts.Range, + consts.UserAgent, + consts.Cid, + "Amz-*", + "amz-*", + "X-Amz*", + "x-amz*", + "*", +} + +const defaultCorsMaxAge = "36000" + +var defaultHeaders = map[string][]string{ + consts.AccessControlAllowOrigin: {"*"}, + consts.AccessControlAllowMethods: defaultCorsMethods, + consts.AccessControlAllowHeaders: defaultCorsHeaders, + consts.AccessControlExposeHeaders: defaultCorsHeaders, + consts.AccessControlAllowCredentials: {"true"}, + consts.AccessControlMaxAge: {defaultCorsMaxAge}, +} + +type Option func(handlers *Handlers) + +func WithHeaders(headers map[string][]string) Option { + return func(handlers *Handlers) { + if headers != nil { + handlers.headers = headers + } + } +} diff --git a/s3/api/handlers/proto.go b/s3/api/handlers/proto.go new file mode 100644 index 000000000..4f55ff5f6 --- /dev/null +++ b/s3/api/handlers/proto.go @@ -0,0 +1,41 @@ +package handlers + +import ( + "net/http" +) + +type Handlerser interface { + // Middlewares + + Cors(handler http.Handler) http.Handler + Sign(handler http.Handler) http.Handler + Log(handler http.Handler) http.Handler + + // Bucket + + CreateBucketHandler(w http.ResponseWriter, r *http.Request) + HeadBucketHandler(w http.ResponseWriter, r *http.Request) + DeleteBucketHandler(w http.ResponseWriter, r *http.Request) + ListBucketsHandler(w http.ResponseWriter, r *http.Request) + PutBucketACLHandler(w http.ResponseWriter, r *http.Request) + GetBucketACLHandler(w http.ResponseWriter, r *http.Request) + + // Object + + PutObjectHandler(w http.ResponseWriter, r *http.Request) + CopyObjectHandler(w http.ResponseWriter, r *http.Request) + HeadObjectHandler(w http.ResponseWriter, r *http.Request) + GetObjectHandler(w http.ResponseWriter, r *http.Request) + DeleteObjectHandler(w http.ResponseWriter, r *http.Request) + DeleteObjectsHandler(w http.ResponseWriter, r *http.Request) + ListObjectsHandler(w http.ResponseWriter, r *http.Request) + ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) + GetObjectACLHandler(w http.ResponseWriter, r *http.Request) + + // Multipart + + CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Request) + UploadPartHandler(w http.ResponseWriter, r *http.Request) + AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) + CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) +} diff --git a/s3/api/providers/btfs_api.go b/s3/api/providers/btfs_api.go new file mode 100644 index 000000000..66f8d55ee --- /dev/null +++ b/s3/api/providers/btfs_api.go @@ -0,0 +1,97 @@ +package providers + +import ( + shell "github.com/bittorrent/go-btfs-api" + "github.com/mitchellh/go-homedir" + "io" + "net/http" + "os" + "path" + "strings" + "time" +) + +var _ FileStorer = (*BtfsAPI)(nil) + +type BtfsAPI struct { + shell *shell.Shell + headerTimout time.Duration + timeout time.Duration + endpointUrl string +} + +func NewBtfsAPI(options ...BtfsAPIOption) (api *BtfsAPI, err error) { + api = &BtfsAPI{ + headerTimout: defaultBtfsAPIResponseHeaderTimeout, + timeout: defaultBtfsAPITimeout, + endpointUrl: defaultBtfsAPIEndpointUrl, + } + for _, option := range options { + option(api) + } + + if api.endpointUrl == "" { + api.endpointUrl, err = api.getLocalUrl() + if err != nil { + return + } + } + + client := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DisableKeepAlives: true, + ResponseHeaderTimeout: api.headerTimout, + }, + Timeout: api.timeout, + } + + api.shell = shell.NewShellWithClient( + api.endpointUrl, client, + ) + + return +} + +func (api *BtfsAPI) Store(r io.Reader) (id string, err error) { + id, err = api.shell.Add(r, shell.Pin(true)) + return +} + +func (api *BtfsAPI) Remove(id string) (err error) { + err = api.shell.Unpin(id) + return +} + +func (api *BtfsAPI) Cat(id string) (rc io.ReadCloser, err error) { + rc, err = api.shell.Cat(id) + return +} + +func (api *BtfsAPI) getLocalUrl() (url string, err error) { + baseDir := os.Getenv(shell.EnvDir) + if baseDir == "" { + baseDir = shell.DefaultPathRoot + } + + baseDir, err = homedir.Expand(baseDir) + if err != nil { + return + } + + apiFile := path.Join(baseDir, shell.DefaultApiFile) + + _, err = os.Stat(apiFile) + if err != nil { + return + } + + bs, err := os.ReadFile(apiFile) + if err != nil { + return + } + + url = strings.TrimSpace(string(bs)) + return + +} diff --git a/s3/api/providers/btfs_api_options.go b/s3/api/providers/btfs_api_options.go new file mode 100644 index 000000000..448689342 --- /dev/null +++ b/s3/api/providers/btfs_api_options.go @@ -0,0 +1,29 @@ +package providers + +import "time" + +type BtfsAPIOption func(api *BtfsAPI) + +const ( + defaultBtfsAPIEndpointUrl = "" + defaultBtfsAPITimeout = 20 * time.Minute + defaultBtfsAPIResponseHeaderTimeout = 1 * time.Minute +) + +func BtfsAPIWithTimeout(timeout time.Duration) BtfsAPIOption { + return func(api *BtfsAPI) { + api.timeout = timeout + } +} + +func BtfsAPIWithBtfsAPIHeaderTimeout(timeout time.Duration) BtfsAPIOption { + return func(api *BtfsAPI) { + api.headerTimout = timeout + } +} + +func BtfsAPIWithEndpointUrl(url string) BtfsAPIOption { + return func(api *BtfsAPI) { + api.endpointUrl = url + } +} diff --git a/s3/api/providers/proto.go b/s3/api/providers/proto.go new file mode 100644 index 000000000..661d36d5e --- /dev/null +++ b/s3/api/providers/proto.go @@ -0,0 +1,31 @@ +package providers + +import ( + "errors" + "io" +) + +var ( + ErrStateStoreNotFound = errors.New("not found in state store") + ErrFileStoreNotFound = errors.New("not found in file store") +) + +type Providerser interface { + FileStore() FileStorer + StateStore() StateStorer +} + +type FileStorer interface { + Store(r io.Reader) (id string, err error) + Remove(id string) (err error) + Cat(id string) (readCloser io.ReadCloser, err error) +} + +type StateStorer interface { + Get(key string, i interface{}) (err error) + Put(key string, i interface{}) (err error) + Delete(key string) (err error) + Iterate(prefix string, iterFunc StateStoreIterFunc) (err error) +} + +type StateStoreIterFunc func(key, value []byte) (stop bool, err error) diff --git a/s3/api/providers/providers.go b/s3/api/providers/providers.go new file mode 100644 index 000000000..f5d84b3ca --- /dev/null +++ b/s3/api/providers/providers.go @@ -0,0 +1,27 @@ +package providers + +var _ Providerser = (*Providers)(nil) + +type Providers struct { + stateStore StateStorer + fileStore FileStorer +} + +func NewProviders(stateStore StateStorer, fileStore FileStorer, options ...Option) (providers *Providers) { + providers = &Providers{ + stateStore: stateStore, + fileStore: fileStore, + } + for _, option := range options { + option(providers) + } + return +} + +func (p *Providers) StateStore() StateStorer { + return p.stateStore +} + +func (p *Providers) FileStore() FileStorer { + return p.fileStore +} diff --git a/s3/api/providers/providers_options.go b/s3/api/providers/providers_options.go new file mode 100644 index 000000000..f8792d8b7 --- /dev/null +++ b/s3/api/providers/providers_options.go @@ -0,0 +1,3 @@ +package providers + +type Option func(providers *Providers) diff --git a/s3/api/providers/storage_state_store_proxy.go b/s3/api/providers/storage_state_store_proxy.go new file mode 100644 index 000000000..58dacd35f --- /dev/null +++ b/s3/api/providers/storage_state_store_proxy.go @@ -0,0 +1,42 @@ +package providers + +import ( + "errors" + "github.com/bittorrent/go-btfs/transaction/storage" +) + +var _ StateStorer = (*StorageStateStoreProxy)(nil) + +type StorageStateStoreProxy struct { + to storage.StateStorer +} + +func NewStorageStateStoreProxy(to storage.StateStorer) *StorageStateStoreProxy { + return &StorageStateStoreProxy{ + to: to, + } +} + +func (s *StorageStateStoreProxy) Put(key string, val interface{}) (err error) { + return s.to.Put(key, val) +} + +func (s *StorageStateStoreProxy) Get(key string, i interface{}) (err error) { + err = s.to.Get(key, i) + if errors.Is(err, storage.ErrNotFound) { + err = ErrStateStoreNotFound + } + return +} + +func (s *StorageStateStoreProxy) Delete(key string) (err error) { + err = s.to.Delete(key) + if errors.Is(err, storage.ErrNotFound) { + err = ErrStateStoreNotFound + } + return +} + +func (s *StorageStateStoreProxy) Iterate(prefix string, iterFunc StateStoreIterFunc) (err error) { + return s.to.Iterate(prefix, storage.StateIterFunc(iterFunc)) +} diff --git a/s3/api/requests/errors.go b/s3/api/requests/errors.go new file mode 100644 index 000000000..c26b3be58 --- /dev/null +++ b/s3/api/requests/errors.go @@ -0,0 +1,103 @@ +package requests + +import ( + "errors" + "fmt" + "reflect" +) + +var ( + ErrBucketNameInvalid = errors.New("the bucket name is invalid") + ErrObjectNameInvalid = errors.New("the object name is invalid") + ErrObjectNameTooLong = errors.New("the object name cannot be longer than 1024 characters") + ErrObjectNamePrefixSlash = errors.New("the object name cannot start with slash") + ErrRegionUnsupported = errors.New("the location is not supported by this server") + ErrACLUnsupported = errors.New("the ACL is not supported by this server") + ErrContentMd5Invalid = errors.New("the content md5 is invalid") + ErrChecksumSha256Invalid = errors.New("the checksum-sha256 is invalid") + ErrContentLengthMissing = errors.New("the content-length is missing") + ErrContentLengthTooSmall = errors.New("the content-length is too small") + ErrContentLengthTooLarge = errors.New("the content-length is too large") + ErrCopySrcInvalid = errors.New("the copy-source is invalid") + ErrCopyDestInvalid = errors.New("the copy-destination is invalid") + ErrDeletesCountInvalid = errors.New("the deletes-count is invalid") + ErrMaxKeysInvalid = errors.New("the max-keys is invalid") + ErrEncodingTypeInvalid = errors.New("the encoding-type is invalid") + ErrPrefixInvalid = errors.New("the prefix is invalid") + ErrMarkerInvalid = errors.New("the marker is invalid") + ErrMarkerPrefixCombinationInvalid = errors.New("the marker-prefix combination is invalid") + ErrContinuationTokenInvalid = errors.New("the continuation-token is invalid") + ErrStartAfterInvalid = errors.New("the start-after is invalid") + ErrPartNumberInvalid = errors.New("the part-number is invalid") + ErrPartsCountInvalid = errors.New("the parts-count is invalid") + ErrPartInvalid = errors.New("the part is invalid") + ErrPartOrderInvalid = errors.New("the part-order is invalid") +) + +// ErrInvalidInputValue . +type ErrInvalidInputValue struct { + msg string +} + +func (err ErrInvalidInputValue) Error() string { + return fmt.Sprintf("invalid input value: %s", err.msg) +} + +// ErrTypeNotSet . +type ErrTypeNotSet struct { + typ reflect.Type +} + +func (err ErrTypeNotSet) Error() string { + return fmt.Sprintf("type <%s> not set", err.typ.String()) +} + +// ErrPayloadNotSet . +type ErrPayloadNotSet struct { + el string +} + +func (err ErrPayloadNotSet) Error() string { + return fmt.Sprintf("payload <%s> not set", err.el) +} + +// ErrFailedDecodeXML . +type ErrFailedDecodeXML struct { + err error +} + +func (err ErrFailedDecodeXML) Error() string { + return fmt.Sprintf("decode xml: %v", err.err) +} + +// ErrWithUnsupportedParam . +type ErrWithUnsupportedParam struct { + param string +} + +func (err ErrWithUnsupportedParam) Error() string { + return fmt.Sprintf("param %s is unsported", err.param) +} + +// ErrFailedParseValue . +type ErrFailedParseValue struct { + name string + err error +} + +func (err ErrFailedParseValue) Name() string { + return err.name +} + +func (err ErrFailedParseValue) Error() string { + return fmt.Sprintf("parse <%s> value: %v", err.name, err.err) +} + +// ErrMissingRequiredParam . +type ErrMissingRequiredParam struct { + param string +} + +func (err ErrMissingRequiredParam) Error() string { + return fmt.Sprintf("missing required param <%s>", err.param) +} diff --git a/s3/api/requests/parsers.go b/s3/api/requests/parsers.go new file mode 100644 index 000000000..37c6f89c4 --- /dev/null +++ b/s3/api/requests/parsers.go @@ -0,0 +1,286 @@ +package requests + +import ( + "encoding/base64" + "encoding/xml" + "errors" + "github.com/aws/aws-sdk-go/private/protocol" + "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" + "github.com/gorilla/mux" + "net/http" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +type fields map[string]bool + +func ParseLocation(r *http.Request, input interface{}, supports fields) (err error) { + inv, err := valueOf(input) + if err != nil { + return + } + err = parseLocation(r, inv, supports) + return +} + +func ParseXMLBody(r *http.Request, input interface{}) (err error) { + inv, err := valueOf(input) + if err != nil { + return + } + pft, ok := getPayloadField(inv) + if !ok { + err = ErrPayloadNotSet{"field"} + return + } + ptyp := pft.Tag.Get("type") + if ptyp != "structure" { + err = ErrPayloadNotSet{"structure"} + return + } + decoder := xml.NewDecoder(r.Body) + err = xmlutil.UnmarshalXML(inv.Addr().Interface(), decoder, "") + if err != nil { + err = ErrFailedDecodeXML{err} + } + return +} + +func valueOf(input interface{}) (inv reflect.Value, err error) { + inv = reflect.Indirect(reflect.ValueOf(input)) + if !inv.IsValid() { + err = ErrInvalidInputValue{"input is nil"} + return + } + if inv.Kind() != reflect.Struct { + err = ErrInvalidInputValue{"input is not point to struct"} + } + return +} + +func parseLocation(r *http.Request, inv reflect.Value, supports fields) (err error) { + vars := mux.Vars(r) + headers := r.Header + query := r.URL.Query() + for i := 0; i < inv.NumField(); i++ { + fv := inv.Field(i) + ft := inv.Type().Field(i) + err = parseLocationField(vars, query, headers, fv, ft, supports) + if err != nil { + return + } + } + return +} + +func parseLocationField(vars map[string]string, query url.Values, headers http.Header, fv reflect.Value, + ft reflect.StructField, supports fields) (err error) { + if ft.Name[0:1] == strings.ToLower(ft.Name[0:1]) { + return + } + ftag := ft.Tag + loca := ftag.Get("location") + name := ftag.Get("locationName") + requ := ftag.Get("required") == "true" + supp := supports[ft.Name] + var ( + vals map[string]*string + isVals bool + val string + has bool + ) + switch loca { + case "querystring": + val, has = query.Get(name), query.Has(name) + case "uri": + val, has = vars[name] + case "header": + val, has = headers.Get(name), len(headers.Values(name)) > 0 + case "headers": + vals, has = getHeaderValues(headers, name) + isVals = true + default: + return + } + if !supp && has { + err = ErrWithUnsupportedParam{name} + return + } + if requ && !has { + err = ErrMissingRequiredParam{name} + return + } + if !has { + return + } + if isVals { + err = parseValues(vals, fv) + } else { + err = parseValue(val, fv, ftag) + if err != nil && !errors.As(err, new(ErrTypeNotSet)) { + err = ErrFailedParseValue{name, err} + } + } + return +} + +func getPayloadField(inv reflect.Value) (ft reflect.StructField, ok bool) { + mt, ok := inv.Type().FieldByName("_") + if !ok { + return + } + if mt.Tag.Get("nopayload") != "" { + return + } + pname := mt.Tag.Get("payload") + if pname == "" { + return + } + ft, ok = inv.Type().FieldByName(pname) + return +} + +func getHeaderValues(header http.Header, prefix string) (vals map[string]*string, has bool) { + defer func() { + has = len(vals) > 0 + }() + vals = make(map[string]*string) + if len(header) == 0 { + return + } + for key := range header { + if len(key) >= len(prefix) && strings.EqualFold(key[:len(prefix)], prefix) { + val := header.Get(key) + k := strings.ToLower(key[len(prefix):]) + vals[k] = &val + } + } + return +} + +func parseValues(values map[string]*string, fv reflect.Value) (err error) { + _, ok := fv.Interface().(map[string]*string) + if !ok { + err = ErrTypeNotSet{fv.Type()} + return + } + fv.Set(reflect.ValueOf(values)) + return +} + +func parseValue(value string, rv reflect.Value, tag reflect.StructTag) (err error) { + switch rv.Interface().(type) { + case *string: + rv.Set(reflect.ValueOf(&value)) + return + case []*string: + var val []*string + val, err = split(value) + if err != nil { + return + } + rv.Set(reflect.ValueOf(&val)) + return + case []byte: + var val []byte + val, err = base64.StdEncoding.DecodeString(value) + if err != nil { + return + } + rv.Set(reflect.ValueOf(val)) + return + case *bool: + var val bool + val, err = strconv.ParseBool(value) + if err != nil { + return + } + rv.Set(reflect.ValueOf(&val)) + return + case *int64: + var val int64 + val, err = strconv.ParseInt(value, 10, 64) + if err != nil { + return + } + rv.Set(reflect.ValueOf(&val)) + return + case *time.Time: + var val time.Time + format := getTimeFormat(tag) + val, err = protocol.ParseTime(format, value) + if err != nil { + return + } + rv.Set(reflect.ValueOf(&val)) + return + default: + err = ErrTypeNotSet{rv.Type()} + return + } +} + +func getTimeFormat(tag reflect.StructTag) (format string) { + format = tag.Get("timestampFormat") + if format != "" { + return + } + if tag.Get("location") == "querystring" { + format = protocol.ISO8601TimeFormatName + return + } + format = protocol.RFC822TimeFormatName + return +} + +func split(value string) (vals []*string, err error) { + pv := ' ' + start := 0 + quote := false + for i, v := range value { + opv := pv + pv = v + if quote { + if v == '"' && opv != '\\' { + quote = false + val := value[start : i+1] + val, err = strconv.Unquote(val) + if err != nil { + return + } + val = strings.TrimSpace(val) + vals = append(vals, &val) + start = i + 1 + } + continue + } + if v == '"' && opv != '\\' { + quote = true + continue + } + if v == ',' && opv == '"' { + start += 1 + continue + } + if v == ',' { + val := value[start:i] + val = strings.TrimSpace(val) + vals = append(vals, &val) + start = i + 1 + } + continue + } + if quote { + err = errors.New("unquote part") + return + } + if start < len(value) || pv == ',' { + val := value[start:] + val = strings.TrimSpace(val) + vals = append(vals, &val) + } + return +} diff --git a/s3/api/requests/parsers_bucket.go b/s3/api/requests/parsers_bucket.go new file mode 100644 index 000000000..a4d441038 --- /dev/null +++ b/s3/api/requests/parsers_bucket.go @@ -0,0 +1,122 @@ +package requests + +import ( + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "net/http" +) + +var createBucketSupports = fields{ + "ACL": true, + "Bucket": true, + "CreateBucketConfiguration": true, +} + +func ParseCreateBucketRequest(r *http.Request) (args *object.CreateBucketArgs, err error) { + var input s3.CreateBucketInput + err = ParseLocation(r, &input, createBucketSupports) + if err != nil { + return + } + args = &object.CreateBucketArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.ACL, err = ValidateBucketACL(input.ACL) + if err != nil { + return + } + args.Region, err = ValidateCreateBucketConfiguration(input.CreateBucketConfiguration) + return +} + +var headBucketSupports = fields{ + "Bucket": true, +} + +func ParseHeadBucketRequest(r *http.Request) (args *object.GetBucketArgs, err error) { + var input s3.HeadBucketInput + err = ParseLocation(r, &input, headBucketSupports) + if err != nil { + return + } + args = &object.GetBucketArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + return +} + +var deleteBucketSupports = fields{ + "Bucket": true, +} + +func ParseDeleteBucketRequest(r *http.Request) (args *object.DeleteBucketArgs, err error) { + var input s3.DeleteBucketInput + err = ParseLocation(r, &input, deleteBucketSupports) + if err != nil { + return + } + args = &object.DeleteBucketArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + return +} + +var listBucketsSupports = fields{} + +func ParseListBucketsRequest(r *http.Request) (args *object.ListBucketsArgs, err error) { + var input s3.ListBucketsInput + err = ParseLocation(r, input, listBucketsSupports) + if err != nil { + return + } + args = &object.ListBucketsArgs{ + UserId: contexts.GetAccessKey(r), + } + return +} + +var putBucketACLSupports = fields{ + "ACL": true, + "Bucket": true, +} + +func ParsePutBucketAclRequest(r *http.Request) (args *object.PutBucketACLArgs, err error) { + var input s3.PutBucketAclInput + err = ParseLocation(r, &input, putBucketACLSupports) + if err != nil { + return + } + args = &object.PutBucketACLArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.ACL, err = ValidateBucketACL(input.ACL) + return +} + +var getBucketACLSupports = fields{ + "Bucket": true, +} + +func ParseGetBucketACLRequest(r *http.Request) (args *object.GetBucketACLArgs, err error) { + var input s3.GetBucketAclInput + err = ParseLocation(r, &input, getBucketACLSupports) + if err != nil { + return + } + args = &object.GetBucketACLArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + return +} diff --git a/s3/api/requests/parsers_multipart.go b/s3/api/requests/parsers_multipart.go new file mode 100644 index 000000000..fc99de44c --- /dev/null +++ b/s3/api/requests/parsers_multipart.go @@ -0,0 +1,184 @@ +package requests + +import ( + "errors" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/hash" + "net/http" +) + +var createMultipartUploadSupports = fields{ + "Bucket": true, + "Key": true, + "ContentLength": true, + "ContentEncoding": true, + "ContentType": true, + "Expires": true, +} + +func ParseCreateMultipartUploadRequest(r *http.Request) (args *object.CreateMultipartUploadArgs, err error) { + var input s3.CreateMultipartUploadInput + err = ParseLocation(r, &input, createMultipartUploadSupports) + if err != nil { + return + } + args = &object.CreateMultipartUploadArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.ContentType, err = ValidateContentType(input.ContentType) + if err != nil { + return + } + args.ContentEncoding, err = ValidateContentEncoding(input.ContentEncoding) + if err != nil { + return + } + args.Expires, err = ValidateExpires(input.Expires) + return +} + +var uploadPartSupports = fields{ + "Body": true, + "Bucket": true, + "Key": true, + "UploadId": true, + "PartNumber": true, + "ContentLength": true, + "ContentMD5": true, + "ChecksumSHA256": true, +} + +func ParseUploadPartRequest(r *http.Request) (args *object.UploadPartArgs, err error) { + var input s3.UploadPartInput + err = ParseLocation(r, &input, uploadPartSupports) + if err != nil { + var er ErrFailedParseValue + if errors.As(err, &er) && er.Name() == consts.PartNumber { + err = ErrPartNumberInvalid + } + return + } + args = &object.UploadPartArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.UploadId, err = ValidateUploadId(input.UploadId) + if err != nil { + return + } + args.PartNumber, err = ValidatePartNumber(input.PartNumber) + if err != nil { + return + } + args.ContentLength, err = ValidateContentLength(input.ContentLength, consts.MaxPartSize) + if err != nil { + return + } + contentMD5, err := ValidateContentMD5(input.ContentMD5) + if err != nil { + return + } + checksumSHA256, err := ValidateChecksumSHA256(input.ChecksumSHA256) + if err != nil { + return + } + args.Body, err = hash.NewReader( + r.Body, args.ContentLength, contentMD5, + checksumSHA256, args.ContentLength, + ) + return +} + +var abortMultipartUploadSupports = fields{ + "Bucket": true, + "Key": true, + "UploadId": true, +} + +func ParseAbortMultipartUploadRequest(r *http.Request) (args *object.AbortMultipartUploadArgs, err error) { + var input s3.AbortMultipartUploadInput + err = ParseLocation(r, &input, abortMultipartUploadSupports) + if err != nil { + return + } + args = &object.AbortMultipartUploadArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.UploadId, err = ValidateUploadId(input.UploadId) + return +} + +var completeMultipartUploadSupports = fields{ + "Bucket": true, + "Key": true, + "UploadId": true, + "MultipartUpload": true, + "ChecksumSHA256": true, +} + +func ParseCompleteMultipartUploadRequest(r *http.Request) (args *object.CompleteMultipartUploadArgs, err error) { + var input s3.CompleteMultipartUploadInput + err = ParseLocation(r, &input, completeMultipartUploadSupports) + if err != nil { + return + } + args = &object.CompleteMultipartUploadArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.UploadId, err = ValidateUploadId(input.UploadId) + if err != nil { + return + } + size, err := ValidateContentLength(&r.ContentLength, consts.MaxXMLBodySize) + if err != nil { + return + } + checksumSHA256, err := ValidateChecksumSHA256(input.ChecksumSHA256) + if err != nil { + return + } + r.Body, err = hash.NewReader(r.Body, size, "", checksumSHA256, size) + if err != nil { + return + } + err = ParseXMLBody(r, &input) + if err != nil { + return + } + args.CompletedParts, err = ValidateCompletedMultipartUpload(input.MultipartUpload) + return +} diff --git a/s3/api/requests/parsers_object.go b/s3/api/requests/parsers_object.go new file mode 100644 index 000000000..726d5b180 --- /dev/null +++ b/s3/api/requests/parsers_object.go @@ -0,0 +1,368 @@ +package requests + +import ( + "errors" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/contexts" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/hash" + "net/http" +) + +var putObjectSupports = fields{ + "Body": true, + "Bucket": true, + "Key": true, + // The browser some time automatically add this CacheControl header + // just allow, do not handle + "CacheControl": true, + "ContentLength": true, + "ContentEncoding": true, + "ContentType": true, + "Expires": true, + "ContentMD5": true, + "ChecksumSHA256": true, +} + +func ParsePutObjectRequest(r *http.Request) (args *object.PutObjectArgs, err error) { + var input s3.PutObjectInput + err = ParseLocation(r, &input, putObjectSupports) + if err != nil { + return + } + args = &object.PutObjectArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.ContentLength, err = ValidateContentLength(input.ContentLength, consts.MaxObjectSize) + if err != nil { + return + } + args.ContentType, err = ValidateContentType(input.ContentType) + if err != nil { + return + } + args.ContentEncoding, err = ValidateContentEncoding(input.ContentEncoding) + if err != nil { + return + } + args.Expires, err = ValidateExpires(input.Expires) + if err != nil { + return + } + contentMD5, err := ValidateContentMD5(input.ContentMD5) + if err != nil { + return + } + checksumSHA256, err := ValidateChecksumSHA256(input.ChecksumSHA256) + if err != nil { + return + } + args.Body, err = hash.NewReader( + r.Body, args.ContentLength, contentMD5, + checksumSHA256, args.ContentLength, + ) + return +} + +var copyObjectSupports = fields{ + "Bucket": true, + "Key": true, + "CopySource": true, + // The browser some time automatically add this CacheControl header + // just allow, do not handle + "CacheControl": true, + "ContentEncoding": true, + "ContentType": true, + "Expires": true, + "MetadataDirective": true, +} + +func ParseCopyObjectRequest(r *http.Request) (args *object.CopyObjectArgs, err error) { + var input s3.CopyObjectInput + err = ParseLocation(r, &input, copyObjectSupports) + if err != nil { + return + } + args = &object.CopyObjectArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.SrcBucket, args.SrcObject, err = ValidateCopySource(input.CopySource) + if err != nil { + return + } + args.ReplaceMeta, err = ValidateMetadataDirective(input.MetadataDirective) + if err != nil { + return + } + if args.Bucket == args.SrcBucket && args.Object == args.SrcObject { + err = ErrCopyDestInvalid + return + } + args.ContentType, err = ValidateContentType(input.ContentType) + if err != nil { + return + } + args.ContentEncoding, err = ValidateContentEncoding(input.ContentEncoding) + if err != nil { + return + } + args.Expires, err = ValidateExpires(input.Expires) + return +} + +var headObjectSupports = fields{ + "Bucket": true, + "Key": true, +} + +func ParseHeadObjectRequest(r *http.Request) (args *object.GetObjectArgs, err error) { + var input s3.HeadObjectInput + err = ParseLocation(r, &input, headObjectSupports) + if err != nil { + return + } + args = &object.GetObjectArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.WithBody = false + return +} + +var getObjectSupports = fields{ + "Bucket": true, + "Key": true, +} + +func ParseGetObjectRequest(r *http.Request) (args *object.GetObjectArgs, err error) { + var input s3.GetObjectInput + err = ParseLocation(r, &input, getObjectSupports) + if err != nil { + return + } + args = &object.GetObjectArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + if err != nil { + return + } + args.WithBody = true + return +} + +var deleteObjectSupports = fields{ + "Bucket": true, + "Key": true, +} + +func ParseDeleteObjectRequest(r *http.Request) (args *object.DeleteObjectArgs, err error) { + var input s3.DeleteObjectInput + err = ParseLocation(r, &input, deleteObjectSupports) + if err != nil { + return + } + args = &object.DeleteObjectArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + return +} + +var deleteObjectsSupports = fields{ + "Bucket": true, + "Delete": true, +} + +func ParseDeleteObjectsRequest(r *http.Request) (args *object.DeleteObjectsArgs, err error) { + var input s3.DeleteObjectsInput + err = ParseLocation(r, &input, deleteObjectsSupports) + if err != nil { + return + } + args = &object.DeleteObjectsArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + size, err := ValidateContentLength(&r.ContentLength, consts.MaxXMLBodySize) + if err != nil { + return + } + r.Body, err = hash.NewReader(r.Body, size, "", "", size) + if err != nil { + return + } + err = ParseXMLBody(r, &input) + if err != nil { + return + } + args.ToDeleteObjects, args.Quite, err = ValidateObjectsDelete(input.Delete) + return +} + +var listObjectsSupports = fields{ + "Bucket": true, + "MaxKeys": true, + "Prefix": true, + "Marker": true, + "Delimiter": true, + "EncodingType": true, +} + +func ParseListObjectsRequest(r *http.Request) (args *object.ListObjectsArgs, err error) { + var input s3.ListObjectsInput + err = ParseLocation(r, &input, listObjectsSupports) + if err != nil { + var er ErrFailedParseValue + if errors.As(err, &er) && er.Name() == consts.MaxKeys { + err = ErrMaxKeysInvalid + } + return + } + args = &object.ListObjectsArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.MaxKeys, err = ValidateMaxKeys(input.MaxKeys) + if err != nil { + return + } + args.Marker, err = ValidateMarker(input.Marker) + if err != nil { + return + } + args.Prefix, err = ValidatePrefix(input.Prefix) + if err != nil { + return + } + err = ValidateMarkerAndPrefixCombination(args.Marker, args.Prefix) + if err != nil { + return + } + args.Delimiter, err = ValidateDelimiter(input.Delimiter) + if err != nil { + return + } + args.EncodingType, err = ValidateEncodingType(input.EncodingType) + return +} + +var listObjectsV2Supports = fields{ + "Bucket": true, + "MaxKeys": true, + "Prefix": true, + "ContinuationToken": true, + "StartAfter": true, + "Delimiter": true, + "EncodingType": true, + "FetchOwner": true, +} + +func ParseListObjectsV2Request(r *http.Request) (args *object.ListObjectsV2Args, err error) { + var input s3.ListObjectsV2Input + err = ParseLocation(r, &input, listObjectsV2Supports) + if err != nil { + var er ErrFailedParseValue + if errors.As(err, &er) && er.Name() == "max-keys" { + err = ErrMaxKeysInvalid + } + return + } + args = &object.ListObjectsV2Args{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.MaxKeys, err = ValidateMaxKeys(input.MaxKeys) + if err != nil { + return + } + args.Token, err = ValidateContinuationToken(input.ContinuationToken) + if err != nil { + return + } + args.After, err = ValidateStartAfter(input.StartAfter) + if err != nil { + return + } + err = ValidateMarkerAndPrefixCombination(args.Token, args.Prefix) + if err != nil { + return + } + err = ValidateMarkerAndPrefixCombination(args.After, args.Prefix) + if err != nil { + return + } + args.Delimiter, err = ValidateDelimiter(input.Delimiter) + if err != nil { + return + } + args.EncodingType, err = ValidateEncodingType(input.EncodingType) + if err != nil { + return + } + args.FetchOwner, err = ValidateFetchOwner(input.FetchOwner) + return +} + +var getObjectACLSupports = fields{ + "Bucket": true, + "Key": true, +} + +func ParseGetObjectACLRequest(r *http.Request) (args *object.GetObjectACLArgs, err error) { + var input s3.GetObjectAclInput + err = ParseLocation(r, &input, getObjectACLSupports) + if err != nil { + return + } + args = &object.GetObjectACLArgs{ + UserId: contexts.GetAccessKey(r), + } + args.Bucket, err = ValidateBucketName(input.Bucket) + if err != nil { + return + } + args.Object, err = ValidateObjectName(input.Key) + return +} diff --git a/s3/api/requests/validates.go b/s3/api/requests/validates.go new file mode 100644 index 000000000..ea7ae64d4 --- /dev/null +++ b/s3/api/requests/validates.go @@ -0,0 +1,389 @@ +package requests + +import ( + "crypto/md5" + "encoding/base64" + "encoding/hex" + "errors" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/etag" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" +) + +var ( + validBucketName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9\.\-]{1,61}[A-Za-z0-9]$`) + ipAddress = regexp.MustCompile(`^(\d+\.){3}\d+$`) +) + +func ValidateBucketName(bucketName *string) (val string, err error) { + if bucketName == nil { + return + } + if *bucketName == "" { + err = ErrBucketNameInvalid + return + } + val = *bucketName + if ipAddress.MatchString(val) || + !validBucketName.MatchString(val) || + strings.Contains(val, "..") || + strings.Contains(val, ".-") || + strings.Contains(val, "-.") { + err = ErrBucketNameInvalid + } + return +} + +func ValidateBucketACL(acl *string) (val string, err error) { + if acl == nil || *acl == "" { + val = consts.DefaultBucketACL + } else { + val = *acl + } + if !consts.SupportedBucketACLs[val] { + err = ErrACLUnsupported + } + return +} + +func ValidateCreateBucketConfiguration(configuration *s3.CreateBucketConfiguration) (val string, err error) { + if configuration == nil || configuration.LocationConstraint == nil || *configuration.LocationConstraint == "" { + val = consts.DefaultBucketRegion + } + if !consts.SupportedBucketRegions[val] { + err = ErrRegionUnsupported + } + return +} + +func ValidateObjectName(objectName *string) (val string, err error) { + if objectName == nil { + return + } + if *objectName == "" { + err = ErrObjectNameInvalid + return + } + val, err = url.PathUnescape(*objectName) + if err != nil { + err = ErrObjectNameInvalid + return + } + if len(val) > 1024 { + err = ErrObjectNameTooLong + return + } + if strings.HasPrefix(val, "/") { + err = ErrObjectNamePrefixSlash + return + } + if !utf8.ValidString(val) || strings.Contains(val, `//`) { + err = ErrObjectNameInvalid + } + for _, p := range strings.Split(val, "/") { + switch strings.TrimSpace(p) { + case "..", ".": + err = ErrObjectNameInvalid + return + } + } + return +} + +func ValidateContentMD5(contentMD5 *string) (val string, err error) { + if contentMD5 == nil { + return + } + if *contentMD5 == "" { + err = ErrContentMd5Invalid + return + } + b, err := base64.StdEncoding.Strict().DecodeString(*contentMD5) + if err != nil || len(b) != md5.Size { + err = ErrContentMd5Invalid + return + } + val = etag.ETag(b).String() + return +} + +func ValidateChecksumSHA256(checksumSHA256 *string) (val string, err error) { + if checksumSHA256 == nil || *checksumSHA256 == "" { + return + } + if *checksumSHA256 == consts.UnsignedSHA256 { + return + } + b, err := hex.DecodeString(*checksumSHA256) + if err != nil || len(b) == 0 { + err = ErrChecksumSha256Invalid + return + } + val = hex.EncodeToString(b) + return +} + +func ValidateContentLength(contentLength *int64, max int64) (val int64, err error) { + if contentLength == nil { + return + } + if *contentLength == -1 { + err = ErrContentLengthMissing + return + } + if *contentLength < 1 { + err = ErrContentLengthTooSmall + return + } + if *contentLength > max { + err = ErrContentLengthTooLarge + return + } + val = *contentLength + return +} + +func ValidateContentType(contentType *string) (val string, err error) { + if contentType == nil || *contentType == "" { + val = consts.DefaultContentType + return + } + val = *contentType + return +} + +func ValidateContentEncoding(contentEncoding *string) (val string, err error) { + if contentEncoding == nil || *contentEncoding == "" { + return + } + encs := make([]string, 0) + for _, enc := range strings.Split(*contentEncoding, ",") { + if enc != consts.StreamingContentEncoding { + encs = append(encs, enc) + } + } + val = strings.Join(encs, ",") + return +} + +func ValidateExpires(expires *time.Time) (val time.Time, err error) { + if expires == nil { + return + } + val = *expires + return +} + +func ValidateCopySource(copySource *string) (val1, val2 string, err error) { + if copySource == nil { + return + } + src, err := url.PathUnescape(*copySource) + if err != nil { + err = ErrCopySrcInvalid + return + } + src = strings.TrimPrefix(src, consts.SlashSeparator) + idx := strings.Index(src, consts.SlashSeparator) + if idx < 0 { + err = ErrCopySrcInvalid + return + } + val1 = src[:idx] + val2 = src[idx+len(consts.SlashSeparator):] + if val1 == "" || val2 == "" { + err = ErrCopySrcInvalid + return + } + val1, err = ValidateBucketName(&val1) + if err != nil { + return + } + val2, err = ValidateObjectName(&val2) + return +} + +func ValidateMetadataDirective(metadataDirective *string) (val bool, err error) { + if metadataDirective == nil { + return + } + if *metadataDirective == "REPLACE" { + val = true + } + return +} + +func ValidateObjectsDelete(delete *s3.Delete) (vals []*object.ToDeleteObject, quite bool, err error) { + if delete == nil { + err = ErrFailedDecodeXML{errors.New("delete is nil")} + return + } + if len(delete.Objects) < 1 || len(delete.Objects) > consts.MaxDeleteList { + err = ErrDeletesCountInvalid + return + } + if delete.Quiet != nil && *delete.Quiet == true { + quite = true + } + for _, obj := range delete.Objects { + deleteObj := &object.ToDeleteObject{} + deleteObj.Object, deleteObj.ValidateErr = ValidateObjectName(obj.Key) + vals = append(vals, deleteObj) + } + return +} + +func ValidateMaxKeys(maxKeys *int64) (val int64, err error) { + if maxKeys == nil || *maxKeys > consts.MaxObjectList { + val = consts.MaxObjectList + return + } + if *maxKeys < 0 { + err = ErrMaxKeysInvalid + return + } + val = *maxKeys + return +} + +func ValidateMarker(marker *string) (val string, err error) { + if marker == nil || *marker == "" { + return + } + val, err = ValidateObjectName(marker) + if err != nil { + err = ErrMarkerInvalid + } + return +} + +func ValidatePrefix(prefix *string) (val string, err error) { + if prefix == nil || *prefix == "" { + return + } + val, err = ValidateObjectName(prefix) + if err != nil { + err = ErrPrefixInvalid + } + return +} + +func ValidateMarkerAndPrefixCombination(marker, prefix string) (err error) { + if marker != "" && !strings.HasPrefix(marker, prefix) { + err = ErrMarkerPrefixCombinationInvalid + } + return +} + +func ValidateDelimiter(delimiter *string) (val string, err error) { + if delimiter == nil { + return + } + val = *delimiter + return +} + +func ValidateEncodingType(encodingType *string) (val string, err error) { + if encodingType == nil || *encodingType == "" { + return + } + if !strings.EqualFold(*encodingType, consts.DefaultEncodingType) { + err = ErrEncodingTypeInvalid + return + } + val = consts.DefaultEncodingType + return +} + +func ValidateContinuationToken(continuationToken *string) (val string, err error) { + if continuationToken == nil || *continuationToken == "" { + return + } + token, err := base64.StdEncoding.DecodeString(*continuationToken) + if err != nil { + err = ErrContinuationTokenInvalid + return + } + tokenStr := string(token) + val, err = ValidateObjectName(&tokenStr) + if err != nil { + err = ErrContinuationTokenInvalid + } + return +} + +func ValidateStartAfter(startAfter *string) (val string, err error) { + if startAfter == nil || *startAfter == "" { + return + } + val, err = ValidateObjectName(startAfter) + if err != nil { + err = ErrStartAfterInvalid + } + return +} + +func ValidateFetchOwner(fetchOwner *bool) (val bool, err error) { + if fetchOwner == nil { + return + } + val = *fetchOwner + return +} + +func ValidateUploadId(uploadId *string) (val string, err error) { + if uploadId == nil { + return + } + val = *uploadId + return +} + +func ValidatePartNumber(partNumber *int64) (val int64, err error) { + if partNumber == nil { + return + } + if *partNumber < consts.MinPartNumber || *partNumber > consts.MaxPartNumber { + err = ErrPartNumberInvalid + return + } + val = *partNumber + return +} + +func ValidateCompletedMultipartUpload(upload *s3.CompletedMultipartUpload) (val object.CompletedParts, err error) { + if upload == nil { + err = ErrFailedDecodeXML{errors.New("complete-upload is nil")} + return + } + if len(upload.Parts) < 1 || len(upload.Parts) > consts.MaxPartNumber { + err = ErrPartsCountInvalid + return + } + for _, part := range upload.Parts { + if part.PartNumber == nil || part.ETag == nil { + err = ErrPartInvalid + return + } + + opart := &object.CompletePart{ + ETag: *part.ETag, + } + opart.PartNumber, err = ValidatePartNumber(part.PartNumber) + if err != nil { + return + } + val = append(val, opart) + } + if !sort.IsSorted(val) { + err = ErrPartOrderInvalid + } + return +} diff --git a/s3/api/responses/errors.go b/s3/api/responses/errors.go new file mode 100644 index 000000000..24bdfdd13 --- /dev/null +++ b/s3/api/responses/errors.go @@ -0,0 +1,1039 @@ +package responses + +import ( + "fmt" + "net/http" +) + +type Error struct { + code string + description string + httpStatusCode int +} + +func (err *Error) Code() string { + return err.code +} + +func (err *Error) Description() string { + return err.description +} + +func (err *Error) HTTPStatusCode() int { + return err.httpStatusCode +} + +func (err *Error) Error() string { + return fmt.Sprintf("<%s> %s", err.code, err.description) +} + +// Errors http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html +var ( + ErrInvalidCopyDest = &Error{ + code: "InvalidRequest", + description: "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidCopySource = &Error{ + code: "InvalidArgument", + description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidMetadataDirective = &Error{ + code: "InvalidArgument", + description: "Unknown metadata directive.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidRequestBody = &Error{ + code: "InvalidArgument", + description: "Body shouldn't be set for this request.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidUploads = &Error{ + code: "InvalidArgument", + description: "Argument max-uploads must be an integer between 0 and 2147483647", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidMaxKeys = &Error{ + code: "InvalidArgument", + description: "Argument maxKeys must be an integer between 0 and 2147483647", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidEncodingMethod = &Error{ + code: "InvalidArgument", + description: "Invalid Encoding Method specified in Request", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidPartNumber = &Error{ + code: "InvalidArgument", + description: "Part number must be an integer between 1 and 10000, inclusive", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidPartNumberMarker = &Error{ + code: "InvalidArgument", + description: "Argument partNumberMarker must be an integer.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidPolicyDocument = &Error{ + code: "InvalidPolicyDocument", + description: "The content of the form does not meet the conditions specified in the policy document.", + httpStatusCode: http.StatusBadRequest, + } + ErrAccessDenied = &Error{ + code: "AccessDenied", + description: "Access Denied.", + httpStatusCode: http.StatusForbidden, + } + ErrBadDigest = &Error{ + code: "BadDigest", + description: "The Content-Md5 you specified did not match what we received.", + httpStatusCode: http.StatusBadRequest, + } + ErrEntityTooSmall = &Error{ + code: "EntityTooSmall", + description: "Your proposed upload is smaller than the minimum allowed object size.", + httpStatusCode: http.StatusBadRequest, + } + ErrEntityTooLarge = &Error{ + code: "EntityTooLarge", + description: "Your proposed upload exceeds the maximum allowed object size.", + httpStatusCode: http.StatusBadRequest, + } + ErrIncompleteBody = &Error{ + code: "IncompleteBody", + description: "You did not provide the number of bytes specified by the Content-Length HTTP header.", + httpStatusCode: http.StatusBadRequest, + } + ErrInternalError = &Error{ + code: "InternalError", + description: "We encountered an internal error, please try again.", + httpStatusCode: http.StatusInternalServerError, + } + ErrInvalidAccessKeyID = &Error{ + code: "InvalidAccessKeyId", + description: "The Access Key Id you provided does not exist in our records.", + httpStatusCode: http.StatusForbidden, + } + ErrAccessKeyDisabled = &Error{ + code: "InvalidAccessKeyId", + description: "Your account is disabled; please contact your administrator.", + httpStatusCode: http.StatusForbidden, + } + ErrInvalidBucketName = &Error{ + code: "InvalidBucketName", + description: "The specified bucket is not valid.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidDigest = &Error{ + code: "InvalidDigest", + description: "The Content-Md5 you specified is not valid.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidRange = &Error{ + code: "InvalidRange", + description: "The requested range is not satisfiable", + httpStatusCode: http.StatusRequestedRangeNotSatisfiable, + } + ErrInvalidRangePartNumber = &Error{ + code: "InvalidRequest", + description: "Cannot specify both Range header and partNumber query parameter", + httpStatusCode: http.StatusBadRequest, + } + ErrMalformedXML = &Error{ + code: "MalformedXML", + description: "The XML you provided was not well-formed or did not validate against our published schema.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingContentLength = &Error{ + code: "MissingContentLength", + description: "You must provide the Content-Length HTTP header.", + httpStatusCode: http.StatusLengthRequired, + } + ErrMissingContentMD5 = &Error{ + code: "MissingContentMD5", + description: "Missing required header for this request: Content-Md5.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingSecurityHeader = &Error{ + code: "MissingSecurityHeader", + description: "Your request was missing a required header", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingRequestBodyError = &Error{ + code: "MissingRequestBodyError", + description: "Request body is empty.", + httpStatusCode: http.StatusLengthRequired, + } + ErrNoSuchBucket = &Error{ + code: "NoSuchBucket", + description: "The specified bucket does not exist", + httpStatusCode: http.StatusNotFound, + } + ErrNoSuchBucketPolicy = &Error{ + code: "NoSuchBucketPolicy", + description: "The bucket policy does not exist", + httpStatusCode: http.StatusNotFound, + } + ErrNoSuchLifecycleConfiguration = &Error{ + code: "NoSuchLifecycleConfiguration", + description: "The lifecycle configuration does not exist", + httpStatusCode: http.StatusNotFound, + } + ErrNoSuchUser = &Error{ + code: "NoSuchUser", + description: "The specified user does not exist", + httpStatusCode: http.StatusConflict, + } + ErrUserAlreadyExists = &Error{ + code: "UserAlreadyExists", + description: "The request was rejected because it attempted to create a resource that already exists .", + httpStatusCode: http.StatusConflict, + } + ErrNoSuchUserPolicy = &Error{ + code: "NoSuchUserPolicy", + description: "The specified user policy does not exist", + httpStatusCode: http.StatusConflict, + } + ErrUserPolicyAlreadyExists = &Error{ + code: "UserPolicyAlreadyExists", + description: "The same user policy already exists .", + httpStatusCode: http.StatusConflict, + } + ErrNoSuchKey = &Error{ + code: "NoSuchKey", + description: "The specified key does not exist.", + httpStatusCode: http.StatusNotFound, + } + ErrNoSuchUpload = &Error{ + code: "NoSuchUpload", + description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", + httpStatusCode: http.StatusNotFound, + } + ErrInvalidVersionID = &Error{ + code: "InvalidArgument", + description: "Invalid version id specified", + httpStatusCode: http.StatusBadRequest, + } + ErrNoSuchVersion = &Error{ + code: "NoSuchVersion", + description: "The specified version does not exist.", + httpStatusCode: http.StatusNotFound, + } + ErrNotImplemented = &Error{ + code: "NotImplemented", + description: "A header you provided implies functionality that is not implemented", + httpStatusCode: http.StatusNotImplemented, + } + ErrPreconditionFailed = &Error{ + code: "PreconditionFailed", + description: "At least one of the pre-conditions you specified did not hold", + httpStatusCode: http.StatusPreconditionFailed, + } + ErrRequestTimeTooSkewed = &Error{ + code: "RequestTimeTooSkewed", + description: "The difference between the request time and the server's time is too large.", + httpStatusCode: http.StatusForbidden, + } + ErrSignatureDoesNotMatch = &Error{ + code: "SignatureDoesNotMatch", + description: "The request signature we calculated does not match the signature you provided. Check your key and signing method.", + httpStatusCode: http.StatusForbidden, + } + ErrMethodNotAllowed = &Error{ + code: "MethodNotAllowed", + description: "The specified method is not allowed against this resource.", + httpStatusCode: http.StatusMethodNotAllowed, + } + ErrInvalidPart = &Error{ + code: "InvalidPart", + description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidPartOrder = &Error{ + code: "InvalidPartOrder", + description: "The list of parts was not in ascending order. The parts list must be specified in order by part number.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidObjectState = &Error{ + code: "InvalidObjectState", + description: "The operation is not valid for the current state of the object.", + httpStatusCode: http.StatusForbidden, + } + ErrAuthorizationHeaderMalformed = &Error{ + code: "AuthorizationHeaderMalformed", + description: "The authorization header is malformed; the region is wrong; expecting 'us-east-1'.", + httpStatusCode: http.StatusBadRequest, + } + ErrMalformedDate = &Error{ // todo + code: "ErrMalformedDate", + description: "ErrMalformedDate", + httpStatusCode: http.StatusBadRequest, + } + ErrMalformedPOSTRequest = &Error{ + code: "MalformedPOSTRequest", + description: "The body of your POST request is not well-formed multipart/form-data.", + httpStatusCode: http.StatusBadRequest, + } + ErrPOSTFileRequired = &Error{ + code: "InvalidArgument", + description: "POST requires exactly one file upload per request.", + httpStatusCode: http.StatusBadRequest, + } + ErrSignatureVersionNotSupported = &Error{ + code: "InvalidRequest", + description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.", + httpStatusCode: http.StatusBadRequest, + } + ErrBucketNotEmpty = &Error{ + code: "BucketNotEmpty", + description: "The bucket you tried to delete is not empty", + httpStatusCode: http.StatusConflict, + } + ErrBucketAlreadyExists = &Error{ + code: "BucketAlreadyExists", + description: "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.", + httpStatusCode: http.StatusConflict, + } + ErrAllAccessDisabled = &Error{ + code: "AllAccessDisabled", + description: "All access to this resource has been disabled.", + httpStatusCode: http.StatusForbidden, + } + ErrMalformedPolicy = &Error{ + code: "MalformedPolicy", + description: "Policy has invalid resource.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingFields = &Error{ // todo + code: "InvalidRequest", + description: "ErrMissingFields", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingCredTag = &Error{ + code: "InvalidRequest", + description: "Missing Credential field for this request.", + httpStatusCode: http.StatusBadRequest, + } + ErrCredMalformed = &Error{ // todo + code: "InvalidRequest", + description: "ErrCredMalformed", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidRegion = &Error{ + code: "InvalidRegion", + description: "Region does not match.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingSignTag = &Error{ + code: "AccessDenied", + description: "Signature header missing Signature field.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingSignHeadersTag = &Error{ + code: "InvalidArgument", + description: "Signature header missing SignedHeaders field.", + httpStatusCode: http.StatusBadRequest, + } + ErrAuthHeaderEmpty = &Error{ + code: "InvalidArgument", + description: "Authorization header is invalid -- one and only one ' ' (space) required.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingDateHeader = &Error{ + code: "AccessDenied", + description: "AWS authentication requires a valid Date or x-amz-date header", + httpStatusCode: http.StatusBadRequest, + } + ErrExpiredPresignRequest = &Error{ + code: "AccessDenied", + description: "Request has expired", + httpStatusCode: http.StatusForbidden, + } + ErrRequestNotReadyYet = &Error{ + code: "AccessDenied", + description: "Request is not valid yet", + httpStatusCode: http.StatusForbidden, + } + ErrSlowDown = &Error{ + code: "SlowDown", + description: "Resource requested is unreadable, please reduce your request rate", + httpStatusCode: http.StatusServiceUnavailable, + } + ErrBadRequest = &Error{ + code: "BadRequest", + description: "400 BadRequest", + httpStatusCode: http.StatusBadRequest, + } + ErrKeyTooLongError = &Error{ + code: "KeyTooLongError", + description: "Your key is too long", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsignedHeaders = &Error{ + code: "AccessDenied", + description: "There were headers present in the request which were not signed", + httpStatusCode: http.StatusBadRequest, + } + ErrBucketAlreadyOwnedByYou = &Error{ + code: "BucketAlreadyOwnedByYou", + description: "Your previous request to create the named bucket succeeded and you already own it.", + httpStatusCode: http.StatusConflict, + } + ErrInvalidDuration = &Error{ + code: "InvalidDuration", + description: "Duration provided in the request is invalid.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidBucketObjectLockConfiguration = &Error{ + code: "InvalidRequest", + description: "Bucket is missing ObjectLockConfiguration", + httpStatusCode: http.StatusBadRequest, + } + ErrBucketTaggingNotFound = &Error{ + code: "NoSuchTagSet", + description: "The TagSet does not exist", + httpStatusCode: http.StatusNotFound, + } + ErrObjectLockConfigurationNotAllowed = &Error{ + code: "InvalidBucketState", + description: "Object Lock configuration cannot be enabled on existing buckets", + httpStatusCode: http.StatusConflict, + } + ErrNoSuchCORSConfiguration = &Error{ + code: "NoSuchCORSConfiguration", + description: "The CORS configuration does not exist", + httpStatusCode: http.StatusNotFound, + } + ErrNoSuchWebsiteConfiguration = &Error{ + code: "NoSuchWebsiteConfiguration", + description: "The specified bucket does not have a website configuration", + httpStatusCode: http.StatusNotFound, + } + ErrReplicationConfigurationNotFoundError = &Error{ + code: "ReplicationConfigurationNotFoundError", + description: "The replication configuration was not found", + httpStatusCode: http.StatusNotFound, + } + ErrReplicationNeedsVersioningError = &Error{ + code: "InvalidRequest", + description: "Versioning must be 'Enabled' on the bucket to apply a replication configuration", + httpStatusCode: http.StatusBadRequest, + } + ErrReplicationBucketNeedsVersioningError = &Error{ + code: "InvalidRequest", + description: "Versioning must be 'Enabled' on the bucket to add a replication target", + httpStatusCode: http.StatusBadRequest, + } + ErrNoSuchObjectLockConfiguration = &Error{ + code: "NoSuchObjectLockConfiguration", + description: "The specified object does not have a ObjectLock configuration", + httpStatusCode: http.StatusBadRequest, + } + ErrObjectLocked = &Error{ + code: "InvalidRequest", + description: "Object is WORM protected and cannot be overwritten", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidRetentionDate = &Error{ + code: "InvalidRequest", + description: "Date must be provided in ISO 8601 format", + httpStatusCode: http.StatusBadRequest, + } + ErrPastObjectLockRetainDate = &Error{ + code: "InvalidRequest", + description: "the retain until date must be in the future", + httpStatusCode: http.StatusBadRequest, + } + ErrUnknownWORMModeDirective = &Error{ + code: "InvalidRequest", + description: "unknown wormMode directive", + httpStatusCode: http.StatusBadRequest, + } + ErrObjectLockInvalidHeaders = &Error{ + code: "InvalidRequest", + description: "x-amz-object-lock-retain-until-date and x-amz-object-lock-mode must both be supplied", + httpStatusCode: http.StatusBadRequest, + } + ErrObjectRestoreAlreadyInProgress = &Error{ + code: "RestoreAlreadyInProgress", + description: "Object restore is already in progress", + httpStatusCode: http.StatusConflict, + } + // Bucket notification related errors. + ErrEventNotification = &Error{ + code: "InvalidArgument", + description: "A specified event is not supported for notifications.", + httpStatusCode: http.StatusBadRequest, + } + ErrARNNotification = &Error{ + code: "InvalidArgument", + description: "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", + httpStatusCode: http.StatusBadRequest, + } + ErrRegionNotification = &Error{ + code: "InvalidArgument", + description: "A specified destination is in a different region than the bucket. You must use a destination that resides in the same region as the bucket.", + httpStatusCode: http.StatusBadRequest, + } + ErrOverlappingFilterNotification = &Error{ + code: "InvalidArgument", + description: "An object key name filtering rule defined with overlapping prefixes, overlapping suffixes, or overlapping combinations of prefixes and suffixes for the same event types.", + httpStatusCode: http.StatusBadRequest, + } + ErrFilterNameInvalid = &Error{ + code: "InvalidArgument", + description: "filter rule name must be either prefix or suffix", + httpStatusCode: http.StatusBadRequest, + } + ErrFilterNamePrefix = &Error{ + code: "InvalidArgument", + description: "Cannot specify more than one prefix rule in a filter.", + httpStatusCode: http.StatusBadRequest, + } + ErrFilterNameSuffix = &Error{ + code: "InvalidArgument", + description: "Cannot specify more than one suffix rule in a filter.", + httpStatusCode: http.StatusBadRequest, + } + ErrFilterValueInvalid = &Error{ + code: "InvalidArgument", + description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation", + httpStatusCode: http.StatusBadRequest, + } + ErrOverlappingConfigs = &Error{ + code: "InvalidArgument", + description: "Configurations overlap. Configurations on the same bucket cannot share a common event type.", + httpStatusCode: http.StatusBadRequest, + } + ErrContentSHA256Mismatch = &Error{ //todo + code: "InvalidArgument", + description: "ErrContentSHA256Mismatch", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidCopyPartRange = &Error{ + code: "InvalidArgument", + description: "The x-amz-copy-source-range value must be of the form bytes=first-last where first and last are the zero-based offsets of the first and last bytes to copy", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidCopyPartRangeSource = &Error{ + code: "InvalidArgument", + description: "Range specified is not valid for source object", + httpStatusCode: http.StatusBadRequest, + } + ErrMetadataTooLarge = &Error{ + code: "MetadataTooLarge", + description: "Your metadata headers exceed the maximum allowed metadata size.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidTagDirective = &Error{ + code: "InvalidArgument", + description: "Unknown tag directive.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidEncryptionMethod = &Error{ + code: "InvalidRequest", + description: "The encryption method specified is not supported", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidQueryParams = &Error{ + code: "AuthorizationQueryParametersError", + description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.", + httpStatusCode: http.StatusBadRequest, + } + ErrNoAccessKey = &Error{ + code: "AccessDenied", + description: "No AWSAccessKey was presented", + httpStatusCode: http.StatusForbidden, + } + ErrInvalidToken = &Error{ + code: "InvalidTokenId", + description: "The security token included in the request is invalid", + httpStatusCode: http.StatusForbidden, + } + + // S3 extensions. + ErrInvalidObjectName = &Error{ + code: "InvalidObjectName", + description: "Object name contains unsupported characters.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidObjectNamePrefixSlash = &Error{ + code: "InvalidObjectName", + description: "Object name contains a leading slash.", + httpStatusCode: http.StatusBadRequest, + } + ErrClientDisconnected = &Error{ + code: "ClientDisconnected", + description: "Client disconnected before response was ready", + httpStatusCode: 499, // No official code, use nginx value. + } + ErrOperationTimedOut = &Error{ + code: "RequestTimeout", + description: "A timeout occurred while trying to lock a resource, please reduce your request rate", + httpStatusCode: http.StatusServiceUnavailable, + } + ErrOperationMaxedOut = &Error{ + code: "SlowDown", + description: "A timeout exceeded while waiting to proceed with the request, please reduce your request rate", + httpStatusCode: http.StatusServiceUnavailable, + } + ErrUnsupportedMetadata = &Error{ + code: "InvalidArgument", + description: "Your metadata headers are not supported.", + httpStatusCode: http.StatusBadRequest, + } + // Generic Invalid-Request error. Should be used for response errors only for unlikely + // corner case errors for which introducing new APIorcode is not worth it. LogIf() + // should be used to log the error at the source of the error for debugging purposes. + ErrInvalidRequest = &Error{ + code: "InvalidRequest", + description: "Invalid Request", + httpStatusCode: http.StatusBadRequest, + } + ErrIncorrectContinuationToken = &Error{ + code: "InvalidArgument", + description: "The continuation token provided is incorrect", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidFormatAccessKey = &Error{ + code: "InvalidAccessKeyId", + description: "The Access Key Id you provided contains invalid characters.", + httpStatusCode: http.StatusBadRequest, + } + // S3 Select API ors + ErrErrEmptyRequestBody = &Error{ + code: "EmptyRequestBody", + description: "Request body cannot be empty.", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsupportedFunction = &Error{ + code: "UnsupportedFunction", + description: "Encountered an unsupported SQL function.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidDataSource = &Error{ + code: "InvalidDataSource", + description: "Invalid data source type. Only CSV and JSON are supported at this time.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidExpressionType = &Error{ + code: "InvalidExpressionType", + description: "The ExpressionType is invalid. Only SQL expressions are supported at this time.", + httpStatusCode: http.StatusBadRequest, + } + ErrBusy = &Error{ + code: "Busy", + description: "The service is unavailable. Please retry.", + httpStatusCode: http.StatusServiceUnavailable, + } + ErrUnauthorizedAccess = &Error{ + code: "UnauthorizedAccess", + description: "You are not authorized to perform this operation", + httpStatusCode: http.StatusUnauthorized, + } + ErrExpressionTooLong = &Error{ + code: "ExpressionTooLong", + description: "The SQL expression is too long: The maximum byte-length for the SQL expression is 256 KB.", + httpStatusCode: http.StatusBadRequest, + } + ErrIllegalSQLFunctionArgument = &Error{ + code: "IllegalSqlFunctionArgument", + description: "Illegal argument was used in the SQL function.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidKeyPath = &Error{ + code: "InvalidKeyPath", + description: "Key path in the SQL expression is invalid.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidCompressionFormat = &Error{ + code: "InvalidCompressionFormat", + description: "The file is not in a supported compression format. Only GZIP is supported at this time.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidFileHeaderInfo = &Error{ + code: "InvalidFileHeaderInfo", + description: "The FileHeaderInfo is invalid. Only NONE, USE, and IGNORE are supported.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidJSONType = &Error{ + code: "InvalidJsonType", + description: "The JsonType is invalid. Only DOCUMENT and LINES are supported at this time.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidQuoteFields = &Error{ + code: "InvalidQuoteFields", + description: "The QuoteFields is invalid. Only ALWAYS and ASNEEDED are supported.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidRequestParameter = &Error{ + code: "InvalidRequestParameter", + description: "The value of a parameter in SelectRequest element is invalid. Check the service API documentation and try again.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidDataType = &Error{ + code: "InvalidDataType", + description: "The SQL expression contains an invalid data type.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidTextEncoding = &Error{ + code: "InvalidTextEncoding", + description: "Invalid encoding type. Only UTF-8 encoding is supported at this time.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidTableAlias = &Error{ + code: "InvalidTableAlias", + description: "The SQL expression contains an invalid table alias.", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingRequiredParameter = &Error{ + code: "MissingRequiredParameter", + description: "The SelectRequest entity is missing a required parameter. Check the service documentation and try again.", + httpStatusCode: http.StatusBadRequest, + } + ErrObjectSerializationConflict = &Error{ + code: "ObjectSerializationConflict", + description: "The SelectRequest entity can only contain one of CSV or JSON. Check the service documentation and try again.", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsupportedSQLOperation = &Error{ + code: "UnsupportedSqlOperation", + description: "Encountered an unsupported SQL operation.", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsupportedSQLStructure = &Error{ + code: "UnsupportedSqlStructure", + description: "Encountered an unsupported SQL structure. Check the SQL Reference.", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsupportedSyntax = &Error{ + code: "UnsupportedSyntax", + description: "Encountered invalid syntax.", + httpStatusCode: http.StatusBadRequest, + } + ErrUnsupportedRangeHeader = &Error{ + code: "UnsupportedRangeHeader", + description: "Range header is not supported for this operation.", + httpStatusCode: http.StatusBadRequest, + } + ErrLexerInvalidChar = &Error{ + code: "LexerInvalidChar", + description: "The SQL expression contains an invalid character.", + httpStatusCode: http.StatusBadRequest, + } + ErrLexerInvalidOperator = &Error{ + code: "LexerInvalidOperator", + description: "The SQL expression contains an invalid literal.", + httpStatusCode: http.StatusBadRequest, + } + ErrLexerInvalidLiteral = &Error{ + code: "LexerInvalidLiteral", + description: "The SQL expression contains an invalid operator.", + httpStatusCode: http.StatusBadRequest, + } + ErrLexerInvalidIONLiteral = &Error{ + code: "LexerInvalidIONLiteral", + description: "The SQL expression contains an invalid operator.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedDatePart = &Error{ + code: "ParseExpectedDatePart", + description: "Did not find the expected date part in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedKeyword = &Error{ + code: "ParseExpectedKeyword", + description: "Did not find the expected keyword in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedTokenType = &Error{ + code: "ParseExpectedTokenType", + description: "Did not find the expected token in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpected2TokenTypes = &Error{ + code: "ParseExpected2TokenTypes", + description: "Did not find the expected token in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedNumber = &Error{ + code: "ParseExpectedNumber", + description: "Did not find the expected number in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedRightParenBuiltinFunctionCall = &Error{ + code: "ParseExpectedRightParenBuiltinFunctionCall", + description: "Did not find the expected right parenthesis character in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedTypeName = &Error{ + code: "ParseExpectedTypeName", + description: "Did not find the expected type name in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedWhenClause = &Error{ + code: "ParseExpectedWhenClause", + description: "Did not find the expected WHEN clause in the SQL expression. CASE is not supported.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedToken = &Error{ + code: "ParseUnsupportedToken", + description: "The SQL expression contains an unsupported token.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedLiteralsGroupBy = &Error{ + code: "ParseUnsupportedLiteralsGroupBy", + description: "The SQL expression contains an unsupported use of GROUP BY.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedMember = &Error{ + code: "ParseExpectedMember", + description: "The SQL expression contains an unsupported use of MEMBER.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedSelect = &Error{ + code: "ParseUnsupportedSelect", + description: "The SQL expression contains an unsupported use of SELECT.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedCase = &Error{ + code: "ParseUnsupportedCase", + description: "The SQL expression contains an unsupported use of CASE.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedCaseClause = &Error{ + code: "ParseUnsupportedCaseClause", + description: "The SQL expression contains an unsupported use of CASE.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedAlias = &Error{ + code: "ParseUnsupportedAlias", + description: "The SQL expression contains an unsupported use of ALIAS.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedSyntax = &Error{ + code: "ParseUnsupportedSyntax", + description: "The SQL expression contains unsupported syntax.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnknownOperator = &Error{ + code: "ParseUnknownOperator", + description: "The SQL expression contains an invalid operator.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseMissingIdentAfterAt = &Error{ + code: "ParseMissingIdentAfterAt", + description: "Did not find the expected identifier after the @ symbol in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnexpectedOperator = &Error{ + code: "ParseUnexpectedOperator", + description: "The SQL expression contains an unexpected operator.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnexpectedTerm = &Error{ + code: "ParseUnexpectedTerm", + description: "The SQL expression contains an unexpected term.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnexpectedToken = &Error{ + code: "ParseUnexpectedToken", + description: "The SQL expression contains an unexpected token.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnexpectedKeyword = &Error{ + code: "ParseUnexpectedKeyword", + description: "The SQL expression contains an unexpected keyword.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedExpression = &Error{ + code: "ParseExpectedExpression", + description: "Did not find the expected SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedLeftParenAfterCast = &Error{ + code: "ParseExpectedLeftParenAfterCast", + description: "Did not find expected the left parenthesis in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedLeftParenValueConstructor = &Error{ + code: "ParseExpectedLeftParenValueConstructor", + description: "Did not find expected the left parenthesis in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedLeftParenBuiltinFunctionCall = &Error{ + code: "ParseExpectedLeftParenBuiltinFunctionCall", + description: "Did not find the expected left parenthesis in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedArgumentDelimiter = &Error{ + code: "ParseExpectedArgumentDelimiter", + description: "Did not find the expected argument delimiter in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseCastArity = &Error{ + code: "ParseCastArity", + description: "The SQL expression CAST has incorrect arity.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseInvalidTypeParam = &Error{ + code: "ParseInvalidTypeParam", + description: "The SQL expression contains an invalid parameter value.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseEmptySelect = &Error{ + code: "ParseEmptySelect", + description: "The SQL expression contains an empty SELECT.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseSelectMissingFrom = &Error{ + code: "ParseSelectMissingFrom", + description: "GROUP is not supported in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedIdentForGroupName = &Error{ + code: "ParseExpectedIdentForGroupName", + description: "GROUP is not supported in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedIdentForAlias = &Error{ + code: "ParseExpectedIdentForAlias", + description: "Did not find the expected identifier for the alias in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseUnsupportedCallWithStar = &Error{ + code: "ParseUnsupportedCallWithStar", + description: "Only COUNT with (*) as a parameter is supported in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseNonUnaryAgregateFunctionCall = &Error{ + code: "ParseNonUnaryAgregateFunctionCall", + description: "Only one argument is supported for aggregate functions in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseMalformedJoin = &Error{ + code: "ParseMalformedJoin", + description: "JOIN is not supported in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseExpectedIdentForAt = &Error{ + code: "ParseExpectedIdentForAt", + description: "Did not find the expected identifier for AT name in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseAsteriskIsNotAloneInSelectList = &Error{ + code: "ParseAsteriskIsNotAloneInSelectList", + description: "Other expressions are not allowed in the SELECT list when '*' is used without dot notation in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseCannotMixSqbAndWildcardInSelectList = &Error{ + code: "ParseCannotMixSqbAndWildcardInSelectList", + description: "Cannot mix [] and * in the same expression in a SELECT list in SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrParseInvalidContextForWildcardInSelectList = &Error{ + code: "ParseInvalidContextForWildcardInSelectList", + description: "Invalid use of * in SELECT list in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrIncorrectSQLFunctionArgumentType = &Error{ + code: "IncorrectSqlFunctionArgumentType", + description: "Incorrect type of arguments in function call in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrValueParseFailure = &Error{ + code: "ValueParseFailure", + description: "Time stamp parse failure in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorInvalidArguments = &Error{ + code: "EvaluatorInvalidArguments", + description: "Incorrect number of arguments in the function call in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrIntegerOverflow = &Error{ + code: "IntegerOverflow", + description: "Int overflow or underflow in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrLikeInvalidInputs = &Error{ + code: "LikeInvalidInputs", + description: "Invalid argument given to the LIKE clause in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrCastFailed = &Error{ + code: "CastFailed", + description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidCast = &Error{ + code: "InvalidCast", + description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorInvalidTimestampFormatPattern = &Error{ + code: "EvaluatorInvalidTimestampFormatPattern", + description: "Time stamp format pattern requires additional fields in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing = &Error{ + code: "EvaluatorInvalidTimestampFormatPatternSymbolForParsing", + description: "Time stamp format pattern contains a valid format symbol that cannot be applied to time stamp parsing in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorTimestampFormatPatternDuplicateFields = &Error{ + code: "EvaluatorTimestampFormatPatternDuplicateFields", + description: "Time stamp format pattern contains multiple format specifiers representing the time stamp field in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch = &Error{ + code: "EvaluatorUnterminatedTimestampFormatPatternToken", + description: "Time stamp format pattern contains unterminated token in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorUnterminatedTimestampFormatPatternToken = &Error{ + code: "EvaluatorInvalidTimestampFormatPatternToken", + description: "Time stamp format pattern contains an invalid token in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorInvalidTimestampFormatPatternToken = &Error{ + code: "EvaluatorInvalidTimestampFormatPatternToken", + description: "Time stamp format pattern contains an invalid token in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorInvalidTimestampFormatPatternSymbol = &Error{ + code: "EvaluatorInvalidTimestampFormatPatternSymbol", + description: "Time stamp format pattern contains an invalid symbol in the SQL expression.", + httpStatusCode: http.StatusBadRequest, + } + ErrEvaluatorBindingDoesNotExist = &Error{ + code: "ErrEvaluatorBindingDoesNotExist", + description: "A column name or a path provided does not exist in the SQL expression", + httpStatusCode: http.StatusBadRequest, + } + ErrMissingHeaders = &Error{ + code: "MissingHeaders", + description: "Some headers in the query are missing from the file. Check the file and try again.", + httpStatusCode: http.StatusBadRequest, + } + ErrInvalidColumnIndex = &Error{ + code: "InvalidColumnIndex", + description: "The column index is invalid. Please check the service documentation and try again.", + httpStatusCode: http.StatusBadRequest, + } + ErrPostPolicyConditionInvalidFormat = &Error{ + code: "PostPolicyInvalidKeyName", + description: "Invalid according to Policy: Policy Conditions failed", + httpStatusCode: http.StatusForbidden, + } + ErrMalformedJSON = &Error{ + code: "MalformedJSON", + description: "The JSON was not well-formed or did not validate against our published format.", + httpStatusCode: http.StatusBadRequest, + } + ErrMalformedACLError = &Error{ + code: "MalformedACLError", + description: "The ACL that you provided was not well formed or did not validate against our published schema.", + httpStatusCode: http.StatusBadRequest, + } +) diff --git a/s3/api/responses/writers.go b/s3/api/responses/writers.go new file mode 100644 index 000000000..ac1805b27 --- /dev/null +++ b/s3/api/responses/writers.go @@ -0,0 +1,316 @@ +package responses + +import ( + "bytes" + "encoding/base64" + "encoding/xml" + "errors" + "fmt" + "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/utils" + "io" + "net/http" + "reflect" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go/private/protocol" +) + +const ( + mimeTypeXml = "application/xml" + noPayload = "nopayload" +) + +var errValueNotSet = fmt.Errorf("value not set") + +func WriteSuccessResponse(w http.ResponseWriter, output interface{}, locationName string) { + _ = WriteResponse(w, http.StatusOK, output, locationName) +} + +type ErrorOutput struct { + _ struct{} `type:"structure"` + Code string `locationName:"Code"` + Message string `locationName:"Message"` + Resource string `locationName:"Resource"` + RequestID string `locationName:"RequestID"` +} + +func WriteErrorResponse(w http.ResponseWriter, r *http.Request, rerr *Error) { + _ = WriteResponse(w, rerr.HTTPStatusCode(), &ErrorOutput{ + Code: rerr.Code(), + Message: rerr.Description(), + Resource: r.URL.Path, + RequestID: "", + }, "Error") +} + +func WriteResponse(w http.ResponseWriter, statusCode int, output interface{}, locationName string) (err error) { + setCommonHeaders(w.Header()) + + outv := reflect.Indirect(reflect.ValueOf(wrapOutput(output, locationName))) + if !outv.IsValid() { + w.WriteHeader(statusCode) + return + } + + defer func() { + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + }() + + err = setFieldRequestID(w.Header(), outv) + if err != nil { + return + } + + body, clen, ctyp, err := extractBody(outv) + if err != nil { + return + } + + if body == nil { + err = setLocationHeaders(w.Header(), outv) + if err != nil { + return + } + w.WriteHeader(statusCode) + return + } + + defer body.Close() + + w.Header().Set(consts.ContentLength, fmt.Sprintf("%d", clen)) + w.Header().Set(consts.ContentType, ctyp) + + err = setLocationHeaders(w.Header(), outv) + if err != nil { + return + } + + w.WriteHeader(statusCode) + + _, err = io.Copy(w, body) + + return +} + +func wrapOutput(output interface{}, locationName string) (wrapper interface{}) { + if locationName == "" { + wrapper = output + return + } + + outputTag := fmt.Sprintf(`locationName:"%s" type:"structure"`, locationName) + fields := []reflect.StructField{ + { + Name: "_", + Type: reflect.TypeOf(struct{}{}), + Tag: `payload:"Output" type:"structure"`, + PkgPath: "responses", + }, + { + Name: "Output", + Type: reflect.TypeOf(output), + Tag: reflect.StructTag(outputTag), + }, + } + wrtyp := reflect.StructOf(fields) + wrval := reflect.New(wrtyp) + wrval.Elem().FieldByName("Output").Set(reflect.ValueOf(output)) + wrapper = wrval.Interface() + return +} + +func extractBody(v reflect.Value) (body io.ReadCloser, clen int, ctyp string, err error) { + ptyp, pfvl := getPayload(v) + if ptyp == noPayload { + return + } + + if ptyp == "structure" || ptyp == "" { + var buf bytes.Buffer + buf.WriteString(xml.Header) + err = xmlutil.BuildXML(v.Interface(), xml.NewEncoder(&buf)) + if err != nil { + return + } + body = io.NopCloser(&buf) + clen = buf.Len() + ctyp = mimeTypeXml + return + } + + if pfvl.Interface() == nil { + return + } + + clen = -1 + + body, ok := pfvl.Interface().(io.ReadCloser) + if !ok { + err = fmt.Errorf("unsupported payload type <%s>", pfvl.Type()) + } + + return +} + +func setFieldRequestID(headers http.Header, outv reflect.Value) (err error) { + reqId := headers.Get(consts.AmzRequestID) + + idv := outv.FieldByName("RequestID") + if !idv.IsValid() { + return + } + + switch idv.Interface().(type) { + case *string: + idv.Set(reflect.ValueOf(&reqId)) + case string: + idv.Set(reflect.ValueOf(reqId)) + default: + err = errValueNotSet + } + + return +} + +func setCommonHeaders(headers http.Header) { + headers.Set(consts.ServerInfo, consts.DefaultServerInfo) + headers.Set(consts.AcceptRanges, "bytes") + headers.Set(consts.AmzRequestID, getRequestID()) +} + +func getRequestID() string { + return fmt.Sprintf("%d", time.Now().UnixNano()) +} + +func setLocationHeaders(header http.Header, v reflect.Value) (err error) { + for i := 0; i < v.NumField(); i++ { + fv := reflect.Indirect(v.Field(i)) + ft := v.Type().Field(i) + + if n := ft.Name; n[0:1] == strings.ToLower(n[0:1]) { + continue + } + + if !fv.IsValid() { + continue + } + + if fv.Kind() == reflect.Interface && !fv.Elem().IsValid() { + continue + } + + switch ft.Tag.Get("location") { + case "header": + name := utils.CoalesceStr(ft.Tag.Get("locationName"), ft.Name) + err = setHeaders(&header, fv, name, ft.Tag) + case "headers": + err = setHeadersMap(&header, fv, ft.Tag) + } + + if err != nil { + return + } + } + + return +} + +func setHeaders(header *http.Header, v reflect.Value, name string, tag reflect.StructTag) (err error) { + str, err := convertType(v, tag) + if err != nil { + return + } + name = strings.TrimSpace(name) + str = strings.TrimSpace(str) + header.Add(name, str) + return +} + +func setHeadersMap(header *http.Header, v reflect.Value, tag reflect.StructTag) (err error) { + prefix := tag.Get("locationName") + for _, key := range v.MapKeys() { + var str string + str, err = convertType(v.MapIndex(key), tag) + if errors.Is(err, errValueNotSet) { + err = nil + continue + } + if err != nil { + return + } + keyStr := strings.TrimSpace(key.String()) + str = strings.TrimSpace(str) + header.Add(prefix+keyStr, str) + } + return +} + +func getPayload(v reflect.Value) (ptyp string, pfvl reflect.Value) { + ptyp = noPayload + + field, ok := v.Type().FieldByName("_") + if !ok { + return + } + + noPayloadValue := field.Tag.Get(noPayload) + if noPayloadValue != "" { + return + } + + payloadName := field.Tag.Get("payload") + if payloadName == "" { + return + } + + pfld, ok := v.Type().FieldByName(payloadName) + if !ok { + return + } + + ptyp = pfld.Tag.Get("type") + pfvl = reflect.Indirect(v.FieldByName(payloadName)) + + return +} + +func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error) { + v = reflect.Indirect(v) + if !v.IsValid() { + return + } + + switch value := v.Interface().(type) { + case string: + str = value + case []byte: + str = base64.StdEncoding.EncodeToString(value) + case bool: + str = strconv.FormatBool(value) + case int64: + str = strconv.FormatInt(value, 10) + case time.Time: + str = protocol.FormatTime(getTimeFormat(tag), value) + default: + err = fmt.Errorf("unsupported value type <%s>", v.Type()) + } + + return +} + +func getTimeFormat(tag reflect.StructTag) string { + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.RFC822TimeFormatName + if tag.Get("location") == "querystring" { + format = protocol.ISO8601TimeFormatName + } + } + return format +} diff --git a/s3/api/responses/writers_bucket.go b/s3/api/responses/writers_bucket.go new file mode 100644 index 000000000..6024f29ac --- /dev/null +++ b/s3/api/responses/writers_bucket.go @@ -0,0 +1,82 @@ +package responses + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" +) + +func newS3Owner(userId string) *s3.Owner { + return new(s3.Owner).SetID(userId).SetDisplayName(userId) +} + +func newS3FullControlGrant(userId string) *s3.Grant { + return new(s3.Grant).SetGrantee(new(s3.Grantee).SetType(s3.TypeCanonicalUser).SetID(userId).SetDisplayName(userId)).SetPermission(s3.PermissionFullControl) +} + +var ( + s3AllUsersReadGrant = new(s3.Grant).SetGrantee(new(s3.Grantee).SetType(s3.TypeGroup).SetURI(consts.AllUsersURI)).SetPermission(s3.PermissionRead) + s3AllUsersWriteGrant = new(s3.Grant).SetGrantee(new(s3.Grantee).SetType(s3.TypeGroup).SetURI(consts.AllUsersURI)).SetPermission(s3.PermissionWrite) +) + +func WriteCreateBucketResponse(w http.ResponseWriter, r *http.Request, buc *object.Bucket) { + output := new(s3.CreateBucketOutput).SetLocation(r.URL.Path) + w.Header().Add(consts.AmzACL, buc.ACL) + WriteSuccessResponse(w, output, "") + return +} + +func WriteHeadBucketResponse(w http.ResponseWriter, r *http.Request, buc *object.Bucket) { + output := new(s3.HeadBucketOutput) + w.Header().Add(consts.AmzACL, buc.ACL) + WriteSuccessResponse(w, output, "") + return +} + +func WriteDeleteBucketResponse(w http.ResponseWriter) { + output := new(s3.DeleteBucketOutput) + WriteSuccessResponse(w, output, "") + return +} + +func WriteListBucketsResponse(w http.ResponseWriter, r *http.Request, list *object.BucketList) { + output := new(s3.ListBucketsOutput) + output.SetOwner(newS3Owner(list.Owner)) + s3Buckets := make([]*s3.Bucket, 0) + for _, buc := range list.Buckets { + s3Bucket := new(s3.Bucket).SetName(buc.Name).SetCreationDate(buc.Created) + s3Buckets = append(s3Buckets, s3Bucket) + w.Header().Add(consts.AmzACL, buc.ACL) + } + output.SetBuckets(s3Buckets) + WriteSuccessResponse(w, output, "ListAllMyBucketsResult") + return +} + +func WritePutBucketAclResponse(w http.ResponseWriter, r *http.Request) { + output := new(s3.PutBucketAclOutput) + WriteSuccessResponse(w, output, "") + return +} + +func WriteGetBucketACLResponse(w http.ResponseWriter, r *http.Request, acl *object.ACL) { + output := new(s3.GetBucketAclOutput) + output.SetOwner(newS3Owner(acl.Owner)) + grants := make([]*s3.Grant, 0) + grants = append(grants, newS3FullControlGrant(acl.Owner)) + switch acl.ACL { + case s3.BucketCannedACLPrivate: + case s3.BucketCannedACLPublicRead: + grants = append(grants, s3AllUsersReadGrant) + case s3.BucketCannedACLPublicReadWrite: + grants = append(grants, s3AllUsersReadGrant, s3AllUsersWriteGrant) + default: + panic(fmt.Sprintf("unknwon acl <%s>", acl.ACL)) + } + output.SetGrants(grants) + w.Header().Add(consts.AmzACL, acl.ACL) + WriteSuccessResponse(w, output, "AccessControlPolicy") + return +} diff --git a/s3/api/responses/writers_multipart.go b/s3/api/responses/writers_multipart.go new file mode 100644 index 000000000..a50ceca1f --- /dev/null +++ b/s3/api/responses/writers_multipart.go @@ -0,0 +1,37 @@ +package responses + +import ( + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" +) + +func WriteCreateMultipartUploadResponse(w http.ResponseWriter, r *http.Request, multipart *object.Multipart) { + output := new(s3.CreateMultipartUploadOutput) + output.SetBucket(multipart.Bucket) + output.SetKey(multipart.Object) + output.SetUploadId(multipart.UploadID) + WriteSuccessResponse(w, output, "InitiateMultipartUploadResult") +} + +func WriteUploadPartResponse(w http.ResponseWriter, r *http.Request, part *object.Part) { + output := new(s3.UploadPartOutput) + output.SetETag(`"` + part.ETag + `"`) + w.Header().Set(consts.Cid, part.CID) + WriteSuccessResponse(w, output, "") +} + +func WriteAbortMultipartUploadResponse(w http.ResponseWriter, r *http.Request) { + output := new(s3.AbortMultipartUploadOutput) + WriteSuccessResponse(w, output, "") +} + +func WriteCompleteMultipartUploadResponse(w http.ResponseWriter, r *http.Request, obj *object.Object) { + output := new(s3.CompleteMultipartUploadOutput) + output.SetBucket(obj.Bucket) + output.SetKey(obj.Name) + output.SetETag(`"` + obj.ETag + `"`) + w.Header().Set(consts.Cid, obj.CID) + WriteSuccessResponse(w, output, "CompleteMultipartUploadResult") +} diff --git a/s3/api/responses/writers_object.go b/s3/api/responses/writers_object.go new file mode 100644 index 000000000..a5ac45c98 --- /dev/null +++ b/s3/api/responses/writers_object.go @@ -0,0 +1,182 @@ +package responses + +import ( + "encoding/base64" + "fmt" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/utils" + "io" + "net/http" +) + +func WritePutObjectResponse(w http.ResponseWriter, r *http.Request, obj *object.Object) { + output := new(s3.PutObjectOutput) + output.SetETag(`"` + obj.ETag + `"`) + w.Header().Set(consts.Cid, obj.CID) + WriteSuccessResponse(w, output, "") +} + +func WriteCopyObjectResponse(w http.ResponseWriter, r *http.Request, obj *object.Object) { + output := new(s3.CopyObjectResult) + output.SetETag(`"` + obj.ETag + `"`) + output.SetLastModified(obj.ModTime) + w.Header().Set(consts.Cid, obj.CID) + WriteSuccessResponse(w, output, "CopyObjectResult") +} + +func WriteHeadObjectResponse(w http.ResponseWriter, r *http.Request, obj *object.Object) { + output := new(s3.HeadObjectOutput) + output.SetETag(`"` + obj.ETag + `"`) + output.SetLastModified(obj.ModTime) + output.SetContentLength(obj.Size) + output.SetContentType(obj.ContentType) + output.SetContentEncoding(obj.ContentEncoding) + if !obj.Expires.IsZero() { + output.SetExpiration(obj.Expires.UTC().Format(http.TimeFormat)) + } + w.Header().Set(consts.Cid, obj.CID) + output.SetMetadata(map[string]*string{ + consts.Cid: &obj.CID, + }) + WriteSuccessResponse(w, output, "") +} + +func WriteDeleteObjectResponse(w http.ResponseWriter, r *http.Request, obj *object.Object) { + output := new(s3.DeleteObjectOutput) + WriteSuccessResponse(w, output, "") +} + +func WriteDeleteObjectsResponse(w http.ResponseWriter, r *http.Request, toErr func(error) *Error, deletes []*object.DeletedObject) { + output := new(s3.DeleteObjectsOutput) + objs := make([]*s3.DeletedObject, 0) + errs := make([]*s3.Error, 0) + for _, obj := range deletes { + if obj.DeleteErr != nil { + rerr := toErr(obj.DeleteErr) + s3Err := new(s3.Error) + s3Err.SetCode(rerr.Code()) + s3Err.SetMessage(rerr.Description()) + s3Err.SetKey(obj.Object) + errs = append(errs, s3Err) + continue + } + s3Obj := new(s3.DeletedObject) + s3Obj.SetKey(obj.Object) + objs = append(objs, s3Obj) + } + if len(errs) > 0 { + output.SetErrors(errs) + } + if len(objs) > 0 { + output.SetDeleted(objs) + } + WriteSuccessResponse(w, output, "DeleteResult") +} + +func WriteGetObjectResponse(w http.ResponseWriter, r *http.Request, obj *object.Object, body io.ReadCloser) { + output := new(s3.GetObjectOutput) + output.SetLastModified(obj.ModTime) + output.SetContentLength(obj.Size) + output.SetContentType(obj.ContentType) + output.SetContentEncoding(obj.ContentEncoding) + output.SetBody(body) + if !obj.Expires.IsZero() { + output.SetExpiration(obj.Expires.UTC().Format(http.TimeFormat)) + } + w.Header().Set(consts.Cid, obj.CID) + output.SetMetadata(map[string]*string{ + consts.Cid: &obj.CID, + }) + WriteSuccessResponse(w, output, "") +} + +func WriteListObjectsResponse(w http.ResponseWriter, r *http.Request, list *object.ObjectsList) { + out := new(s3.ListObjectsOutput) + out.SetName(list.Args.Bucket) + out.SetEncodingType(list.Args.EncodingType) + out.SetPrefix(utils.S3Encode(list.Args.Prefix, list.Args.EncodingType)) + out.SetMarker(utils.S3Encode(list.Args.Marker, list.Args.EncodingType)) + out.SetDelimiter(utils.S3Encode(list.Args.Delimiter, list.Args.EncodingType)) + out.SetMaxKeys(list.Args.MaxKeys) + out.SetNextMarker(list.NextMarker) + out.SetIsTruncated(list.IsTruncated) + s3Objs := make([]*s3.Object, len(list.Objects)) + for i, obj := range list.Objects { + s3Obj := new(s3.Object) + s3Obj.SetETag(`"` + obj.ETag + `"`) + s3Obj.SetOwner(newS3Owner(list.Owner)) + s3Obj.SetLastModified(obj.ModTime) + s3Obj.SetKey(utils.S3Encode(obj.Name, list.Args.EncodingType)) + s3Obj.SetSize(obj.Size) + s3Obj.SetStorageClass("") + s3Objs[i] = s3Obj + w.Header().Add(consts.Cid, obj.CID) + } + out.SetContents(s3Objs) + s3CommPrefixes := make([]*s3.CommonPrefix, len(list.Prefixes)) + for i, cpf := range list.Prefixes { + pfx := new(s3.CommonPrefix) + pfx.SetPrefix(utils.S3Encode(cpf, list.Args.EncodingType)) + s3CommPrefixes[i] = pfx + } + out.SetCommonPrefixes(s3CommPrefixes) + WriteSuccessResponse(w, out, "ListBucketResult") +} + +func WriteListObjectsV2Response(w http.ResponseWriter, r *http.Request, list *object.ObjectsListV2) { + out := new(s3.ListObjectsV2Output) + out.SetName(list.Args.Bucket) + out.SetEncodingType(list.Args.EncodingType) + out.SetStartAfter(utils.S3Encode(list.Args.After, list.Args.EncodingType)) + out.SetDelimiter(utils.S3Encode(list.Args.Delimiter, list.Args.EncodingType)) + out.SetPrefix(utils.S3Encode(list.Args.Prefix, list.Args.EncodingType)) + out.SetMaxKeys(list.Args.MaxKeys) + out.SetContinuationToken(base64.StdEncoding.EncodeToString([]byte(list.Args.Token))) + out.SetNextContinuationToken(base64.StdEncoding.EncodeToString([]byte(list.NextContinuationToken))) + out.SetIsTruncated(list.IsTruncated) + s3Objs := make([]*s3.Object, len(list.Objects)) + for i, obj := range list.Objects { + s3Obj := new(s3.Object) + s3Obj.SetETag(`"` + obj.ETag + `"`) + if list.Args.FetchOwner { + s3Obj.SetOwner(newS3Owner(list.Owner)) + } + s3Obj.SetLastModified(obj.ModTime) + s3Obj.SetKey(utils.S3Encode(obj.Name, list.Args.EncodingType)) + s3Obj.SetSize(obj.Size) + s3Obj.SetStorageClass("") + s3Objs[i] = s3Obj + w.Header().Add(consts.Cid, obj.CID) + } + out.SetContents(s3Objs) + s3CommPrefixes := make([]*s3.CommonPrefix, len(list.Prefixes)) + for i, cpf := range list.Prefixes { + pfx := new(s3.CommonPrefix) + pfx.SetPrefix(utils.S3Encode(cpf, list.Args.EncodingType)) + s3CommPrefixes[i] = pfx + } + out.SetCommonPrefixes(s3CommPrefixes) + out.SetKeyCount(int64(len(list.Objects) + len(list.Prefixes))) + WriteSuccessResponse(w, out, "ListBucketResult") +} + +func WriteGetObjectACLResponse(w http.ResponseWriter, r *http.Request, acl *object.ACL) { + output := new(s3.GetObjectAclOutput) + output.SetOwner(newS3Owner(acl.Owner)) + grants := make([]*s3.Grant, 0) + grants = append(grants, newS3FullControlGrant(acl.Owner)) + switch acl.ACL { + case s3.BucketCannedACLPrivate: + case s3.BucketCannedACLPublicRead: + grants = append(grants, s3AllUsersReadGrant) + case s3.BucketCannedACLPublicReadWrite: + grants = append(grants, s3AllUsersReadGrant, s3AllUsersWriteGrant) + default: + panic(fmt.Sprintf("unknwo acl <%s>", acl.ACL)) + } + output.SetGrants(grants) + WriteSuccessResponse(w, output, "AccessControlPolicy") + return +} diff --git a/s3/api/routers/options.go b/s3/api/routers/options.go new file mode 100644 index 000000000..2dce7e18f --- /dev/null +++ b/s3/api/routers/options.go @@ -0,0 +1,3 @@ +package routers + +type Option func(routers *Routers) diff --git a/s3/api/routers/proto.go b/s3/api/routers/proto.go new file mode 100644 index 000000000..665e1271b --- /dev/null +++ b/s3/api/routers/proto.go @@ -0,0 +1,7 @@ +package routers + +import "net/http" + +type Routerser interface { + Register() http.Handler +} diff --git a/s3/api/routers/routers.go b/s3/api/routers/routers.go new file mode 100644 index 000000000..4ef8187c8 --- /dev/null +++ b/s3/api/routers/routers.go @@ -0,0 +1,96 @@ +package routers + +import ( + "github.com/bittorrent/go-btfs/s3/api/handlers" + "github.com/gorilla/mux" + "net/http" +) + +type Routers struct { + handlers handlers.Handlerser +} + +func NewRouters(handlers handlers.Handlerser, options ...Option) (routers *Routers) { + routers = &Routers{ + handlers: handlers, + } + for _, option := range options { + option(routers) + } + return +} + +func (routers *Routers) Register() http.Handler { + root := mux.NewRouter() + + hs := routers.handlers + + // Middlewares + root.Use(hs.Cors, hs.Log, hs.Sign) + + bucket := root.PathPrefix("/{Bucket}").Subrouter() + + // HeadObject + bucket.Methods(http.MethodHead).Path("/{Key:.+}").HandlerFunc(hs.HeadObjectHandler) + + // CreateMultipart + bucket.Methods(http.MethodPost).Path("/{Key:.+}").HandlerFunc(hs.CreateMultipartUploadHandler).Queries("uploads", "") + + // CompleteMultipartUpload + bucket.Methods(http.MethodPost).Path("/{Key:.+}").HandlerFunc(hs.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") + + // UploadPart + bucket.Methods(http.MethodPut).Path("/{Key:.+}").HandlerFunc(hs.UploadPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") + + // CopyObject + bucket.Methods(http.MethodPut).Path("/{Key:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(hs.CopyObjectHandler) + + // PutObject + bucket.Methods(http.MethodPut).Path("/{Key:.+}").HandlerFunc(hs.PutObjectHandler) + + // AbortMultipart + bucket.Methods(http.MethodDelete).Path("/{Key:.+}").HandlerFunc(hs.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") + + // DeleteObject + bucket.Methods(http.MethodDelete).Path("/{Key:.+}").HandlerFunc(hs.DeleteObjectHandler) + + // GetObjectACL + bucket.Methods(http.MethodGet).Path("/{Key:.+}").HandlerFunc(hs.GetObjectACLHandler).Queries("acl", "") + + // GetObject + bucket.Methods(http.MethodGet).Path("/{Key:.+}").HandlerFunc(hs.GetObjectHandler) + + // GetBucketACL + bucket.Methods(http.MethodGet).HandlerFunc(hs.GetBucketACLHandler).Queries("acl", "") + + // ListObjectsV2 + bucket.Methods(http.MethodGet).HandlerFunc(hs.ListObjectsV2Handler).Queries("list-type", "2") + + // ListObjects + bucket.Methods(http.MethodGet).HandlerFunc(hs.ListObjectsHandler) + + // PutBucketACL + bucket.Methods(http.MethodPut).HandlerFunc(hs.PutBucketACLHandler).Queries("acl", "") + + // CreateBucket + bucket.Methods(http.MethodPut).HandlerFunc(hs.CreateBucketHandler) + + // HeadBucket + bucket.Methods(http.MethodHead).HandlerFunc(hs.HeadBucketHandler) + + // DeleteObjects + bucket.Methods(http.MethodPost).HandlerFunc(hs.DeleteObjectsHandler).Queries("delete", "") + + // DeleteBucket + bucket.Methods(http.MethodDelete).HandlerFunc(hs.DeleteBucketHandler) + + // ListBuckets + root.Methods(http.MethodGet).HandlerFunc(hs.ListBucketsHandler) + + // Options + root.Methods(http.MethodOptions).HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + return root +} diff --git a/s3/api/server/options.go b/s3/api/server/options.go new file mode 100644 index 000000000..7d34ed0bb --- /dev/null +++ b/s3/api/server/options.go @@ -0,0 +1,13 @@ +package server + +const defaultServerAddress = "127.0.0.1:6001" + +type Option func(*Server) + +func WithAddress(address string) Option { + return func(s *Server) { + if address != "" { + s.address = address + } + } +} diff --git a/s3/api/server/server.go b/s3/api/server/server.go new file mode 100644 index 000000000..c38b38453 --- /dev/null +++ b/s3/api/server/server.go @@ -0,0 +1,72 @@ +package server + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/api/routers" + "net/http" + "sync" +) + +var ( + ErrServerStarted = errors.New("server started") + ErrServerNotStarted = errors.New("server not started") +) + +type Server struct { + routers routers.Routerser + address string + + shutdown func() error + mutex sync.Mutex +} + +func NewServer(routers routers.Routerser, options ...Option) (s *Server) { + s = &Server{ + routers: routers, + address: defaultServerAddress, + shutdown: nil, + mutex: sync.Mutex{}, + } + for _, option := range options { + option(s) + } + return +} + +func (s *Server) Start() (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.shutdown != nil { + err = ErrServerStarted + return + } + + httpSvr := &http.Server{ + Addr: s.address, + Handler: s.routers.Register(), + } + + s.shutdown = func() error { + return httpSvr.Shutdown(context.TODO()) + } + + go func() { + _ = httpSvr.ListenAndServe() + }() + + return +} + +func (s *Server) Stop() (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + if s.shutdown == nil { + err = ErrServerNotStarted + return + } + err = s.shutdown() + s.shutdown = nil + return +} diff --git a/s3/api/services/accesskey/instance.go b/s3/api/services/accesskey/instance.go new file mode 100644 index 000000000..254abd174 --- /dev/null +++ b/s3/api/services/accesskey/instance.go @@ -0,0 +1,48 @@ +package accesskey + +import ( + "github.com/bittorrent/go-btfs/s3/api/providers" + "sync" +) + +var svcInstance Service + +var once sync.Once + +func InitService(providers providers.Providerser, options ...Option) { + once.Do(func() { + svcInstance = NewService(providers, options...) + }) +} + +func GetServiceInstance() Service { + return svcInstance +} + +func Generate() (ack *AccessKey, err error) { + return svcInstance.Generate() +} + +func Enable(key string) (err error) { + return svcInstance.Enable(key) +} + +func Disable(key string) (err error) { + return svcInstance.Disable(key) +} + +func Reset(key string) (err error) { + return svcInstance.Reset(key) +} + +func Delete(key string) (err error) { + return svcInstance.Delete(key) +} + +func Get(key string) (record *AccessKey, err error) { + return svcInstance.Get(key) +} + +func List() (list []*AccessKey, err error) { + return svcInstance.List() +} diff --git a/s3/api/services/accesskey/options.go b/s3/api/services/accesskey/options.go new file mode 100644 index 000000000..f959d6560 --- /dev/null +++ b/s3/api/services/accesskey/options.go @@ -0,0 +1,40 @@ +package accesskey + +import ( + "github.com/bittorrent/go-btfs/s3/ctxmu" + "time" +) + +const ( + defaultSecretLength = 32 + defaultStoreKeyPrefix = "access-keys:" + defaultWaitLockTimout = 2 * time.Minute +) + +var defaultLock = ctxmu.NewDefaultMultiCtxRWMutex() + +type Option func(svc *service) + +func WithSecretLength(length int) Option { + return func(svc *service) { + svc.secretLength = length + } +} + +func WithStoreKeyPrefix(prefix string) Option { + return func(svc *service) { + svc.storeKeyPrefix = prefix + } +} + +func WithWaitLockTimout(timout time.Duration) Option { + return func(svc *service) { + svc.waitLockTimeout = timout + } +} + +func WithLock(lock ctxmu.MultiCtxRWLocker) Option { + return func(svc *service) { + svc.lock = lock + } +} diff --git a/s3/api/services/accesskey/proto.go b/s3/api/services/accesskey/proto.go new file mode 100644 index 000000000..40535f0b2 --- /dev/null +++ b/s3/api/services/accesskey/proto.go @@ -0,0 +1,27 @@ +package accesskey + +import ( + "errors" + "time" +) + +var ErrNotFound = errors.New("not found") + +type Service interface { + Generate() (record *AccessKey, err error) + Enable(key string) (err error) + Disable(key string) (err error) + Reset(key string) (err error) + Delete(key string) (err error) + Get(key string) (ack *AccessKey, err error) + List() (list []*AccessKey, err error) +} + +type AccessKey struct { + Key string `json:"key"` + Secret string `json:"secret"` + Enable bool `json:"enable"` + IsDeleted bool `json:"is_deleted"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/s3/api/services/accesskey/service.go b/s3/api/services/accesskey/service.go new file mode 100644 index 000000000..3deabe580 --- /dev/null +++ b/s3/api/services/accesskey/service.go @@ -0,0 +1,170 @@ +package accesskey + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/ctxmu" + "github.com/bittorrent/go-btfs/transaction/storage" + "github.com/bittorrent/go-btfs/utils" + "github.com/google/uuid" + "time" +) + +var _ Service = (*service)(nil) + +type service struct { + providers providers.Providerser + secretLength int + storeKeyPrefix string + lock ctxmu.MultiCtxRWLocker + waitLockTimeout time.Duration +} + +func NewService(providers providers.Providerser, options ...Option) Service { + svc := &service{ + providers: providers, + secretLength: defaultSecretLength, + storeKeyPrefix: defaultStoreKeyPrefix, + lock: defaultLock, + waitLockTimeout: defaultWaitLockTimout, + } + for _, option := range options { + option(svc) + } + return svc +} + +func (svc *service) Generate() (record *AccessKey, err error) { + now := time.Now() + record = &AccessKey{ + Key: svc.newKey(), + Secret: svc.newSecret(), + Enable: true, + IsDeleted: false, + CreatedAt: now, + UpdatedAt: now, + } + err = svc.providers.StateStore().Put(svc.getStoreKey(record.Key), record) + return +} + +func (svc *service) Enable(key string) (err error) { + enable := true + err = svc.update(key, &updateArgs{ + Enable: &enable, + }) + return +} + +func (svc *service) Disable(key string) (err error) { + enable := false + err = svc.update(key, &updateArgs{ + Enable: &enable, + }) + return +} + +func (svc *service) Reset(key string) (err error) { + secret := svc.newSecret() + err = svc.update(key, &updateArgs{ + Secret: &secret, + }) + return +} + +func (svc *service) Delete(key string) (err error) { + isDelete := true + err = svc.update(key, &updateArgs{ + IsDelete: &isDelete, + }) + return +} + +func (svc *service) Get(key string) (ack *AccessKey, err error) { + ack = &AccessKey{} + err = svc.providers.StateStore().Get(svc.getStoreKey(key), ack) + if err != nil && !errors.Is(err, providers.ErrStateStoreNotFound) { + return + } + if errors.Is(err, providers.ErrStateStoreNotFound) || ack.IsDeleted { + err = ErrNotFound + } + return +} + +func (svc *service) List() (list []*AccessKey, err error) { + err = svc.providers.StateStore().Iterate(svc.storeKeyPrefix, func(key, _ []byte) (stop bool, er error) { + record := &AccessKey{} + er = svc.providers.StateStore().Get(string(key), record) + if er != nil { + return + } + if record.IsDeleted { + return + } + list = append(list, record) + return + }) + return +} + +func (svc *service) newKey() (key string) { + key = uuid.NewString() + return +} + +func (svc *service) newSecret() (secret string) { + secret = utils.RandomString(svc.secretLength) + return +} + +func (svc *service) getStoreKey(key string) (storeKey string) { + storeKey = svc.storeKeyPrefix + key + return +} + +type updateArgs struct { + Enable *bool + Secret *string + IsDelete *bool +} + +func (svc *service) update(key string, args *updateArgs) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), svc.waitLockTimeout) + defer cancel() + + err = svc.lock.Lock(ctx, key) + if err != nil { + return + } + defer svc.lock.Unlock(key) + + ack := &AccessKey{} + stk := svc.getStoreKey(key) + + err = svc.providers.StateStore().Get(stk, ack) + if err != nil && !errors.Is(err, storage.ErrNotFound) { + return + } + if errors.Is(err, storage.ErrNotFound) || ack.IsDeleted { + err = ErrNotFound + return + } + + if args.Enable != nil { + ack.Enable = *args.Enable + } + if args.Secret != nil { + ack.Secret = *args.Secret + } + if args.IsDelete != nil { + ack.IsDeleted = *args.IsDelete + } + + ack.UpdatedAt = time.Now() + + err = svc.providers.StateStore().Put(stk, ack) + + return +} diff --git a/s3/api/services/object/clean_read_closer.go b/s3/api/services/object/clean_read_closer.go new file mode 100644 index 000000000..fd767da8d --- /dev/null +++ b/s3/api/services/object/clean_read_closer.go @@ -0,0 +1,41 @@ +package object + +import ( + "context" + "io" + "time" +) + +func WrapCleanReadCloser(rc io.ReadCloser, timeout time.Duration, afterCloseHooks ...func()) io.ReadCloser { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + arc := &cleanReadCloser{ + rc: rc, + cancel: cancel, + } + go func() { + <-ctx.Done() + _ = rc.Close() + // call after hooks by stack order + for len(afterCloseHooks) > 0 { + idx := len(afterCloseHooks) - 1 + f := afterCloseHooks[idx] + f() + afterCloseHooks = afterCloseHooks[:idx] + } + }() + return arc +} + +type cleanReadCloser struct { + rc io.ReadCloser + cancel context.CancelFunc +} + +func (h *cleanReadCloser) Read(p []byte) (n int, err error) { + return h.rc.Read(p) +} + +func (h *cleanReadCloser) Close() error { + defer h.cancel() + return h.rc.Close() +} diff --git a/s3/api/services/object/options.go b/s3/api/services/object/options.go new file mode 100644 index 000000000..a913b7ef2 --- /dev/null +++ b/s3/api/services/object/options.go @@ -0,0 +1,68 @@ +package object + +import ( + "github.com/bittorrent/go-btfs/s3/ctxmu" + "time" +) + +const ( + defaultKeySeparator = "/" + defaultBucketSpace = "s3:bkt" + defaultObjectSpace = "s3:obj" + defaultUploadSpace = "s3:upl" + defaultCidrefSpace = "s3:cid" + defaultOperationTimeout = 5 * time.Minute + defaultCloseBodyTimeout = 10 * time.Minute +) + +var defaultLock = ctxmu.NewDefaultMultiCtxRWMutex() + +type Option func(svc *service) + +func WithKeySeparator(separator string) Option { + return func(svc *service) { + svc.keySeparator = separator + } +} + +func WithBucketSpace(space string) Option { + return func(svc *service) { + svc.bucketSpace = space + } +} + +func WithObjectSpace(space string) Option { + return func(svc *service) { + svc.objectSpace = space + } +} + +func WithUploadSpace(space string) Option { + return func(svc *service) { + svc.uploadSpace = space + } +} + +func WithCidrefSpace(space string) Option { + return func(svc *service) { + svc.cidrefSpace = space + } +} + +func WithOperationTimeout(timeout time.Duration) Option { + return func(svc *service) { + svc.operationTimeout = timeout + } +} + +func WithCloseBodyTimeout(timeout time.Duration) Option { + return func(svc *service) { + svc.closeBodyTimeout = timeout + } +} + +func WithLock(lock ctxmu.MultiCtxRWLocker) Option { + return func(svc *service) { + svc.lock = lock + } +} diff --git a/s3/api/services/object/proto.go b/s3/api/services/object/proto.go new file mode 100644 index 000000000..2fbe251de --- /dev/null +++ b/s3/api/services/object/proto.go @@ -0,0 +1,279 @@ +package object + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/hash" + "io" + "time" +) + +var ( + ErrBucketNotFound = errors.New("bucket not found") + ErrBucketNotEmpty = errors.New("bucket not empty") + ErrObjectNotFound = errors.New("object not found") + ErrUploadNotFound = errors.New("upload not found") + ErrNotAllowed = errors.New("authentication not allowed") + ErrBucketAlreadyExists = errors.New("bucket already exists") + ErrPartNotExists = errors.New("part not exists") + ErrPartETagNotMatch = errors.New("part etag not match") + ErrPartTooSmall = errors.New("part size too small") + ErrCanceled = context.Canceled + ErrTimout = context.DeadlineExceeded +) + +type Service interface { + CreateBucket(ctx context.Context, args *CreateBucketArgs) (bucket *Bucket, err error) + GetBucket(ctx context.Context, args *GetBucketArgs) (bucket *Bucket, err error) + DeleteBucket(ctx context.Context, args *DeleteBucketArgs) (err error) + ListBuckets(ctx context.Context, args *ListBucketsArgs) (list *BucketList, err error) + PutBucketACL(ctx context.Context, args *PutBucketACLArgs) (err error) + GetBucketACL(ctx context.Context, args *GetBucketACLArgs) (acl *ACL, err error) + + PutObject(ctx context.Context, args *PutObjectArgs) (object *Object, err error) + CopyObject(ctx context.Context, args *CopyObjectArgs) (object *Object, err error) + GetObject(ctx context.Context, args *GetObjectArgs) (object *Object, body io.ReadCloser, err error) + DeleteObject(ctx context.Context, args *DeleteObjectArgs) (err error) + DeleteObjects(ctx context.Context, args *DeleteObjectsArgs) (deletes []*DeletedObject, err error) + ListObjects(ctx context.Context, args *ListObjectsArgs) (list *ObjectsList, err error) + ListObjectsV2(ctx context.Context, args *ListObjectsV2Args) (list *ObjectsListV2, err error) + GetObjectACL(ctx context.Context, args *GetObjectACLArgs) (acl *ACL, err error) + + CreateMultipartUpload(ctx context.Context, args *CreateMultipartUploadArgs) (multipart *Multipart, err error) + UploadPart(ctx context.Context, args *UploadPartArgs) (part *Part, err error) + AbortMultipartUpload(ctx context.Context, args *AbortMultipartUploadArgs) (err error) + CompleteMultiPartUpload(ctx context.Context, args *CompleteMultipartUploadArgs) (object *Object, err error) +} + +type CreateBucketArgs struct { + UserId string + ACL string + Bucket string + Region string +} + +type GetBucketArgs struct { + UserId string + Bucket string +} + +type DeleteBucketArgs struct { + UserId string + Bucket string +} + +type ListBucketsArgs struct { + UserId string +} + +type GetBucketACLArgs struct { + UserId string + Bucket string +} + +type PutBucketACLArgs struct { + UserId string + ACL string + Bucket string +} + +type PutObjectArgs struct { + UserId string + Body *hash.Reader + Bucket string + Object string + ContentEncoding string + ContentLength int64 + ContentType string + Expires time.Time +} + +type CopyObjectArgs struct { + UserId string + Bucket string + Object string + SrcBucket string + SrcObject string + ContentEncoding string + ContentType string + Expires time.Time + ReplaceMeta bool +} + +type GetObjectArgs struct { + UserId string + Bucket string + Object string + WithBody bool +} + +type DeleteObjectArgs struct { + UserId string + Bucket string + Object string +} + +type DeleteObjectsArgs struct { + UserId string + Bucket string + ToDeleteObjects []*ToDeleteObject + Quite bool +} + +type ToDeleteObject struct { + Object string + ValidateErr error +} + +type ListObjectsArgs struct { + UserId string + Bucket string + MaxKeys int64 + Marker string + Prefix string + Delimiter string + EncodingType string +} + +type ListObjectsV2Args struct { + UserId string + Bucket string + MaxKeys int64 + Prefix string + Delimiter string + EncodingType string + Token string + After string + FetchOwner bool +} + +type GetObjectACLArgs struct { + UserId string + Bucket string + Object string +} + +type CreateMultipartUploadArgs struct { + UserId string + Bucket string + Object string + ContentEncoding string + ContentType string + Expires time.Time +} + +type UploadPartArgs struct { + UserId string + Body *hash.Reader + Bucket string + Object string + UploadId string + PartNumber int64 + ContentLength int64 +} + +type AbortMultipartUploadArgs struct { + UserId string + Body *hash.Reader + Bucket string + Object string + UploadId string +} + +type CompleteMultipartUploadArgs struct { + UserId string + Bucket string + Object string + UploadId string + CompletedParts CompletedParts +} + +type ACL struct { + Owner string + ACL string +} + +type DeletedObject struct { + Object string + DeleteErr error +} + +type Bucket struct { + Name string + Region string + Owner string + ACL string + Created time.Time +} + +type BucketList struct { + Owner string + Buckets []*Bucket +} + +type Object struct { + Bucket string + Name string + ModTime time.Time + Size int64 + IsDir bool + ETag string + CID string + ACL string + VersionID string + IsLatest bool + DeleteMarker bool + ContentType string + ContentEncoding string + Expires time.Time + AccTime time.Time + SuccessorModTime time.Time +} + +type Multipart struct { + Bucket string + Object string + UploadID string + Initiated time.Time + ContentType string + ContentEncoding string + Expires time.Time + Parts []*Part +} + +type Part struct { + ETag string `json:"etag,omitempty"` + CID string `json:"cid,omitempty"` + Number int64 `json:"number"` + Size int64 `json:"size"` + ModTime time.Time `json:"mod_time"` +} + +type ObjectsList struct { + Args *ListObjectsArgs + Owner string + IsTruncated bool + NextMarker string + Objects []*Object + Prefixes []string +} + +type ObjectsListV2 struct { + Args *ListObjectsV2Args + Owner string + IsTruncated bool + NextContinuationToken string + Objects []*Object + Prefixes []string +} + +type CompletePart struct { + PartNumber int64 + ETag string +} + +type CompletedParts []*CompletePart + +func (a CompletedParts) Len() int { return len(a) } +func (a CompletedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a CompletedParts) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber } diff --git a/s3/api/services/object/service.go b/s3/api/services/object/service.go new file mode 100644 index 000000000..e99fd58dd --- /dev/null +++ b/s3/api/services/object/service.go @@ -0,0 +1,191 @@ +package object + +import ( + "context" + "fmt" + "github.com/bittorrent/go-btfs/s3/action" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/ctxmu" + "github.com/bittorrent/go-btfs/s3/policy" + "io" + "strings" + "time" +) + +var _ Service = (*service)(nil) + +type service struct { + providers providers.Providerser + lock ctxmu.MultiCtxRWLocker + keySeparator string + bucketSpace string + objectSpace string + uploadSpace string + cidrefSpace string + operationTimeout time.Duration + closeBodyTimeout time.Duration +} + +func NewService(providers providers.Providerser, options ...Option) Service { + s := &service{ + providers: providers, + lock: defaultLock, + keySeparator: defaultKeySeparator, + bucketSpace: defaultBucketSpace, + objectSpace: defaultObjectSpace, + uploadSpace: defaultUploadSpace, + cidrefSpace: defaultCidrefSpace, + operationTimeout: defaultOperationTimeout, + closeBodyTimeout: defaultCloseBodyTimeout, + } + for _, option := range options { + option(s) + } + return s +} + +// common helper methods + +func (s *service) getAllBucketsKeyPrefix() (prefix string) { + prefix = strings.Join([]string{s.bucketSpace, ""}, s.keySeparator) + return +} + +func (s *service) getBucketKey(bucname string) (key string) { + key = s.getAllBucketsKeyPrefix() + bucname + return +} + +func (s *service) getAllObjectsKeyPrefix(bucname string) (prefix string) { + prefix = strings.Join([]string{s.objectSpace, bucname, ""}, s.keySeparator) + return +} + +func (s *service) getObjectKey(bucname, objname string) (key string) { + key = s.getAllObjectsKeyPrefix(bucname) + objname + return +} + +func (s *service) getAllUploadsKeyPrefix(bucname string) (prefix string) { + prefix = strings.Join([]string{s.uploadSpace, bucname, ""}, s.keySeparator) + return +} + +func (s *service) getUploadKey(bucname, objname, uploadid string) (key string) { + key = s.getAllUploadsKeyPrefix(bucname) + strings.Join([]string{objname, uploadid}, s.keySeparator) + return +} + +func (s *service) getUploadPartKey(uplkey string, idx int) (key string) { + key = fmt.Sprintf("%s_%d", uplkey, idx) + return +} + +func (s *service) getAllCidrefsKeyPrefix(cid string) (prefix string) { + prefix = strings.Join([]string{s.cidrefSpace, cid, ""}, s.keySeparator) + return +} + +func (s *service) getCidrefKey(cid, to string) (key string) { + key = s.getAllCidrefsKeyPrefix(cid) + to + return +} + +func (s *service) opctx(parent context.Context) (ctx context.Context, cancel context.CancelFunc) { + ctx, cancel = context.WithTimeout(parent, s.operationTimeout) + return +} + +func (s *service) checkACL(owner, acl, user string, act action.Action) (allow bool) { + own := user != "" && user == owner + allow = policy.IsAllowed(own, acl, act) + return +} + +func (s *service) addBodyRef(ctx context.Context, cid, tokey string) (err error) { + // Cid reference key + crfkey := s.getCidrefKey(cid, tokey) + + // Add cid reference + err = s.providers.StateStore().Put(crfkey, nil) + + return +} + +func (s *service) removeBodyRef(ctx context.Context, cid, tokey string) (err error) { + // This object cid reference key + crfkey := s.getCidrefKey(cid, tokey) + + // Delete cid ref of this object + err = s.providers.StateStore().Delete(crfkey) + + return +} + +func (s *service) storeBody(ctx context.Context, body io.Reader, tokey string) (cid string, err error) { + // RLock all cid refs to enable no cid will be deleted + err = s.lock.RLock(ctx, s.cidrefSpace) + if err != nil { + return + } + defer s.lock.RUnlock(s.cidrefSpace) + + // Store body and get the cid + cid, err = s.providers.FileStore().Store(body) + if err != nil { + return + } + + // Add cid reference + err = s.addBodyRef(ctx, cid, tokey) + + return +} + +func (s *service) removeBody(ctx context.Context, cid, tokey string) (err error) { + // Flag to mark cid be referenced by other object + otherRef := false + + // Log removing + defer func() { + fmt.Printf("s3-api: remove <%s>, ref <%s>, other-ref - %v, err: %v\n", cid, tokey, otherRef, err) + }() + + // Lock all cid refs to enable new cid reference can not be added when + // remove is executing + err = s.lock.Lock(ctx, s.cidrefSpace) + if err != nil { + return + } + defer s.lock.Unlock(s.cidrefSpace) + + // Remove cid ref of this object + err = s.removeBodyRef(ctx, cid, tokey) + if err != nil { + return + } + + // All this cid references prefix + allRefsPrefix := s.getAllCidrefsKeyPrefix(cid) + + // Iterate all this cid refs, if exists other object's ref, set + // the otherRef mark to true + err = s.providers.StateStore().Iterate(allRefsPrefix, func(key, _ []byte) (stop bool, err error) { + otherRef = true + stop = true + return + }) + if err != nil { + return + } + + // Exists other refs, cid body can not be removed + if otherRef { + return + } + + // No other refs to this cid, remove it + err = s.providers.FileStore().Remove(cid) + + return +} diff --git a/s3/api/services/object/service_bucket.go b/s3/api/services/object/service_bucket.go new file mode 100644 index 000000000..de61066d5 --- /dev/null +++ b/s3/api/services/object/service_bucket.go @@ -0,0 +1,328 @@ +package object + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/policy" + "time" + + "github.com/bittorrent/go-btfs/s3/action" +) + +// CreateBucket create a new bucket for the specified user +func (s *service) CreateBucket(ctx context.Context, args *CreateBucketArgs) (bucket *Bucket, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // Lock bucket + err = s.lock.Lock(ctx, buckey) + if err != nil { + return + } + defer s.lock.Unlock(buckey) + + // Get old bucket + bucketOld, err := s.getBucket(buckey) + if err != nil { + return + } + if bucketOld != nil { + err = ErrBucketAlreadyExists + return + } + + // Check action ACL + allow := s.checkACL(args.UserId, policy.Private, args.UserId, action.CreateBucketAction) + if !allow { + err = ErrNotAllowed + return + } + + // now + now := time.Now().UTC() + + // Bucket + bucket = &Bucket{ + Name: args.Bucket, + Region: args.Region, + Owner: args.UserId, + ACL: args.ACL, + Created: now, + } + + // Put bucket + err = s.providers.StateStore().Put(buckey, bucket) + + return +} + +// GetBucket get a user specified bucket +func (s *service) GetBucket(ctx context.Context, args *GetBucketArgs) (bucket *Bucket, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err = s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.HeadBucketAction) + if !allow { + err = ErrNotAllowed + } + + return +} + +// DeleteBucket delete a user specified bucket and clear all bucket objects and uploads +func (s *service) DeleteBucket(ctx context.Context, args *DeleteBucketArgs) (err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // Lock bucket + err = s.lock.Lock(ctx, buckey) + if err != nil { + return + } + defer s.lock.Unlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.DeleteBucketAction) + if !allow { + err = ErrNotAllowed + return + } + + // Check if bucket is empty + empty, err := s.isBucketEmpty(args.Bucket) + if err != nil { + return + } + if !empty { + err = ErrBucketNotEmpty + return + } + + // Delete bucket + err = s.providers.StateStore().Delete(buckey) + + return +} + +// ListBuckets list all buckets of the specified user +func (s *service) ListBuckets(ctx context.Context, args *ListBucketsArgs) (list *BucketList, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Check action ACL + allow := s.checkACL(args.UserId, policy.Private, args.UserId, action.ListBucketAction) + if !allow { + err = ErrNotAllowed + return + } + + // List + list = &BucketList{ + Owner: args.UserId, + } + + // All buckets prefix + bucketsPrefix := s.getAllBucketsKeyPrefix() + + // Collect user's buckets from all buckets + err = s.providers.StateStore().Iterate(bucketsPrefix, func(key, _ []byte) (stop bool, er error) { + // Stop the iteration if error occurred + defer func() { + if er != nil { + stop = true + } + }() + + // Bucket key + buckey := string(key) + + // Get Bucket + bucket, er := s.getBucket(buckey) + if er != nil { + return + } + + // Bucket has been deleted + if bucket == nil { + return + } + + // Collect user's bucket + if bucket.Owner == args.UserId { + list.Buckets = append(list.Buckets, bucket) + } + + return + }) + + return +} + +// PutBucketACL update user specified bucket's ACL field value +func (s *service) PutBucketACL(ctx context.Context, args *PutBucketACLArgs) (err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // Lock bucket + err = s.lock.Lock(ctx, buckey) + if err != nil { + return + } + defer s.lock.Unlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.PutBucketAclAction) + if !allow { + err = ErrNotAllowed + return + } + + // Update bucket ACL + bucket.ACL = args.ACL + + // Put bucket + err = s.providers.StateStore().Put(buckey, bucket) + + return +} + +// GetBucketACL get user specified bucket ACL +func (s *service) GetBucketACL(ctx context.Context, args *GetBucketACLArgs) (acl *ACL, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.GetBucketAclAction) + if !allow { + err = ErrNotAllowed + return + } + + // ACL + acl = &ACL{ + Owner: bucket.Owner, + ACL: bucket.ACL, + } + + return +} + +// EmptyBucket check if the user specified bucked is empty +func (s *service) isBucketEmpty(bucname string) (empty bool, err error) { + // All bucket objects prefix + objectsPrefix := s.getAllObjectsKeyPrefix(bucname) + + // Initially set empty to true + empty = true + + // Iterate the bucket objects, if no item, empty keep true + // if at least one, set empty to false, and stop iterate + err = s.providers.StateStore().Iterate(objectsPrefix, func(_, _ []byte) (stop bool, er error) { + empty = false + stop = true + return + }) + + // If bucket have at least one object, return not empty, else check if bucket + // have at least one upload + if !empty { + return + } + + // All bucket uploads prefix + uploadsPrefix := s.getAllUploadsKeyPrefix(bucname) + + // Set empty to false if bucket has at least one upload + err = s.providers.StateStore().Iterate(uploadsPrefix, func(_, _ []byte) (stop bool, er error) { + empty = false + stop = true + return + }) + + return +} + +func (s *service) getBucket(buckey string) (bucket *Bucket, err error) { + err = s.providers.StateStore().Get(buckey, &bucket) + if errors.Is(err, providers.ErrStateStoreNotFound) { + err = nil + } + return +} diff --git a/s3/api/services/object/service_multipart.go b/s3/api/services/object/service_multipart.go new file mode 100644 index 000000000..b08c9e0f9 --- /dev/null +++ b/s3/api/services/object/service_multipart.go @@ -0,0 +1,490 @@ +package object + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/action" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/consts" + "github.com/bittorrent/go-btfs/s3/etag" + "github.com/google/uuid" + "io" + "regexp" + "time" +) + +// CreateMultipartUpload create user specified multipart upload +func (s *service) CreateMultipartUpload(ctx context.Context, args *CreateMultipartUploadArgs) (multipart *Multipart, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.CreateMultipartUploadAction) + if !allow { + err = ErrNotAllowed + return + } + + // Upload id + uplid := uuid.NewString() + + // upload key + uplkey := s.getUploadKey(args.Bucket, args.Object, uplid) + + // Lock upload + err = s.lock.Lock(ctx, uplkey) + if err != nil { + return + } + defer s.lock.Unlock(uplkey) + + // now + now := time.Now().UTC() + + // Multipart upload + multipart = &Multipart{ + Bucket: args.Bucket, + Object: args.Object, + UploadID: uplid, + ContentType: args.ContentType, + ContentEncoding: args.ContentEncoding, + Expires: args.Expires, + Initiated: now, + } + + // Put multipart upload + err = s.providers.StateStore().Put(uplkey, multipart) + + return +} + +// UploadPart upload user specified multipart part +func (s *service) UploadPart(ctx context.Context, args *UploadPartArgs) (part *Part, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.UploadPartAction) + if !allow { + err = ErrNotAllowed + return + } + + // Upload key + uplkey := s.getUploadKey(args.Bucket, args.Object, args.UploadId) + + // Lock upload + err = s.lock.Lock(ctx, uplkey) + if err != nil { + return + } + defer s.lock.Unlock(uplkey) + + // Get multipart upload + multipart, err := s.getMultipart(uplkey) + if err != nil { + return + } + if multipart == nil { + err = ErrUploadNotFound + return + } + + // Upload part key + prtkey := s.getUploadPartKey(uplkey, len(multipart.Parts)) + + // Store part body + cid, err := s.storeBody(ctx, args.Body, prtkey) + if err != nil { + return + } + + // Init a flag to mark if the part body should be removed, this + // flag will be set to false if the multipart has been successfully put + var removePartBody = true + + // Try to remove the part body + defer func() { + if removePartBody { + _ = s.removeBody(ctx, cid, prtkey) + } + }() + + // Now + now := time.Now().UTC() + + // Part + part = &Part{ + Number: args.PartNumber, + ETag: args.Body.ETag().String(), + CID: cid, + Size: args.ContentLength, + ModTime: now, + } + + // Append part + multipart.Parts = append(multipart.Parts, part) + + // Put multipart upload + err = s.providers.StateStore().Put(uplkey, multipart) + if err != nil { + return + } + + // Set remove part body flag to false, because this part body has been referenced by the upload + removePartBody = false + + return +} + +// AbortMultipartUpload abort user specified multipart upload +func (s *service) AbortMultipartUpload(ctx context.Context, args *AbortMultipartUploadArgs) (err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.AbortMultipartUploadAction) + if !allow { + err = ErrNotAllowed + return + } + + // Multipart upload key + uplkey := s.getUploadKey(args.Bucket, args.Object, args.UploadId) + + // Lock upload + err = s.lock.Lock(ctx, uplkey) + if err != nil { + return + } + defer s.lock.Unlock(uplkey) + + // Get multipart upload + multipart, err := s.getMultipart(uplkey) + if err != nil { + return + } + if multipart == nil { + err = ErrUploadNotFound + return + } + + // Delete multipart upload + err = s.providers.StateStore().Delete(uplkey) + if err != nil { + return + } + + // Try to remove all parts body + for i, part := range multipart.Parts { + prtkey := s.getUploadPartKey(uplkey, i) + _ = s.removeBody(ctx, part.CID, prtkey) + } + + return +} + +// CompleteMultiPartUpload complete user specified multipart upload +func (s *service) CompleteMultiPartUpload(ctx context.Context, args *CompleteMultipartUploadArgs) (object *Object, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.CompleteMultipartUploadAction) + if !allow { + err = ErrNotAllowed + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, args.Object) + + // Lock object + err = s.lock.Lock(ctx, objkey) + if err != nil { + return + } + defer s.lock.Unlock(objkey) + + // Get old object for try to remove the old body + objectOld, err := s.getObject(objkey) + if err != nil { + return + } + + // Upload key + uplkey := s.getUploadKey(args.Bucket, args.Object, args.UploadId) + + // Lock upload + err = s.lock.Lock(ctx, uplkey) + if err != nil { + return + } + defer s.lock.Unlock(uplkey) + + // Get multipart upload + multipart, err := s.getMultipart(uplkey) + if err != nil { + return + } + if multipart == nil { + err = ErrUploadNotFound + return + } + + // All parts body readers + var readers []io.Reader + + // Try to close all parts body readers + defer func() { + for _, rdr := range readers { + _ = rdr.(io.ReadCloser).Close() + } + }() + + // Total object size + var size int64 + + // Mapping of part number to part index in multipart.Parts + idxmp := s.partIdxMap(multipart.Parts) + + // Iterate all parts to collect all body readers + for i, part := range args.CompletedParts { + // Index in multipart.Parts + partIndex, ok := idxmp[part.PartNumber] + + // Part not exists in multipart + if !ok { + err = ErrPartNotExists + return + } + + // Got part in multipart.Parts + gotPart := multipart.Parts[partIndex] + + // Canonicalize part etag + part.ETag = s.canonicalizeETag(part.ETag) + + // Check got part etag with part etag + if gotPart.ETag != part.ETag { + err = ErrPartETagNotMatch + return + } + + // All parts except the last part has to be at least 5MB. + if (i < len(args.CompletedParts)-1) && !(gotPart.Size >= consts.MinPartSize) { + err = ErrPartTooSmall + return + } + + // Save for total object size. + size += gotPart.Size + + // Get part body reader + var rdr io.ReadCloser + rdr, err = s.providers.FileStore().Cat(gotPart.CID) + if err != nil { + return + } + + // Collect part body reader + readers = append(readers, rdr) + } + + // Concat all parts body to one + body := io.MultiReader(readers...) + + // Store object body + cid, err := s.storeBody(ctx, body, objkey) + if err != nil { + return + } + + // Init a flag to mark if the object body should be removed, this + // flag will be set to false if the object has been successfully put + var removeObjectBody = true + + // Try to remove stored body if put object failed + defer func() { + if removeObjectBody { + _ = s.providers.FileStore().Remove(cid) + } + }() + + // Calculate multipart etag + multiEtag, err := s.calcMultiETag(args.CompletedParts) + if err != nil { + return + } + + // Current time + now := time.Now().UTC() + + // Object + object = &Object{ + Bucket: args.Bucket, + Name: args.Object, + ModTime: now, + Size: size, + IsDir: false, + ETag: multiEtag.String(), + CID: cid, + ACL: "", + VersionID: "", + IsLatest: true, + DeleteMarker: false, + ContentType: multipart.ContentType, + ContentEncoding: multipart.ContentEncoding, + Expires: multipart.Expires, + AccTime: time.Time{}, + SuccessorModTime: now, + } + + // Put object + err = s.providers.StateStore().Put(objkey, object) + if err != nil { + return + } + + // Set remove object body flag to false, because it has been referenced by the object + removeObjectBody = false + + // Try to remove old object body if exists, because it has been covered by new one + if objectOld != nil { + _ = s.removeBody(ctx, objectOld.CID, objkey) + } + + // Remove multipart upload + err = s.providers.StateStore().Delete(uplkey) + if err != nil { + return + } + + // Try to remove all parts body, because they are no longer be referenced + for i, part := range multipart.Parts { + prtkey := s.getUploadPartKey(uplkey, i) + _ = s.removeBody(ctx, part.CID, prtkey) + } + + return +} + +func (s *service) getMultipart(uplkey string) (multipart *Multipart, err error) { + err = s.providers.StateStore().Get(uplkey, &multipart) + if errors.Is(err, providers.ErrStateStoreNotFound) { + err = nil + } + return +} + +func (s *service) partIdxMap(parts []*Part) map[int64]int { + mp := make(map[int64]int) + for i, part := range parts { + mp[part.Number] = i + } + return mp +} + +var etagRegex = regexp.MustCompile("\"*?([^\"]*?)\"*?$") + +func (s *service) canonicalizeETag(etag string) string { + return etagRegex.ReplaceAllString(etag, "$1") +} + +func (s *service) calcMultiETag(parts []*CompletePart) (multiEtag etag.ETag, err error) { + var completeETags []etag.ETag + for _, part := range parts { + var etg etag.ETag + etg, err = etag.Parse(part.ETag) + if err != nil { + return + } + completeETags = append(completeETags, etg) + } + multiEtag = etag.Multipart(completeETags...) + return +} diff --git a/s3/api/services/object/service_object.go b/s3/api/services/object/service_object.go new file mode 100644 index 000000000..283642f58 --- /dev/null +++ b/s3/api/services/object/service_object.go @@ -0,0 +1,768 @@ +package object + +import ( + "context" + "errors" + "github.com/bittorrent/go-btfs/s3/action" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/utils" + "io" + "strings" + "time" +) + +// PutObject put a user specified object +func (s *service) PutObject(ctx context.Context, args *PutObjectArgs) (object *Object, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.PutObjectAction) + if !allow { + err = ErrNotAllowed + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, args.Object) + + // Lock object + err = s.lock.Lock(ctx, objkey) + if err != nil { + return + } + defer s.lock.Unlock(objkey) + + // Get old object + objectOld, err := s.getObject(objkey) + if err != nil { + return + } + + // Store object body + cid, err := s.storeBody(ctx, args.Body, objkey) + if err != nil { + return + } + + // Init a flag to mark if the object body should be removed, this + // flag will be set to false if the object has been successfully put + var removeObjectBody = true + + // Try to remove stored body if put object failed + defer func() { + if removeObjectBody { + _ = s.removeBody(ctx, cid, objkey) + } + }() + + // now + now := time.Now().UTC() + + // new object + object = &Object{ + Bucket: args.Bucket, + Name: args.Object, + ModTime: now, + Size: args.ContentLength, + IsDir: false, + ETag: args.Body.ETag().String(), + CID: cid, + VersionID: "", + IsLatest: true, + DeleteMarker: false, + ACL: "", + ContentType: args.ContentType, + ContentEncoding: args.ContentEncoding, + SuccessorModTime: now, + Expires: args.Expires, + } + + // put object + err = s.putObject(objkey, object) + if err != nil { + return + } + + // Set remove object body flag to false, because it has been referenced by the object + removeObjectBody = false + + // Try to remove old object body if exists, because it has been covered by new one + if objectOld != nil { + _ = s.removeBody(ctx, objectOld.CID, objkey) + } + + return +} + +// CopyObject copy from a user specified source object to a desert object +func (s *service) CopyObject(ctx context.Context, args *CopyObjectArgs) (dstObject *Object, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Source bucket key + srcBuckey := s.getBucketKey(args.SrcBucket) + + // RLock source bucket + err = s.lock.RLock(ctx, srcBuckey) + if err != nil { + return + } + defer s.lock.RUnlock(srcBuckey) + + // Get source bucket + srcBucket, err := s.getBucket(srcBuckey) + if err != nil { + return + } + if srcBucket == nil { + err = ErrBucketNotFound + return + } + + // Check source action ACL + srcAllow := s.checkACL(srcBucket.Owner, srcBucket.ACL, args.UserId, action.GetObjectAction) + if !srcAllow { + err = ErrNotAllowed + return + } + + // Source object key + srcObjkey := s.getObjectKey(args.SrcBucket, args.SrcObject) + + // RLock source object + err = s.lock.RLock(ctx, srcObjkey) + if err != nil { + return + } + defer s.lock.RUnlock(srcObjkey) + + // Get source object + srcObject, err := s.getObject(srcObjkey) + if err != nil { + return + } + if srcObject == nil { + err = ErrObjectNotFound + return + } + + // Desert bucket key + dstBuckey := s.getBucketKey(args.Bucket) + + // RLock destination bucket + err = s.lock.RLock(ctx, dstBuckey) + if err != nil { + return + } + defer s.lock.RUnlock(dstBuckey) + + // Get destination bucket + dstBucket, err := s.getBucket(dstBuckey) + if err != nil { + return + } + if dstBucket == nil { + err = ErrBucketNotFound + return + } + + // Check destination action ACL + dstAllow := s.checkACL(dstBucket.Owner, dstBucket.ACL, args.UserId, action.PutObjectAction) + if !dstAllow { + err = ErrNotAllowed + return + } + + // Destination object key + dstObjkey := s.getObjectKey(args.Bucket, args.Object) + + // Lock Destination object + err = s.lock.Lock(ctx, dstObjkey) + if err != nil { + return + } + defer s.lock.Unlock(dstObjkey) + + // Add body Refer + err = s.addBodyRef(ctx, srcObject.CID, dstObjkey) + if err != nil { + return + } + + // Mark if delete the cid ref + deleteRef := true + + // If put new object failed, try to delete its reference + defer func() { + if deleteRef { + _ = s.removeBodyRef(ctx, srcObject.CID, dstObjkey) + } + }() + + // Old desert object + oldDstObject, err := s.getObject(dstObjkey) + if err != nil { + return + } + + // now + now := time.Now().UTC() + + // Destination object + dstObject = &Object{ + Bucket: args.Bucket, + Name: args.Object, + ModTime: now, + Size: srcObject.Size, + IsDir: false, + ETag: srcObject.ETag, + CID: srcObject.CID, + VersionID: "", + IsLatest: true, + DeleteMarker: false, + ContentType: srcObject.ContentType, + ContentEncoding: srcObject.ContentEncoding, + SuccessorModTime: now, + Expires: args.Expires, + } + + // Replace metadata + if args.ReplaceMeta { + dstObject.ContentType = args.ContentType + dstObject.ContentEncoding = args.ContentEncoding + } + + // Put destination object + err = s.putObject(dstObjkey, dstObject) + if err != nil { + return + } + + // Mark the delete ref to false + deleteRef = false + + // Try to remove the old object body + if oldDstObject != nil { + _ = s.removeBody(ctx, oldDstObject.CID, dstObjkey) + } + + return +} + +// GetObject get a user specified object +func (s *service) GetObject(ctx context.Context, args *GetObjectArgs) (object *Object, body io.ReadCloser, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Unlock-later is a flag mark if the bucket or object will be unlocked later + // if the flag is true, the bucket and object should not be unlocked as soon as leave the function call + // they will be automatically unlocked after completely written the object body or write object body timeout + unlockLater := false + + // bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer func() { + if !unlockLater { + s.lock.RUnlock(buckey) + } + }() + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.GetObjectAction) + if !allow { + err = ErrNotAllowed + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, args.Object) + + // RLock object + err = s.lock.RLock(ctx, objkey) + if err != nil { + return + } + defer func() { + if !unlockLater { + s.lock.RUnlock(objkey) + } + }() + + // Get object + object, err = s.getObject(objkey) + if err != nil { + return + } + if object == nil { + err = ErrObjectNotFound + return + } + + // no need body + if !args.WithBody { + return + } + + // Get object body + body, err = s.providers.FileStore().Cat(object.CID) + if err != nil { + return + } + + // Set unlock-later flag to true to enable the bucket and object + // will not be unlocked before completely written the response body + unlockLater = true + + // Wrap the body with timeout and unlock hooks, + // this will enable the bucket and object keep rlocked until + // read timout or read closed. Normally, these locks will + // be released as soon as leave from the call + body = WrapCleanReadCloser(body, s.closeBodyTimeout, func() { + s.lock.RUnlock(objkey) // Note: Release object first + s.lock.RUnlock(buckey) + }) + + return +} + +// DeleteObject delete a user specified object +func (s *service) DeleteObject(ctx context.Context, args *DeleteObjectArgs) (err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.DeleteObjectAction) + if !allow { + err = ErrNotAllowed + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, args.Object) + + // Lock object + err = s.lock.Lock(ctx, objkey) + if err != nil { + return + } + defer s.lock.Unlock(objkey) + + // Get object + object, err := s.getObject(objkey) + if err != nil { + return + } + if object == nil { + err = ErrObjectNotFound + return + } + + // Delete object + err = s.deleteObject(objkey) + if err != nil { + return + } + + // Try to delete object body + _ = s.removeBody(ctx, object.CID, objkey) + + return +} + +// DeleteObjects delete multiple user specified objects +func (s *service) DeleteObjects(ctx context.Context, args *DeleteObjectsArgs) (deletes []*DeletedObject, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.DeleteObjectAction) + if !allow { + err = ErrNotAllowed + return + } + + for _, deleteObj := range args.ToDeleteObjects { + func(deleteObj *ToDeleteObject) { + var er error + // Collection delete result + defer func() { + if er != nil || !args.Quite { + deletes = append(deletes, &DeletedObject{ + Object: deleteObj.Object, + DeleteErr: er, + }) + } + }() + + // Validate failed + er = deleteObj.ValidateErr + if er != nil { + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, deleteObj.Object) + + // Lock object + er = s.lock.Lock(ctx, objkey) + if er != nil { + return + } + defer s.lock.Unlock(objkey) + + // Get object + object, er := s.getObject(objkey) + if er != nil { + return + } + if object == nil { + er = ErrObjectNotFound + return + } + + // Delete object + er = s.deleteObject(objkey) + if er != nil { + return + } + + // Try to delete object body + _ = s.removeBody(ctx, object.CID, objkey) + + }(deleteObj) + } + + return +} + +// ListObjects list user specified objects +func (s *service) ListObjects(ctx context.Context, args *ListObjectsArgs) (list *ObjectsList, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Object list + list = &ObjectsList{ + Args: args, + } + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.ListObjectsAction) + if !allow { + err = ErrNotAllowed + return + } + + // Set objects owner(objects owner is the bucket owner included them) + list.Owner = bucket.Owner + + // MaxKeys is zero + if args.MaxKeys == 0 { + list.IsTruncated = true + return + } + + // All bucket objects key prefix + allObjectsKeyPrefix := s.getAllObjectsKeyPrefix(args.Bucket) + + // List objects key prefix + listObjectsKeyPrefix := allObjectsKeyPrefix + args.Prefix + + // Accumulate count + count := int64(0) + + // Flag mark if begin collect, it initialized to true if + // marker is "" + begin := args.Marker == "" + + // Seen keys, used to group common keys + seen := make(map[string]bool) + + // Delimiter length + dl := len(args.Delimiter) + + // Prefix length + pl := len(args.Prefix) + + // Iterate all objects with the specified prefix to collect and group specified range items + err = s.providers.StateStore().Iterate(listObjectsKeyPrefix, func(key, _ []byte) (stop bool, er error) { + // Object key + objkey := string(key) + + // Object name + objname := strings.TrimPrefix(objkey, allObjectsKeyPrefix) + + // Common prefix: if the part of object name without prefix include delimiter + // it is the string truncated object name after the delimiter, else + // it is empty string + commonPrefix := "" + if dl > 0 { + di := strings.Index(objname[pl:], args.Delimiter) + if di >= 0 { + commonPrefix = objname[:(pl + di + dl)] + } + } + + // If collect not begin, check the marker, if it is matched + // with the common prefix or object name, then begin collection from next iterate + // and if common prefix matched, mark this common prefix as seen + if !begin { + if commonPrefix != "" && args.Marker == commonPrefix { + seen[commonPrefix] = true + begin = true + } else if args.Marker == objname { + begin = true + } + return + } + + // ToDeleteObjects with same common prefix will be grouped into one + // note: the objects without common prefix will present only once, so + // it is not necessary to add these objects names in the seen map + + // ToDeleteObjects with common prefix grouped int one + if commonPrefix != "" { + if seen[commonPrefix] { + return + } + list.Prefixes = append(list.Prefixes, commonPrefix) + list.NextMarker = commonPrefix + seen[commonPrefix] = true + } else { + // object without common prefix + var object *Object + er = s.providers.StateStore().Get(objkey, &object) + if er != nil { + return + } + list.Objects = append(list.Objects, object) + list.NextMarker = objname + } + + // Increment collection count + count++ + + // Check the count, if it matched the max, means + // the collect is complete, but the items may remain, so stop the + // iteration, and mark the list was truncated + if count == args.MaxKeys { + list.IsTruncated = true + stop = true + } + + return + }) + + return +} + +func (s *service) ListObjectsV2(ctx context.Context, args *ListObjectsV2Args) (list *ObjectsListV2, err error) { + // Args v1 + v1Args := &ListObjectsArgs{ + UserId: args.UserId, + Bucket: args.Bucket, + MaxKeys: args.MaxKeys, + Marker: utils.CoalesceStr(args.Token, args.After), + Prefix: args.Prefix, + Delimiter: args.Delimiter, + EncodingType: args.EncodingType, + } + + // Get v1 list + v1List, err := s.ListObjects(ctx, v1Args) + if err != nil { + return + } + + // List v2 + list = &ObjectsListV2{ + Args: args, + Owner: v1List.Owner, + IsTruncated: v1List.IsTruncated, + NextContinuationToken: v1List.NextMarker, + Objects: v1List.Objects, + Prefixes: v1List.Prefixes, + } + return +} + +func (s *service) deleteObject(objkey string) (err error) { + err = s.providers.StateStore().Delete(objkey) + return +} + +func (s *service) putObject(objkey string, object *Object) (err error) { + err = s.providers.StateStore().Put(objkey, object) + return +} + +func (s *service) getObject(objkey string) (object *Object, err error) { + err = s.providers.StateStore().Get(objkey, &object) + if errors.Is(err, providers.ErrStateStoreNotFound) { + err = nil + } + return +} + +// GetObjectACL get user specified object ACL(bucket acl) +func (s *service) GetObjectACL(ctx context.Context, args *GetObjectACLArgs) (acl *ACL, err error) { + // Operation context + ctx, cancel := s.opctx(ctx) + defer cancel() + + // Bucket key + buckey := s.getBucketKey(args.Bucket) + + // RLock bucket + err = s.lock.RLock(ctx, buckey) + if err != nil { + return + } + defer s.lock.RUnlock(buckey) + + // Get bucket + bucket, err := s.getBucket(buckey) + if err != nil { + return + } + if bucket == nil { + err = ErrBucketNotFound + return + } + + // Check action ACL + allow := s.checkACL(bucket.Owner, bucket.ACL, args.UserId, action.GetBucketAclAction) + if !allow { + err = ErrNotAllowed + return + } + + // Object key + objkey := s.getObjectKey(args.Bucket, args.Object) + + // RLock object + err = s.lock.RLock(ctx, objkey) + if err != nil { + return + } + defer s.lock.RUnlock(objkey) + + // Get object + object, err := s.getObject(objkey) + if err != nil { + return + } + if object == nil { + err = ErrObjectNotFound + } + + // ACL + acl = &ACL{ + Owner: bucket.Owner, + ACL: bucket.ACL, + } + + return +} diff --git a/s3/api/services/sign/options.go b/s3/api/services/sign/options.go new file mode 100644 index 000000000..46f7ad63c --- /dev/null +++ b/s3/api/services/sign/options.go @@ -0,0 +1,3 @@ +package sign + +type Option func(svc *service) diff --git a/s3/api/services/sign/proto.go b/s3/api/services/sign/proto.go new file mode 100644 index 000000000..e2af57ca7 --- /dev/null +++ b/s3/api/services/sign/proto.go @@ -0,0 +1,11 @@ +package sign + +import ( + "github.com/bittorrent/go-btfs/s3/api/responses" + "net/http" +) + +type Service interface { + SetSecretGetter(f func(key string) (secret string, exists, enable bool, err error)) + VerifyRequestSignature(r *http.Request) (ack string, rerr *responses.Error) +} diff --git a/s3/api/services/sign/service.go b/s3/api/services/sign/service.go new file mode 100644 index 000000000..35dedceec --- /dev/null +++ b/s3/api/services/sign/service.go @@ -0,0 +1,49 @@ +package sign + +import ( + "github.com/bittorrent/go-btfs/s3/api/responses" + "net/http" + "sync" +) + +var _ Service = (*service)(nil) + +type service struct { + getSecret func(key string) (secret string, exists, enable bool, err error) + once sync.Once +} + +func NewService(options ...Option) Service { + svc := &service{ + getSecret: func(key string) (secret string, exists, enable bool, err error) { + return + }, + once: sync.Once{}, + } + for _, option := range options { + option(svc) + } + return svc +} + +func (s *service) SetSecretGetter(f func(key string) (secret string, exists, enable bool, err error)) { + s.once.Do(func() { + s.getSecret = f + }) +} + +func (s *service) VerifyRequestSignature(r *http.Request) (ack string, rerr *responses.Error) { + switch GetRequestAuthType(r) { + case AuthTypeUnknown: + return + case AuthTypeSigned: + ack, rerr = s.reqSignatureV4Verify(r, "") + return + case AuthTypeStreamingSigned: + ack, rerr = s.setReqBodySignV4ChunkedReader(r, "") + return + default: + rerr = responses.ErrSignatureVersionNotSupported + return + } +} diff --git a/s3/api/services/sign/signature-type.go b/s3/api/services/sign/signature-type.go new file mode 100644 index 000000000..70220b2c7 --- /dev/null +++ b/s3/api/services/sign/signature-type.go @@ -0,0 +1,96 @@ +package sign + +import ( + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" + "net/url" + "strings" +) + +// Verify if request has JWT. +func isRequestJWT(r *http.Request) bool { + return strings.HasPrefix(r.Header.Get("Authorization"), "Bearer") +} + +// IsRequestSignatureV4 Verify if request has AWS Signature Version '4'. +func IsRequestSignatureV4(r *http.Request) bool { + return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) +} + +// Verify if request has AWS Signature Version '2'. +func isRequestSignatureV2(r *http.Request) bool { + return !strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) && + strings.HasPrefix(r.Header.Get("Authorization"), signV2Algorithm) +} + +// Verify if request has AWS PreSign Version '4'. +func isRequestPresignedSignatureV4(r *http.Request) bool { + _, ok := r.URL.Query()["X-Amz-Credential"] + return ok +} + +// Verify request has AWS PreSign Version '2'. +func isRequestPresignedSignatureV2(r *http.Request) bool { + _, ok := r.URL.Query()["AWSAccessKeyId"] + return ok +} + +// Verify if request has AWS Post policy Signature Version '4'. +func isRequestPostPolicySignatureV4(r *http.Request) bool { + return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && + r.Method == http.MethodPost +} + +// Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation. +func isRequestSignStreamingV4(r *http.Request) bool { + return r.Header.Get("x-amz-content-sha256") == consts.StreamingContentSHA256 && + r.Method == http.MethodPut +} + +// AuthType Authorization type. +type AuthType int + +// List of all supported auth types. +const ( + AuthTypeUnknown AuthType = iota + AuthTypeAnonymous + AuthTypePresigned + AuthTypePresignedV2 + AuthTypePostPolicy + AuthTypeStreamingSigned + AuthTypeSigned + AuthTypeSignedV2 + AuthTypeJWT + AuthTypeSTS +) + +// GetRequestAuthType Get request authentication type. +func GetRequestAuthType(r *http.Request) AuthType { + if r.URL != nil { + var err error + r.Form, err = url.ParseQuery(r.URL.RawQuery) + if err != nil { + return AuthTypeUnknown + } + } + if isRequestSignatureV2(r) { + return AuthTypeSignedV2 + } else if isRequestPresignedSignatureV2(r) { + return AuthTypePresignedV2 + } else if isRequestSignStreamingV4(r) { + return AuthTypeStreamingSigned + } else if IsRequestSignatureV4(r) { + return AuthTypeSigned + } else if isRequestPresignedSignatureV4(r) { + return AuthTypePresigned + } else if isRequestJWT(r) { + return AuthTypeJWT + } else if isRequestPostPolicySignatureV4(r) { + return AuthTypePostPolicy + } else if _, ok := r.Form[consts.StsAction]; ok { + return AuthTypeSTS + } else if _, ok := r.Header[consts.Authorization]; !ok { + return AuthTypeAnonymous + } + return AuthTypeUnknown +} diff --git a/s3/api/services/sign/signature-v4-parser.go b/s3/api/services/sign/signature-v4-parser.go new file mode 100644 index 000000000..026b6d10b --- /dev/null +++ b/s3/api/services/sign/signature-v4-parser.go @@ -0,0 +1,190 @@ +/* + * The following code tries to reverse engineer the Amazon S3 APIs, + * and is mostly copied from minio implementation. + */ + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. + +package sign + +import ( + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/consts" + "strings" + "time" +) + +// credentialHeader data type represents structured form of Credential +// string from authorization header. +type credentialHeader struct { + accessKey string + scope struct { + date time.Time + region string + service string + request string + } +} + +// Return scope string. +func (c credentialHeader) getScope() string { + return strings.Join([]string{ + c.scope.date.Format(yyyymmdd), + c.scope.region, + c.scope.service, + c.scope.request, + }, consts.SlashSeparator) +} + +// parse credentialHeader string into its structured form. +func parseCredentialHeader(credElement string, region string) (ch credentialHeader, rerr *responses.Error) { + creds := strings.SplitN(strings.TrimSpace(credElement), "=", 2) + if len(creds) != 2 { + return ch, responses.ErrMissingFields + } + if creds[0] != "Credential" { + return ch, responses.ErrMissingCredTag + } + credElements := strings.Split(strings.TrimSpace(creds[1]), consts.SlashSeparator) + if len(credElements) < 5 { + return ch, responses.ErrCredMalformed + } + accessKey := strings.Join(credElements[:len(credElements)-4], consts.SlashSeparator) // The access key may contain one or more `/` + + // Save access key id. + cred := credentialHeader{ + accessKey: accessKey, + } + credElements = credElements[len(credElements)-4:] + var e error + cred.scope.date, e = time.Parse(yyyymmdd, credElements[0]) + if e != nil { + return ch, responses.ErrAuthorizationHeaderMalformed + } + + cred.scope.region = credElements[1] + // Verify if region is valid. + sRegion := cred.scope.region + // Region is set to be empty, we use whatever was sent by the + // request and proceed further. This is a work-around to address + // an important problem for ListBuckets() getting signed with + // different regions. + if region == "" { + region = sRegion + } + // Should validate region, only if region is set. + if !isValidRegion(sRegion, region) { + return ch, responses.ErrAuthorizationHeaderMalformed + } + if credElements[2] != "s3" { + return ch, responses.ErrAuthorizationHeaderMalformed + } + cred.scope.service = credElements[2] + if credElements[3] != "aws4_request" { + return ch, responses.ErrAuthorizationHeaderMalformed + } + cred.scope.request = credElements[3] + return cred, nil +} + +// Parse signature from signature tag. +func parseSignature(signElement string) (string, *responses.Error) { + signFields := strings.Split(strings.TrimSpace(signElement), "=") + if len(signFields) != 2 { + return "", responses.ErrMissingFields + } + if signFields[0] != "Signature" { + return "", responses.ErrMissingSignTag + } + if signFields[1] == "" { + return "", responses.ErrMissingFields + } + signature := signFields[1] + return signature, nil +} + +// Parse slice of signed headers from signed headers tag. +func parseSignedHeader(signedHdrElement string) ([]string, *responses.Error) { + signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=") + if len(signedHdrFields) != 2 { + return nil, responses.ErrMissingFields + } + if signedHdrFields[0] != "SignedHeaders" { + return nil, responses.ErrMissingSignHeadersTag + } + if signedHdrFields[1] == "" { + return nil, responses.ErrMissingFields + } + signedHeaders := strings.Split(signedHdrFields[1], ";") + return signedHeaders, nil +} + +// signValues data type represents structured form of AWS Signature V4 header. +type signValues struct { + Credential credentialHeader + SignedHeaders []string + Signature string +} + +// Parses signature version '4' header of the following form. +// +// Authorization: algorithm Credential=accessKeyID/credScope, \ +// SignedHeaders=signedHeaders, Signature=signature +func parseSignV4(v4Auth string, region string) (sv signValues, aec *responses.Error) { + // credElement is fetched first to skip replacing the space in access key. + credElement := strings.TrimPrefix(strings.Split(strings.TrimSpace(v4Auth), ",")[0], signV4Algorithm) + // Replace all spaced strings, some clients can send spaced + // parameters and some won't. So we pro-actively remove any spaces + // to make parsing easier. + v4Auth = strings.ReplaceAll(v4Auth, " ", "") + if v4Auth == "" { + return sv, responses.ErrAuthHeaderEmpty + } + + // Verify if the header algorithm is supported or not. + if !strings.HasPrefix(v4Auth, signV4Algorithm) { + return sv, responses.ErrSignatureVersionNotSupported + } + + // Strip off the Algorithm prefix. + v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm) + authFields := strings.Split(strings.TrimSpace(v4Auth), ",") + if len(authFields) != 3 { + return sv, responses.ErrMissingFields + } + + // Initialize signature version '4' structured header. + signV4Values := signValues{} + + var s3Err *responses.Error + // Save credential values. + signV4Values.Credential, s3Err = parseCredentialHeader(strings.TrimSpace(credElement), region) + if s3Err != nil { + return sv, s3Err + } + + // Save signed headers. + signV4Values.SignedHeaders, s3Err = parseSignedHeader(authFields[1]) + if s3Err != nil { + return sv, s3Err + } + + // Save signature. + signV4Values.Signature, s3Err = parseSignature(authFields[2]) + if s3Err != nil { + return sv, s3Err + } + + // Return the structure here. + return signV4Values, nil +} diff --git a/s3/api/services/sign/signature-v4-streaming.go b/s3/api/services/sign/signature-v4-streaming.go new file mode 100644 index 000000000..d03b31ae6 --- /dev/null +++ b/s3/api/services/sign/signature-v4-streaming.go @@ -0,0 +1,421 @@ +package sign + +import ( + "bufio" + "bytes" + "crypto/sha256" + "encoding/hex" + "errors" + "github.com/bittorrent/go-btfs/s3/api/responses" + "hash" + "io" + "net/http" + "time" + + "github.com/bittorrent/go-btfs/s3/consts" + humanize "github.com/dustin/go-humanize" +) + +// Streaming AWS Signature Version '4' constants. +const ( + emptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + streamingContentSHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" + signV4ChunkedAlgorithm = "AWS4-HMAC-SHA256-PAYLOAD" +) + +// errSignatureMismatch means signature did not match. +var errSignatureMismatch = errors.New("Signature does not match") + +// getChunkSignature - get chunk signature. +func getChunkSignature(secret, seedSignature string, region string, date time.Time, hashedChunk string) string { + // Calculate string to sign. + stringToSign := signV4ChunkedAlgorithm + "\n" + + date.Format(iso8601Format) + "\n" + + getScope(date, region) + "\n" + + seedSignature + "\n" + + emptySHA256 + "\n" + + hashedChunk + + // Get hmac signing key. + signingKey := GetSigningKey(secret, date, region) + + // Calculate signature. + newSignature := GetSignature(signingKey, stringToSign) + + return newSignature +} + +// calculateSeedSignature - Calculate seed signature in accordance with +// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html +// +// returns signature, error otherwise if the signature mismatches or any other +// error while parsing and validating. +func (s *service) calculateSeedSignature(r *http.Request, iregion string) (ack, sec string, signature string, region string, date time.Time, rerr *responses.Error) { + // Copy request. + req := *r + + // Save authorization header. + v4Auth := req.Header.Get(consts.Authorization) + + // Parse signature version '4' header. + signV4Values, rerr := parseSignV4(v4Auth, "") + if rerr != nil { + return + } + + // Payload streaming. + payload := streamingContentSHA256 + + // Payload for STREAMING signature should be 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD' + if payload != req.Header.Get(consts.AmzContentSha256) { + rerr = responses.ErrContentSHA256Mismatch + return + } + + // Extract all the signed headers along with its values. + extractedSignedHeaders, rerr := extractSignedHeaders(signV4Values.SignedHeaders, r) + if rerr != nil { + return + } + + ack = signV4Values.Credential.accessKey + + sec, rerr = s.checkKeyValid(ack) + if rerr != nil { + return + } + + // Verify if region is valid. + region = iregion + if region == "" { + region = signV4Values.Credential.scope.region + } + + // Extract date, if not present throw error. + var dateStr string + if dateStr = req.Header.Get("x-amz-date"); dateStr == "" { + if dateStr = r.Header.Get("Date"); dateStr == "" { + rerr = responses.ErrMissingDateHeader + return + } + } + + // Parse date header. + date, err := time.Parse(iso8601Format, dateStr) + if err != nil { + rerr = responses.ErrMalformedDate + return + } + + // Query string. + queryStr := req.Form.Encode() + + // Get canonical request. + canonicalRequest := GetCanonicalRequest(extractedSignedHeaders, payload, queryStr, req.URL.Path, req.Method) + + // Get string to sign from canonical request. + stringToSign := GetStringToSign(canonicalRequest, date, signV4Values.Credential.getScope()) + + // Get hmac signing key. + signingKey := GetSigningKey(sec, signV4Values.Credential.scope.date, region) + + // Calculate signature. + newSignature := GetSignature(signingKey, stringToSign) + + // Verify if signature match. + if !compareSignatureV4(newSignature, signV4Values.Signature) { + rerr = responses.ErrSignatureDoesNotMatch + return + } + + // Return caculated signature. + return +} + +const maxLineLength = 4 * humanize.KiByte // assumed <= bufio.defaultBufSize 4KiB + +// lineTooLong is generated as chunk header is bigger than 4KiB. +var errLineTooLong = errors.New("header line too long") + +// malformed encoding is generated when chunk header is wrongly formed. +var errMalformedEncoding = errors.New("malformed chunked encoding") + +// chunk is considered too big if its bigger than > 16MiB. +var errChunkTooBig = errors.New("chunk too big: choose chunk size <= 16MiB") + +func (s *service) setReqBodySignV4ChunkedReader(r *http.Request, region string) (ack string, rerr *responses.Error) { + ack, sec, seedSignature, region, seedDate, rerr := s.calculateSeedSignature(r, region) + if rerr != nil { + return + } + + r.Body = &s3ChunkedReader{ + reader: bufio.NewReader(r.Body), + secret: sec, + seedSignature: seedSignature, + seedDate: seedDate, + region: region, + chunkSHA256Writer: sha256.New(), + buffer: make([]byte, 64*1024), + } + + return +} + +// Represents the overall state that is required for decoding a +// AWS Signature V4 chunked reader. +type s3ChunkedReader struct { + reader *bufio.Reader + secret string + seedSignature string + seedDate time.Time + region string + chunkSHA256Writer hash.Hash // Calculates sha256 of chunk data. + buffer []byte + offset int + err error +} + +func (cr *s3ChunkedReader) Close() (err error) { + return nil +} + +// Now, we read one chunk from the underlying reader. +// A chunk has the following format: +// +// + ";chunk-signature=" + + "\r\n" + + "\r\n" +// +// First, we read the chunk size but fail if it is larger +// than 16 MiB. We must not accept arbitrary large chunks. +// One 16 MiB is a reasonable max limit. +// +// Then we read the signature and payload data. We compute the SHA256 checksum +// of the payload and verify that it matches the expected signature value. +// +// The last chunk is *always* 0-sized. So, we must only return io.EOF if we have encountered +// a chunk with a chunk size = 0. However, this chunk still has a signature and we must +// verify it. +const maxChunkSize = 16 << 20 // 16 MiB + +// Read - implements `io.Reader`, which transparently decodes +// the incoming AWS Signature V4 streaming signature. +func (cr *s3ChunkedReader) Read(buf []byte) (n int, err error) { + // First, if there is any unread data, copy it to the client + // provided buffer. + if cr.offset > 0 { + n = copy(buf, cr.buffer[cr.offset:]) + if n == len(buf) { + cr.offset += n + return n, nil + } + cr.offset = 0 + buf = buf[n:] + } + + var size int + for { + b, err := cr.reader.ReadByte() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + cr.err = err + return n, cr.err + } + if b == ';' { // separating character + break + } + + // Manually deserialize the size since AWS specified + // the chunk size to be of variable width. In particular, + // a size of 16 is encoded as `10` while a size of 64 KB + // is `10000`. + switch { + case b >= '0' && b <= '9': + size = size<<4 | int(b-'0') + case b >= 'a' && b <= 'f': + size = size<<4 | int(b-('a'-10)) + case b >= 'A' && b <= 'F': + size = size<<4 | int(b-('A'-10)) + default: + cr.err = errMalformedEncoding + return n, cr.err + } + if size > maxChunkSize { + cr.err = errChunkTooBig + return n, cr.err + } + } + + // Now, we read the signature of the following payload and expect: + // chunk-signature=" + + "\r\n" + // + // The signature is 64 bytes long (hex-encoded SHA256 hash) and + // starts with a 16 byte header: len("chunk-signature=") + 64 == 80. + var signature [80]byte + _, err = io.ReadFull(cr.reader, signature[:]) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + cr.err = err + return n, cr.err + } + if !bytes.HasPrefix(signature[:], []byte("chunk-signature=")) { + cr.err = errMalformedEncoding + return n, cr.err + } + b, err := cr.reader.ReadByte() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + cr.err = err + return n, cr.err + } + if b != '\r' { + cr.err = errMalformedEncoding + return n, cr.err + } + b, err = cr.reader.ReadByte() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + cr.err = err + return n, cr.err + } + if b != '\n' { + cr.err = errMalformedEncoding + return n, cr.err + } + + if cap(cr.buffer) < size { + cr.buffer = make([]byte, size) + } else { + cr.buffer = cr.buffer[:size] + } + + // Now, we read the payload and compute its SHA-256 hash. + _, err = io.ReadFull(cr.reader, cr.buffer) + if err == io.EOF && size != 0 { + err = io.ErrUnexpectedEOF + } + if err != nil && err != io.EOF { + cr.err = err + return n, cr.err + } + b, err = cr.reader.ReadByte() + if b != '\r' { + cr.err = errMalformedEncoding + return n, cr.err + } + b, err = cr.reader.ReadByte() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + cr.err = err + return n, cr.err + } + if b != '\n' { + cr.err = errMalformedEncoding + return n, cr.err + } + + // Once we have read the entire chunk successfully, we verify + // that the received signature matches our computed signature. + cr.chunkSHA256Writer.Write(cr.buffer) + newSignature := getChunkSignature(cr.secret, cr.seedSignature, cr.region, cr.seedDate, hex.EncodeToString(cr.chunkSHA256Writer.Sum(nil))) + if !compareSignatureV4(string(signature[16:]), newSignature) { + cr.err = errSignatureMismatch + return n, cr.err + } + cr.seedSignature = newSignature + cr.chunkSHA256Writer.Reset() + + // If the chunk size is zero we return io.EOF. As specified by AWS, + // only the last chunk is zero-sized. + if size == 0 { + cr.err = io.EOF + return n, cr.err + } + + cr.offset = copy(buf, cr.buffer) + n += cr.offset + return n, err +} + +// readCRLF - check if reader only has '\r\n' CRLF character. +// returns malformed encoding if it doesn't. +func readCRLF(reader io.Reader) error { + buf := make([]byte, 2) + _, err := io.ReadFull(reader, buf[:2]) + if err != nil { + return err + } + if buf[0] != '\r' || buf[1] != '\n' { + return errMalformedEncoding + } + return nil +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are owned by the bufio.Reader +// so they are only valid until the next bufio read. +func readChunkLine(b *bufio.Reader) ([]byte, []byte, error) { + buf, err := b.ReadSlice('\n') + if err != nil { + // We always know when EOF is coming. + // If the caller asked for a line, there should be a line. + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = errLineTooLong + } + return nil, nil, err + } + if len(buf) >= maxLineLength { + return nil, nil, errLineTooLong + } + // Parse s3 specific chunk extension and fetch the values. + hexChunkSize, hexChunkSignature := parseS3ChunkExtension(buf) + return hexChunkSize, hexChunkSignature, nil +} + +// trimTrailingWhitespace - trim trailing white space. +func trimTrailingWhitespace(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] + } + return b +} + +// isASCIISpace - is ascii space? +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} + +// Constant s3 chunk encoding signature. +const s3ChunkSignatureStr = ";chunk-signature=" + +// parses3ChunkExtension removes any s3 specific chunk-extension from buf. +// For example, +// +// "10000;chunk-signature=..." => "10000", "chunk-signature=..." +func parseS3ChunkExtension(buf []byte) ([]byte, []byte) { + buf = trimTrailingWhitespace(buf) + semi := bytes.Index(buf, []byte(s3ChunkSignatureStr)) + // Chunk signature not found, return the whole buffer. + if semi == -1 { + return buf, nil + } + return buf[:semi], parseChunkSignature(buf[semi:]) +} + +// parseChunkSignature - parse chunk signature. +func parseChunkSignature(chunk []byte) []byte { + chunkSplits := bytes.SplitN(chunk, []byte(s3ChunkSignatureStr), 2) + return chunkSplits[1] +} diff --git a/s3/api/services/sign/signature-v4-utils.go b/s3/api/services/sign/signature-v4-utils.go new file mode 100644 index 000000000..5e8d03a1a --- /dev/null +++ b/s3/api/services/sign/signature-v4-utils.go @@ -0,0 +1,276 @@ +package sign + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "encoding/hex" + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +func GetContentSHA256Checksum(r *http.Request) (string, error) { + v, ok := r.Header[consts.AmzContentSha256] + if ok { + return v[0], nil + } + return consts.EmptySHA256, nil +} + +// isValidRegion - verify if incoming region value is valid with configured Region. +func isValidRegion(reqRegion string, confRegion string) bool { + if confRegion == "" { + return true + } + if confRegion == "US" { + confRegion = consts.DefaultBucketRegion + } + // Some older s3 clients set region as "US" instead of + // globalDefaultRegion, handle it. + if reqRegion == "US" { + reqRegion = consts.DefaultBucketRegion + } + return reqRegion == confRegion +} + +func contains(slice interface{}, elem interface{}) bool { + v := reflect.ValueOf(slice) + if v.Kind() == reflect.Slice { + for i := 0; i < v.Len(); i++ { + if v.Index(i).Interface() == elem { + return true + } + } + } + return false +} + +// extractSignedHeaders extract signed headers from Authorization header +func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, *responses.Error) { + reqHeaders := r.Header + reqQueries := r.Form + // find whether "host" is part of list of signed headers. + // if not return ErrUnsignedHeaders. "host" is mandatory. + if !contains(signedHeaders, "host") { + return nil, responses.ErrUnsignedHeaders + } + extractedSignedHeaders := make(http.Header) + for _, header := range signedHeaders { + // `host` will not be found in the headers, can be found in r.Host. + // but its alway necessary that the list of signed headers containing host in it. + val, ok := reqHeaders[http.CanonicalHeaderKey(header)] + if !ok { + // try to set headers from Query String + val, ok = reqQueries[header] + } + if ok { + extractedSignedHeaders[http.CanonicalHeaderKey(header)] = val + continue + } + switch header { + case "expect": + // Golang http server strips off 'Expect' header, if the + // client sent this as part of signed headers we need to + // handle otherwise we would see a signature mismatch. + // `aws-cli` sets this as part of signed headers. + // + // According to + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20 + // Expect header is always of form: + // + // Expect = "Expect" ":" 1#expectation + // expectation = "100-continue" | expectation-extension + // + // So it safe to assume that '100-continue' is what would + // be sent, for the time being keep this work around. + // Adding a *TODO* to remove this later when Golang server + // doesn't filter out the 'Expect' header. + extractedSignedHeaders.Set(header, "100-continue") + case "host": + // Go http server removes "host" from Request.Header + + //extractedSignedHeaders.Set(header, r.Host) + // todo use r.Host, or filedag-web deal with + //value := strings.Split(r.Host, ":") + extractedSignedHeaders.Set(header, r.Host) + case "transfer-encoding": + // Go http server removes "host" from Request.Header + extractedSignedHeaders[http.CanonicalHeaderKey(header)] = r.TransferEncoding + case "content-length": + // Signature-V4 spec excludes Content-Length from signed headers list for signature calculation. + // But some clients deviate from this rule. Hence we consider Content-Length for signature + // calculation to be compatible with such clients. + extractedSignedHeaders.Set(header, strconv.FormatInt(r.ContentLength, 10)) + default: + return nil, responses.ErrUnsignedHeaders + } + } + return extractedSignedHeaders, nil +} + +// if object matches reserved string, no need to encode them +var reservedObjectNames = regexp.MustCompile("^[a-zA-Z0-9-_.~/]+$") + +// EncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences +// +// This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8 +// non english characters cannot be parsed due to the nature in which url.Encode() is written +// +// This function on the other hand is a direct replacement for url.Encode() technique to support +// pretty much every UTF-8 character. +func EncodePath(pathName string) string { + if reservedObjectNames.MatchString(pathName) { + return pathName + } + var encodedPathname string + for _, s := range pathName { + if 'A' <= s && s <= 'Z' || 'a' <= s && s <= 'z' || '0' <= s && s <= '9' { // §2.3 Unreserved characters (mark) + encodedPathname = encodedPathname + string(s) + continue + } + switch s { + case '-', '_', '.', '~', '/': // §2.3 Unreserved characters (mark) + encodedPathname = encodedPathname + string(s) + continue + default: + len := utf8.RuneLen(s) + if len < 0 { + // if utf8 cannot convert return the same string as is + return pathName + } + u := make([]byte, len) + utf8.EncodeRune(u, s) + for _, r := range u { + hex := hex.EncodeToString([]byte{r}) + encodedPathname = encodedPathname + "%" + strings.ToUpper(hex) + } + } + } + return encodedPathname +} + +// GetCanonicalRequest generate a canonical request of style +// +// canonicalRequest = +// +// \n +// \n +// \n +// \n +// \n +// +func GetCanonicalRequest(extractedSignedHeaders http.Header, payload, queryStr, urlPath, method string) string { + rawQuery := strings.ReplaceAll(queryStr, "+", "%20") + encodedPath := EncodePath(urlPath) + canonicalRequest := strings.Join([]string{ + method, + encodedPath, + rawQuery, + getCanonicalHeaders(extractedSignedHeaders), + GetSignedHeaders(extractedSignedHeaders), + payload, + }, "\n") + return canonicalRequest +} + +// GetSignedHeaders generate a string i.e alphabetically sorted, semicolon-separated list of lowercase request header names +func GetSignedHeaders(signedHeaders http.Header) string { + var headers []string + for k := range signedHeaders { + headers = append(headers, strings.ToLower(k)) + } + sort.Strings(headers) + return strings.Join(headers, ";") +} + +// getCanonicalHeaders generate a list of request headers with their values +func getCanonicalHeaders(signedHeaders http.Header) string { + var headers []string + vals := make(http.Header) + for k, vv := range signedHeaders { + headers = append(headers, strings.ToLower(k)) + vals[strings.ToLower(k)] = vv + } + sort.Strings(headers) + + var buf bytes.Buffer + for _, k := range headers { + buf.WriteString(k) + buf.WriteByte(':') + for idx, v := range vals[k] { + if idx > 0 { + buf.WriteByte(',') + } + buf.WriteString(signV4TrimAll(v)) + } + buf.WriteByte('\n') + } + return buf.String() +} + +// GetStringToSign a string based on selected query values. +func GetStringToSign(canonicalRequest string, t time.Time, scope string) string { + stringToSign := signV4Algorithm + "\n" + t.Format(iso8601Format) + "\n" + stringToSign += scope + "\n" + canonicalRequestBytes := sha256.Sum256([]byte(canonicalRequest)) + stringToSign += hex.EncodeToString(canonicalRequestBytes[:]) + return stringToSign +} + +// GetSigningKey hmac seed to calculate final signature. +func GetSigningKey(secretKey string, t time.Time, region string) []byte { + date := sumHMAC([]byte("AWS4"+secretKey), []byte(t.Format(yyyymmdd))) + regionBytes := sumHMAC(date, []byte(region)) + service := sumHMAC(regionBytes, []byte("s3")) + signingKey := sumHMAC(service, []byte("aws4_request")) + return signingKey +} + +// GetSignature final signature in hexadecimal form. +func GetSignature(signingKey []byte, stringToSign string) string { + return hex.EncodeToString(sumHMAC(signingKey, []byte(stringToSign))) +} + +// sumHMAC calculate hmac between two input byte array. +func sumHMAC(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// Trim leading and trailing spaces and replace sequential spaces with one space, following Trimall() +// in http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html +func signV4TrimAll(input string) string { + // Compress adjacent spaces (a space is determined by + // unicode.IsSpace() internally here) to one space and return + return strings.Join(strings.Fields(input), " ") +} + +// getScope generate a string of a specific date, an AWS region, and a service. +func getScope(t time.Time, region string) string { + scope := strings.Join([]string{ + t.Format(yyyymmdd), + region, + "s3", + "aws4_request", + }, consts.SlashSeparator) + return scope +} + +// compareSignatureV4 returns true if and only if both signatures +// are equal. The signatures are expected to be HEX encoded strings +// according to the AWS S3 signature V4 spec. +func compareSignatureV4(sig1, sig2 string) bool { + // The CTC using []byte(str) works because the hex encoding + // is unique for a sequence of bytes. See also compareSignatureV2. + return subtle.ConstantTimeCompare([]byte(sig1), []byte(sig2)) == 1 +} diff --git a/s3/api/services/sign/signature-v4.go b/s3/api/services/sign/signature-v4.go new file mode 100644 index 000000000..ce17b86ca --- /dev/null +++ b/s3/api/services/sign/signature-v4.go @@ -0,0 +1,119 @@ +package sign + +import ( + "github.com/bittorrent/go-btfs/s3/api/responses" + "github.com/bittorrent/go-btfs/s3/consts" + "net/http" + "time" +) + +// AWS Signature Version '4' constants. +const ( + signV2Algorithm = "AWS" + signV4Algorithm = "AWS4-HMAC-SHA256" + iso8601Format = "20060102T150405Z" + yyyymmdd = "20060102" +) + +func (s *service) reqSignatureV4Verify(r *http.Request, region string) (ack string, rerr *responses.Error) { + sha256sum, err := GetContentSHA256Checksum(r) + if err != nil { + rerr = responses.ErrInternalError + return + } + ack, rerr = s.doesSignatureMatch(sha256sum, r, region) + return +} + +// doesSignatureMatch - Verify authorization header with calculated header in accordance with +// - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html +// +// returns nil if signature matches. +func (s *service) doesSignatureMatch(hashedPayload string, r *http.Request, region string) (ack string, rerr *responses.Error) { + // Copy request. + req := *r + + // Save authorization header. + v4Auth := req.Header.Get(consts.Authorization) + + // Parse signature version '4' header. + signV4Values, rerr := parseSignV4(v4Auth, region) + if rerr != nil { + return + } + + // Extract all the signed headers along with its values. + extractedSignedHeaders, rerr := extractSignedHeaders(signV4Values.SignedHeaders, r) + if rerr != nil { + return + } + + ack = signV4Values.Credential.accessKey + secret, rerr := s.checkKeyValid(ack) + if rerr != nil { + return + } + + // Extract date, if not present throw error. + var date string + if date = req.Header.Get(consts.AmzDate); date == "" { + if date = r.Header.Get(consts.Date); date == "" { + rerr = responses.ErrMissingDateHeader + return + } + } + + // Parse date header. + t, err := time.Parse(iso8601Format, date) + if err != nil { + rerr = responses.ErrAuthorizationHeaderMalformed + return + } + + // Query string. + queryStr := req.URL.Query().Encode() + + // Get canonical request. + canonicalRequest := GetCanonicalRequest(extractedSignedHeaders, hashedPayload, queryStr, req.URL.Path, req.Method) + + // Get string to sign from canonical request. + stringToSign := GetStringToSign(canonicalRequest, t, signV4Values.Credential.getScope()) + + // Get hmac signing key. + signingKey := GetSigningKey(secret, signV4Values.Credential.scope.date, + signV4Values.Credential.scope.region) + + // Calculate signature. + newSignature := GetSignature(signingKey, stringToSign) + + // Verify if signature match. + if !compareSignatureV4(newSignature, signV4Values.Signature) { + rerr = responses.ErrSignatureDoesNotMatch + return + } + + // Return error none. + return +} + +// check if the access key is valid and recognized, additionally +// also returns if the access key is owner/admin. +func (s *service) checkKeyValid(ack string) (secret string, rerr *responses.Error) { + secret, exists, enable, err := s.getSecret(ack) + if err != nil { + rerr = responses.ErrInternalError + return + } + + if !exists { + rerr = responses.ErrInvalidAccessKeyID + return + } + + if !enable { + rerr = responses.ErrAccessKeyDisabled + return + } + + return +} diff --git a/s3/consts/consts.go b/s3/consts/consts.go new file mode 100644 index 000000000..8a0690239 --- /dev/null +++ b/s3/consts/consts.go @@ -0,0 +1,95 @@ +package consts + +import ( + "github.com/aws/aws-sdk-go/service/s3" + "github.com/dustin/go-humanize" +) + +const ( + StreamingContentSHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" + EmptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + UnsignedSHA256 = "UNSIGNED-PAYLOAD" + SlashSeparator = "/" + StsAction = "Action" + StreamingContentEncoding = "aws-chunked" + DefaultEncodingType = "url" + DefaultContentType = "binary/octet-stream" + DefaultServerInfo = "BTFS" + DefaultBucketRegion = "us-east-1" + DefaultBucketACL = s3.BucketCannedACLPublicRead + AllUsersURI = "http://acs.amazonaws.com/groups/global/AllUsers" +) + +var SupportedBucketRegions = map[string]bool{ + DefaultBucketRegion: true, +} + +var SupportedBucketACLs = map[string]bool{ + s3.BucketCannedACLPrivate: true, + s3.BucketCannedACLPublicRead: true, + s3.BucketCannedACLPublicReadWrite: true, +} + +// Standard S3 HTTP request constants +const ( + AmzACL = "x-amz-acl" + AmzContentSha256 = "X-Amz-Content-Sha256" + AmzDate = "X-Amz-Date" + AmzRequestID = "x-amz-request-id" +) + +// Standard S3 HTTP response constants +const ( + LastModified = "Last-Modified" + Date = "Date" + ETag = "ETag" + ContentType = "Content-Type" + ContentMD5 = "Content-Md5" + ContentEncoding = "Content-Encoding" + Expires = "Expires" + ContentLength = "Content-Length" + ContentLanguage = "Content-Language" + ContentRange = "Content-Range" + Connection = "Connection" + AcceptRanges = "Accept-Ranges" + AmzBucketRegion = "X-Amz-Bucket-Region" + ServerInfo = "Server" + RetryAfter = "Retry-After" + Location = "Location" + CacheControl = "Cache-Control" + ContentDisposition = "Content-Disposition" + Authorization = "Authorization" + Action = "Action" + XRequestWith = "X-Requested-With" + Range = "Range" + UserAgent = "User-Agent" + Cid = "Cid" +) + +// Standard HTTP cors headers +const ( + AccessControlAllowOrigin = "Access-Control-Allow-Origin" + AccessControlAllowMethods = "Access-Control-Allow-Methods" + AccessControlAllowHeaders = "Access-Control-Allow-Headers" + AccessControlExposeHeaders = "Access-Control-Expose-Headers" + AccessControlAllowCredentials = "Access-Control-Allow-Credentials" + AccessControlMaxAge = "Access-Control-Max-Age" +) + +// object const +const ( + MaxXMLBodySize = 5 * humanize.MiByte + MaxObjectSize = 5 * humanize.TiByte + MinPartSize = 5 * humanize.MiByte + MaxPartSize = 5 * humanize.GiByte + MinPartNumber = 1 + MaxPartNumber = 10000 + MaxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse. + MaxDeleteList = 1000 // Limit number of objects deleted in a delete call. +) + +// Common http query params S3 API +const ( + MaxKeys = "max-keys" + PartNumber = "partNumber" +) diff --git a/s3/ctxmu/ctx_rwlock.go b/s3/ctxmu/ctx_rwlock.go new file mode 100644 index 000000000..c6b76bcaf --- /dev/null +++ b/s3/ctxmu/ctx_rwlock.go @@ -0,0 +1,67 @@ +package ctxmu + +import ( + "golang.org/x/net/context" + "math/rand" + "sync" + "time" +) + +const lockRetryInterval = 50 * time.Millisecond + +type CtxLocker interface { + Lock(ctx context.Context) (err error) + Unlock() +} + +type CtxRWLocker interface { + CtxLocker + RLock(ctx context.Context) (err error) + RUnlock() +} + +type CtxRWMutex struct { + lock sync.RWMutex +} + +func (c *CtxRWMutex) Lock(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if c.lock.TryLock() { + return nil + } + } + time.Sleep(c.getRandInterval()) + } +} + +func (c *CtxRWMutex) Unlock() { + c.lock.Unlock() +} + +func (c *CtxRWMutex) RLock(ctx context.Context) (err error) { + for { + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + if c.lock.TryRLock() { + return + } + } + time.Sleep(c.getRandInterval()) + } +} + +func (c *CtxRWMutex) RUnlock() { + c.lock.RUnlock() +} + +func (c *CtxRWMutex) getRandInterval() time.Duration { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + return time.Duration(r.Float64() * float64(lockRetryInterval)) +} diff --git a/s3/ctxmu/multi_ctx_rwlock.go b/s3/ctxmu/multi_ctx_rwlock.go new file mode 100644 index 000000000..67df52b6a --- /dev/null +++ b/s3/ctxmu/multi_ctx_rwlock.go @@ -0,0 +1,137 @@ +package ctxmu + +import ( + "context" + "sync" + "sync/atomic" + "time" +) + +type MultiCtxLocker interface { + Lock(ctx context.Context, key interface{}) (err error) + Unlock(key interface{}) +} + +type MultiCtxRWLocker interface { + MultiCtxLocker + RLock(ctx context.Context, key interface{}) (err error) + RUnlock(key interface{}) +} + +type MultiCtxRWMutex struct { + locks sync.Map + pool sync.Pool +} + +func NewDefaultMultiCtxRWMutex() *MultiCtxRWMutex { + return NewMultiCtxRWMutex(func() CtxRWLocker { + return &CtxRWMutex{} + }) +} + +func NewMultiCtxRWMutex(newCtxRWLock func() CtxRWLocker) *MultiCtxRWMutex { + return &MultiCtxRWMutex{ + locks: sync.Map{}, + pool: sync.Pool{ + New: func() interface{} { + return newCtxRWLock() + }, + }, + } +} + +type ctxRWLockRefCounter struct { + count int64 + lock CtxRWLocker +} + +func (m *MultiCtxRWMutex) Lock(ctx context.Context, key interface{}) (err error) { + counter, err := m.incrGetRWLockRefCounter(ctx, key) + if err != nil { + return + } + err = (counter.lock).Lock(ctx) + if err != nil { + m.decrPutRWLockRefCounter(key, counter) + } + return +} + +func (m *MultiCtxRWMutex) LockWithTimout(timeout time.Duration, key interface{}) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + err = m.Lock(ctx, key) + return +} + +func (m *MultiCtxRWMutex) Unlock(key interface{}) { + counter := m.mustGetCounter(key) + counter.lock.Unlock() + m.decrPutRWLockRefCounter(key, counter) + return +} + +func (m *MultiCtxRWMutex) RLock(ctx context.Context, key interface{}) (err error) { + counter, err := m.incrGetRWLockRefCounter(ctx, key) + if err != nil { + return + } + err = (counter.lock).RLock(ctx) + if err != nil { + m.decrPutRWLockRefCounter(key, counter) + } + return +} + +func (m *MultiCtxRWMutex) RLockWithTimout(timeout time.Duration, key interface{}) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + err = m.RLock(ctx, key) + return +} + +func (m *MultiCtxRWMutex) RUnlock(key interface{}) { + counter := m.mustGetCounter(key) + counter.lock.RUnlock() + m.decrPutRWLockRefCounter(key, counter) + return +} + +func (m *MultiCtxRWMutex) mustGetCounter(key interface{}) (counter *ctxRWLockRefCounter) { + actual, ok := m.locks.Load(key) + if !ok { + panic("key's lock has been invalidly freed") + } + counter = actual.(*ctxRWLockRefCounter) + return +} + +func (m *MultiCtxRWMutex) incrGetRWLockRefCounter(ctx context.Context, key interface{}) (counter *ctxRWLockRefCounter, err error) { + for { + err = ctx.Err() + if err != nil { + return + } + actual, _ := m.locks.LoadOrStore(key, &ctxRWLockRefCounter{ + count: 0, + lock: m.pool.Get().(*CtxRWMutex), + }) + counter = actual.(*ctxRWLockRefCounter) + old := counter.count + if old < 0 { + continue + } + if atomic.CompareAndSwapInt64(&counter.count, old, old+1) { + break + } + } + return +} + +func (m *MultiCtxRWMutex) decrPutRWLockRefCounter(key interface{}, counter *ctxRWLockRefCounter) { + atomic.AddInt64(&counter.count, -1) + if atomic.CompareAndSwapInt64(&counter.count, 0, -1) { + m.pool.Put(counter.lock) + m.locks.Delete(key) + } +} diff --git a/s3/ctxmu/multi_ctx_rwlock_test.go b/s3/ctxmu/multi_ctx_rwlock_test.go new file mode 100644 index 000000000..ab3cca3a1 --- /dev/null +++ b/s3/ctxmu/multi_ctx_rwlock_test.go @@ -0,0 +1,109 @@ +package ctxmu + +import ( + "context" + "errors" + "fmt" + "golang.org/x/sync/errgroup" + "math/rand" + "testing" + "time" +) + +func TestMultiCtxRWMutex_Lock(t *testing.T) { + locks := NewMultiCtxRWMutex(func() CtxRWLocker { + return &CtxRWMutex{} + }) + eg := errgroup.Group{} + key := "test_key" + err := locks.Lock(context.Background(), key) + if err != nil { + t.Fatalf("can not lock") + } else { + t.Logf("can lock") + } + eg.Go(func() error { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) + defer cancel() + lerr := locks.Lock(ctx, key) + if lerr == nil { + t.Fatalf("can lock after locked") + } else if !errors.Is(lerr, context.DeadlineExceeded) { + t.Fatalf("timout lock return non DeadlineExceeded error: %v", lerr) + } else { + t.Logf("can not lock after locked") + } + lerr = locks.RLock(ctx, key) + if lerr == nil { + t.Fatalf("can rlock after locked") + } else if !errors.Is(lerr, context.DeadlineExceeded) { + t.Fatalf("timout rlock return non DeadlineExceeded error: %v", lerr) + } else { + t.Logf("can not rlock after locked") + } + locks.Unlock(key) + lerr = locks.Lock(context.Background(), key) + if lerr != nil { + t.Fatalf("can not lock after unlocked") + } else { + t.Logf("can lock after unlocked") + } + locks.Unlock(key) + lerr = locks.RLock(context.Background(), key) + if lerr != nil { + t.Fatalf("can not rlock after unlocked") + } else { + t.Logf("can rlock after unlocked") + } + locks.RUnlock(key) + return nil + }) + + _ = eg.Wait() +} + +func TestMultiCtxRWMutex_LockWithTimout(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + eg := errgroup.Group{} + timeout := 50 * time.Millisecond + locks := NewMultiCtxRWMutex(func() CtxRWLocker { + return &CtxRWMutex{} + }) + for i := 0; i < 1000; i++ { + okey := fmt.Sprintf("key_%d", i) + for j := 0; j < 100; j++ { + key := okey + n := j + wt := rand.Intn(200) + if j == 0 || j == 30 { + eg.Go(func() error { + lerr := locks.LockWithTimout(timeout, key) + if lerr == nil { + defer func() { + t.Logf("%s %d Unlock: %v, %d", key, n, lerr, wt) + locks.Unlock(key) + }() + } + t.Logf("%s %d Lock: %v, %d", key, n, lerr, wt) + time.Sleep(time.Duration(wt) * time.Millisecond) + return nil + }) + } else { + eg.Go(func() error { + lerr := locks.RLockWithTimout(timeout, key) + if lerr == nil { + defer func() { + t.Logf("%s %d RLock: %v, %d", key, n, lerr, wt) + locks.RUnlock(key) + }() + } + t.Logf("%s %d RLock: %v, %d", key, n, lerr, wt) + time.Sleep(time.Duration(wt) * time.Millisecond) + return nil + }) + + } + } + } + _ = eg.Wait() +} diff --git a/s3/etag/etag.go b/s3/etag/etag.go new file mode 100644 index 000000000..ccfafaab2 --- /dev/null +++ b/s3/etag/etag.go @@ -0,0 +1,194 @@ +package etag + +import ( + "bytes" + "crypto/md5" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "net/http" + "strconv" + "strings" +) + +// ETag is a single S3 ETag. +// +// An S3 ETag sometimes corresponds to the MD5 of +// the S3 object content. However, when an object +// is encrypted, compressed or uploaded using +// the S3 multipart API then its ETag is not +// necessarily the MD5 of the object content. +// +// For a more detailed description of S3 ETags +// take a look at the package documentation. +type ETag []byte + +// String returns the string representation of the ETag. +// +// The returned string is a hex representation of the +// binary ETag with an optional '-' suffix. +func (e ETag) String() string { + if e.IsMultipart() { + return hex.EncodeToString(e[:16]) + string(e[16:]) + } + return hex.EncodeToString(e) +} + +// IsEncrypted reports whether the ETag is encrypted. +func (e ETag) IsEncrypted() bool { + return len(e) > 16 && !bytes.ContainsRune(e, '-') +} + +// IsMultipart reports whether the ETag belongs to an +// object that has been uploaded using the S3 multipart +// API. +// An S3 multipart ETag has a - suffix. +func (e ETag) IsMultipart() bool { + return len(e) > 16 && bytes.ContainsRune(e, '-') +} + +// Parts returns the number of object parts that are +// referenced by this ETag. It returns 1 if the object +// has been uploaded using the S3 singlepart API. +// +// Parts may panic if the ETag is an invalid multipart +// ETag. +func (e ETag) Parts() int { + if !e.IsMultipart() { + return 1 + } + + n := bytes.IndexRune(e, '-') + parts, err := strconv.Atoi(string(e[n+1:])) + if err != nil { + panic(err) // malformed ETag + } + return parts +} + +var _ Tagger = ETag{} // compiler check + +// ETag returns the ETag itself. +// +// By providing this method ETag implements +// the Tagger interface. +func (e ETag) ETag() ETag { return e } + +// FromContentMD5 decodes and returns the Content-MD5 +// as ETag, if set. If no Content-MD5 header is set +// it returns an empty ETag and no error. +func FromContentMD5(h http.Header) (ETag, error) { + v, ok := h["Content-Md5"] + if !ok { + return nil, nil + } + if v[0] == "" { + return nil, errors.New("etag: content-md5 is set but contains no value") + } + b, err := base64.StdEncoding.Strict().DecodeString(v[0]) + if err != nil { + return nil, err + } + if len(b) != md5.Size { + return nil, errors.New("etag: invalid content-md5") + } + return ETag(b), nil +} + +// Multipart computes an S3 multipart ETag given a list of +// S3 singlepart ETags. It returns nil if the list of +// ETags is empty. +// +// Any encrypted or multipart ETag will be ignored and not +// used to compute the returned ETag. +func Multipart(etags ...ETag) ETag { + if len(etags) == 0 { + return nil + } + + var n int64 + h := md5.New() + for _, etag := range etags { + if !etag.IsMultipart() && !etag.IsEncrypted() { + h.Write(etag) + n++ + } + } + etag := append(h.Sum(nil), '-') + return strconv.AppendInt(etag, n, 10) +} + +// Equal returns true if and only if the two ETags are +// identical. +func Equal(a, b ETag) bool { return bytes.Equal(a, b) } + +// Parse parses s as an S3 ETag, returning the result. +// The string can be an encrypted, singlepart +// or multipart S3 ETag. It returns an error if s is +// not a valid textual representation of an ETag. +func Parse(s string) (ETag, error) { + const strict = false + return parse(s, strict) +} + +// parse parse s as an S3 ETag, returning the result. +// It operates in one of two modes: +// - strict +// - non-strict +// +// In strict mode, parse only accepts ETags that +// are AWS S3 compatible. In particular, an AWS +// S3 ETag always consists of a 128 bit checksum +// value and an optional - suffix. +// Therefore, s must have the following form in +// strict mode: <32-hex-characters>[-] +// +// In non-strict mode, parse also accepts ETags +// that are not AWS S3 compatible - e.g. encrypted +// ETags. +func parse(s string, strict bool) (ETag, error) { + // An S3 ETag may be a double-quoted string. + // Therefore, we remove double quotes at the + // start and end, if any. + if strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`) { + s = s[1 : len(s)-1] + } + + // An S3 ETag may be a multipart ETag that + // contains a '-' followed by a number. + // If the ETag does not a '-' is is either + // a singlepart or encrypted ETag. + n := strings.IndexRune(s, '-') + if n == -1 { + etag, err := hex.DecodeString(s) + if err != nil { + return nil, err + } + if strict && len(etag) != 16 { // AWS S3 ETags are always 128 bit long + return nil, fmt.Errorf("etag: invalid length %d", len(etag)) + } + return ETag(etag), nil + } + + prefix, suffix := s[:n], s[n:] + if len(prefix) != 32 { + return nil, fmt.Errorf("etag: invalid prefix length %d", len(prefix)) + } + if len(suffix) <= 1 { + return nil, errors.New("etag: suffix is not a part number") + } + + etag, err := hex.DecodeString(prefix) + if err != nil { + return nil, err + } + partNumber, err := strconv.Atoi(suffix[1:]) // suffix[0] == '-' Therefore, we start parsing at suffix[1] + if err != nil { + return nil, err + } + if strict && (partNumber == 0 || partNumber > 10000) { + return nil, fmt.Errorf("etag: invalid part number %d", partNumber) + } + return ETag(append(etag, suffix...)), nil +} diff --git a/s3/etag/etag_test.go b/s3/etag/etag_test.go new file mode 100644 index 000000000..70b8c01ea --- /dev/null +++ b/s3/etag/etag_test.go @@ -0,0 +1,210 @@ +package etag + +import ( + "io" + "io/ioutil" + "net/http" + "strings" + "testing" +) + +var _ Tagger = Wrap(nil, nil).(Tagger) // runtime check that wrapReader implements Tagger + +var parseTests = []struct { + String string + ETag ETag + ShouldFail bool +}{ + {String: "3b83ef96387f1465", ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101}}, // 0 + {String: "3b83ef96387f14655fc854ddc3c6bd57", ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}}, // 1 + {String: `"3b83ef96387f14655fc854ddc3c6bd57"`, ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}}, // 2 + {String: "ceb8853ddc5086cc4ab9e149f8f09c88-1", ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 49}}, // 3 + {String: `"ceb8853ddc5086cc4ab9e149f8f09c88-2"`, ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 50}}, // 4 + { // 5 + String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941", + ETag: ETag{144, 64, 44, 120, 210, 220, 205, 222, 225, 233, 232, 98, 34, 206, 44, 99, 97, 103, 95, 53, 41, 210, 96, 0, 174, 46, 144, 15, 242, 22, 179, 203, 89, 225, 48, 224, 146, 216, 162, 152, 30, 119, 111, 77, 11, 214, 9, 65}, + }, + + {String: `"3b83ef96387f14655fc854ddc3c6bd57`, ShouldFail: true}, // 6 + {String: "ceb8853ddc5086cc4ab9e149f8f09c88-", ShouldFail: true}, // 7 + {String: "ceb8853ddc5086cc4ab9e149f8f09c88-2a", ShouldFail: true}, // 8 + {String: "ceb8853ddc5086cc4ab9e149f8f09c88-2-1", ShouldFail: true}, // 9 + {String: "90402c78d2dccddee1e9e86222ce2c-1", ShouldFail: true}, // 10 + {String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941-1", ShouldFail: true}, // 11 +} + +func TestParse(t *testing.T) { + for i, test := range parseTests { + etag, err := Parse(test.String) + if err == nil && test.ShouldFail { + t.Fatalf("Test %d: parse should have failed but succeeded", i) + } + if err != nil && !test.ShouldFail { + t.Fatalf("Test %d: failed to parse ETag %q: %v", i, test.String, err) + } + if !Equal(etag, test.ETag) { + t.Log([]byte(etag)) + t.Fatalf("Test %d: ETags don't match", i) + } + } +} + +var stringTests = []struct { + ETag ETag + String string +}{ + {ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101}, String: "3b83ef96387f1465"}, // 0 + {ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}, String: "3b83ef96387f14655fc854ddc3c6bd57"}, // 1 + {ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 49}, String: "ceb8853ddc5086cc4ab9e149f8f09c88-1"}, // 2 + {ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 50}, String: "ceb8853ddc5086cc4ab9e149f8f09c88-2"}, // 3 + { // 4 + ETag: ETag{144, 64, 44, 120, 210, 220, 205, 222, 225, 233, 232, 98, 34, 206, 44, 99, 97, 103, 95, 53, 41, 210, 96, 0, 174, 46, 144, 15, 242, 22, 179, 203, 89, 225, 48, 224, 146, 216, 162, 152, 30, 119, 111, 77, 11, 214, 9, 65}, + String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941", + }, +} + +func TestString(t *testing.T) { + for i, test := range stringTests { + s := test.ETag.String() + if s != test.String { + t.Fatalf("Test %d: got %s - want %s", i, s, test.String) + } + } +} + +var equalTests = []struct { + A string + B string + Equal bool +}{ + {A: "3b83ef96387f14655fc854ddc3c6bd57", B: "3b83ef96387f14655fc854ddc3c6bd57", Equal: true}, // 0 + {A: "3b83ef96387f14655fc854ddc3c6bd57", B: `"3b83ef96387f14655fc854ddc3c6bd57"`, Equal: true}, // 1 + + {A: "3b83ef96387f14655fc854ddc3c6bd57", B: "3b83ef96387f14655fc854ddc3c6bd57-2", Equal: false}, // 2 + {A: "3b83ef96387f14655fc854ddc3c6bd57", B: "ceb8853ddc5086cc4ab9e149f8f09c88", Equal: false}, // 3 +} + +func TestEqual(t *testing.T) { + for i, test := range equalTests { + A, err := Parse(test.A) + if err != nil { + t.Fatalf("Test %d: %v", i, err) + } + B, err := Parse(test.B) + if err != nil { + t.Fatalf("Test %d: %v", i, err) + } + if equal := Equal(A, B); equal != test.Equal { + t.Fatalf("Test %d: got %v - want %v", i, equal, test.Equal) + } + } +} + +var readerTests = []struct { // Reference values computed by: echo | md5sum + Content string + ETag ETag +}{ + { + Content: "", ETag: ETag{212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126}, + }, + { + Content: " ", ETag: ETag{114, 21, 238, 156, 125, 157, 194, 41, 210, 146, 26, 64, 232, 153, 236, 95}, + }, + { + Content: "Hello World", ETag: ETag{177, 10, 141, 177, 100, 224, 117, 65, 5, 183, 169, 155, 231, 46, 63, 229}, + }, +} + +func TestReader(t *testing.T) { + for i, test := range readerTests { + reader := NewReader(strings.NewReader(test.Content), test.ETag) + if _, err := io.Copy(ioutil.Discard, reader); err != nil { + t.Fatalf("Test %d: read failed: %v", i, err) + } + if ETag := reader.ETag(); !Equal(ETag, test.ETag) { + t.Fatalf("Test %d: ETag mismatch: got %q - want %q", i, ETag, test.ETag) + } + } +} + +var multipartTests = []struct { // Test cases have been generated using AWS S3 + ETags []ETag + Multipart ETag +}{ + { + ETags: []ETag{}, + Multipart: ETag{}, + }, + { + ETags: []ETag{must("b10a8db164e0754105b7a99be72e3fe5")}, + Multipart: must("7b976cc68452e003eec7cb0eb631a19a-1"), + }, + { + ETags: []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6")}, + Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"), + }, + { + ETags: []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("a096eb5968d607c2975fb2c4af9ab225"), must("b10a8db164e0754105b7a99be72e3fe5")}, + Multipart: must("9a0d1febd9265f59f368ceb652770bc2-3"), + }, + { // Check that multipart ETags are ignored + ETags: []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("ceb8853ddc5086cc4ab9e149f8f09c88-1")}, + Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"), + }, + { // Check that encrypted ETags are ignored + ETags: []ETag{ + must("90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941"), + must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6"), + }, + Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"), + }, +} + +func TestMultipart(t *testing.T) { + for i, test := range multipartTests { + if multipart := Multipart(test.ETags...); !Equal(multipart, test.Multipart) { + t.Fatalf("Test %d: got %q - want %q", i, multipart, test.Multipart) + } + } +} + +var fromContentMD5Tests = []struct { + Header http.Header + ETag ETag + ShouldFail bool +}{ + {Header: http.Header{}, ETag: nil}, // 0 + {Header: http.Header{"Content-Md5": []string{"1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: must("d41d8cd98f00b204e9800998ecf8427e")}, // 1 + {Header: http.Header{"Content-Md5": []string{"sQqNsWTgdUEFt6mb5y4/5Q=="}}, ETag: must("b10a8db164e0754105b7a99be72e3fe5")}, // 2 + {Header: http.Header{"Content-MD5": []string{"1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: nil}, // 3 (Content-MD5 vs Content-Md5) + {Header: http.Header{"Content-Md5": []string{"sQqNsWTgdUEFt6mb5y4/5Q==", "1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: must("b10a8db164e0754105b7a99be72e3fe5")}, // 4 + + {Header: http.Header{"Content-Md5": []string{""}}, ShouldFail: true}, // 5 (empty value) + {Header: http.Header{"Content-Md5": []string{"", "sQqNsWTgdUEFt6mb5y4/5Q=="}}, ShouldFail: true}, // 6 (empty value) + {Header: http.Header{"Content-Md5": []string{"d41d8cd98f00b204e9800998ecf8427e"}}, ShouldFail: true}, // 7 (content-md5 is invalid b64 / of invalid length) +} + +func TestFromContentMD5(t *testing.T) { + for i, test := range fromContentMD5Tests { + ETag, err := FromContentMD5(test.Header) + if err != nil && !test.ShouldFail { + t.Fatalf("Test %d: failed to convert Content-MD5 to ETag: %v", i, err) + } + if err == nil && test.ShouldFail { + t.Fatalf("Test %d: should have failed but succeeded", i) + } + if err == nil { + if !Equal(ETag, test.ETag) { + t.Fatalf("Test %d: got %q - want %q", i, ETag, test.ETag) + } + } + } +} + +func must(s string) ETag { + t, err := Parse(s) + if err != nil { + panic(err) + } + return t +} diff --git a/s3/etag/reader.go b/s3/etag/reader.go new file mode 100644 index 000000000..532e5e973 --- /dev/null +++ b/s3/etag/reader.go @@ -0,0 +1,138 @@ +package etag + +import ( + "crypto/md5" + "fmt" + "hash" + "io" +) + +// Tagger is the interface that wraps the basic ETag method. +type Tagger interface { + ETag() ETag +} + +type wrapReader struct { + io.Reader + Tagger +} + +var _ Tagger = wrapReader{} // compiler check + +// ETag returns the ETag of the underlying Tagger. +func (r wrapReader) ETag() ETag { + if r.Tagger == nil { + return nil + } + return r.Tagger.ETag() +} + +// Wrap returns an io.Reader that reads from the wrapped +// io.Reader and implements the Tagger interaface. +// +// If content implements Tagger then the returned Reader +// returns ETag of the content. Otherwise, it returns +// nil as ETag. +// +// Wrap provides an adapter for io.Reader implemetations +// that don't implement the Tagger interface. +// It is mainly used to provide a high-level io.Reader +// access to the ETag computed by a low-level io.Reader: +// +// content := etag.NewReader(r.Body, nil) +// +// compressedContent := Compress(content) +// encryptedContent := Encrypt(compressedContent) +// +// // Now, we need an io.Reader that can access +// // the ETag computed over the content. +// reader := etag.Wrap(encryptedContent, content) +func Wrap(wrapped, content io.Reader) io.Reader { + if t, ok := content.(Tagger); ok { + return wrapReader{ + Reader: wrapped, + Tagger: t, + } + } + return wrapReader{ + Reader: wrapped, + } +} + +// A Reader wraps an io.Reader and computes the +// MD5 checksum of the read content as ETag. +// +// Optionally, a Reader can also verify that +// the computed ETag matches an expected value. +// Therefore, it compares both ETags once the +// underlying io.Reader returns io.EOF. +// If the computed ETag does not match the +// expected ETag then Read returns a VerifyError. +// +// Reader implements the Tagger interface. +type Reader struct { + src io.Reader + + md5 hash.Hash + checksum ETag + + readN int64 +} + +// NewReader returns a new Reader that computes the +// MD5 checksum of the content read from r as ETag. +// +// If the provided etag is not nil the returned +// Reader compares the etag with the computed +// MD5 sum once the r returns io.EOF. +func NewReader(r io.Reader, etag ETag) *Reader { + if er, ok := r.(*Reader); ok { + if er.readN == 0 && Equal(etag, er.checksum) { + return er + } + } + return &Reader{ + src: r, + md5: md5.New(), + checksum: etag, + } +} + +// Read reads up to len(p) bytes from the underlying +// io.Reader as specified by the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + n, err := r.src.Read(p) + r.readN += int64(n) + r.md5.Write(p[:n]) + + if err == io.EOF && len(r.checksum) != 0 { + if etag := r.ETag(); !Equal(etag, r.checksum) { + return n, VerifyError{ + Expected: r.checksum, + Computed: etag, + } + } + } + return n, err +} + +// ETag returns the ETag of all the content read +// so far. Reading more content changes the MD5 +// checksum. Therefore, calling ETag multiple +// times may return different results. +func (r *Reader) ETag() ETag { + sum := r.md5.Sum(nil) + return ETag(sum) +} + +// VerifyError is an error signaling that a +// computed ETag does not match an expected +// ETag. +type VerifyError struct { + Expected ETag + Computed ETag +} + +func (v VerifyError) Error() string { + return fmt.Sprintf("etag: expected ETag %q does not match computed ETag %q", v.Expected, v.Computed) +} diff --git a/s3/hash/errors.go b/s3/hash/errors.go new file mode 100644 index 000000000..2ffea70d9 --- /dev/null +++ b/s3/hash/errors.go @@ -0,0 +1,33 @@ +package hash + +import "fmt" + +// SHA256Mismatch - when content sha256 does not match with what was sent from client. +type SHA256Mismatch struct { + ExpectedSHA256 string + CalculatedSHA256 string +} + +func (e SHA256Mismatch) Error() string { + return "Bad sha256: Expected " + e.ExpectedSHA256 + " does not match calculated " + e.CalculatedSHA256 +} + +// BadDigest - Content-MD5 you specified did not match what we received. +type BadDigest struct { + ExpectedMD5 string + CalculatedMD5 string +} + +func (e BadDigest) Error() string { + return "Bad digest: Expected " + e.ExpectedMD5 + " does not match calculated " + e.CalculatedMD5 +} + +// ErrSizeMismatch error size mismatch +type ErrSizeMismatch struct { + Want int64 + Got int64 +} + +func (e ErrSizeMismatch) Error() string { + return fmt.Sprintf("Size mismatch: got %d, want %d", e.Got, e.Want) +} diff --git a/s3/hash/reader.go b/s3/hash/reader.go new file mode 100644 index 000000000..f3464213d --- /dev/null +++ b/s3/hash/reader.go @@ -0,0 +1,211 @@ +package hash + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "errors" + "github.com/bittorrent/go-btfs/s3/etag" + "hash" + "io" +) + +// A Reader wraps an io.Reader and computes the MD5 checksum +// of the read content as ETag. Optionally, it also computes +// the SHA256 checksum of the content. +// +// If the reference values for the ETag and content SHA26 +// are not empty then it will check whether the computed +// match the reference values. +type Reader struct { + src io.Reader + bytesRead int64 + + size int64 + actualSize int64 + + contentSHA256 []byte + checksum etag.ETag + sha256 hash.Hash +} + +// NewReader returns a new Reader that wraps src and computes +// MD5 checksum of everything it reads as ETag. +// +// It also computes the SHA256 checksum of everything it reads +// if sha256Hex is not the empty string. +// +// If size resp. actualSize is unknown at the time of calling +// NewReader then it should be set to -1. +// +// NewReader may try merge the given size, MD5 and SHA256 values +// into src - if src is a Reader - to avoid computing the same +// checksums multiple times. +func NewReader(src io.Reader, size int64, md5Hex, sha256Hex string, actualSize int64) (*Reader, error) { + MD5, err := hex.DecodeString(md5Hex) + if err != nil { + return nil, BadDigest{ // TODO(aead): Return an error that indicates that an invalid ETag has been specified + ExpectedMD5: md5Hex, + CalculatedMD5: "", + } + } + SHA256, err := hex.DecodeString(sha256Hex) + if err != nil { + return nil, SHA256Mismatch{ // TODO(aead): Return an error that indicates that an invalid Content-SHA256 has been specified + ExpectedSHA256: sha256Hex, + CalculatedSHA256: "", + } + } + + // Merge the size, MD5 and SHA256 values if src is a Reader. + // The size may be set to -1 by callers if unknown. + if r, ok := src.(*Reader); ok { + if r.bytesRead > 0 { + return nil, errors.New("h: already read from h reader") + } + if len(r.checksum) != 0 && len(MD5) != 0 && !etag.Equal(r.checksum, etag.ETag(MD5)) { + return nil, BadDigest{ + ExpectedMD5: r.checksum.String(), + CalculatedMD5: md5Hex, + } + } + if len(r.contentSHA256) != 0 && len(SHA256) != 0 && !bytes.Equal(r.contentSHA256, SHA256) { + return nil, SHA256Mismatch{ + ExpectedSHA256: hex.EncodeToString(r.contentSHA256), + CalculatedSHA256: sha256Hex, + } + } + if r.size >= 0 && size >= 0 && r.size != size { + return nil, ErrSizeMismatch{Want: r.size, Got: size} + } + + r.checksum = etag.ETag(MD5) + r.contentSHA256 = SHA256 + if r.size < 0 && size >= 0 { + r.src = etag.Wrap(io.LimitReader(r.src, size), r.src) + r.size = size + } + if r.actualSize <= 0 && actualSize >= 0 { + r.actualSize = actualSize + } + return r, nil + } + + if size >= 0 { + r := io.LimitReader(src, size) + if _, ok := src.(etag.Tagger); !ok { + src = etag.NewReader(r, etag.ETag(MD5)) + } else { + src = etag.Wrap(r, src) + } + } else if _, ok := src.(etag.Tagger); !ok { + src = etag.NewReader(src, etag.ETag(MD5)) + } + var h hash.Hash + if len(SHA256) != 0 { + h = newSHA256() + } + return &Reader{ + src: src, + size: size, + actualSize: actualSize, + checksum: etag.ETag(MD5), + contentSHA256: SHA256, + sha256: h, + }, nil +} + +func (r *Reader) Read(p []byte) (int, error) { + n, err := r.src.Read(p) + r.bytesRead += int64(n) + if r.sha256 != nil { + r.sha256.Write(p[:n]) + } + + if err == io.EOF { // Verify content SHA256, if set. + if r.sha256 != nil { + if sum := r.sha256.Sum(nil); !bytes.Equal(r.contentSHA256, sum) { + return n, SHA256Mismatch{ + ExpectedSHA256: hex.EncodeToString(r.contentSHA256), + CalculatedSHA256: hex.EncodeToString(sum), + } + } + } + } + if err != nil && err != io.EOF { + if v, ok := err.(etag.VerifyError); ok { + return n, BadDigest{ + ExpectedMD5: v.Expected.String(), + CalculatedMD5: v.Computed.String(), + } + } + } + return n, err +} + +// Size returns the absolute number of bytes the Reader +// will return during reading. It returns -1 for unlimited +// data. +func (r *Reader) Size() int64 { return r.size } + +// ActualSize returns the pre-modified size of the object. +// DecompressedSize - For compressed objects. +func (r *Reader) ActualSize() int64 { return r.actualSize } + +// ETag returns the ETag computed by an underlying etag.Tagger. +// If the underlying io.Reader does not implement etag.Tagger +// it returns nil. +func (r *Reader) ETag() etag.ETag { + if t, ok := r.src.(etag.Tagger); ok { + return t.ETag() + } + return nil +} + +// MD5 returns the MD5 checksum set as reference value. +// +// It corresponds to the checksum that is expected and +// not the actual MD5 checksum of the content. +// Therefore, refer to MD5Current. +func (r *Reader) MD5() []byte { + return r.checksum +} + +// MD5Current returns the MD5 checksum of the content +// that has been read so far. +// +// Calling MD5Current again after reading more data may +// result in a different checksum. +func (r *Reader) MD5Current() []byte { + return r.ETag()[:] +} + +// SHA256 returns the SHA256 checksum set as reference value. +// +// It corresponds to the checksum that is expected and +// not the actual SHA256 checksum of the content. +func (r *Reader) SHA256() []byte { + return r.contentSHA256 +} + +// MD5HexString returns a hex representation of the MD5. +func (r *Reader) MD5HexString() string { + return hex.EncodeToString(r.checksum) +} + +// MD5Base64String returns a hex representation of the MD5. +func (r *Reader) MD5Base64String() string { + return base64.StdEncoding.EncodeToString(r.checksum) +} + +// SHA256HexString returns a hex representation of the SHA256. +func (r *Reader) SHA256HexString() string { + return hex.EncodeToString(r.contentSHA256) +} + +var _ io.Closer = (*Reader)(nil) // compiler check + +// Close and release resources. +func (r *Reader) Close() error { return nil } +func newSHA256() hash.Hash { return sha256.New() } diff --git a/s3/policy/policy.go b/s3/policy/policy.go new file mode 100644 index 000000000..45adbc268 --- /dev/null +++ b/s3/policy/policy.go @@ -0,0 +1,68 @@ +package policy + +import ( + s3action "github.com/bittorrent/go-btfs/s3/action" +) + +const ( + PublicReadWrite = "public-read-write" + PublicRead = "public-read" + Private = "private" +) + +var rwActionMap = map[s3action.Action]struct{}{ + s3action.ListObjectsAction: {}, + s3action.ListObjectsV2Action: {}, + s3action.HeadObjectAction: {}, + s3action.PutObjectAction: {}, + s3action.GetObjectAction: {}, + s3action.CopyObjectAction: {}, + s3action.DeleteObjectAction: {}, + s3action.DeleteObjectsAction: {}, + s3action.CreateMultipartUploadAction: {}, + s3action.AbortMultipartUploadAction: {}, + s3action.CompleteMultipartUploadAction: {}, + s3action.UploadPartAction: {}, +} + +// checkActionInPublicReadWrite - returns whether action is RW or not. +func checkActionInPublicReadWrite(action s3action.Action) bool { + _, ok := rwActionMap[action] + return ok +} + +var rdActionMap = map[s3action.Action]struct{}{ + s3action.ListObjectsAction: {}, + s3action.ListObjectsV2Action: {}, + s3action.HeadObjectAction: {}, + s3action.GetObjectAction: {}, +} + +// checkActionInPublicRead - returns whether action is Read or not. +func checkActionInPublicRead(action s3action.Action) bool { + _, ok := rdActionMap[action] + return ok +} + +func IsAllowed(own bool, acl string, action s3action.Action) (allow bool) { + if own { + return true + } + + if action.IsBucketAction() { + return false + } + + if action.IsObjectAction() { + switch acl { + case Private: + return own + case PublicRead: + return checkActionInPublicRead(action) + case PublicReadWrite: + return checkActionInPublicReadWrite(action) + } + } + + return false +} diff --git a/s3/s3.go b/s3/s3.go new file mode 100644 index 000000000..9512a5473 --- /dev/null +++ b/s3/s3.go @@ -0,0 +1,67 @@ +package s3 + +import ( + config "github.com/bittorrent/go-btfs-config" + "github.com/bittorrent/go-btfs/s3/api/handlers" + "github.com/bittorrent/go-btfs/s3/api/providers" + "github.com/bittorrent/go-btfs/s3/api/routers" + "github.com/bittorrent/go-btfs/s3/api/server" + "github.com/bittorrent/go-btfs/s3/api/services/accesskey" + "github.com/bittorrent/go-btfs/s3/api/services/object" + "github.com/bittorrent/go-btfs/s3/api/services/sign" + "github.com/bittorrent/go-btfs/s3/ctxmu" + "github.com/bittorrent/go-btfs/transaction/storage" + "sync" +) + +var ( + ps *providers.Providers + once sync.Once +) + +func InitProviders(stateStore storage.StateStorer) (err error) { + once.Do(func() { + var ( + sstore providers.StateStorer + fstore providers.FileStorer + ) + sstore = providers.NewStorageStateStoreProxy(stateStore) + fstore, err = providers.NewBtfsAPI() + if err != nil { + return + } + ps = providers.NewProviders(sstore, fstore) + }) + return +} + +func GetProviders() *providers.Providers { + return ps +} + +func NewServer(cfg config.S3CompatibleAPI) *server.Server { + // global multiple keys read write lock + lock := ctxmu.NewDefaultMultiCtxRWMutex() + + // services + sigsvc := sign.NewService() + acksvc := accesskey.NewService(ps, accesskey.WithLock(lock)) + objsvc := object.NewService(ps, object.WithLock(lock)) + + // handlers + hs := handlers.NewHandlers( + acksvc, sigsvc, objsvc, + handlers.WithHeaders(cfg.HTTPHeaders), + ) + + // routers + rs := routers.NewRouters(hs) + + // server + svr := server.NewServer( + rs, + server.WithAddress(cfg.Address), + ) + + return svr +} diff --git a/s3/set/match.go b/s3/set/match.go new file mode 100644 index 000000000..a5cb98015 --- /dev/null +++ b/s3/set/match.go @@ -0,0 +1,48 @@ +package set + +// MatchSimple - finds whether the text matches/satisfies the pattern string. +// supports only '*' wildcard in the pattern. +// considers a file system path as a flat name space. +func MatchSimple(pattern, name string) bool { + if pattern == "" { + return name == pattern + } + if pattern == "*" { + return true + } + // Does only wildcard '*' match. + return deepMatchRune([]rune(name), []rune(pattern), true) +} + +func deepMatchRune(str, pattern []rune, simple bool) bool { + for len(pattern) > 0 { + switch pattern[0] { + default: + if len(str) == 0 || str[0] != pattern[0] { + return false + } + case '?': + if len(str) == 0 && !simple { + return false + } + case '*': + return deepMatchRune(str, pattern[1:], simple) || + (len(str) > 0 && deepMatchRune(str[1:], pattern, simple)) + } + str = str[1:] + pattern = pattern[1:] + } + return len(str) == 0 && len(pattern) == 0 +} + +// Match regular match +func Match(pattern, name string) (matched bool) { + if pattern == "" { + return name == pattern + } + if pattern == "*" { + return true + } + // Does extended wildcard '*' and '?' match. + return deepMatchRune([]rune(name), []rune(pattern), false) +} diff --git a/s3/set/match_test.go b/s3/set/match_test.go new file mode 100644 index 000000000..eec6df487 --- /dev/null +++ b/s3/set/match_test.go @@ -0,0 +1,529 @@ +package set + +import ( + "fmt" + "testing" +) + +// TestMatch - Tests validate the logic of wild card matching. +// `Match` supports '*' and '?' wildcards. +// Sample usage: In resource matching for bucket policy validation. +func TestMatch(t *testing.T) { + testCases := []struct { + pattern string + text string + matched bool + }{ + // Test case - 1. + // Test case with pattern "*". Expected to match any text. + { + pattern: "*", + text: "s3:GetObject", + matched: true, + }, + // Test case - 2. + // Test case with empty pattern. This only matches empty string. + { + pattern: "", + text: "s3:GetObject", + matched: false, + }, + // Test case - 3. + // Test case with empty pattern. This only matches empty string. + { + pattern: "", + text: "", + matched: true, + }, + // Test case - 4. + // Test case with single "*" at the end. + { + pattern: "s3:*", + text: "s3:ListMultipartUploadParts", + matched: true, + }, + // Test case - 5. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucketMultipartUploads", + text: "s3:ListBucket", + matched: false, + }, + // Test case - 6. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucket", + text: "s3:ListBucket", + matched: true, + }, + // Test case - 7. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucketMultipartUploads", + text: "s3:ListBucketMultipartUploads", + matched: true, + }, + // Test case - 8. + // Test case with pattern containing key name with a prefix. Should accept the same text without a "*". + { + pattern: "my-bucket/oo*", + text: "my-bucket/oo", + matched: true, + }, + // Test case - 9. + // Test case with "*" at the end of the pattern. + { + pattern: "my-bucket/In*", + text: "my-bucket/India/Karnataka/", + matched: true, + }, + // Test case - 10. + // Test case with prefixes shuffled. + // This should fail. + { + pattern: "my-bucket/In*", + text: "my-bucket/Karnataka/India/", + matched: false, + }, + // Test case - 11. + // Test case with text expanded to the wildcards in the pattern. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Ban", + matched: true, + }, + // Test case - 12. + // Test case with the keyname part is repeated as prefix several times. + // This is valid. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Ban/Ban/Ban/Ban/Ban", + matched: true, + }, + // Test case - 13. + // Test case to validate that `*` can be expanded into multiple prefixes. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Area1/Area2/Area3/Ban", + matched: true, + }, + // Test case - 14. + // Test case to validate that `*` can be expanded into multiple prefixes. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban", + matched: true, + }, + // Test case - 15. + // Test case where the keyname part of the pattern is expanded in the text. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Bangalore", + matched: false, + }, + // Test case - 16. + // Test case with prefixes and wildcard expanded for all "*". + { + pattern: "my-bucket/In*/Ka*/Ban*", + text: "my-bucket/India/Karnataka/Bangalore", + matched: true, + }, + // Test case - 17. + // Test case with keyname part being a wildcard in the pattern. + { + pattern: "my-bucket/*", + text: "my-bucket/India", + matched: true, + }, + // Test case - 18. + { + pattern: "my-bucket/oo*", + text: "my-bucket/odo", + matched: false, + }, + + // Test case with pattern containing wildcard '?'. + // Test case - 19. + // "my-bucket?/" matches "my-bucket1/", "my-bucket2/", "my-bucket3" etc... + // doesn't match "mybucket/". + { + pattern: "my-bucket?/abc*", + text: "mybucket/abc", + matched: false, + }, + // Test case - 20. + { + pattern: "my-bucket?/abc*", + text: "my-bucket1/abc", + matched: true, + }, + // Test case - 21. + { + pattern: "my-?-bucket/abc*", + text: "my--bucket/abc", + matched: false, + }, + // Test case - 22. + { + pattern: "my-?-bucket/abc*", + text: "my-1-bucket/abc", + matched: true, + }, + // Test case - 23. + { + pattern: "my-?-bucket/abc*", + text: "my-k-bucket/abc", + matched: true, + }, + // Test case - 24. + { + pattern: "my??bucket/abc*", + text: "mybucket/abc", + matched: false, + }, + // Test case - 25. + { + pattern: "my??bucket/abc*", + text: "my4abucket/abc", + matched: true, + }, + // Test case - 26. + { + pattern: "my-bucket?abc*", + text: "my-bucket/abc", + matched: true, + }, + // Test case 27-28. + // '?' matches '/' too. (works with s3). + // This is because the namespace is considered flat. + // "abc?efg" matches both "abcdefg" and "abc/efg". + { + pattern: "my-bucket/abc?efg", + text: "my-bucket/abcdefg", + matched: true, + }, + { + pattern: "my-bucket/abc?efg", + text: "my-bucket/abc/efg", + matched: true, + }, + // Test case - 29. + { + pattern: "my-bucket/abc????", + text: "my-bucket/abc", + matched: false, + }, + // Test case - 30. + { + pattern: "my-bucket/abc????", + text: "my-bucket/abcde", + matched: false, + }, + // Test case - 31. + { + pattern: "my-bucket/abc????", + text: "my-bucket/abcdefg", + matched: true, + }, + // Test case 32-34. + // test case with no '*'. + { + pattern: "my-bucket/abc?", + text: "my-bucket/abc", + matched: false, + }, + { + pattern: "my-bucket/abc?", + text: "my-bucket/abcd", + matched: true, + }, + { + pattern: "my-bucket/abc?", + text: "my-bucket/abcde", + matched: false, + }, + // Test case 35. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnop", + matched: false, + }, + // Test case 36. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnopqrst/mnopqr", + matched: true, + }, + // Test case 37. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnopqrst/mnopqrs", + matched: true, + }, + // Test case 38. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnop", + matched: false, + }, + // Test case 39. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnopq", + matched: true, + }, + // Test case 40. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnopqr", + matched: true, + }, + // Test case 41. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopqand", + matched: true, + }, + // Test case 42. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopand", + matched: false, + }, + // Test case 43. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopqand", + matched: true, + }, + // Test case 44. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mn", + matched: false, + }, + // Test case 45. + { + pattern: "my-bucket/mnop*?", + text: "my-bucket/mnopqrst/mnopqrs", + matched: true, + }, + // Test case 46. + { + pattern: "my-bucket/mnop*??", + text: "my-bucket/mnopqrst", + matched: true, + }, + // Test case 47. + { + pattern: "my-bucket/mnop*qrst", + text: "my-bucket/mnopabcdegqrst", + matched: true, + }, + // Test case 48. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopqand", + matched: true, + }, + // Test case 49. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopand", + matched: false, + }, + // Test case 50. + { + pattern: "my-bucket/mnop*?and?", + text: "my-bucket/mnopqanda", + matched: true, + }, + // Test case 51. + { + pattern: "my-bucket/mnop*?and", + text: "my-bucket/mnopqanda", + matched: false, + }, + // Test case 52. + + { + pattern: "my-?-bucket/abc*", + text: "my-bucket/mnopqanda", + matched: false, + }, + } + // Iterating over the test cases, call the function under test and asert the output. + for i, testCase := range testCases { + t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) { + actualResult := Match(testCase.pattern, testCase.text) + if testCase.matched != actualResult { + t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult) + } + }) + } +} + +// TestMatchSimple - Tests validate the logic of wild card matching. +// `MatchSimple` supports matching for only '*' in the pattern string. +func TestMatchSimple(t *testing.T) { + testCases := []struct { + pattern string + text string + matched bool + }{ + // Test case - 1. + // Test case with pattern "*". Expected to match any text. + { + pattern: "*", + text: "s3:GetObject", + matched: true, + }, + // Test case - 2. + // Test case with empty pattern. This only matches empty string. + { + pattern: "", + text: "s3:GetObject", + matched: false, + }, + // Test case - 3. + // Test case with empty pattern. This only matches empty string. + { + pattern: "", + text: "", + matched: true, + }, + // Test case - 4. + // Test case with single "*" at the end. + { + pattern: "s3:*", + text: "s3:ListMultipartUploadParts", + matched: true, + }, + // Test case - 5. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucketMultipartUploads", + text: "s3:ListBucket", + matched: false, + }, + // Test case - 6. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucket", + text: "s3:ListBucket", + matched: true, + }, + // Test case - 7. + // Test case with a no "*". In this case the pattern and text should be the same. + { + pattern: "s3:ListBucketMultipartUploads", + text: "s3:ListBucketMultipartUploads", + matched: true, + }, + // Test case - 8. + // Test case with pattern containing key name with a prefix. Should accept the same text without a "*". + { + pattern: "my-bucket/oo*", + text: "my-bucket/oo", + matched: true, + }, + // Test case - 9. + // Test case with "*" at the end of the pattern. + { + pattern: "my-bucket/In*", + text: "my-bucket/India/Karnataka/", + matched: true, + }, + // Test case - 10. + // Test case with prefixes shuffled. + // This should fail. + { + pattern: "my-bucket/In*", + text: "my-bucket/Karnataka/India/", + matched: false, + }, + // Test case - 11. + // Test case with text expanded to the wildcards in the pattern. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Ban", + matched: true, + }, + // Test case - 12. + // Test case with the keyname part is repeated as prefix several times. + // This is valid. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Ban/Ban/Ban/Ban/Ban", + matched: true, + }, + // Test case - 13. + // Test case to validate that `*` can be expanded into multiple prefixes. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Area1/Area2/Area3/Ban", + matched: true, + }, + // Test case - 14. + // Test case to validate that `*` can be expanded into multiple prefixes. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban", + matched: true, + }, + // Test case - 15. + // Test case where the keyname part of the pattern is expanded in the text. + { + pattern: "my-bucket/In*/Ka*/Ban", + text: "my-bucket/India/Karnataka/Bangalore", + matched: false, + }, + // Test case - 16. + // Test case with prefixes and wildcard expanded for all "*". + { + pattern: "my-bucket/In*/Ka*/Ban*", + text: "my-bucket/India/Karnataka/Bangalore", + matched: true, + }, + // Test case - 17. + // Test case with keyname part being a wildcard in the pattern. + { + pattern: "my-bucket/*", + text: "my-bucket/India", + matched: true, + }, + // Test case - 18. + { + pattern: "my-bucket/oo*", + text: "my-bucket/odo", + matched: false, + }, + // Test case - 11. + { + pattern: "my-bucket/oo?*", + text: "my-bucket/oo???", + matched: true, + }, + // Test case - 12: + { + pattern: "my-bucket/oo??*", + text: "my-bucket/odo", + matched: false, + }, + // Test case - 13: + { + pattern: "?h?*", + text: "?h?hello", + matched: true, + }, + } + // Iterating over the test cases, call the function under test and asert the output. + for i, testCase := range testCases { + t.Run(fmt.Sprintf("Test case %d", i+1), func(t *testing.T) { + actualResult := MatchSimple(testCase.pattern, testCase.text) + if testCase.matched != actualResult { + t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult) + } + }) + } +} diff --git a/s3/set/stringset.go b/s3/set/stringset.go new file mode 100644 index 000000000..82d619e19 --- /dev/null +++ b/s3/set/stringset.go @@ -0,0 +1,198 @@ +package set + +import ( + "fmt" + "github.com/vmihailenco/msgpack/v4" + "sort" + + jsoniter "github.com/json-iterator/go" +) + +// StringSet - uses map as set of strings. +type StringSet map[string]struct{} + +var json = jsoniter.ConfigCompatibleWithStandardLibrary + +// ToSlice - returns StringSet as string slice. +func (set StringSet) ToSlice() []string { + keys := make([]string, 0, len(set)) + for k := range set { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +// IsEmpty - returns whether the set is empty or not. +func (set StringSet) IsEmpty() bool { + return len(set) == 0 +} + +// Add - adds string to the set. +func (set StringSet) Add(s string) { + set[s] = struct{}{} +} + +// Remove - removes string in the set. It does nothing if string does not exist in the set. +func (set StringSet) Remove(s string) { + delete(set, s) +} + +// Contains - checks if string is in the set. +func (set StringSet) Contains(s string) bool { + _, ok := set[s] + return ok +} + +// FuncMatch - returns new set containing each value who passes match function. +// A 'matchFn' should accept element in a set as first argument and +// 'matchString' as second argument. The function can do any logic to +// compare both the arguments and should return true to accept element in +// a set to include in output set else the element is ignored. +func (set StringSet) FuncMatch(matchFn func(string, string) bool, matchString string) StringSet { + nset := NewStringSet() + for k := range set { + if matchFn(k, matchString) { + nset.Add(k) + } + } + return nset +} + +// ApplyFunc - returns new set containing each value processed by 'applyFn'. +// A 'applyFn' should accept element in a set as a argument and return +// a processed string. The function can do any logic to return a processed +// string. +func (set StringSet) ApplyFunc(applyFn func(string) string) StringSet { + nset := NewStringSet() + for k := range set { + nset.Add(applyFn(k)) + } + return nset +} + +// Equals - checks whether given set is equal to current set or not. +func (set StringSet) Equals(sset StringSet) bool { + // If length of set is not equal to length of given set, the + // set is not equal to given set. + if len(set) != len(sset) { + return false + } + + // As both sets are equal in length, check each elements are equal. + for k := range set { + if _, ok := sset[k]; !ok { + return false + } + } + + return true +} + +// Intersection - returns the intersection with given set as new set. +func (set StringSet) Intersection(sset StringSet) StringSet { + nset := NewStringSet() + for k := range set { + if _, ok := sset[k]; ok { + nset.Add(k) + } + } + + return nset +} + +// Difference - returns the difference with given set as new set. +func (set StringSet) Difference(sset StringSet) StringSet { + nset := NewStringSet() + for k := range set { + if _, ok := sset[k]; !ok { + nset.Add(k) + } + } + + return nset +} + +// Union - returns the union with given set as new set. +func (set StringSet) Union(sset StringSet) StringSet { + nset := NewStringSet() + for k := range set { + nset.Add(k) + } + + for k := range sset { + nset.Add(k) + } + + return nset +} + +// MarshalJSON - converts to JSON data. +func (set StringSet) MarshalJSON() ([]byte, error) { + return json.Marshal(set.ToSlice()) +} + +// UnmarshalJSON - parses JSON data and creates new set with it. +// If 'data' contains JSON string array, the set contains each string. +// If 'data' contains JSON string, the set contains the string as one element. +// If 'data' contains Other JSON types, JSON parse error is returned. +func (set *StringSet) UnmarshalJSON(data []byte) error { + var sl []string + var err error + if err = json.Unmarshal(data, &sl); err == nil { + *set = make(StringSet) + for _, s := range sl { + set.Add(s) + } + } else { + var s string + if err = json.Unmarshal(data, &s); err == nil { + *set = make(StringSet) + set.Add(s) + } + } + + return err +} + +func (set StringSet) MarshalMsgpack() ([]byte, error) { + return msgpack.Marshal(set.ToSlice()) +} + +func (set *StringSet) UnmarshalMsgpack(data []byte) error { + var sl []string + var err error + if err = msgpack.Unmarshal(data, &sl); err == nil { + *set = make(StringSet) + for _, s := range sl { + set.Add(s) + } + } else { + var s string + if err = msgpack.Unmarshal(data, &s); err == nil { + *set = make(StringSet) + set.Add(s) + } + } + + return err +} + +// String - returns printable string of the set. +func (set StringSet) String() string { + return fmt.Sprintf("%s", set.ToSlice()) +} + +// NewStringSet - creates new string set. +func NewStringSet() StringSet { + return make(StringSet) +} + +// CreateStringSet - creates new string set with given string values. +func CreateStringSet(sl ...string) StringSet { + set := make(StringSet) + for _, k := range sl { + set.Add(k) + } + return set +} diff --git a/s3/set/stringset_test.go b/s3/set/stringset_test.go new file mode 100644 index 000000000..4c1996de6 --- /dev/null +++ b/s3/set/stringset_test.go @@ -0,0 +1,359 @@ +package set + +import ( + "fmt" + "strings" + "testing" +) + +// NewStringSet() is called and the result is validated. +func TestNewStringSet(t *testing.T) { + if ss := NewStringSet(); !ss.IsEmpty() { + t.Fatalf("expected: true, got: false") + } +} + +// CreateStringSet() is called and the result is validated. +func TestCreateStringSet(t *testing.T) { + ss := CreateStringSet("foo") + if str := ss.String(); str != `[foo]` { + t.Fatalf("expected: %s, got: %s", `["foo"]`, str) + } +} + +// StringSet.Add() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetAdd(t *testing.T) { + testCases := []struct { + name string + value string + expectedResult string + }{ + // Test first addition. + {"test1", "foo", `[foo]`}, + // Test duplicate addition. + {"test2", "foo", `[foo]`}, + // Test new addition. + {"test3", "bar", `[bar foo]`}, + } + + ss := NewStringSet() + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + ss.Add(testCase.value) + if str := ss.String(); str != testCase.expectedResult { + t.Fatalf("test %v expected: %s, got: %s", testCase.name, testCase.expectedResult, str) + } + }) + } +} + +// StringSet.Remove() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetRemove(t *testing.T) { + ss := CreateStringSet("foo", "bar") + testCases := []struct { + name string + value string + expectedResult string + }{ + // Test removing non-existen item. + {"test1", "baz", `[bar foo]`}, + // Test remove existing item. + {"test2", "foo", `[bar]`}, + // Test remove existing item again. + {"test2", "foo", `[bar]`}, + // Test remove to make set to empty. + {"test3", "bar", `[]`}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + ss.Remove(testCase.value) + if str := ss.String(); str != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, str) + } + }) + } +} + +// StringSet.Contains() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetContains(t *testing.T) { + ss := CreateStringSet("foo") + testCases := []struct { + name string + value string + expectedResult bool + }{ + // Test to check non-existent item. + {"test1", "bar", false}, + // Test to check existent item. + {"test2", "foo", true}, + // Test to verify case sensitivity. + {"test3", "Foo", false}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result := ss.Contains(testCase.value); result != testCase.expectedResult { + t.Fatalf("expected: %t, got: %t", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.FuncMatch() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetFuncMatch(t *testing.T) { + ss := CreateStringSet("foo", "bar") + testCases := []struct { + name string + matchFn func(string, string) bool + value string + expectedResult string + }{ + // Test to check match function doing case insensive compare. + {"test1", func(setValue string, compareValue string) bool { + return strings.EqualFold(setValue, compareValue) + }, "Bar", `[bar]`}, + // Test to check match function doing prefix check. + {"test2", func(setValue string, compareValue string) bool { + return strings.HasPrefix(compareValue, setValue) + }, "foobar", `[foo]`}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + s := ss.FuncMatch(testCase.matchFn, testCase.value) + if result := s.String(); result != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.ApplyFunc() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetApplyFunc(t *testing.T) { + ss := CreateStringSet("foo", "bar") + testCases := []struct { + name string + applyFn func(string) string + expectedResult string + }{ + // Test to apply function prepending a known string. + {"test1", func(setValue string) string { return "mybucket/" + setValue }, `[mybucket/bar mybucket/foo]`}, + // Test to apply function modifying values. + {"test2", func(setValue string) string { return setValue[1:] }, `[ar oo]`}, + } + + for _, testCase := range testCases { + s := ss.ApplyFunc(testCase.applyFn) + if result := s.String(); result != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + } +} + +// StringSet.Equals() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetEquals(t *testing.T) { + testCases := []struct { + name string + set1 StringSet + set2 StringSet + expectedResult bool + }{ + // Test equal set + {"test1", CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar"), true}, + // Test second set with more items + {"test2", CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar", "baz"), false}, + // Test second set with less items + {"test3", CreateStringSet("foo", "bar"), CreateStringSet("bar"), false}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result := testCase.set1.Equals(testCase.set2); result != testCase.expectedResult { + t.Fatalf("expected: %t, got: %t", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.Intersection() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetIntersection(t *testing.T) { + testCases := []struct { + name string + set1 StringSet + set2 StringSet + expectedResult StringSet + }{ + // Test intersecting all values. + {"test1", CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar")}, + // Test intersecting all values in second set. + {"test2", CreateStringSet("foo", "bar", "baz"), CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar")}, + // Test intersecting different values in second set. + {"test3", CreateStringSet("foo", "baz"), CreateStringSet("baz", "bar"), CreateStringSet("baz")}, + // Test intersecting none. + {"test4", CreateStringSet("foo", "baz"), CreateStringSet("poo", "bar"), NewStringSet()}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result := testCase.set1.Intersection(testCase.set2); !result.Equals(testCase.expectedResult) { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.Difference() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetDifference(t *testing.T) { + testCases := []struct { + name string + set1 StringSet + set2 StringSet + expectedResult StringSet + }{ + // Test differing none. + {"test1", CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar"), NewStringSet()}, + // Test differing in first set. + {"test2", CreateStringSet("foo", "bar", "baz"), CreateStringSet("foo", "bar"), CreateStringSet("baz")}, + // Test differing values in both set. + {"test3", CreateStringSet("foo", "baz"), CreateStringSet("baz", "bar"), CreateStringSet("foo")}, + // Test differing all values. + {"test4", CreateStringSet("foo", "baz"), CreateStringSet("poo", "bar"), CreateStringSet("foo", "baz")}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result := testCase.set1.Difference(testCase.set2); !result.Equals(testCase.expectedResult) { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.Union() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetUnion(t *testing.T) { + testCases := []struct { + name string + set1 StringSet + set2 StringSet + expectedResult StringSet + }{ + // Test union same values. + {"test1", CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar")}, + // Test union same values in second set. + {"test2", CreateStringSet("foo", "bar", "baz"), CreateStringSet("foo", "bar"), CreateStringSet("foo", "bar", "baz")}, + // Test union different values in both set. + {"test2", CreateStringSet("foo", "baz"), CreateStringSet("baz", "bar"), CreateStringSet("foo", "baz", "bar")}, + // Test union all different values. + {"test2", CreateStringSet("foo", "baz"), CreateStringSet("poo", "bar"), CreateStringSet("foo", "baz", "poo", "bar")}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result := testCase.set1.Union(testCase.set2); !result.Equals(testCase.expectedResult) { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.MarshalJSON() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetMarshalJSON(t *testing.T) { + testCases := []struct { + name string + set StringSet + expectedResult string + }{ + // Test set with values. + {"test1", CreateStringSet("foo", "bar"), `["bar","foo"]`}, + // Test empty set. + {"test2", NewStringSet(), "[]"}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if result, _ := testCase.set.MarshalJSON(); string(result) != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, string(result)) + } + }) + } +} + +// StringSet.UnmarshalJSON() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetUnmarshalJSON(t *testing.T) { + testCases := []struct { + name string + data []byte + expectedResult string + }{ + // Test to convert JSON array to set. + {"test1", []byte(`["bar","foo"]`), `[bar foo]`}, + // Test to convert JSON string to set. + {"test2", []byte(`"bar"`), `[bar]`}, + // Test to convert JSON empty array to set. + {"test3", []byte(`[]`), `[]`}, + // Test to convert JSON empty string to set. + {"test4", []byte(`""`), `[]`}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var set StringSet + set.UnmarshalJSON(testCase.data) + if result := set.String(); result != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result) + } + }) + } +} + +// StringSet.String() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetString(t *testing.T) { + testCases := []struct { + name string + set StringSet + expectedResult string + }{ + // Test empty set. + {"test1", NewStringSet(), `[]`}, + // Test set with empty value. + {"test2", CreateStringSet(""), `[]`}, + // Test set with value. + {"test3", CreateStringSet("foo"), `[foo]`}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if str := testCase.set.String(); str != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, str) + } + }) + } +} + +// StringSet.ToSlice() is called with series of cases for valid and erroneous inputs and the result is validated. +func TestStringSetToSlice(t *testing.T) { + testCases := []struct { + name string + set StringSet + expectedResult string + }{ + // Test empty set. + {"test1", NewStringSet(), `[]`}, + // Test set with empty value. + {"test2", CreateStringSet(""), `[]`}, + // Test set with value. + {"test3", CreateStringSet("foo"), `[foo]`}, + // Test set with value. + {"test4", CreateStringSet("foo", "bar"), `[bar foo]`}, + } + + for _, testCase := range testCases { + t.Run("testCase.name", func(t *testing.T) { + sslice := testCase.set.ToSlice() + if str := fmt.Sprintf("%s", sslice); str != testCase.expectedResult { + t.Fatalf("expected: %s, got: %s", testCase.expectedResult, str) + } + }) + } +} diff --git a/s3/utils/coalesce.go b/s3/utils/coalesce.go new file mode 100644 index 000000000..25d89ce81 --- /dev/null +++ b/s3/utils/coalesce.go @@ -0,0 +1,11 @@ +package utils + +// CoalesceStr return the first non-empty string in the list +func CoalesceStr(list ...string) string { + for _, str := range list { + if str != "" { + return str + } + } + return "" +} diff --git a/s3/utils/encode.go b/s3/utils/encode.go new file mode 100644 index 000000000..6db0367dc --- /dev/null +++ b/s3/utils/encode.go @@ -0,0 +1,89 @@ +package utils + +import "strings" + +// S3Encode encodes string in response when encodingType is specified in AWS S3 requests. +func S3Encode(name string, encodingType string) (result string) { + // Quick path to exit + if encodingType == "" { + return name + } + encodingType = strings.ToLower(encodingType) + switch encodingType { + case "url": + return s3URLEncode(name) + } + return name +} + +// s3URLEncode is based on Golang's url.QueryEscape() code, +// while considering some S3 exceptions: +// - Avoid encoding '/' and '*' +// - Force encoding of '~' +func s3URLEncode(s string) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c) { + if c == ' ' { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + var buf [64]byte + var t []byte + + required := len(s) + 2*hexCount + if required <= len(buf) { + t = buf[:required] + } else { + t = make([]byte, required) + } + + if hexCount == 0 { + copy(t, s) + for i := 0; i < len(s); i++ { + if s[i] == ' ' { + t[i] = '+' + } + } + return string(t) + } + + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ': + t[j] = '+' + j++ + case shouldEscape(c): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +func shouldEscape(c byte) bool { + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '_', '.', '/', '*': + return false + } + return true +} diff --git a/settlement/swap/vault/cashout.go b/settlement/swap/vault/cashout.go index c6ee34bb5..89540735d 100644 --- a/settlement/swap/vault/cashout.go +++ b/settlement/swap/vault/cashout.go @@ -131,7 +131,7 @@ func cashoutActionKey(vault common.Address, token common.Address) string { // // output, err := s.transactionService.Call(ctx, &transaction.TxRequest{ // To: &vault, -// Data: callData, +// data: callData, // }) // if err != nil { // return nil, err @@ -191,7 +191,7 @@ func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common //} //request := &transaction.TxRequest{ // To: &vault, - // Data: callData, + // data: callData, // Value: big.NewInt(0), // Description: "cheque cashout", //} diff --git a/settlement/swap/vault/factory_test.go b/settlement/swap/vault/factory_test.go index 652eb87e3..f52c352ec 100644 --- a/settlement/swap/vault/factory_test.go +++ b/settlement/swap/vault/factory_test.go @@ -207,12 +207,12 @@ func TestFactoryVerifyVault(t *testing.T) { // Status: 1, // Logs: []*types.Log{ // { -// Data: logData, +// data: logData, // }, // { // Address: factoryAddress, // Topics: []common.Hash{simpleSwapDeployedEvent.ID}, -// Data: logData, +// data: logData, // }, // }, // }, nil diff --git a/settlement/swap/vault/vault.go b/settlement/swap/vault/vault.go index d8fe95800..d4733a3d2 100644 --- a/settlement/swap/vault/vault.go +++ b/settlement/swap/vault/vault.go @@ -526,7 +526,7 @@ func (s *service) LastCheques(token common.Address) (map[common.Address]*SignedC // // request := &transaction.TxRequest{ // To: &s.address, -// Data: callData, +// data: callData, // Value: big.NewInt(0), // Description: fmt.Sprintf("vault withdrawal of %d WBTT", amount), // } diff --git a/statestore/leveldb/leveldb.go b/statestore/leveldb/leveldb.go index 3a6de46a8..c74413d97 100644 --- a/statestore/leveldb/leveldb.go +++ b/statestore/leveldb/leveldb.go @@ -7,11 +7,11 @@ import ( "fmt" "github.com/bittorrent/go-btfs/transaction/storage" - "github.com/syndtr/goleveldb/leveldb" - ldberr "github.com/syndtr/goleveldb/leveldb/errors" logging "github.com/ipfs/go-log" + "github.com/syndtr/goleveldb/leveldb" ldb "github.com/syndtr/goleveldb/leveldb" + ldberr "github.com/syndtr/goleveldb/leveldb/errors" ldbs "github.com/syndtr/goleveldb/leveldb/storage" "github.com/syndtr/goleveldb/leveldb/util" ) diff --git a/utils/random.go b/utils/random.go new file mode 100644 index 000000000..0a402d9c3 --- /dev/null +++ b/utils/random.go @@ -0,0 +1,20 @@ +package utils + +import ( + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func RandomString(l int) string { + b := make([]rune, l) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} From 619d2ac0cf33bf32b402b2a1c458c006e62bf64a Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Fri, 22 Sep 2023 19:48:28 +0800 Subject: [PATCH 03/16] feat: add backup and recovery command (#355) * fix: return an explicit error instead of panic and optimize the error log (#339) (#341) * feat: add multibase commands (#342) * feat: add backup and recovery command * feat: beautify the log * feat: init add recovery option * fix: format error * feat: add backup and recovery command (#348) * feat: add backup and recovery command * feat: beautify the log * feat: init add recovery option * fix: format error * feat: log details --- cmd/btfs/init.go | 26 +++ core/commands/backup.go | 339 +++++++++++++++++++++++++++++++++ core/commands/commands_test.go | 2 + core/commands/root.go | 2 + 4 files changed, 369 insertions(+) create mode 100644 core/commands/backup.go diff --git a/cmd/btfs/init.go b/cmd/btfs/init.go index 1261778a2..63950b5c9 100644 --- a/cmd/btfs/init.go +++ b/cmd/btfs/init.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/bittorrent/go-btfs/assets" "github.com/bittorrent/go-btfs/chain" @@ -36,6 +37,7 @@ const ( rmOnUnpinOptionName = "rm-on-unpin" seedOptionName = "seed" simpleMode = "simple-mode" + recoveryOptionName = "recovery" /* passWordOptionName = "password" passwordFileoptionName = "password-file" @@ -72,6 +74,7 @@ environment variable: cmds.BoolOption(rmOnUnpinOptionName, "r", "Remove unpinned files.").WithDefault(false), cmds.StringOption(seedOptionName, "s", "Import seed phrase"), cmds.BoolOption(simpleMode, "sm", "init with simple mode or not."), + cmds.StringOption(recoveryOptionName, "Recovery data from a backup"), /* cmds.StringOption(passWordOptionName, "", "password for decrypting keys."), cmds.StringOption(passwordFileoptionName, "", "path to a file that contains password for decrypting keys"), @@ -140,7 +143,30 @@ environment variable: password, _ := req.Options[passWordOptionName].(string) passwordFile, _ := req.Options[passwordFileoptionName].(string) */ + backupPath, ok := req.Options[recoveryOptionName].(string) + if ok { + btfsPath := env.(*oldcmds.Context).ConfigRoot + dstPath := filepath.Dir(btfsPath) + if fsrepo.IsInitialized(btfsPath) { + newPath := filepath.Join(dstPath, fmt.Sprintf(".btfs_backup_%d", time.Now().Unix())) + // newPath := filepath.Join(filepath.Dir(btfsPath), backup) + err := os.Rename(btfsPath, newPath) + if err != nil { + return err + } + fmt.Println("btfs configuration file already exists!") + fmt.Println("We have renamed it to ", newPath) + } + if err := commands.UnTar(backupPath, dstPath); err != nil { + err = commands.UnZip(backupPath, dstPath) + if err != nil { + return errors.New("your file format is not tar.gz or zip, please check again") + } + } + fmt.Println("Recovery successful!") + return nil + } return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profile, conf, keyType, importKey, seedPhrase, rmOnUnpin, simpleModeIn) }, } diff --git a/core/commands/backup.go b/core/commands/backup.go new file mode 100644 index 000000000..1de94bf16 --- /dev/null +++ b/core/commands/backup.go @@ -0,0 +1,339 @@ +package commands + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + cmds "github.com/bittorrent/go-btfs-cmds" + commands "github.com/bittorrent/go-btfs/commands" + fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" +) + +const ( + outputFileOption = "o" + compressOption = "a" + backupPathOption = "r" + excludeOption = "exclude" +) + +var BackupCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Back up BTFS's data", + LongDescription: ` +This command will create a backup of the data from the current BTFS node. +`, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("file", true, false, "data to encode").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.StringOption(outputFileOption, "backup output file path"), + cmds.StringOption(compressOption, "gz or zip").WithDefault("gz"), + cmds.StringsOption(excludeOption, "exclude backup output file path"), + }, + Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + r, err := fsrepo.Open(env.(*commands.Context).ConfigRoot) + if err != nil { + return err + } + defer r.Close() + + var fileName = fmt.Sprintf("btfs_backup_%d", time.Now().Unix()) + + outputName, ok := req.Options[outputFileOption].(string) + if ok { + fileName = outputName + } + btfsPath, err := fsrepo.BestKnownPath() + if err != nil { + return err + } + + excludePath, _ := req.Options[excludeOption].([]string) + for _, v := range excludePath { + // TODO + if v != "config" && v != "statestore" && v != "datastore" { + return errors.New("-exclude only support config, statestore or datastore") + } + } + // exclude the repo.lock to avoid dead lock + excludePath = append(excludePath, "repo.lock") + compressWay, _ := req.Options[compressOption].(string) + // TODO + if compressWay != "gz" && compressWay != "zip" { + return errors.New("-a only support zip or gz, gz is default") + } + absPath, err := filepath.Abs(fileName) + if err != nil { + return err + } + if compressWay == "zip" { + absPath += ".zip" + err = Zip(btfsPath, absPath, excludePath) + } else { + absPath += ".tar.gz" + err = Tar(btfsPath, absPath, excludePath) + } + if err != nil { + return err + } + fmt.Printf("Backup successful! The backup path is %s\n", absPath) + return nil + }, +} + +var RecoveryCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Recover BTFS's data from a archived file of backup", + LongDescription: `This command will recover data from a previously created backup file`, + }, + Options: []cmds.Option{ + cmds.StringOption(backupPathOption, "backup output file path"), + }, + Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + backupPath, ok := req.Options[backupPathOption].(string) + if !ok { + return errors.New("you need to specify -r to indicate the path you want to recover") + } + btfsPath := env.(*commands.Context).ConfigRoot + dstPath := filepath.Dir(btfsPath) + if fsrepo.IsInitialized(btfsPath) { + newPath := filepath.Join(dstPath, fmt.Sprintf(".btfs_backup_%d", time.Now().Unix())) + // newPath := filepath.Join(filepath.Dir(btfsPath), backup) + err := os.Rename(btfsPath, newPath) + if err != nil { + return err + } + fmt.Println("btfs configuration file already exists!") + fmt.Println("We have renamed it to ", newPath) + } + + if err := UnTar(backupPath, dstPath); err != nil { + err = UnZip(backupPath, dstPath) + if err != nil { + return errors.New("your file is not exists or your file format is not tar.gz or zip, please check again") + } + } + fmt.Println("Recovery successful!") + return nil + }, +} + +func Tar(src, dst string, excludePath []string) (err error) { + fw, err := os.Create(dst) + if err != nil { + return + } + defer fw.Close() + + gw := gzip.NewWriter(fw) + defer gw.Close() + + tw := tar.NewWriter(gw) + defer tw.Close() + + basePath := filepath.Dir(src) + filepath.Walk(src, func(fileAbsPath string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + for _, v := range excludePath { + excludeAbsPath := filepath.Join(src, v) + if v != "" && strings.HasPrefix(fileAbsPath, excludeAbsPath) { + return nil + } + } + rel, err := filepath.Rel(basePath, fileAbsPath) + if err != nil { + return err + } + hdr, err := tar.FileInfoHeader(fi, "") + if err != nil { + return err + } + hdr.Name = rel + + // 写入文件信息 + if err = tw.WriteHeader(hdr); err != nil { + return err + } + + if fi.IsDir() { + return nil + } + + fr, err := os.Open(fileAbsPath) + if err != nil { + return err + } + defer fr.Close() + + // copy 文件数据到 tw + _, err = io.Copy(tw, fr) + if err != nil { + return err + } + return nil + }) + tw.Flush() + gw.Flush() + return +} + +func UnTar(src, dst string) (err error) { + fr, err := os.Open(src) + if err != nil { + return err + } + defer fr.Close() + gr, err := gzip.NewReader(fr) + if err != nil { + return err + } + defer gr.Close() + // tar read + tr := tar.NewReader(gr) + // 读取文件 + for { + h, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + if h.FileInfo().IsDir() { + err = os.MkdirAll(filepath.Join(dst, h.Name), h.FileInfo().Mode()) + if err != nil { + return err + } + continue + } + + fw, err := os.OpenFile(filepath.Join(dst, h.Name), os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(h.Mode)) + if err != nil { + return err + } + defer fw.Close() + // 写文件 + _, err = io.Copy(fw, tr) + if err != nil { + return err + } + } + return +} + +func Zip(src, dst string, excludePath []string) (err error) { + fw, err := os.Create(dst) + if err != nil { + return err + } + defer fw.Close() + + zw := zip.NewWriter(fw) + defer func() { + if err := zw.Close(); err != nil { + log.Fatalln(err) + } + }() + basePath := filepath.Dir(src) + filepath.Walk(src, func(fileAbsPath string, fi os.FileInfo, errBack error) (err error) { + if errBack != nil { + return errBack + } + for _, v := range excludePath { + excludeAbsPath := filepath.Join(src, v) + if v != "" && strings.HasPrefix(fileAbsPath, excludeAbsPath) { + return nil + } + } + fh, err := zip.FileInfoHeader(fi) + if err != nil { + return + } + + rel, err := filepath.Rel(basePath, fileAbsPath) + if err != nil { + return err + } + fh.Name = rel + + if fi.IsDir() { + fh.Name += "/" + } + + w, err := zw.CreateHeader(fh) + if err != nil { + return + } + + if !fh.Mode().IsRegular() { + return nil + } + + fr, err := os.Open(fileAbsPath) + if err != nil { + return + } + defer fr.Close() + + _, err = io.Copy(w, fr) + if err != nil { + return + } + return nil + }) + zw.Flush() + return +} + +func UnZip(src, dst string) (err error) { + zr, err := zip.OpenReader(src) + if err != nil { + return + } + defer zr.Close() + + for _, file := range zr.File { + err = persistZipFile(dst, file) + if err != nil { + return + } + } + return nil +} + +func persistZipFile(dst string, file *zip.File) (err error) { + path := filepath.Join(dst, file.Name) + + if file.FileInfo().IsDir() { + return os.MkdirAll(path, file.Mode()) + } + + fr, err := file.Open() + if err != nil { + return err + } + defer fr.Close() + + fw, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer fw.Close() + + _, err = io.Copy(fw, fr) + if err != nil { + return err + } + return +} diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 1366d2963..5b4eadcc1 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -347,6 +347,8 @@ func TestCommands(t *testing.T) { "/multibase/decode", "/multibase/transcode", "/multibase/list", + "/backup", + "/recovery", } cmdSet := make(map[string]struct{}) diff --git a/core/commands/root.go b/core/commands/root.go index c6a3fc5c6..0c5bb9a46 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -180,6 +180,8 @@ var rootSubcommands = map[string]*cmds.Command{ "statuscontract": StatusContractCmd, "bittorrent": bittorrentCmd, "multibase": MbaseCmd, + "backup": BackupCmd, + "recovery": RecoveryCmd, "accesskey": AccessKeyCmd, } From 992180116a48f8ca3ed377a5dedd2bd1244f3784 Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Fri, 22 Sep 2023 19:53:42 +0800 Subject: [PATCH 04/16] Integrate S3-Compatible-API (#353) * feat: accesss key * feat: access-key module & access-key cmds * fix: remove redunt error condition * opt: handle wrapper error and remove unused bucket type define in access-key module * feat: add daemon check before execute accesskey commands * optmize: access-key store prefix * optmize: not found error * feat: add s3 signature * chore: * chore: * chore: * chore: * chore: * feat: add store * chore: * feat: s3 access-key, server, handlers, statestore, filestore * chore: * optmize: code structure * style: s3 code structure * style: code structure * optmize: code structure * feat: add multiple context lock * feat: check auth * chore: * chore: * feat: add bucket service * chore: * mod: update bucket lock * chore: * chore: * chore: * del s3d * chore: s3 req & rsp structure * chore: * feat: add pubBucket api * feat: add more bucket api * chore: * chore: * feat: add request and response * chore: * feat: server build * chore: check acl * chore * chore: adjust bucket url *  * feat: add auth middleware * feat: adjust code structure * optmize: code structure & auth bug * feat: put object * chore: * mod: mod bucket parse req * optmize: adjust place of response error * chore: mig sig 01 * chore: clear sig * chore: mig sig 02 * chore: mig sig 03 * chore: mig sig 04 * optmize: refractor codes * optmize: rename auth to sign * optmize: code structure & h.name * fix: h.name * feat: put-object * feat: multipart * fix: nslock key * chore: rename s3 constructor file name * fix: tidy example go-ipfs-as-a-library go mod * chore: change default s3 server address to local * mod: add object api * feat: s3-compatible-api - 1. add start option and configure; 2. optmize providers interfaces and implements; 3. rewrite the server construct function * merge: object * chore: add object lock * chore: of delete objs * chore: * fix: list objects bug * chore: rename ListObjetV1Handler to ListObjectHandler, rename BTFS-Hash to CID * refractor: bucket service * refactor: object service * refractor: refract object service * refractor: handlers * refractor: bucket handler * refractor: bucket handler * refractor: response * refractor: response func * refractor: response * refractor: object * refractor: object * refractor: objects * refractor: btf api add timeout & add cid refs to enable referred cid can not be deleted * ref: fix delete object remove body * ref: format code * fix: routers * fix: add cors header * fix: router option * feat: add delete objects handler * refractor: multipart * ref: multipart * fix: multipart etag calculation * chore: add min part size todo * chore: upgrade 'github.com/anacrolix/torrent' from v1.47.0 to v1.52.5 * opt: comment and amz header * opt: code * opt: preflight cache max age * feat: bucket response add acl header * opt: change cid-list header to cid * fix: required check exlude unknow location * fix: get object acl * ref: requests * ref: complete refractor * fix: args parse * fix: get object unlock * fix: object acl writer * fix: delete objects error * fix: Sign handler name * fix: object name escape * fix: copy source validate * opt: s3 log * opt: s3 api log * fix: allow Cache-Control header in PutObject and CopyObject Action * opt: add access-key command taglines * opt: add accesskey command description * chore: add accesskey commands test path * chore: add accesskey test path --------- Co-authored-by: fish <920886811@163.com> --- core/commands/commands_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 5b4eadcc1..0a56d2d1b 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -342,6 +342,14 @@ func TestCommands(t *testing.T) { "/bittorrent/scrape", "/bittorrent/metainfo", "/bittorrent/bencode", + "/accesskey", + "/accesskey/generate", + "/accesskey/enable", + "/accesskey/disable", + "/accesskey/reset", + "/accesskey/delete", + "/accesskey/get", + "/accesskey/list", "/multibase", "/multibase/encode", "/multibase/decode", From 45abbb337f50b0bff58b56c1edd84b48dc8b8abf Mon Sep 17 00:00:00 2001 From: laocheng-cheng <73106671+laocheng-cheng@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:20:39 +0800 Subject: [PATCH 05/16] fix: cheque cashout bug (#356) * feat: accesss key * feat: access-key module & access-key cmds * fix: remove redunt error condition * opt: handle wrapper error and remove unused bucket type define in access-key module * fix: return an explicit error instead of panic and optimize the error log (#339) (#341) * feat: add daemon check before execute accesskey commands * optmize: access-key store prefix * optmize: not found error * feat: add s3 signature * chore: * chore: * chore: * chore: * chore: * feat: add store * chore: * feat: s3 access-key, server, handlers, statestore, filestore * chore: * optmize: code structure * style: s3 code structure * style: code structure * optmize: code structure * feat: add multiple context lock * feat: check auth * chore: * chore: * feat: add bucket service * chore: * mod: update bucket lock * chore: * chore: * chore: * del s3d * chore: s3 req & rsp structure * chore: * feat: add multibase commands (#342) * feat: add pubBucket api * feat: add more bucket api * chore: * chore: * feat: add request and response * chore: * feat: server build * chore: check acl * chore * chore: adjust bucket url *  * feat: add auth middleware * feat: adjust code structure * optmize: code structure & auth bug * feat: put object * chore: * mod: mod bucket parse req * optmize: adjust place of response error * chore: mig sig 01 * chore: clear sig * chore: mig sig 02 * chore: mig sig 03 * chore: mig sig 04 * optmize: refractor codes * optmize: rename auth to sign * optmize: code structure & h.name * fix: h.name * feat: put-object * feat: multipart * fix: nslock key * chore: rename s3 constructor file name * fix: tidy example go-ipfs-as-a-library go mod * chore: change default s3 server address to local * mod: add object api * feat: s3-compatible-api - 1. add start option and configure; 2. optmize providers interfaces and implements; 3. rewrite the server construct function * merge: object * chore: add object lock * chore: of delete objs * chore: * fix: list objects bug * chore: rename ListObjetV1Handler to ListObjectHandler, rename BTFS-Hash to CID * refractor: bucket service * refactor: object service * refractor: refract object service * refractor: handlers * refractor: bucket handler * refractor: bucket handler * refractor: response * refractor: response func * refractor: response * refractor: object * feat: add backup and recovery command (#348) * feat: add backup and recovery command * feat: beautify the log * feat: init add recovery option * fix: format error * refractor: object * refractor: objects * refractor: btf api add timeout & add cid refs to enable referred cid can not be deleted * ref: fix delete object remove body * ref: format code * fix: routers * fix: add cors header * fix: router option * feat: add delete objects handler * refractor: multipart * ref: multipart * fix: multipart etag calculation * chore: add min part size todo * chore: upgrade 'github.com/anacrolix/torrent' from v1.47.0 to v1.52.5 * opt: comment and amz header * opt: code * opt: preflight cache max age * feat: bucket response add acl header * opt: change cid-list header to cid * fix: required check exlude unknow location * fix: get object acl * ref: requests * ref: complete refractor * fix: args parse * fix: get object unlock * fix: object acl writer * fix: delete objects error * fix: Sign handler name * fix: object name escape * fix: copy source validate * chore: fix * opt: s3 log * opt: s3 api log * fix: allow Cache-Control header in PutObject and CopyObject Action * feat: log details (#349) * feat: add backup and recovery command * feat: beautify the log * feat: init add recovery option * fix: format error * feat: log details * opt: add access-key command taglines * opt: add accesskey command description * chore: add out of FixChequeCashOutCmd * chore: * chore * chore: * chore: * chore: * chore * chore: add accesskey commands test path * chore: * chore: add accesskey test path * chore: * chore: * chore: * chore: --------- Co-authored-by: Steve Co-authored-by: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Co-authored-by: fish <920886811@163.com> --- cmd/btfs/daemon.go | 1 + core/commands/cheque/cheque.go | 25 ++- core/commands/cheque/fix_cheque_cashout.go | 75 +++++++++ core/commands/commands_test.go | 15 +- go.mod | 1 + go.sum | 1 + settlement/swap/swap_test.go | 16 +- settlement/swap/vault/cashout.go | 157 ++++++++++++++++++ settlement/swap/vault/cashout_status_store.go | 99 +++++++++++ settlement/swap/vault/cashout_test.go | 4 + spin/cheque_cash_out.go | 9 + 11 files changed, 387 insertions(+), 16 deletions(-) create mode 100644 core/commands/cheque/fix_cheque_cashout.go create mode 100644 settlement/swap/vault/cashout_status_store.go create mode 100644 spin/cheque_cash_out.go diff --git a/cmd/btfs/daemon.go b/cmd/btfs/daemon.go index 91f14ce38..ab770c05a 100644 --- a/cmd/btfs/daemon.go +++ b/cmd/btfs/daemon.go @@ -759,6 +759,7 @@ If the user need to start multiple nodes on the same machine, the configuration spin.Analytics(api, cctx.ConfigRoot, node, version.CurrentVersionNumber, hValue) spin.Hosts(node, env) spin.Contracts(node, req, env, nodepb.ContractStat_HOST.String()) + spin.RestartFixChequeCashOut() } // Give the user some immediate feedback when they hit C-c diff --git a/core/commands/cheque/cheque.go b/core/commands/cheque/cheque.go index b9bf4783c..7e578acc8 100644 --- a/core/commands/cheque/cheque.go +++ b/core/commands/cheque/cheque.go @@ -36,6 +36,20 @@ type ListChequeRet struct { Len int } +type fixCheque struct { + PeerID string + Token string + Beneficiary string + Vault string + TotalCashedAmount *big.Int + FixCashedAmount *big.Int +} + +type ListFixChequeRet struct { + FixCheques []fixCheque + Len int +} + type ReceiveCheque struct { PeerID string Token common.Address @@ -65,11 +79,12 @@ var ChequeCmd = &cmds.Command{ Vault services include issue cheque to peer, receive cheque and store operations.`, }, Subcommands: map[string]*cmds.Command{ - "cash": CashChequeCmd, - "cashstatus": ChequeCashStatusCmd, - "cashlist": ChequeCashListCmd, - "price": StorePriceCmd, - "price-all": StorePriceAllCmd, + "cash": CashChequeCmd, + "cashstatus": ChequeCashStatusCmd, + "cashlist": ChequeCashListCmd, + "price": StorePriceCmd, + "price-all": StorePriceAllCmd, + "fix_cheque_cashout": FixChequeCashOutCmd, "send": SendChequeCmd, "sendlist": ListSendChequesCmd, diff --git a/core/commands/cheque/fix_cheque_cashout.go b/core/commands/cheque/fix_cheque_cashout.go new file mode 100644 index 000000000..f4908454c --- /dev/null +++ b/core/commands/cheque/fix_cheque_cashout.go @@ -0,0 +1,75 @@ +package cheque + +import ( + "fmt" + cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/chain/tokencfg" + "github.com/bittorrent/go-btfs/utils" + "github.com/google/martian/log" + "golang.org/x/net/context" + "io" +) + +var FixChequeCashOutCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "List cheque(s) received from peers.", + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + err := utils.CheckSimpleMode(env) + if err != nil { + return err + } + + listRet := ListFixChequeRet{} + listRet.FixCheques = make([]fixCheque, 0) + + for _, tokenAddr := range tokencfg.MpTokenAddr { + cheques, err := chain.SettleObject.SwapService.LastReceivedCheques(tokenAddr) + if err != nil { + return err + } + + for k, v := range cheques { + totalCashOutAmount, newCashOutAmount, err := chain.SettleObject.CashoutService.AdjustCashCheque( + context.Background(), v.Vault, v.Beneficiary, tokenAddr, false) + if err != nil { + return err + } + if newCashOutAmount != nil && newCashOutAmount.Uint64() > 0 { + var record fixCheque + record.PeerID = k + record.Token = v.Token.String() + record.Beneficiary = v.Beneficiary.String() + record.Vault = v.Vault.String() + record.TotalCashedAmount = totalCashOutAmount + record.FixCashedAmount = newCashOutAmount + + listRet.FixCheques = append(listRet.FixCheques, record) + } + } + } + listRet.Len = len(listRet.FixCheques) + + log.Infof("FixChequeCashOutCmd, listRet = %+v", listRet) + + return cmds.EmitOnce(res, &listRet) + }, + Type: ListFixChequeRet{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ListFixChequeRet) error { + fmt.Fprintf(w, "fix: \n\t%-55s\t%-46s\t%-46s\t%-46s\tfix_cash_amount: \n", "peerID:", "vault:", "beneficiary:", "total_cash_amount:") + for iter := 0; iter < out.Len; iter++ { + fmt.Fprintf(w, "\t%-55s\t%-46s\t%-46s\t%d\t%d \n", + out.FixCheques[iter].PeerID, + out.FixCheques[iter].Vault, + out.FixCheques[iter].Beneficiary, + out.FixCheques[iter].TotalCashedAmount.Uint64(), + out.FixCheques[iter].FixCashedAmount.Uint64(), + ) + } + + return nil + }), + }, +} diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 0a56d2d1b..aafe6fc7b 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -342,6 +342,13 @@ func TestCommands(t *testing.T) { "/bittorrent/scrape", "/bittorrent/metainfo", "/bittorrent/bencode", + "/multibase", + "/multibase/encode", + "/multibase/decode", + "/multibase/transcode", + "/multibase/list", + "/backup", + "/recovery", "/accesskey", "/accesskey/generate", "/accesskey/enable", @@ -350,13 +357,7 @@ func TestCommands(t *testing.T) { "/accesskey/delete", "/accesskey/get", "/accesskey/list", - "/multibase", - "/multibase/encode", - "/multibase/decode", - "/multibase/transcode", - "/multibase/list", - "/backup", - "/recovery", + "/cheque/fix_cheque_cashout", } cmdSet := make(map[string]struct{}) diff --git a/go.mod b/go.mod index fbc52759f..26cf9bcde 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/go-bindata/go-bindata/v3 v3.1.3 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.3 + github.com/google/martian v2.1.0+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.7.3 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 1229c7e8e..17535d97b 100644 --- a/go.sum +++ b/go.sum @@ -572,6 +572,7 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= diff --git a/settlement/swap/swap_test.go b/settlement/swap/swap_test.go index 74c778f5b..a603ed26f 100644 --- a/settlement/swap/swap_test.go +++ b/settlement/swap/swap_test.go @@ -124,10 +124,12 @@ func (m *addressbookMock) PutVault(peer string, vault common.Address) error { } type cashoutMock struct { - cashCheque func(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) - cashoutStatus func(ctx context.Context, vaultAddress common.Address, token common.Address) (*vault.CashoutStatus, error) - cashoutResults func() ([]vault.CashOutResult, error) - hasCashoutAction func(ctx context.Context, peer common.Address, token common.Address) (bool, error) + cashCheque func(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) + cashoutStatus func(ctx context.Context, vaultAddress common.Address, token common.Address) (*vault.CashoutStatus, error) + cashoutResults func() ([]vault.CashOutResult, error) + hasCashoutAction func(ctx context.Context, peer common.Address, token common.Address) (bool, error) + adjustCashCheque func(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) + restartFixChequeCashOut func() } func (m *cashoutMock) CashCheque(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) { @@ -142,6 +144,12 @@ func (m *cashoutMock) CashoutResults() ([]vault.CashOutResult, error) { func (m *cashoutMock) HasCashoutAction(ctx context.Context, peer common.Address, token common.Address) (bool, error) { return m.hasCashoutAction(ctx, peer, token) } +func (m *cashoutMock) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { + return m.adjustCashCheque(ctx, vaultAddress, recipient, token, restartPassFlag) +} +func (m *cashoutMock) RestartFixChequeCashOut() { + m.restartFixChequeCashOut() +} func TestReceiveCheque(t *testing.T) { store := mockstore.NewStateStore() vaultService := mockvault.NewVault( diff --git a/settlement/swap/vault/cashout.go b/settlement/swap/vault/cashout.go index 89540735d..9d480beb5 100644 --- a/settlement/swap/vault/cashout.go +++ b/settlement/swap/vault/cashout.go @@ -28,8 +28,10 @@ type CashoutService interface { CashCheque(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) // CashoutStatus gets the status of the latest cashout transaction for the vault CashoutStatus(ctx context.Context, vaultAddress common.Address, token common.Address) (*CashoutStatus, error) + AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) HasCashoutAction(ctx context.Context, peer common.Address, token common.Address) (bool, error) CashoutResults() ([]CashOutResult, error) + RestartFixChequeCashOut() } type cashoutService struct { @@ -178,6 +180,10 @@ func (s *cashoutService) CashoutResults() ([]CashOutResult, error) { // CashCheque sends a cashout transaction for the last cheque of the vault func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) { + if RestartFixCashOutStatusLock { + return common.Hash{}, errors.New("Just started, it can not cash cheque, you will wait for about 40s to do it. ") + } + cheque, err := s.chequeStore.LastReceivedCheque(vault, token) if err != nil { return common.Hash{}, err @@ -215,6 +221,19 @@ func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common return common.Hash{}, err } + // 1.add cash out status + cashOutStateInfo := CashOutStatusStoreInfo{ + Token: token, + Vault: cheque.Vault, + Beneficiary: cheque.Beneficiary, + CumulativePayout: cheque.CumulativePayout, + TxHash: txHash.String(), + } + err = s.AddCashOutStatusStore(cashOutStateInfo) + if err != nil { + return common.Hash{}, err + } + // WaitForReceipt takes long time go func() { defer func() { @@ -223,6 +242,13 @@ func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common } }() s.storeCashResult(context.Background(), vault, txHash, cheque, token) + + // 2.delete cash out status + err = s.DeleteCashOutStatusStore(cashOutStateInfo) + if err != nil { + log.Errorf("delete cashout status, err = %v", err) + return + } }() return txHash, nil } @@ -304,6 +330,137 @@ func (s *cashoutService) storeCashResult(ctx context.Context, vault common.Addre return nil } +// AdjustCashCheque . +func (s *cashoutService) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { + if RestartFixCashOutStatusLock { + if !restartPassFlag { + return nil, nil, errors.New("Just started, it can not fix cash out status, you will wait for about 40s to do it. ") + } + } + + // 1.totalReceivedCashed + totalReceivedCashed := big.NewInt(0) + err = s.store.Get(tokencfg.AddToken(statestore.TotalReceivedCashedKey, token), &totalReceivedCashed) + if err != nil && err != storage.ErrNotFound { + return nil, nil, err + } + + // 2.alreadyPaidOut in renter contract + // blockchain calls below + contract := newVaultContractMuti(vaultAddress, s.transactionService) + alreadyPaidOutOnline, err := contract.PaidOut(ctx, recipient, token) + if err != nil { + return nil, nil, err + } + + // 3.compare it to fix. + diff := big.NewInt(0).Sub(alreadyPaidOutOnline, totalReceivedCashed) + log.Infof("AdjustCashCheque: diff > 0, vault=%s, recipient=%s, online=%s, local=%s, diff=%s", + vaultAddress.String(), recipient.String(), + alreadyPaidOutOnline.String(), totalReceivedCashed.String(), diff.String(), + ) + + if diff.Cmp(big.NewInt(0)) > 0 { + cashResult, err := s.fixStoreCashResult(vaultAddress, diff, token) + if err != nil { + return nil, nil, err + } + newCashOutAmount = cashResult.Amount + } + + return alreadyPaidOutOnline, newCashOutAmount, nil +} + +func (s *cashoutService) RestartFixChequeCashOut() { + if RestartFixCashOutStatusLock { + list, err := s.GetAllCashOutStatusStore() + if err != nil { + log.Infof("RestartFixChequeCashOut: GetAllCashOutStatusStore err = %v", err) + return + } + + if len(list) > 0 { + log.Infof("wait 30s, for fixing cash out status") + + // wait 30s, for online cashing out ok. + time.Sleep(time.Second * RestartWaitCashOutOnlineTime) + + for _, v := range list { + _, _, err := s.AdjustCashCheque(context.Background(), v.Vault, v.Beneficiary, v.Token, true) + if err != nil { + log.Infof("RestartFixChequeCashOut: AdjustCashCheque err = %v, info = %+v", err, v) + continue + } + + err = s.DeleteCashOutStatusStore(v) + if err != nil { + log.Infof("RestartFixChequeCashOut: DeleteCashOutStatusStore err = %v, info = %+v", err, v) + continue + } + } + } + RestartFixCashOutStatusLock = false + } + return +} + +func (s *cashoutService) fixStoreCashResult(vault common.Address, shouldPaidOut *big.Int, token common.Address) (cashResult *CashOutResult, err error) { + txHash := common.Hash{} //fix txHash: 0x0000... + cashResult = &CashOutResult{ + TxHash: txHash, + Vault: vault, + Token: token, + Amount: shouldPaidOut, + CashTime: time.Now().Unix(), + Status: "success", + } + + totalReceivedCashed := big.NewInt(0) + if err = s.store.Get(tokencfg.AddToken(statestore.TotalReceivedCashedKey, token), &totalReceivedCashed); err == nil || err == storage.ErrNotFound { + totalReceivedCashed = totalReceivedCashed.Add(totalReceivedCashed, shouldPaidOut) + err := s.store.Put(tokencfg.AddToken(statestore.TotalReceivedCashedKey, token), totalReceivedCashed) + if err != nil { + log.Infof("fixStoreCashResult:put totalReceivedCashdKey err:%+v", err) + } + } + + totalDailyReceivedCashed := big.NewInt(0) + if err = s.store.Get(statestore.GetTodayTotalDailyReceivedCashedKey(token), &totalDailyReceivedCashed); err == nil || err == storage.ErrNotFound { + totalDailyReceivedCashed = totalDailyReceivedCashed.Add(totalDailyReceivedCashed, shouldPaidOut) + err := s.store.Put(statestore.GetTodayTotalDailyReceivedCashedKey(token), totalDailyReceivedCashed) + if err != nil { + log.Infof("fixStoreCashResult:put totalReceivedDailyCashdKey err:%+v", err) + } + } + + // update TotalReceivedCountCashed + uncashed := 0 + err = s.store.Get(statestore.PeerReceivedUncashRecordsCountKey(vault, token), &uncashed) + if err != nil { + log.Infof("fixStoreCashResult:put totalReceivedCountCashed err:%+v", err) + } else { + cashedCount := 0 + err := s.store.Get(tokencfg.AddToken(statestore.TotalReceivedCashedCountKey, token), &cashedCount) + if err == nil || err == storage.ErrNotFound { + err := s.store.Put(tokencfg.AddToken(statestore.TotalReceivedCashedCountKey, token), cashedCount+uncashed) + if err != nil { + log.Infof("fixStoreCashResult:put totalReceivedCashedConuntKey err:%+v", err) + } else { + err := s.store.Put(statestore.PeerReceivedUncashRecordsCountKey(vault, token), 0) + if err != nil { + log.Infof("fixStoreCashResult:put totalReceivedCashedConuntKey err:%+v", err) + } + } + } + } + + err = s.store.Put(statestore.CashoutResultKey(vault), &cashResult) + if err != nil { + log.Infof("fixStoreCashResult:put cashoutResultKey err:%+v", err) + } + return +} + // CashoutStatus gets the status of the latest cashout transaction for the vault func (s *cashoutService) CashoutStatus(ctx context.Context, vaultAddress common.Address, token common.Address) (*CashoutStatus, error) { cheque, err := s.chequeStore.LastReceivedCheque(vaultAddress, token) diff --git a/settlement/swap/vault/cashout_status_store.go b/settlement/swap/vault/cashout_status_store.go new file mode 100644 index 000000000..c547670ab --- /dev/null +++ b/settlement/swap/vault/cashout_status_store.go @@ -0,0 +1,99 @@ +package vault + +import ( + "errors" + "fmt" + "github.com/ethereum/go-ethereum/common" + "math/big" + "time" +) + +var RestartFixCashOutStatusLock bool = true +var RestartWaitCashOutOnlineTime time.Duration = 30 //seconds + +// CashOutStatus from leveldb +var prefixKeyCashOutStatusStore = "keyCashOutStatusStore" // + txHash. +type CashOutStatusStoreInfo struct { + Token common.Address + Vault common.Address + Beneficiary common.Address + CumulativePayout *big.Int + TxHash string +} + +func getkeyCashOutStatusStore(txHash string) string { + return fmt.Sprintf("%s-%s", prefixKeyCashOutStatusStore, txHash) +} + +// AddCashOutStatusStore . +func (s *cashoutService) AddCashOutStatusStore(info CashOutStatusStoreInfo) (err error) { + if s.store == nil { + return errors.New("please start btfs node, at first! ") + } + + err = s.store.Put(getkeyCashOutStatusStore(info.TxHash), info) + if err != nil { + return err + } + + return nil +} + +// DeleteCashOutStatusStore . +func (s *cashoutService) DeleteCashOutStatusStore(info CashOutStatusStoreInfo) (err error) { + if s.store == nil { + return errors.New("please start btfs node, at first! ") + } + + err = s.store.Delete(getkeyCashOutStatusStore(info.TxHash)) + if err != nil { + if err.Error() == "storage: not found" { + return nil + } else { + return err + } + } + return +} + +// GetCashOutStatusStore . +func (s *cashoutService) GetCashOutStatusStore(txHash string) (bl bool, err error) { + if s.store == nil { + return bl, errors.New("please start btfs node, at first! ") + } + + var info CashOutStatusStoreInfo + err = s.store.Get(getkeyCashOutStatusStore(txHash), &info) + if err != nil { + if err.Error() == "storage: not found" { + return false, nil + } else { + return false, err + } + } + + return true, nil +} + +// GetAllCashOutStatusStore . +func (s *cashoutService) GetAllCashOutStatusStore() (infoList []CashOutStatusStoreInfo, err error) { + if s.store == nil { + return nil, errors.New("please start btfs node, at first! ") + } + + infoList = make([]CashOutStatusStoreInfo, 0) + err = s.store.Iterate(prefixKeyCashOutStatusStore, func(key, val []byte) (stop bool, err error) { + var info CashOutStatusStoreInfo + err = s.store.Get(string(key), &info) + if err != nil { + return false, err + } + infoList = append(infoList, info) + return false, nil + }) + if err != nil { + return nil, err + } + + return infoList, nil +} diff --git a/settlement/swap/vault/cashout_test.go b/settlement/swap/vault/cashout_test.go index 252d130f4..bca0f953b 100644 --- a/settlement/swap/vault/cashout_test.go +++ b/settlement/swap/vault/cashout_test.go @@ -23,6 +23,7 @@ var ( ) func TestCashout(t *testing.T) { + vault.RestartFixCashOutStatusLock = false vaultAddress := common.HexToAddress("abcd") recipientAddress := common.HexToAddress("efff") txHash := common.HexToHash("dddd") @@ -123,6 +124,7 @@ func TestCashout(t *testing.T) { } func TestCashoutBounced(t *testing.T) { + vault.RestartFixCashOutStatusLock = false vaultAddress := common.HexToAddress("abcd") recipientAddress := common.HexToAddress("efff") txHash := common.HexToHash("dddd") @@ -227,6 +229,7 @@ func TestCashoutBounced(t *testing.T) { } func TestCashoutStatusReverted(t *testing.T) { + vault.RestartFixCashOutStatusLock = false vaultAddress := common.HexToAddress("abcd") recipientAddress := common.HexToAddress("efff") txHash := common.HexToHash("dddd") @@ -307,6 +310,7 @@ func TestCashoutStatusReverted(t *testing.T) { } func TestCashoutStatusPending(t *testing.T) { + vault.RestartFixCashOutStatusLock = false vaultAddress := common.HexToAddress("abcd") recipientAddress := common.HexToAddress("efff") txHash := common.HexToHash("dddd") diff --git a/spin/cheque_cash_out.go b/spin/cheque_cash_out.go new file mode 100644 index 000000000..bfe5b2fd8 --- /dev/null +++ b/spin/cheque_cash_out.go @@ -0,0 +1,9 @@ +package spin + +import ( + "github.com/bittorrent/go-btfs/chain" +) + +func RestartFixChequeCashOut() { + chain.SettleObject.CashoutService.RestartFixChequeCashOut() +} From fb5f97b8d510984d1ad340f12b5560dfc3d2f915 Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:05:45 +0800 Subject: [PATCH 06/16] feat: update dashboard and version (#357) --- core/corehttp/webui.go | 3 ++- version.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index 559da4f4c..b754343d5 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,10 +1,11 @@ package corehttp -const WebUIPath = "/btfs/QmUKCyDc4h9KN93AdZ7ZVqgPProsKs8NAbVJkK3ux9788d" // v2.3.2 +const WebUIPath = "/btfs/QmQBTkD34AYcNwm2JG1u27LYBuejVgwDkuHD7BjVDLgqU6" // v2.3.2 // this is a list of all past webUI paths. var WebUIPaths = []string{ WebUIPath, + "/btfs/QmUKCyDc4h9KN93AdZ7ZVqgPProsKs8NAbVJkK3ux9788d", // v2.3.2 "/btfs/QmRdt8SzRBz5px7KfU4hFveJSKzBMFqv73YE4xXJBsVdDJ", // v2.3.1 "/btfs/QmbNHqcL9PEhFdT5mXjNnkaAE8SEFkjr2jD7we2ckTL4Li", // v2.3.0 "/btfs/QmZvpBNMribwdjNMrA9gXz27t2gzbae3N2tbCLtjpRTqJn", // v2.2.1.1 diff --git a/version.go b/version.go index e41ac97b7..04dee3da2 100644 --- a/version.go +++ b/version.go @@ -4,7 +4,7 @@ package btfs var CurrentCommit string // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "2.3.2" +const CurrentVersionNumber = "2.3.3" const ApiVersion = "/go-btfs/" + CurrentVersionNumber + "/" From 444c1f31c3ed412346304cb618b0620fce5cb280 Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:26:35 +0800 Subject: [PATCH 07/16] fix: backup should run in offline mode (#358) --- core/commands/backup.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/commands/backup.go b/core/commands/backup.go index 1de94bf16..340a0f5e1 100644 --- a/core/commands/backup.go +++ b/core/commands/backup.go @@ -14,6 +14,7 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" commands "github.com/bittorrent/go-btfs/commands" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" fsrepo "github.com/bittorrent/go-btfs/repo/fsrepo" ) @@ -40,6 +41,14 @@ This command will create a backup of the data from the current BTFS node. cmds.StringsOption(excludeOption, "exclude backup output file path"), }, Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error { + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + if nd.IsOnline { + return errors.New("this action must be run in offline mode, please stop your 'btfs daemon' first") + } r, err := fsrepo.Open(env.(*commands.Context).ConfigRoot) if err != nil { return err From 7ee8a0d17ac1175dc996089c91905ff145bc757f Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Tue, 26 Sep 2023 11:44:47 +0800 Subject: [PATCH 08/16] fix: s3 create-multipart-upload cache-control header not allowed (#361) --- s3/api/requests/parsers_multipart.go | 4 +++- s3/api/requests/parsers_object.go | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/s3/api/requests/parsers_multipart.go b/s3/api/requests/parsers_multipart.go index fc99de44c..2c588eaa4 100644 --- a/s3/api/requests/parsers_multipart.go +++ b/s3/api/requests/parsers_multipart.go @@ -2,17 +2,19 @@ package requests import ( "errors" + "net/http" + "github.com/aws/aws-sdk-go/service/s3" "github.com/bittorrent/go-btfs/s3/api/contexts" "github.com/bittorrent/go-btfs/s3/api/services/object" "github.com/bittorrent/go-btfs/s3/consts" "github.com/bittorrent/go-btfs/s3/hash" - "net/http" ) var createMultipartUploadSupports = fields{ "Bucket": true, "Key": true, + "CacheControl": true, "ContentLength": true, "ContentEncoding": true, "ContentType": true, diff --git a/s3/api/requests/parsers_object.go b/s3/api/requests/parsers_object.go index 726d5b180..68916bf97 100644 --- a/s3/api/requests/parsers_object.go +++ b/s3/api/requests/parsers_object.go @@ -2,12 +2,13 @@ package requests import ( "errors" + "net/http" + "github.com/aws/aws-sdk-go/service/s3" "github.com/bittorrent/go-btfs/s3/api/contexts" "github.com/bittorrent/go-btfs/s3/api/services/object" "github.com/bittorrent/go-btfs/s3/consts" "github.com/bittorrent/go-btfs/s3/hash" - "net/http" ) var putObjectSupports = fields{ From aed156bf75a866c4a19949336977eaa83be66f54 Mon Sep 17 00:00:00 2001 From: laocheng-cheng <73106671+laocheng-cheng@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:08:40 +0800 Subject: [PATCH 09/16] chore: add txhash checking (#363) Co-authored-by: fish <920886811@163.com> --- core/commands/cheque/fix_cheque_cashout.go | 2 +- settlement/swap/swap_test.go | 11 ++- settlement/swap/vault/cashout.go | 78 +++++++++++++++++++--- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/core/commands/cheque/fix_cheque_cashout.go b/core/commands/cheque/fix_cheque_cashout.go index f4908454c..43fa6123b 100644 --- a/core/commands/cheque/fix_cheque_cashout.go +++ b/core/commands/cheque/fix_cheque_cashout.go @@ -32,7 +32,7 @@ var FixChequeCashOutCmd = &cmds.Command{ for k, v := range cheques { totalCashOutAmount, newCashOutAmount, err := chain.SettleObject.CashoutService.AdjustCashCheque( - context.Background(), v.Vault, v.Beneficiary, tokenAddr, false) + context.Background(), v.Vault, v.Beneficiary, tokenAddr) if err != nil { return err } diff --git a/settlement/swap/swap_test.go b/settlement/swap/swap_test.go index a603ed26f..d90166899 100644 --- a/settlement/swap/swap_test.go +++ b/settlement/swap/swap_test.go @@ -128,7 +128,8 @@ type cashoutMock struct { cashoutStatus func(ctx context.Context, vaultAddress common.Address, token common.Address) (*vault.CashoutStatus, error) cashoutResults func() ([]vault.CashOutResult, error) hasCashoutAction func(ctx context.Context, peer common.Address, token common.Address) (bool, error) - adjustCashCheque func(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) + adjustCashCheque func(ctx context.Context, vaultAddress, recipient common.Address, token common.Address) (totalCashOutAmount, newCashOutAmount *big.Int, err error) + adjustCashChequeTxHash func(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, txHash common.Hash, cumulativePayout *big.Int) (totalCashOutAmount, newCashOutAmount *big.Int, err error) restartFixChequeCashOut func() } @@ -144,8 +145,12 @@ func (m *cashoutMock) CashoutResults() ([]vault.CashOutResult, error) { func (m *cashoutMock) HasCashoutAction(ctx context.Context, peer common.Address, token common.Address) (bool, error) { return m.hasCashoutAction(ctx, peer, token) } -func (m *cashoutMock) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { - return m.adjustCashCheque(ctx, vaultAddress, recipient, token, restartPassFlag) +func (m *cashoutMock) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { + return m.adjustCashCheque(ctx, vaultAddress, recipient, token) +} + +func (m *cashoutMock) AdjustCashChequeTxHash(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, txHash common.Hash, cumulativePayout *big.Int) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { + return m.adjustCashChequeTxHash(ctx, vaultAddress, recipient, token, txHash, cumulativePayout) } func (m *cashoutMock) RestartFixChequeCashOut() { m.restartFixChequeCashOut() diff --git a/settlement/swap/vault/cashout.go b/settlement/swap/vault/cashout.go index 9d480beb5..e4a168026 100644 --- a/settlement/swap/vault/cashout.go +++ b/settlement/swap/vault/cashout.go @@ -28,7 +28,8 @@ type CashoutService interface { CashCheque(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) // CashoutStatus gets the status of the latest cashout transaction for the vault CashoutStatus(ctx context.Context, vaultAddress common.Address, token common.Address) (*CashoutStatus, error) - AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) + AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address) (totalCashOutAmount, newCashOutAmount *big.Int, err error) + AdjustCashChequeTxHash(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, txHash common.Hash, cumulativePayout *big.Int) (totalCashOutAmount, newCashOutAmount *big.Int, err error) HasCashoutAction(ctx context.Context, peer common.Address, token common.Address) (bool, error) CashoutResults() ([]CashOutResult, error) RestartFixChequeCashOut() @@ -181,7 +182,7 @@ func (s *cashoutService) CashoutResults() ([]CashOutResult, error) { // CashCheque sends a cashout transaction for the last cheque of the vault func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common.Address, token common.Address) (common.Hash, error) { if RestartFixCashOutStatusLock { - return common.Hash{}, errors.New("Just started, it can not cash cheque, you will wait for about 40s to do it. ") + return common.Hash{}, errors.New("Just started, it can not cash cheque for processing the cash cheque out status last time, you will wait for about 40s to do it. ") } cheque, err := s.chequeStore.LastReceivedCheque(vault, token) @@ -331,11 +332,9 @@ func (s *cashoutService) storeCashResult(ctx context.Context, vault common.Addre } // AdjustCashCheque . -func (s *cashoutService) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, restartPassFlag bool) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { +func (s *cashoutService) AdjustCashCheque(ctx context.Context, vaultAddress, recipient common.Address, token common.Address) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { if RestartFixCashOutStatusLock { - if !restartPassFlag { - return nil, nil, errors.New("Just started, it can not fix cash out status, you will wait for about 40s to do it. ") - } + return nil, nil, errors.New("Just started, it can not fix cash out info for processing the cash cheque out status last time, you will wait for about 40s to do it. ") } // 1.totalReceivedCashed @@ -361,7 +360,51 @@ func (s *cashoutService) AdjustCashCheque(ctx context.Context, vaultAddress, rec ) if diff.Cmp(big.NewInt(0)) > 0 { - cashResult, err := s.fixStoreCashResult(vaultAddress, diff, token) + txHash := common.Hash{} //fix txHash: 0x0000... + cashResult, err := s.fixStoreCashResult(vaultAddress, diff, token, txHash) + if err != nil { + return nil, nil, err + } + newCashOutAmount = cashResult.Amount + } + + return alreadyPaidOutOnline, newCashOutAmount, nil +} + +// AdjustCashChequeTxHash . when node starts. +func (s *cashoutService) AdjustCashChequeTxHash(ctx context.Context, vaultAddress, recipient common.Address, token common.Address, txHash common.Hash, cumulativePayout *big.Int) (totalCashOutAmount, newCashOutAmount *big.Int, err error) { + // 1.totalReceivedCashed + totalReceivedCashed := big.NewInt(0) + err = s.store.Get(tokencfg.AddToken(statestore.TotalReceivedCashedKey, token), &totalReceivedCashed) + if err != nil && err != storage.ErrNotFound { + return nil, nil, err + } + + // 2.alreadyPaidOut in renter contract + // blockchain calls below + contract := newVaultContractMuti(vaultAddress, s.transactionService) + alreadyPaidOutOnline, err := contract.PaidOut(ctx, recipient, token) + if err != nil { + return nil, nil, err + } + + // 3.compare it to fix. + diffReceived := big.NewInt(0).Sub(alreadyPaidOutOnline, totalReceivedCashed) + log.Infof("AdjustCashCheque: diffReceived, vault=%s, recipient=%s, txHash=%s, online=%s, local=%s, diffReceived=%s", + vaultAddress.String(), recipient.String(), txHash.String(), + alreadyPaidOutOnline.String(), totalReceivedCashed.String(), diffReceived.String(), + ) + + if diffReceived.Cmp(big.NewInt(0)) > 0 { + diffCheque := big.NewInt(0).Sub(alreadyPaidOutOnline, cumulativePayout) + log.Infof("AdjustCashCheque: diffCheque, vault=%s, recipient=%s, txHash=%s, online=%s, cumulativePayout=%s, diffCheque=%s", + vaultAddress.String(), recipient.String(), txHash.String(), + alreadyPaidOutOnline.String(), cumulativePayout.String(), diffCheque.String(), + ) + if diffCheque.Cmp(big.NewInt(0)) > 0 { + txHash = common.Hash{} //fix txHash: 0x0000... + } + cashResult, err := s.fixStoreCashResult(vaultAddress, diffReceived, token, txHash) if err != nil { return nil, nil, err } @@ -386,12 +429,27 @@ func (s *cashoutService) RestartFixChequeCashOut() { time.Sleep(time.Second * RestartWaitCashOutOnlineTime) for _, v := range list { - _, _, err := s.AdjustCashCheque(context.Background(), v.Vault, v.Beneficiary, v.Token, true) + txHash := common.HexToHash(v.TxHash) + + // 1.check txHash is ok + receipt, err := s.backend.TransactionReceipt(context.Background(), txHash) + if err != nil { + log.Infof("RestartFixChequeCashOut: TransactionReceipt err = %v, info = %+v", err, v) + continue + } + if receipt.Status == types.ReceiptStatusFailed { + log.Infof("RestartFixChequeCashOut: TransactionReceipt err = the txHash is failed, info = %+v", v) + continue + } + + // 2.adjust cash cheque info + _, _, err = s.AdjustCashChequeTxHash(context.Background(), v.Vault, v.Beneficiary, v.Token, txHash, v.CumulativePayout) if err != nil { log.Infof("RestartFixChequeCashOut: AdjustCashCheque err = %v, info = %+v", err, v) continue } + // 3.delete cash out status err = s.DeleteCashOutStatusStore(v) if err != nil { log.Infof("RestartFixChequeCashOut: DeleteCashOutStatusStore err = %v, info = %+v", err, v) @@ -404,8 +462,8 @@ func (s *cashoutService) RestartFixChequeCashOut() { return } -func (s *cashoutService) fixStoreCashResult(vault common.Address, shouldPaidOut *big.Int, token common.Address) (cashResult *CashOutResult, err error) { - txHash := common.Hash{} //fix txHash: 0x0000... +func (s *cashoutService) fixStoreCashResult(vault common.Address, shouldPaidOut *big.Int, token common.Address, txHash common.Hash) (cashResult *CashOutResult, err error) { + //txHash := common.Hash{} //fix txHash: 0x0000... cashResult = &CashOutResult{ TxHash: txHash, Vault: vault, From cf4d580a651a1cdac3ab17fc5db0ee4132b20953 Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Wed, 27 Sep 2023 11:56:24 +0800 Subject: [PATCH 10/16] chore: update dashboard latest cid (#364) --- core/corehttp/webui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index b754343d5..99e39250a 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,6 +1,6 @@ package corehttp -const WebUIPath = "/btfs/QmQBTkD34AYcNwm2JG1u27LYBuejVgwDkuHD7BjVDLgqU6" // v2.3.2 +const WebUIPath = "/btfs/QmNZZoPQiVgUDRQFUP85oc2vhKJAgbysQeNmUvwtGWTp9D" // v2.3.3 // this is a list of all past webUI paths. var WebUIPaths = []string{ From 6b506c524aaf81228cc384d02b2297fee412965a Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Wed, 27 Sep 2023 13:19:49 +0800 Subject: [PATCH 11/16] feat: update dashboard cid (#365) --- core/corehttp/webui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index 99e39250a..7c11ea571 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,6 +1,6 @@ package corehttp -const WebUIPath = "/btfs/QmNZZoPQiVgUDRQFUP85oc2vhKJAgbysQeNmUvwtGWTp9D" // v2.3.3 +const WebUIPath = "/btfs/QmSjmigZcnd4CyYS5R6cM6V1dV1WTQ3sddRsr6iecMsRYK" // v2.3.3 // this is a list of all past webUI paths. var WebUIPaths = []string{ From 270144ba89ef783f3919c2a3364f9f9d20dee942 Mon Sep 17 00:00:00 2001 From: Shawn-Huang-Tron <107823650+Shawn-Huang-Tron@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:01:25 +0800 Subject: [PATCH 12/16] feat: update cid (#368) --- core/corehttp/webui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index 7c11ea571..73d629223 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,6 +1,6 @@ package corehttp -const WebUIPath = "/btfs/QmSjmigZcnd4CyYS5R6cM6V1dV1WTQ3sddRsr6iecMsRYK" // v2.3.3 +const WebUIPath = "/btfs/QmPFT7PscyJ1FZ4FeFPFgikszBugQSBFNycPpy5zpK2pZe" // v2.3.3 // this is a list of all past webUI paths. var WebUIPaths = []string{ From 2bba3872ddb6cf19473f1c828c2f8f292d32f86a Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Wed, 27 Sep 2023 16:03:35 +0800 Subject: [PATCH 13/16] fix: recover change (#369) --- blocks/blockstoreutil/remove.go | 4 ++-- core/commands/cid.go | 2 +- core/commands/cmdenv/cidbase.go | 2 +- core/commands/commands.go | 2 +- core/commands/files.go | 6 +++--- core/commands/filestore.go | 4 ++-- core/commands/object/object.go | 10 +++++----- core/commands/object/patch.go | 4 ++-- core/commands/refs.go | 2 +- core/corehttp/gateway/gateway.go | 2 +- core/corehttp/gateway/gateway_test.go | 6 +++--- fuse/ipns/ipns_test.go | 8 ++++---- fuse/readonly/readonly_unix.go | 2 +- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/blocks/blockstoreutil/remove.go b/blocks/blockstoreutil/remove.go index 81780c124..e08a95d47 100644 --- a/blocks/blockstoreutil/remove.go +++ b/blocks/blockstoreutil/remove.go @@ -71,8 +71,8 @@ func RmBlocks(ctx context.Context, blocks bs.GCBlockstore, pins pin.Pinner, cids } // FilterPinned takes a slice of Cids and returns it with the pinned Cids -// removed. If a CID is pinned, it will place RemovedBlock objects in the given -// out channel, with an error which indicates that the CID is pinned. +// removed. If a Cid is pinned, it will place RemovedBlock objects in the given +// out channel, with an error which indicates that the Cid is pinned. // This function is used in RmBlocks to filter out any blocks which are not // to be removed (because they are pinned). func FilterPinned(ctx context.Context, pins pin.Pinner, out chan<- interface{}, cids []cid.Cid) []cid.Cid { diff --git a/core/commands/cid.go b/core/commands/cid.go index 16d972985..0bbc3ce26 100644 --- a/core/commands/cid.go +++ b/core/commands/cid.go @@ -117,7 +117,7 @@ The optional format string is a printf style format string: } type CidFormatRes struct { - CidStr string // Original CID String passed in + CidStr string // Original Cid String passed in Formatted string // Formatted Result ErrorMsg string // Error } diff --git a/core/commands/cmdenv/cidbase.go b/core/commands/cmdenv/cidbase.go index 445705d28..0e0822af1 100644 --- a/core/commands/cmdenv/cidbase.go +++ b/core/commands/cmdenv/cidbase.go @@ -58,7 +58,7 @@ func CidBaseDefined(req *cmds.Request) bool { } // CidEncoderFromPath creates a new encoder that is influenced from -// the encoded CID in a Path. For CidV0 the multibase from the base +// the encoded Cid in a Path. For CidV0 the multibase from the base // encoder is used and automatic upgrades are disabled. For CidV1 the // multibase from the CID is used and upgrades are enabled. // diff --git a/core/commands/commands.go b/core/commands/commands.go index 3d1d6ecc1..4f92bd252 100644 --- a/core/commands/commands.go +++ b/core/commands/commands.go @@ -12,7 +12,7 @@ import ( "sort" "strings" - cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs-cmds" ) type commandEncoder struct { diff --git a/core/commands/files.go b/core/commands/files.go index ae9272310..1b9768bf1 100644 --- a/core/commands/files.go +++ b/core/commands/files.go @@ -87,8 +87,8 @@ const ( filesHashOptionName = "hash" ) -var cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "CID version to use. (experimental)") -var hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set CID version to 1 if used. (experimental)") +var cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "Cid version to use. (experimental)") +var hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set Cid version to 1 if used. (experimental)") var errFormat = errors.New("format was set by multiple options. Only one format option is allowed") @@ -735,7 +735,7 @@ stat' on the file or any of its ancestors. }, Arguments: []cmds.Argument{ cmds.StringArg("path", true, false, "Path to write to."), - cmds.FileArg("data", true, false, "data to write.").EnableStdin(), + cmds.FileArg("data", true, false, "Data to write.").EnableStdin(), }, Options: []cmds.Option{ cmds.Int64Option(filesOffsetOptionName, "o", "Byte offset to begin writing at."), diff --git a/core/commands/filestore.go b/core/commands/filestore.go index 808bf2d9d..e6520984e 100644 --- a/core/commands/filestore.go +++ b/core/commands/filestore.go @@ -45,7 +45,7 @@ The output is: `, }, Arguments: []cmds.Argument{ - cmds.StringArg("obj", false, true, "CID of objects to list."), + cmds.StringArg("obj", false, true, "Cid of objects to list."), }, Options: []cmds.Option{ cmds.BoolOption(fileOrderOptionName, "sort the results based on the path of the backing file"), @@ -122,7 +122,7 @@ For ERROR entries the error will also be printed to stderr. `, }, Arguments: []cmds.Argument{ - cmds.StringArg("obj", false, true, "CID of objects to verify."), + cmds.StringArg("obj", false, true, "Cid of objects to verify."), }, Options: []cmds.Option{ cmds.BoolOption(fileOrderOptionName, "verify the objects based on the order of the backing file"), diff --git a/core/commands/object/object.go b/core/commands/object/object.go index 3a9d4c28b..524e490f0 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -270,7 +270,7 @@ Supported values are: Type: Node{}, Encoders: cmds.EncoderMap{ cmds.Protobuf: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Node) error { - // deserialize the data field as text as this was the standard behaviour + // deserialize the Data field as text as this was the standard behaviour object, err := deserializeNode(out, "text") if err != nil { return nil @@ -371,20 +371,20 @@ It reads from stdin, and the output is a base58 encoded multihash. 'btfs object put' is a plumbing command for storing DAG nodes. It reads from stdin, and the output is a base58 encoded multihash. -data should be in the format specified by the --inputenc flag. +Data should be in the format specified by the --inputenc flag. --inputenc may be one of the following: * "protobuf" * "json" (default) Examples: - $ echo '{ "data": "abc" }' | btfs object put + $ echo '{ "Data": "abc" }' | btfs object put This creates a node with the data 'abc' and no links. For an object with links, create a file named 'node.json' with the contents: { - "data": "another", + "Data": "another", "Links": [ { "Name": "some link", "Hash": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", @@ -399,7 +399,7 @@ And then run: }, Arguments: []cmds.Argument{ - cmds.FileArg("data", true, false, "data to be stored as a DAG object.").EnableStdin(), + cmds.FileArg("data", true, false, "Data to be stored as a DAG object.").EnableStdin(), }, Options: []cmds.Option{ cmds.StringOption(inputencOptionName, "Encoding type of input data. One of: {\"protobuf\", \"json\"}.").WithDefault("json"), diff --git a/core/commands/object/patch.go b/core/commands/object/patch.go index b196738b2..f2eb0dc4e 100644 --- a/core/commands/object/patch.go +++ b/core/commands/object/patch.go @@ -40,13 +40,13 @@ Example: $ echo "hello" | btfs object patch $HASH append-data NOTE: This does not append data to a file - it modifies the actual raw -data within an object. ToDeleteObjects have a max size of 1MB and objects larger than +data within an object. Objects have a max size of 1MB and objects larger than the limit will not be respected by the network. `, }, Arguments: []cmds.Argument{ cmds.StringArg("root", true, false, "The hash of the node to modify."), - cmds.FileArg("data", true, false, "data to append.").EnableStdin(), + cmds.FileArg("data", true, false, "Data to append.").EnableStdin(), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) diff --git a/core/commands/refs.go b/core/commands/refs.go index bda6b2c73..9f564297c 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -296,7 +296,7 @@ func (rw *RefWriter) visit(c cid.Cid, depth int) (bool, bool) { // Unique == true && depth < MaxDepth (or unlimited) from this point // Branch pruning cases: - // - We saw the CID before and either: + // - We saw the Cid before and either: // - Depth is unlimited (MaxDepth = -1) // - We saw it higher (smaller depth) in the DAG (means we must have // explored deep enough before) diff --git a/core/corehttp/gateway/gateway.go b/core/corehttp/gateway/gateway.go index 9e8e946e6..1b9423f72 100644 --- a/core/corehttp/gateway/gateway.go +++ b/core/corehttp/gateway/gateway.go @@ -92,7 +92,7 @@ type IPFSBackend interface { // Get returns a GetResponse with UnixFS file, directory or a block in IPLD // format e.g., (DAG-)CBOR/JSON. // - // Returned Directories are preferably a minimum info required for enumeration: Name, Size, and CID. + // Returned Directories are preferably a minimum info required for enumeration: Name, Size, and Cid. // // Optional ranges follow [HTTP Byte Ranges] notation and can be used for // pre-fetching specific sections of a file or a block. diff --git a/core/corehttp/gateway/gateway_test.go b/core/corehttp/gateway/gateway_test.go index 96d47180a..304051a95 100644 --- a/core/corehttp/gateway/gateway_test.go +++ b/core/corehttp/gateway/gateway_test.go @@ -418,7 +418,7 @@ func TestIPNSHostnameRedirect(t *testing.T) { // assert.Contains(t, s, "", "expected backlink in directory listing") // assert.Contains(t, s, "", "expected file in directory listing") -// assert.Contains(t, s, s, k2.CID().String(), "expected hash in directory listing") +// assert.Contains(t, s, s, k2.Cid().String(), "expected hash in directory listing") // // make request to directory listing at root // req, err = http.NewRequest(http.MethodGet, ts.URL, nil) @@ -440,7 +440,7 @@ func TestIPNSHostnameRedirect(t *testing.T) { // assert.Contains(t, s, "", "expected file in directory listing") // // https://github.com/btfs/dir-index-html/issues/42 // assert.Contains(t, s, "example.net/foo? #<'/bar"), "expected a path in directory listing") // assert.Contains(t, s, "", "expected backlink in directory listing") // assert.Contains(t, s, "", "expected file in directory listing") -// assert.Contains(t, s, k3.CID().String(), "expected hash in directory listing") +// assert.Contains(t, s, k3.Cid().String(), "expected hash in directory listing") // } func TestPretty404(t *testing.T) { diff --git a/fuse/ipns/ipns_test.go b/fuse/ipns/ipns_test.go index 9ac110d60..276f6a0dc 100644 --- a/fuse/ipns/ipns_test.go +++ b/fuse/ipns/ipns_test.go @@ -66,10 +66,10 @@ func verifyFile(t *testing.T, path string, wantData []byte) { t.Fatal(err) } if len(isData) != len(wantData) { - t.Fatal("data not equal - length check failed") + t.Fatal("Data not equal - length check failed") } if !bytes.Equal(isData, wantData) { - t.Fatal("data not equal") + t.Fatal("Data not equal") } } @@ -328,7 +328,7 @@ func TestAppendFile(t *testing.T) { t.Fatal(err) } if !bytes.Equal(rbuf, data) { - t.Fatal("data inconsistent!") + t.Fatal("Data inconsistent!") } } @@ -458,7 +458,7 @@ func TestFSThrash(t *testing.T) { } if !bytes.Equal(data, out) { - t.Errorf("data didn't match in %s: expected %v, got %v", name, data, out) + t.Errorf("Data didn't match in %s: expected %v, got %v", name, data, out) } } } diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go index dc7451d42..7e92aa6bf 100644 --- a/fuse/readonly/readonly_unix.go +++ b/fuse/readonly/readonly_unix.go @@ -272,7 +272,7 @@ func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadR if err != nil { return err } - // data has a capacity of Size + // Data has a capacity of Size buf := resp.Data[:int(req.Size)] n, err := io.ReadFull(r, buf) resp.Data = buf[:n] From ea77a6fb8b3a6f861fd0c905473fc8d714c2788f Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Wed, 27 Sep 2023 16:15:06 +0800 Subject: [PATCH 14/16] fix: recover change (#370) From 9052f09c8de002f754860ed0a4ad65af852bf73b Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Wed, 27 Sep 2023 16:48:32 +0800 Subject: [PATCH 15/16] Fix/recover (#371) * fix: recover change * fix: recover change --- settlement/swap/vault/cashout.go | 4 ++-- settlement/swap/vault/factory_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/settlement/swap/vault/cashout.go b/settlement/swap/vault/cashout.go index e4a168026..9ac7bedae 100644 --- a/settlement/swap/vault/cashout.go +++ b/settlement/swap/vault/cashout.go @@ -134,7 +134,7 @@ func cashoutActionKey(vault common.Address, token common.Address) string { // // output, err := s.transactionService.Call(ctx, &transaction.TxRequest{ // To: &vault, -// data: callData, +// Data: callData, // }) // if err != nil { // return nil, err @@ -198,7 +198,7 @@ func (s *cashoutService) CashCheque(ctx context.Context, vault, recipient common //} //request := &transaction.TxRequest{ // To: &vault, - // data: callData, + // Data: callData, // Value: big.NewInt(0), // Description: "cheque cashout", //} diff --git a/settlement/swap/vault/factory_test.go b/settlement/swap/vault/factory_test.go index f52c352ec..652eb87e3 100644 --- a/settlement/swap/vault/factory_test.go +++ b/settlement/swap/vault/factory_test.go @@ -207,12 +207,12 @@ func TestFactoryVerifyVault(t *testing.T) { // Status: 1, // Logs: []*types.Log{ // { -// data: logData, +// Data: logData, // }, // { // Address: factoryAddress, // Topics: []common.Hash{simpleSwapDeployedEvent.ID}, -// data: logData, +// Data: logData, // }, // }, // }, nil From 01451fe93fba649eec746d8821efe5e08f258152 Mon Sep 17 00:00:00 2001 From: StevE Zhang Date: Wed, 27 Sep 2023 16:52:05 +0800 Subject: [PATCH 16/16] Fix/recover (#372) * fix: recover change * fix: recover change * fix: recover change --- settlement/swap/vault/vault.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settlement/swap/vault/vault.go b/settlement/swap/vault/vault.go index d4733a3d2..d8fe95800 100644 --- a/settlement/swap/vault/vault.go +++ b/settlement/swap/vault/vault.go @@ -526,7 +526,7 @@ func (s *service) LastCheques(token common.Address) (map[common.Address]*SignedC // // request := &transaction.TxRequest{ // To: &s.address, -// data: callData, +// Data: callData, // Value: big.NewInt(0), // Description: fmt.Sprintf("vault withdrawal of %d WBTT", amount), // }