From 5d3dd82bb4f9bb9338214df1f9ec11774f4ffb7b Mon Sep 17 00:00:00 2001 From: hopeyen Date: Mon, 8 Jan 2024 12:27:12 -0600 Subject: [PATCH] feat: transaction allocate subcommand --- docs/cli_usage.md | 384 ------------------ subfile-exchange/src/config.rs | 103 +++-- subfile-exchange/src/main.rs | 37 +- subfile-exchange/src/subfile_client/mod.rs | 7 +- .../src/transaction_manager/mod.rs | 48 ++- .../src/transaction_manager/staking.rs | 235 +++++------ subfile-exchange/tests/allocate.rs | 64 +++ 7 files changed, 291 insertions(+), 587 deletions(-) delete mode 100644 docs/cli_usage.md create mode 100644 subfile-exchange/tests/allocate.rs diff --git a/docs/cli_usage.md b/docs/cli_usage.md deleted file mode 100644 index 142ffd3..0000000 --- a/docs/cli_usage.md +++ /dev/null @@ -1,384 +0,0 @@ - - -### Publisher - -| Environment Variable | CLI Argument | Value | -| --------------------------------------------- | ------------------------------- | ------------------------------------------------ | -| `READ_DIR` | `--read-dir` | Read directory for the files to publish | -| `SUBFILE_NAME` | `--subfile-name` | Give a name to the subfile (Q: removable as it can cause unnecessary change in subfile hash) | -| `FILE_TYPE` | `--file-type` | flatfile, snapshot, ... | -| `FILE_VERSION` | `--file-version` | Subfile version | -| `FILE_NAMES` | `--file-names` | name of the files to include in the package | -| `IDENTIFIER` | `--identifier` | Optional, Useful for deployment specific files | -| `PUBLISHER_URL` | `--publisher-url` | Optional, include in subfile manifest for self advertisement | -| `DESCRIPTION` | `--description` | Descibe the subfile content | -| `SUBFILE_SERVICE_ETHEREUM_NETWORK` | `--chain-id` | mainnet: `1`, goerli: `5`, arbitrum-one: `42161`, sepolia: `58008` | - -### To add -| Environment Variable | CLI Argument | Value | -| --------------------------------------------- | ------------------------------- | ------------------------------------------------ | -| `SUBFILE_SERVICE_MNEMONIC` | `--mnemonic` | Ethereum mnemonic for connecting to a wallet for publishing on-chain | - -Consider access such as to postgres or files requiring authentication - - -``` -➜ subfile-exchange git:(main) ✗ cargo run -p subfile-exchange publisher \ - --read-dir ./example-file/ \ - --subfile-name "blah" \ - --file-names example0017686312.dbin,example-create-17686085.dbin \ - --file-type nothing \ - --file-version 0.0.0 \ - --identifier example0017686312 \ - --publisher-url "http://localhost:5678" - - Running `target/debug/subfile-exchange publisher --read-dir ./example-file/ --subfile-name blah --file-names example0017686312.dbin,example-create-17686085.dbin --file-type nothing --file-version 0.0.0 --identifier example0017686312 --publisher-url 'http://localhost:5678'` - 2023-11-13T20:54:00.508237Z INFO subfile_cli: Running cli, cli: Cli { role: Publisher(PublisherArgs { yaml_store: "./example-file/subfile.yaml", read_dir: "./example-file/", subfile_name: "blah", file_names: ["example0017686312.dbin", "example-create-17686085.dbin"], file_type: "nothing", file_version: "0.0.0", identifier: "example0017686312", start_block: None, end_block: None, publisher_url: "http://localhost:5678" }), ipfs_gateway: "https://ipfs.network.thegraph.com", log_format: Pretty } - at subfile-exchange/src/main.rs:16 - - 2023-11-13T20:54:00.521591Z INFO subfile_cli: Publisher request, config: PublisherArgs { yaml_store: "./example-file/subfile.yaml", read_dir: "./example-file/", subfile_name: "blah", file_names: ["example0017686312.dbin", "example-create-17686085.dbin"], file_type: "nothing", file_version: "0.0.0", identifier: "example0017686312", start_block: None, end_block: None, publisher_url: "http://localhost:5678" } - at subfile-exchange/src/main.rs:43 - - 2023-11-13T20:54:00.521793Z INFO crate::publisher: hash_and_publish_files, file_names: ["example0017686312.dbin", "example-create-17686085.dbin"] - at subfile-exchange/src/publisher.rs:65 - - 2023-11-13T20:54:00.521916Z INFO crate::file_hasher: write_chunk_file, read_dir: "./example-file/", file_name: "example0017686312.dbin" - at subfile-exchange/src/file_hasher.rs:115 - - 2023-11-13T20:54:00.523842Z DEBUG crate::file_hasher: Chunked file, file: "./example-file//example0017686312.dbin", total_bytes: 1508787, num_chunks: 2 - at subfile-exchange/src/file_hasher.rs:51 - - 2023-11-13T20:54:00.576871Z DEBUG crate::file_hasher: Chunk hash, hash: [202, 227, 94, 140, 125, 254, 145, 68, 217, 119, 24, 19, 178, 40, 120, 144, 232, 4, 19, 190, 183, 234, 100, 36, 76, 83, 65, 117, 96, 23, 26, 146] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:54:00.576961Z DEBUG crate::file_hasher: Chunk hash, hash_str: "yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:54:00.600089Z DEBUG crate::file_hasher: Chunk hash, hash: [191, 27, 225, 27, 22, 159, 177, 171, 252, 216, 173, 16, 107, 156, 16, 160, 148, 174, 246, 237, 228, 234, 254, 114, 77, 246, 129, 45, 195, 205, 203, 231] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:54:00.600109Z DEBUG crate::file_hasher: Chunk hash, hash_str: "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:54:00.600226Z TRACE crate::file_hasher: Created chunk file, file: ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] } - at subfile-exchange/src/file_hasher.rs:123 - - 2023-11-13T20:54:01.523414Z DEBUG crate::publisher: Added yaml file to IPFS, added: AddResponse { name: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ", size: "194" } - at subfile-exchange/src/publisher.rs:51 - - 2023-11-13T20:54:01.523533Z INFO crate::file_hasher: write_chunk_file, read_dir: "./example-file/", file_name: "example-create-17686085.dbin" - at subfile-exchange/src/file_hasher.rs:115 - - 2023-11-13T20:54:01.525699Z DEBUG crate::file_hasher: Chunked file, file: "./example-file//example-create-17686085.dbin", total_bytes: 1052737, num_chunks: 2 - at subfile-exchange/src/file_hasher.rs:51 - - 2023-11-13T20:54:01.609974Z DEBUG crate::file_hasher: Chunk hash, hash: [184, 160, 246, 197, 215, 233, 213, 107, 51, 186, 242, 15, 214, 113, 77, 53, 54, 40, 103, 188, 239, 155, 98, 151, 232, 161, 68, 151, 12, 1, 125, 210] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:54:01.610021Z DEBUG crate::file_hasher: Chunk hash, hash_str: "uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:54:01.610260Z DEBUG crate::file_hasher: Chunk hash, hash: [78, 187, 172, 71, 70, 126, 17, 136, 55, 222, 142, 10, 69, 113, 175, 72, 223, 117, 211, 38, 175, 10, 72, 195, 236, 173, 233, 96, 137, 134, 101, 164] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:54:01.610274Z DEBUG crate::file_hasher: Chunk hash, hash_str: "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:54:01.610292Z TRACE crate::file_hasher: Created chunk file, file: ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] } - at subfile-exchange/src/file_hasher.rs:123 - - 2023-11-13T20:54:02.132445Z DEBUG crate::publisher: Added yaml file to IPFS, added: AddResponse { name: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv", size: "200" } - at subfile-exchange/src/publisher.rs:51 - - 2023-11-13T20:54:02.133021Z INFO crate::publisher: hash_and_publish_files, meta_info: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] - at subfile-exchange/src/publisher.rs:121 - - 2023-11-13T20:54:02.356114Z INFO crate::publisher: Published subfile manifest to IPFS with hash: QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw - at subfile-exchange/src/publisher.rs:128 - - 2023-11-13T20:54:02.356867Z INFO subfile_cli: Published, result: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw" - at subfile-exchange/src/main.rs:49 - - - -``` - - - -### Server - -Serve multiple files in multiple subfile - -## To add -| Environment Variable | CLI Argument | Value | -| --------------------------------------------- | ------------------------------- | ------------------------------------------------ | -| `SUBFILE_SERVICE_NETWORK_SUBGRAPH_DEPLOYMENT` | `--network-subgraph-deployment` | `QmVQrrgeGGHEqRdjAByeLvnNnDMjdt85jZZB5EFZk62JWs` (`mainnet`) | -| `SUBFILE_SERVICE_NETWORK_SUBGRAPH_ENDPOINT` | `--network-subgraph-endpoint` | `https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet` | -| `SUBFILE_SERVICE_METRICS_PORT` | `--metrics-port` | Default: `7200` | -| `SUBFILE_SERVICE_CLIENT_SIGNER_ADDRESS` | `--client-signer-address` | `0x982D10c56b8BBbD6e09048F5c5f01b43C65D5aE0` | -| `SUBFILE_SERVICE_LOG_LEVEL` | `--log-level` | Default: `info` | -| `SUBFILE_SERVICE_ALLOCATION_SYNCING_INTERVAL` | `--allocation-syncing-interval` | Default: `120000` | -| `SUBFILE_SERVICE_ESCROW_SUBGRAPH_ENDPOINT` | `--escrow-subgraph-endpoint` | `'https://api.studio.thegraph.com/proxy/53925/eth-goerli-tap-subgraph/version/latest/'` | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--escrow-syncing-interval` | Default: `120000` | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--receipts-verifier-chain-id` | 1, 5 | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--receipts-verifier-address ` | '0xD46c60558F7960407F4D00098145D77Fd061aD90' | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--rav-request-trigger-value` | Default: `10000000000000000000` | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--rav-request-timestamp-buffer-ns` | Default: `1000000000` | -| `SUBFILE_SERVICE_ESCROW_SYNCING_INTERVAL` | `--sender-aggregator-endpoints-file` | Default: `"aggregators.yaml"` | - -Consider access such as to postgres or files requiring authentication - -``` -➜ subfile-exchange git:(main) ✗ cargo run -p subfile-exchange server \ - --host 0.0.0.0 \ - --port 5678 \ - --mnemonic "blah" \ - --subfiles "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw:./example-file/" - - 2023-11-13T20:56:37.038316Z INFO subfile_cli: Running cli, cli: Cli { role: Server(ServerArgs { host: "0.0.0.0", port: 5678, subfiles: ["QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw:./example-file/"] }), ipfs_gateway: "https://ipfs.network.thegraph.com", log_format: Pretty } - at subfile-exchange/src/main.rs:16 - - 2023-11-13T20:56:37.042506Z INFO subfile_cli: Tracker request, server: ServerArgs { host: "0.0.0.0", port: 5678, subfiles: ["QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw:./example-file/"] } - at subfile-exchange/src/main.rs:57 - - 2023-11-13T20:56:37.042762Z DEBUG crate::subfile_server: Validated subfile entries, entries: [("QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", "./example-file/")] - at subfile-exchange/src/subfile_server/mod.rs:79 - - 2023-11-13T20:56:37.044076Z INFO crate::subfile_server::util: Running package version PackageVersion { - version: "0.0.1", - dependencies: {}, -} - at subfile-exchange/src/subfile_server/util.rs:56 - - 2023-11-13T20:56:37.594728Z INFO crate::subfile_reader: Read file content, content: Mapping {"files": Sequence [Mapping {"name": String("example0017686312.dbin"), "hash": String("QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ")}, Mapping {"name": String("example-create-17686085.dbin"), "hash": String("QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv")}]} - at subfile-exchange/src/subfile_reader.rs:24 - - 2023-11-13T20:56:37.595171Z DEBUG crate::subfile_reader: subfile manifest, subfile: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] } - at subfile-exchange/src/subfile_reader.rs:31 - - 2023-11-13T20:56:37.595636Z DEBUG crate::subfile_reader: Fetch chunk file from IPFS, ipfs_hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" - at subfile-exchange/src/subfile_reader.rs:50 - - 2023-11-13T20:56:37.799012Z INFO crate::subfile_reader: Read file content, content: Mapping {"file_name": String("example0017686312.dbin"), "total_bytes": Number(1508787), "chunk_size": Number(1048576), "chunk_hashes": Sequence [String("yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI="), String("vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c=")]} - at subfile-exchange/src/subfile_reader.rs:59 - - 2023-11-13T20:56:37.800539Z DEBUG crate::subfile_reader: Fetch chunk file from IPFS, ipfs_hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" - at subfile-exchange/src/subfile_reader.rs:50 - - 2023-11-13T20:56:37.996021Z INFO crate::subfile_reader: Read file content, content: Mapping {"file_name": String("example-create-17686085.dbin"), "total_bytes": Number(1052737), "chunk_size": Number(1048576), "chunk_hashes": Sequence [String("uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI="), String("TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ=")]} - at subfile-exchange/src/subfile_reader.rs:59 - - 2023-11-13T20:56:37.996495Z DEBUG crate::subfile_server: Read subfile, subfile: Subfile { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", local_path: "./example-file/", manifest: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] }, chunk_files: [ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] }, ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] }] } - at subfile-exchange/src/subfile_server/mod.rs:94 - - 2023-11-13T20:56:37.997204Z INFO crate::subfile_server: Server listening on https://0.0.0.0:5678 - at subfile-exchange/src/subfile_server/mod.rs:66 - - 2023-11-13T20:57:45.443240Z TRACE crate::subfile_server: Received request - at subfile-exchange/src/subfile_server/mod.rs:109 - - 2023-11-13T20:57:45.443240Z TRACE crate::subfile_server: Received request - at subfile-exchange/src/subfile_server/mod.rs:109 - - 2023-11-13T20:57:45.444357Z DEBUG crate::subfile_server: Received file range request - at subfile-exchange/src/subfile_server/mod.rs:126 - - 2023-11-13T20:57:45.444495Z DEBUG crate::subfile_server: Received file range request - at subfile-exchange/src/subfile_server/mod.rs:126 - - 2023-11-13T20:57:45.444839Z DEBUG crate::subfile_server: Received file range request, subfiles: ServerState { subfiles: {"QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw": Subfile { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", local_path: "./example-file/", manifest: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] }, chunk_files: [ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] }, ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] }] }}, release: PackageVersion { version: "0.0.1", dependencies: {} } }, id: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw" - at subfile-exchange/src/subfile_server/mod.rs:130 - - 2023-11-13T20:57:45.445315Z DEBUG crate::subfile_server: Parse content range header - at subfile-exchange/src/subfile_server/mod.rs:169 - - 2023-11-13T20:57:45.445788Z DEBUG crate::subfile_server::range: Serve file range, file_path: "./example-file/example0017686312.dbin", start_byte: 1048576, end_byte: 1508786 - at subfile-exchange/src/subfile_server/range.rs:44 - - 2023-11-13T20:57:45.446019Z DEBUG crate::subfile_server::range: Range validity check, start: 1048576, end: 1508786, file_size: 1508787 - at subfile-exchange/src/subfile_server/range.rs:71 - - 2023-11-13T20:57:45.446409Z TRACE crate::subfile_server::range: File seek to start at 1048576 - at subfile-exchange/src/subfile_server/range.rs:89 - - 2023-11-13T20:57:45.449501Z DEBUG crate::subfile_server: Received file range request, subfiles: ServerState { subfiles: {"QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw": Subfile { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", local_path: "./example-file/", manifest: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] }, chunk_files: [ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] }, ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] }] }}, release: PackageVersion { version: "0.0.1", dependencies: {} } }, id: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw" - at subfile-exchange/src/subfile_server/mod.rs:130 - - 2023-11-13T20:57:45.449681Z DEBUG crate::subfile_server: Parse content range header - at subfile-exchange/src/subfile_server/mod.rs:169 - - 2023-11-13T20:57:45.449708Z DEBUG crate::subfile_server::range: Serve file range, file_path: "./example-file/example0017686312.dbin", start_byte: 0, end_byte: 1048575 - at subfile-exchange/src/subfile_server/range.rs:44 - - 2023-11-13T20:57:45.449751Z DEBUG crate::subfile_server::range: Range validity check, start: 0, end: 1048575, file_size: 1508787 - at subfile-exchange/src/subfile_server/range.rs:71 - - 2023-11-13T20:57:45.449773Z TRACE crate::subfile_server::range: File seek to start at 0 - at subfile-exchange/src/subfile_server/range.rs:89 - - 2023-11-13T20:57:45.728403Z TRACE crate::subfile_server: Received request - at subfile-exchange/src/subfile_server/mod.rs:109 - - 2023-11-13T20:57:45.728403Z TRACE crate::subfile_server: Received request - at subfile-exchange/src/subfile_server/mod.rs:109 - - 2023-11-13T20:57:45.728534Z DEBUG crate::subfile_server: Received file range request - at subfile-exchange/src/subfile_server/mod.rs:126 - - 2023-11-13T20:57:45.728565Z DEBUG crate::subfile_server: Received file range request - at subfile-exchange/src/subfile_server/mod.rs:126 - - 2023-11-13T20:57:45.728603Z DEBUG crate::subfile_server: Received file range request, subfiles: ServerState { subfiles: {"QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw": Subfile { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", local_path: "./example-file/", manifest: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] }, chunk_files: [ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] }, ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] }] }}, release: PackageVersion { version: "0.0.1", dependencies: {} } }, id: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw" - at subfile-exchange/src/subfile_server/mod.rs:130 - - 2023-11-13T20:57:45.731898Z DEBUG crate::subfile_server: Parse content range header - at subfile-exchange/src/subfile_server/mod.rs:169 - - 2023-11-13T20:57:45.731985Z DEBUG crate::subfile_server::range: Serve file range, file_path: "./example-file/example-create-17686085.dbin", start_byte: 1048576, end_byte: 1052736 - at subfile-exchange/src/subfile_server/range.rs:44 - - 2023-11-13T20:57:45.732090Z DEBUG crate::subfile_server::range: Range validity check, start: 1048576, end: 1052736, file_size: 1052737 - at subfile-exchange/src/subfile_server/range.rs:71 - - 2023-11-13T20:57:45.732132Z TRACE crate::subfile_server::range: File seek to start at 1048576 - at subfile-exchange/src/subfile_server/range.rs:89 - - 2023-11-13T20:57:45.732498Z DEBUG crate::subfile_server: Received file range request, subfiles: ServerState { subfiles: {"QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw": Subfile { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", local_path: "./example-file/", manifest: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] }, chunk_files: [ChunkFile { file_name: "example0017686312.dbin", total_bytes: 1508787, chunk_size: 1048576, chunk_hashes: ["yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=", "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c="] }, ChunkFile { file_name: "example-create-17686085.dbin", total_bytes: 1052737, chunk_size: 1048576, chunk_hashes: ["uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=", "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ="] }] }}, release: PackageVersion { version: "0.0.1", dependencies: {} } }, id: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw" - at subfile-exchange/src/subfile_server/mod.rs:130 - - 2023-11-13T20:57:45.732611Z DEBUG crate::subfile_server: Parse content range header - at subfile-exchange/src/subfile_server/mod.rs:169 - - 2023-11-13T20:57:45.732639Z DEBUG crate::subfile_server::range: Serve file range, file_path: "./example-file/example-create-17686085.dbin", start_byte: 0, end_byte: 1048575 - at subfile-exchange/src/subfile_server/range.rs:44 - - 2023-11-13T20:57:45.732688Z DEBUG crate::subfile_server::range: Range validity check, start: 0, end: 1048575, file_size: 1052737 - at subfile-exchange/src/subfile_server/range.rs:71 - - 2023-11-13T20:57:45.732719Z TRACE crate::subfile_server::range: File seek to start at 0 - at subfile-exchange/src/subfile_server/range.rs:89 - - -`````` - - -Corresponding client - -``` -➜ ~ curl -H "Range: bytes=0-102000003" http://localhost:5678/subfiles/id/QmPUsWnSoosNmM2uaKQwZRfEDJpxVciV2UjwycBdv7HsoX -> range out of bound - -➜ ~ curl -H "Range: bytes=0-1023" http://localhost:5678/subfiles/id/QmPUsWnSoosNmM2uaKQwZRfEDJpxVciV2UjwycBdv7HsoX --output - -> [partial file...] - -➜ ~ curl http://localhost:5678/subfiles/id/QmPUsWnSoosNmM2uaKQwZRfEDJpxVciV2UjwycBdv7HsoX -> [whole file...] -``` - - - -### Downloader - -``` -➜ subfile-exchange git:(main) ✗ cargo run -p subfile-exchange downloader \ - --ipfs-hash QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw \ - --gateway-url http://localhost:5678/subfiles/id/ - - 2023-11-13T20:57:44.211311Z INFO subfile_cli: Downloader request, config: DownloaderArgs { ipfs_hash: "QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", gateway_url: "http://localhost:5678/subfiles/id/", indexer_endpoints: [], output_dir: "./example-download" } - at subfile-exchange/src/main.rs:26 - - 2023-11-13T20:57:44.885806Z INFO crate::subfile_reader: Read file content, content: Mapping {"files": Sequence [Mapping {"name": String("example0017686312.dbin"), "hash": String("QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ")}, Mapping {"name": String("example-create-17686085.dbin"), "hash": String("QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv")}]} - at subfile-exchange/src/subfile_reader.rs:24 - - 2023-11-13T20:57:44.886232Z DEBUG crate::subfile_reader: subfile manifest, subfile: SubfileManifest { files: [FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" }, FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" }] } - at subfile-exchange/src/subfile_reader.rs:31 - - 2023-11-13T20:57:44.886955Z DEBUG crate::subfile_client: Download chunk file, info: FileMetaInfo { name: "example0017686312.dbin", hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" } - at subfile-exchange/src/subfile_client/mod.rs:109 - - 2023-11-13T20:57:44.887215Z DEBUG crate::subfile_reader: Fetch chunk file from IPFS, ipfs_hash: "QmSy2UtZNJbwWFED6CroKzRmMz43WjrN8Y1Bns1EFqjeKJ" - at subfile-exchange/src/subfile_reader.rs:50 - - 2023-11-13T20:57:45.425129Z INFO crate::subfile_reader: Read file content, content: Mapping {"file_name": String("example0017686312.dbin"), "total_bytes": Number(1508787), "chunk_size": Number(1048576), "chunk_hashes": Sequence [String("yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI="), String("vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c=")]} - at subfile-exchange/src/subfile_reader.rs:59 - - 2023-11-13T20:57:45.426529Z TRACE crate::subfile_client: Download chunk index, i: 0 - at subfile-exchange/src/subfile_client/mod.rs:140 - - 2023-11-13T20:57:45.426912Z TRACE crate::subfile_client: Download chunk index, i: 1 - at subfile-exchange/src/subfile_client/mod.rs:140 - - 2023-11-13T20:57:45.428446Z DEBUG crate::subfile_client: Make range request, query_endpoint: "http://localhost:5678/subfiles/id/QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", range: "bytes=0-1048575" - at subfile-exchange/src/subfile_client/mod.rs:220 - - 2023-11-13T20:57:45.428707Z DEBUG crate::subfile_client: Make range request, query_endpoint: "http://localhost:5678/subfiles/id/QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", range: "bytes=1048576-1508786" - at subfile-exchange/src/subfile_client/mod.rs:220 - - 2023-11-13T20:57:45.486215Z DEBUG crate::file_hasher: Chunk hash, hash: [191, 27, 225, 27, 22, 159, 177, 171, 252, 216, 173, 16, 107, 156, 16, 160, 148, 174, 246, 237, 228, 234, 254, 114, 77, 246, 129, 45, 195, 205, 203, 231] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:57:45.486334Z DEBUG crate::file_hasher: Chunk hash, hash_str: "vxvhGxafsav82K0Qa5wQoJSu9u3k6v5yTfaBLcPNy+c=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:57:45.519673Z DEBUG crate::file_hasher: Chunk hash, hash: [202, 227, 94, 140, 125, 254, 145, 68, 217, 119, 24, 19, 178, 40, 120, 144, 232, 4, 19, 190, 183, 234, 100, 36, 76, 83, 65, 117, 96, 23, 26, 146] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:57:45.519710Z DEBUG crate::file_hasher: Chunk hash, hash_str: "yuNejH3+kUTZdxgTsih4kOgEE7636mQkTFNBdWAXGpI=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:57:45.519972Z INFO crate::subfile_client: chunk_download_result: () - at subfile-exchange/src/subfile_client/mod.rs:169 - - 2023-11-13T20:57:45.520002Z INFO crate::subfile_client: chunk_download_result: () - at subfile-exchange/src/subfile_client/mod.rs:169 - - 2023-11-13T20:57:45.520485Z DEBUG crate::subfile_client: Download chunk file, info: FileMetaInfo { name: "example-create-17686085.dbin", hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" } - at subfile-exchange/src/subfile_client/mod.rs:109 - - 2023-11-13T20:57:45.520504Z DEBUG crate::subfile_reader: Fetch chunk file from IPFS, ipfs_hash: "QmSgzLLsQzdRAQRA2d7X3wqLEUTBLSbRe2tqv9rJBy7Wqv" - at subfile-exchange/src/subfile_reader.rs:50 - - 2023-11-13T20:57:45.726127Z INFO crate::subfile_reader: Read file content, content: Mapping {"file_name": String("example-create-17686085.dbin"), "total_bytes": Number(1052737), "chunk_size": Number(1048576), "chunk_hashes": Sequence [String("uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI="), String("TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ=")]} - at subfile-exchange/src/subfile_reader.rs:59 - - 2023-11-13T20:57:45.726644Z TRACE crate::subfile_client: Download chunk index, i: 0 - at subfile-exchange/src/subfile_client/mod.rs:140 - - 2023-11-13T20:57:45.726751Z TRACE crate::subfile_client: Download chunk index, i: 1 - at subfile-exchange/src/subfile_client/mod.rs:140 - - 2023-11-13T20:57:45.726820Z DEBUG crate::subfile_client: Make range request, query_endpoint: "http://localhost:5678/subfiles/id/QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", range: "bytes=0-1048575" - at subfile-exchange/src/subfile_client/mod.rs:220 - - 2023-11-13T20:57:45.726821Z DEBUG crate::subfile_client: Make range request, query_endpoint: "http://localhost:5678/subfiles/id/QmakV6VEwnydfe7PXFR3TRxHbhVm7mQRXqVHdsizhTRrGw", range: "bytes=1048576-1052736" - at subfile-exchange/src/subfile_client/mod.rs:220 - - 2023-11-13T20:57:45.734061Z DEBUG crate::file_hasher: Chunk hash, hash: [78, 187, 172, 71, 70, 126, 17, 136, 55, 222, 142, 10, 69, 113, 175, 72, 223, 117, 211, 38, 175, 10, 72, 195, 236, 173, 233, 96, 137, 134, 101, 164] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:57:45.734187Z DEBUG crate::file_hasher: Chunk hash, hash_str: "TrusR0Z+EYg33o4KRXGvSN910yavCkjD7K3pYImGZaQ=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:57:45.812445Z DEBUG crate::file_hasher: Chunk hash, hash: [184, 160, 246, 197, 215, 233, 213, 107, 51, 186, 242, 15, 214, 113, 77, 53, 54, 40, 103, 188, 239, 155, 98, 151, 232, 161, 68, 151, 12, 1, 125, 210] - at subfile-exchange/src/file_hasher.rs:26 - - 2023-11-13T20:57:45.812480Z DEBUG crate::file_hasher: Chunk hash, hash_str: "uKD2xdfp1WszuvIP1nFNNTYoZ7zvm2KX6KFElwwBfdI=" - at subfile-exchange/src/file_hasher.rs:29 - - 2023-11-13T20:57:45.812641Z INFO crate::subfile_client: chunk_download_result: () - at subfile-exchange/src/subfile_client/mod.rs:169 - - 2023-11-13T20:57:45.812663Z INFO crate::subfile_client: chunk_download_result: () - at subfile-exchange/src/subfile_client/mod.rs:169 - - 2023-11-13T20:57:45.813860Z INFO crate::subfile_client: Chunk files download results, completed_files: [Ok(()), Ok(())] - at subfile-exchange/src/subfile_client/mod.rs:96 - - 2023-11-13T20:57:45.814023Z INFO subfile_cli: Download result: Ok( - (), -) - at subfile-exchange/src/main.rs:40 - -➜ subfile-exchange git:(main) ✗ -``` - diff --git a/subfile-exchange/src/config.rs b/subfile-exchange/src/config.rs index 92ce572..de0d425 100644 --- a/subfile-exchange/src/config.rs +++ b/subfile-exchange/src/config.rs @@ -1,5 +1,6 @@ use clap::{arg, ValueEnum}; use clap::{command, Args, Parser, Subcommand}; +use ethers_core::types::U256; use serde::{Deserialize, Serialize}; use std::fmt; @@ -56,43 +57,38 @@ pub enum Role { Wallet(WalletArgs), } +/// Server enable payments through the staking contract, +/// assume indexer is already registered on the staking registry contract +///1. `allocate` - indexer address, Qm hash in bytes32, token amount, allocation_id, metadata: utils.hexlify(Array(32).fill(0)), allocation_id_proof +///2. `close_allocate` -allocationID: String, poi: BytesLike (0x0 32bytes) +///3. `close_allocate` and then `allocate` +/// receipt validation and storage is handled by the indexer-service framework +/// receipt redemption is handled by indexer-agent +/// +/// Client payments - assume client signer is valid (should work without gateways) +///1. `deposit` - to a sender address and an amount +///2. `depositMany` - to Vec +#[derive(Clone, Debug, Subcommand, Serialize, Deserialize)] +#[group(required = false, multiple = true)] +pub enum OnchainAction { + Allocate(AllocateArgs), + Unallocate(UnallocateArgs), +} + #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] #[group(required = false, multiple = true)] pub struct WalletArgs { - #[arg( - long, - value_name = "HOST", - default_value = "127.0.0.1", - env = "HOST", - help = "Subfile server host" - )] - pub host: Option, - #[arg( - long, - value_name = "PORT", - default_value = "5678", - env = "PORT", - help = "Subfile server port" - )] - pub port: Option, - #[clap( - long, - value_name = "KEY", - value_parser = parse_key, - env = "PRIVATE_KEY", - hide_env_values = true, - help = "Private key to the Graphcast ID wallet (Precendence over mnemonics)", - )] - pub private_key: Option, + #[clap(subcommand)] + pub action: Option, #[clap( long, value_name = "KEY", value_parser = parse_key, env = "MNEMONIC", hide_env_values = true, - help = "Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of private key or mnemonic is needed)", + help = "Mnemonic to the Indexer operator wallet (first address of the wallet is used", )] - pub mnemonic: Option, + pub mnemonic: String, #[clap( long, value_name = "provider_url", @@ -100,14 +96,6 @@ pub struct WalletArgs { help = "Blockchain provider endpoint" )] pub provider: String, - //TODO: chain id should be resolvable through provider - // #[clap( - // long, - // value_name = "chain_id", - // env = "CHAIN_ID", - // help = "Protocol network's Chain ID" - // )] - // pub chain_id: u64, #[clap( long, value_name = "verifier", @@ -318,6 +306,51 @@ pub struct PublisherArgs { pub chain_id: String, } +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = false, multiple = true)] +pub struct AllocateArgs { + #[clap( + long, + value_name = "tokens", + env = "TOKENS", + help = "Token amount to allocate" + )] + pub tokens: U256, + #[clap( + long, + value_name = "deployment_ipfs", + env = "DEPLOYMENT_IPFS", + help = "Deployment IPFS hash to allocate" + )] + pub deployment_ipfs: String, + #[clap( + long, + value_name = "epoch", + env = "EPOCH", + help = "Epoch field to generate unique allocation id (Should be auto-resolve through network query)" + )] + pub epoch: u64, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = false, multiple = true)] +pub struct UnallocateArgs { + #[clap( + long, + value_name = "deployment_ipfs", + env = "DEPLOYMENT_IPFS", + help = "Deployment IPFS hash to unallocate" + )] + pub deployment_ipfs: String, + #[clap( + long, + value_name = "allocation_id", + env = "ALLOCATION_ID", + help = "Deployment IPFS hash to unallocate" + )] + pub allocation_id: String, +} + #[allow(unused)] #[derive(ValueEnum, Clone, Debug, Serialize, Deserialize, Default)] pub enum FileType { diff --git a/subfile-exchange/src/main.rs b/subfile-exchange/src/main.rs index 4d20457..ef66f3b 100644 --- a/subfile-exchange/src/main.rs +++ b/subfile-exchange/src/main.rs @@ -1,10 +1,11 @@ use dotenv::dotenv; use subfile_exchange::{ - config::{Cli, Role}, + config::{Cli, OnchainAction, Role}, publisher::SubfilePublisher, subfile::ipfs::IpfsClient, subfile_client::SubfileDownloader, + transaction_manager::TransactionManager, }; #[tokio::main] @@ -59,17 +60,31 @@ async fn main() { "Use the provided wallet to send transactions" ); - // Server enable payments through the staking contract, - // assume indexer is already registered on the staking registry contract - //1. `allocate` - indexer address, Qm hash in bytes32, token amount, allocation_id, metadata: utils.hexlify(Array(32).fill(0)), allocation_id_proof - //2. `close_allocate` -allocationID: String, poi: BytesLike (0x0 32bytes) - //3. `close_allocate` and then `allocate` - // receipt validation and storage is handled by the indexer-service framework - // receipt redemption is handled by indexer-agent + let transaction_manager = TransactionManager::new(wallet_args) + .await + .expect("Cannot initate transaction manager"); - // Client payments - assume client signer is valid (should work without gateways) - //1. `deposit` - to a sender address and an amount - //2. `depositMany` - to Vec + match transaction_manager.args.action.clone() { + Some(OnchainAction::Allocate(allocate_args)) => { + let (allocation_id, tx_receipt) = transaction_manager + .allocate( + &allocate_args.deployment_ipfs, + allocate_args.tokens, + allocate_args.epoch, + ) + .await + .unwrap(); + tracing::info!( + allocation_id = tracing::field::debug(&allocation_id), + tx_receipt = tracing::field::debug(&tx_receipt), + "Allocation transaction finished" + ); + } + Some(OnchainAction::Unallocate(_unallocate_args)) => { + todo!() + } + None => {} + } } } } diff --git a/subfile-exchange/src/subfile_client/mod.rs b/subfile-exchange/src/subfile_client/mod.rs index 753d53a..2da9326 100644 --- a/subfile-exchange/src/subfile_client/mod.rs +++ b/subfile-exchange/src/subfile_client/mod.rs @@ -21,7 +21,7 @@ use crate::subfile::{ Subfile, }; use crate::subfile_finder::{IndexerEndpoint, SubfileFinder}; -use crate::transaction_manager::TransactionManager; + use crate::util::build_wallet; use self::signer::{ReceiptSigner, TapReceipt}; @@ -58,11 +58,6 @@ impl SubfileDownloader { let wallet = build_wallet(&args.mnemonic).expect("Mnemonic build wallet"); //TODO: Factor away from client, Transactions could be a separate entity - let transaction_manager = TransactionManager::new(&args.provider, wallet.clone()).await; - tracing::info!( - transaction_manager = tracing::field::debug(&transaction_manager), - "transaction_manager" - ); let signing_key = wallet.signer().to_bytes(); let secp256k1_private_key = SecretKey::from_slice(&signing_key).expect("Private key from wallet"); diff --git a/subfile-exchange/src/transaction_manager/mod.rs b/subfile-exchange/src/transaction_manager/mod.rs index ba3d906..5977f79 100644 --- a/subfile-exchange/src/transaction_manager/mod.rs +++ b/subfile-exchange/src/transaction_manager/mod.rs @@ -7,10 +7,12 @@ use std::fs; use std::str::FromStr; use std::sync::Arc; +use crate::config::WalletArgs; +use crate::transaction_manager::staking::L2Staking; +use crate::util::build_wallet; + pub mod staking; -/// Contracts: (contract name, contract object) -pub type NetworkContracts = HashMap; /// Contracts: (contract name, contract address) pub type ContractAddresses = HashMap; /// Client with provider endpoint and a wallet @@ -20,36 +22,46 @@ pub type ContractClient = SignerMiddleware, Wallet>; #[allow(dead_code)] pub struct TransactionManager { client: Arc, - contracts: NetworkContracts, + staking_contract: L2Staking, Wallet>>>, + pub args: WalletArgs, } impl TransactionManager { // Constructor to create a new instance - pub async fn new( - provider_url: &str, - wallet: Wallet, - ) -> Result { - let provider = Provider::::try_from(provider_url)?; + pub async fn new(args: WalletArgs) -> Result { + tracing::info!("Initialize transaction manager"); + let provider = Provider::::try_from(&args.provider)?; let chain_id = provider.get_chainid().await?; - let wallet = wallet.with_chain_id(provider.get_chainid().await.unwrap().as_u64()); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = build_wallet(&args.mnemonic) + .expect("Mnemonic build wallet") + .with_chain_id(provider.get_chainid().await.unwrap().as_u64()); + let client = Arc::new(SignerMiddleware::new(provider, wallet.clone())); // Access contracts for the specified chain_id let contract_addresses = network_contract_addresses("addresses.json", &chain_id.to_string())?; - // Initiate contract instances - let contracts = NetworkContracts::new(); - // Test reading the function - let value = - staking::controller(&client, *contract_addresses.get("L2Staking").unwrap()).await?; - tracing::debug!("test read - controller value: {:#?}", value); - Ok(TransactionManager { client, contracts }) + // // Test reading the function + // let tokens = U256::from(100000); + // let _value = staking::allocate(&client, *contract_addresses.get("L2Staking").unwrap(), "QmeaPp764FjQjPB66M9ijmQKmLhwBpHQhA7dEbH2FA1j3v", token).await?; + + // let value = + // staking::controller(&client, *contract_addresses.get("L2Staking").unwrap()).await?; + // tracing::debug!("test read - controller value: {:#?}", value); + + let staking_addr = contract_addresses.get("L2Staking").unwrap(); + let staking_contract = L2Staking::new(*staking_addr, Arc::new(client.clone())); + + Ok(TransactionManager { + client, + staking_contract, + args, + }) } } /// Track network contract addresses given an address book in json -fn network_contract_addresses( +pub fn network_contract_addresses( file_path: &str, chain_id: &str, ) -> Result { diff --git a/subfile-exchange/src/transaction_manager/staking.rs b/subfile-exchange/src/transaction_manager/staking.rs index fd36a31..7dcb879 100644 --- a/subfile-exchange/src/transaction_manager/staking.rs +++ b/subfile-exchange/src/transaction_manager/staking.rs @@ -4,18 +4,19 @@ use ethers::providers::{Http, Provider}; use ethers::signers::coins_bip39::English; use ethers::signers::{Signer, Wallet}; use ethers_core::k256::ecdsa::SigningKey; -use ethers_core::types::{Bytes, H160, U256}; +use ethers_core::types::{Bytes, TransactionReceipt, H160, U256}; use ethers_core::utils::keccak256; use hdwallet::{DefaultKeyChain, ExtendedPrivKey}; use std::collections::HashMap; use std::str::FromStr; -use std::sync::Arc; use crate::errors::Error; use crate::transaction_manager::coins_bip39::Mnemonic; use crate::util::{build_wallet, derive_key_pair}; +use super::TransactionManager; + pub type NetworkContracts = HashMap, Wallet>>>; pub type ContractAddresses = HashMap; @@ -24,111 +25,112 @@ pub type ContractClient = SignerMiddleware, Wallet>; abigen!( L2Staking, "abis/L2Staking.json", - // "npm:@graphprotocol/contracts@latest/dist/abis/L2Staking.json", + // "npm:@graphprotocol/contracts@latest/abis/L2Staking.json", event_derives(serde::Deserialize, serde::Serialize) ); -/// Test function to simply call a read fn of a contract -pub async fn controller(client: &ContractClient, contract_addr: H160) -> Result { - let contract = L2Staking::new(contract_addr, Arc::new(client.clone())); - - let value = contract - .controller() - .call() - .await - .map_err(|e| Error::ContractError(e.to_string()))?; - - Ok(value) -} - -/// call staking contract allocate function -pub async fn allocate( - client: &ContractClient, - contract_addr: H160, - deployment: &str, -) -> Result { - //TODO: Start with hardcoding, later add field indexer address to TX manager, tokens to fn params - let contract = L2Staking::new(contract_addr, Arc::new(client.clone())); - let mnemonic = - "culture alcohol unfair success pupil economy stomach dignity beyond absurd client latin"; - let epoch: u64 = 1030; - let existing_ids: Vec = vec![]; - let tokens = U256::from(100000); - let metadata: [u8; 32] = [0; 32]; - - let (allocation_signer, allocation_id) = - unique_allocation_id(mnemonic, epoch, deployment, &existing_ids)?; - let deployment_byte32 = ipfs_hash_to_bytes(deployment)?; - let indexer_address = build_wallet(mnemonic)?.address(); - let proof = allocation_id_proof(&allocation_signer, indexer_address, allocation_id).await?; - - tracing::info!( - dep_bytes = tracing::field::debug(&deployment_byte32), - tokens = tracing::field::debug(&tokens), - allocation_id = tracing::field::debug(&allocation_id), - metadata = tracing::field::debug(&metadata), - proof = tracing::field::debug(&proof), - "allocate params", - ); - - let populated_tx = contract.allocate(deployment_byte32, tokens, allocation_id, metadata, proof); - let estimated_gas = populated_tx - .estimate_gas() - .await - .map_err(|e| Error::ContractError(e.to_string()))?; - tracing::debug!( - estimated_gas = tracing::field::debug(&estimated_gas), - "estimate gas" - ); - - // Attempt to send the populated tx with estimated gas, can later add a slippage - let tx_result = populated_tx - .gas(estimated_gas) - .send() - .await - .map_err(|e| { - // let encoded_error = &e.to_string()[2..]; - // let error_message_hex = &encoded_error[8 + 64..]; - // let bytes = hex::decode(error_message_hex).unwrap(); - // let message = String::from_utf8(bytes).unwrap(); - - Error::ContractError(e.to_string()) - })? - .await - .map_err(|e| Error::ContractError(e.to_string()))?; - // .map_err(|e| Error::ContractError(e.to_string()))?; - tracing::debug!( - value = tracing::field::debug(&tx_result), - "allocate call result" - ); +impl TransactionManager { + /// Test function to simply call a read fn of a contract + pub async fn controller(&self) -> Result { + let value = self + .staking_contract + .controller() + .call() + .await + .map_err(|e| Error::ContractError(e.to_string()))?; - // Can call to double check but probably not necessary - let value = get_allocation(client, contract_addr, allocation_id) - .await - .map_err(|e| Error::ContractError(e.to_string()))?; - tracing::trace!( - value = tracing::field::debug(&value), - "get_allocation call result" - ); + Ok(value) + } - Ok(value) -} + /// call staking contract allocate function + pub async fn allocate( + &self, + deployment: &str, + tokens: U256, + epoch: u64, + ) -> Result<(H160, Option), Error> { + //TODO: Start with hardcoding, later add field indexer address to TX manager, tokens to fn params + let existing_ids: Vec = vec![]; + let metadata: [u8; 32] = [0; 32]; + + let (allocation_signer, allocation_id) = unique_allocation_id( + &self.args.mnemonic.clone(), + epoch, + deployment, + &existing_ids, + )?; + let deployment_byte32 = ipfs_hash_to_bytes(deployment)?; + let indexer_address = build_wallet(&self.args.mnemonic.clone())?.address(); + let proof = allocation_id_proof(&allocation_signer, indexer_address, allocation_id).await?; + + tracing::info!( + dep_bytes = tracing::field::debug(&deployment_byte32), + tokens = tracing::field::debug(&self.args.action), + allocation_id = tracing::field::debug(&allocation_id), + metadata = tracing::field::debug(&metadata), + proof = tracing::field::debug(&proof), + "allocate params", + ); + + let populated_tx = self.staking_contract.allocate( + deployment_byte32, + tokens, + allocation_id, + metadata, + proof, + ); + let estimated_gas = populated_tx + .estimate_gas() + .await + .map_err(|e| Error::ContractError(e.to_string()))?; + tracing::debug!( + estimated_gas = tracing::field::debug(&estimated_gas), + "estimate gas" + ); + + // Attempt to send the populated tx with estimated gas, can later add a slippage + let tx_result = populated_tx + .gas(estimated_gas) + .send() + .await + .map_err(|e| { + let encoded_error = &e.to_string()[2..]; + let error_message_hex = &encoded_error[8 + 64..]; + let bytes = hex::decode(error_message_hex).unwrap(); + let message = String::from_utf8(bytes).unwrap(); + tracing::error!(message); + + Error::ContractError(e.to_string()) + })? + .await + .map_err(|e| { + let encoded_error = &e.to_string()[2..]; + let error_message_hex = &encoded_error[8 + 64..]; + let bytes = hex::decode(error_message_hex).unwrap(); + let message = String::from_utf8(bytes).unwrap(); + tracing::error!(message); + + Error::ContractError(e.to_string()) + })?; + tracing::debug!( + value = tracing::field::debug(&tx_result), + "allocate call result" + ); + Ok((allocation_id, tx_result)) + } -/// call staking contract allocate function -pub async fn get_allocation( - client: &ContractClient, - contract_addr: H160, - allocation_id: H160, -) -> Result { - let contract = L2Staking::new(contract_addr, Arc::new(client.clone())); - let value = contract - .get_allocation(allocation_id) - .call() - .await - .map_err(|e| Error::ContractError(e.to_string()))?; - tracing::info!(value = tracing::field::debug(&value), "allocate call value"); + /// call staking contract allocate function + pub async fn get_allocation(&self, allocation_id: H160) -> Result { + let value = self + .staking_contract + .get_allocation(allocation_id) + .call() + .await + .map_err(|e| Error::ContractError(e.to_string()))?; + tracing::info!(value = tracing::field::debug(&value), "allocate call value"); - Ok(value) + Ok(value) + } } /// create packed keccak hash for allocation id as proof @@ -211,8 +213,6 @@ fn unique_allocation_id( #[cfg(test)] mod tests { - use crate::transaction_manager::network_contract_addresses; - use super::*; #[test] @@ -264,35 +264,4 @@ mod tests { // assert_eq!(id, "0x6a39615c9f35ef68b4a99584c0566a1fcdd67d0a"); // assert!(proof.to_string() == "0x7f47ee3a4e614c43352f8920e81371b0aa2298bb99ed6fc8eb4ed54f0bb1954c1463abd7a86a21185671f9a827c5ecaa1f509320cf9ee53f77338475018d1d931c") } - - #[tokio::test] - #[ignore] - async fn test_allocate() { - let indexer_mnemonic = - "sheriff obscure trick beauty army fat wink legal flee leader section suit"; - let _deployment = "QmWAsLViTdCbs9zbejzmRndpZpNXU97CzeLJwdZKuvCUdF"; - println!("start"); - let provider = Provider::::try_from( - "https://arbitrum-goerli.infura.io/v3/dc1a550f824a4c6aa428a3376f983145", - ) - .unwrap(); - println!("start provider"); - let wallet = build_wallet(indexer_mnemonic).expect("Mnemonic build wallet"); - println!("start wallet"); - let client = SignerMiddleware::new(provider, wallet); - println!("start client"); - - // Access contracts for the specified chain_id - let contract_addresses = network_contract_addresses("../addresses.json", "421614").unwrap(); - println!("start contract address"); - - let deployment = "QmeaPp764FjQjPB66M9ijmQKmLhwBpHQhA7dEbH2FA1j3v"; - let staking_addr = contract_addresses.get("L2Staking").unwrap(); - println!("start l2 sataking address"); - - let res = allocate(&client, *staking_addr, deployment).await; - println!("finish: {:#?}", res); - - assert!(res.is_ok()) - } } diff --git a/subfile-exchange/tests/allocate.rs b/subfile-exchange/tests/allocate.rs new file mode 100644 index 0000000..a365006 --- /dev/null +++ b/subfile-exchange/tests/allocate.rs @@ -0,0 +1,64 @@ +#[cfg(test)] +mod tests { + + use chrono::Utc; + use ethers_core::types::U256; + use subfile_exchange::{ + config::{AllocateArgs, OnchainAction, WalletArgs}, + transaction_manager::TransactionManager, + }; + + #[tokio::test] + #[ignore] + async fn test_allocate() { + // 1. Basic setup; const + std::env::set_var("RUST_LOG", "off,subfile_exchange=debug,allocate=trace"); + subfile_exchange::config::init_tracing(String::from("pretty")).unwrap(); + let wallet_args = WalletArgs { + mnemonic: String::from( + "sheriff obscure trick beauty army fat wink legal flee leader section suit", + ), + provider: String::from("https://arbitrum-sepolia.infura.io/v3/aaaaaaaaaaaaaaaaaaaa"), + verifier: Some(String::from("0xfC24cE7a4428A6B89B52645243662A02BA734ECF")), + action: Some(subfile_exchange::config::OnchainAction::Allocate( + AllocateArgs { + tokens: U256::from(100), + deployment_ipfs: String::from("QmeKabcCQBtgU6QjM3rp3w6pDHFW4r54ee89nGdhuyDuhi"), + epoch: Utc::now().timestamp() as u64, + }, + )), + }; + + let transaction_manager = TransactionManager::new(wallet_args).await.unwrap(); + + // 2. Send allocate tx + let action = transaction_manager.args.action.clone().unwrap(); + let allocate_args = if let OnchainAction::Allocate(args) = action { + args.clone() + } else { + panic!("No allocate args") + }; + let (allocation_id, tx_receipt) = transaction_manager + .allocate( + &allocate_args.deployment_ipfs, + allocate_args.tokens, + allocate_args.epoch, + ) + .await + .unwrap(); + tracing::trace!( + allocation_id = tracing::field::debug(&allocation_id), + tx_receipt = tracing::field::debug(&tx_receipt), + "get_allocation call result" + ); + + // 3. Validate allocation + let allocation = transaction_manager.get_allocation(allocation_id).await; + tracing::trace!( + allocation = tracing::field::debug(&allocation), + "get_allocation call result" + ); + + assert!(allocation.is_ok()); + } +}