diff --git a/apps/fortuna/.sqlx/query-03901bcfb28b127d99fe8a53e480b88336dd2aab632411114f02ce8dd8fe07e8.json b/apps/fortuna/.sqlx/query-03901bcfb28b127d99fe8a53e480b88336dd2aab632411114f02ce8dd8fe07e8.json new file mode 100644 index 0000000000..28642818e3 --- /dev/null +++ b/apps/fortuna/.sqlx/query-03901bcfb28b127d99fe8a53e480b88336dd2aab632411114f02ce8dd8fe07e8.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE request SET state = ?, last_updated_at = ?, info = ?, provider_random_number = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'", + "describe": { + "columns": [], + "parameters": { + "Right": 8 + }, + "nullable": [] + }, + "hash": "03901bcfb28b127d99fe8a53e480b88336dd2aab632411114f02ce8dd8fe07e8" +} diff --git a/apps/fortuna/.sqlx/query-16635b3d9c6f9b743614e0e08bfa2b26d7ec6346f0323d9f16b98c32fd9a91f6.json b/apps/fortuna/.sqlx/query-16635b3d9c6f9b743614e0e08bfa2b26d7ec6346f0323d9f16b98c32fd9a91f6.json deleted file mode 100644 index 242f9ccf84..0000000000 --- a/apps/fortuna/.sqlx/query-16635b3d9c6f9b743614e0e08bfa2b26d7ec6346f0323d9f16b98c32fd9a91f6.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT INTO request(chain_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 10 - }, - "nullable": [] - }, - "hash": "16635b3d9c6f9b743614e0e08bfa2b26d7ec6346f0323d9f16b98c32fd9a91f6" -} diff --git a/apps/fortuna/.sqlx/query-b848d03ffc893e1719d364beb32976ef879e79727c660c973bdad670082f5c36.json b/apps/fortuna/.sqlx/query-392da9e5fdd212a4a665c86e5fc6d4f619355294490248e656ad0fc97a252471.json similarity index 78% rename from apps/fortuna/.sqlx/query-b848d03ffc893e1719d364beb32976ef879e79727c660c973bdad670082f5c36.json rename to apps/fortuna/.sqlx/query-392da9e5fdd212a4a665c86e5fc6d4f619355294490248e656ad0fc97a252471.json index 1dc670dd6a..4f145288ec 100644 --- a/apps/fortuna/.sqlx/query-b848d03ffc893e1719d364beb32976ef879e79727c660c973bdad670082f5c36.json +++ b/apps/fortuna/.sqlx/query-392da9e5fdd212a4a665c86e5fc6d4f619355294490248e656ad0fc97a252471.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?", + "query": "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?", "describe": { "columns": [ { @@ -9,73 +9,88 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], "parameters": { - "Right": 3 + "Right": 4 }, "nullable": [ false, @@ -88,11 +103,14 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, - "hash": "b848d03ffc893e1719d364beb32976ef879e79727c660c973bdad670082f5c36" + "hash": "392da9e5fdd212a4a665c86e5fc6d4f619355294490248e656ad0fc97a252471" } diff --git a/apps/fortuna/.sqlx/query-9d7448c9bbad50d6242dfc0ba7d5ad4837201a1585bd56cc9a65fe75d0fa5952.json b/apps/fortuna/.sqlx/query-4c8c05ec08e128d847faafdd3d79fa50da70066f30b74f354e5d3a843ba6a2c0.json similarity index 55% rename from apps/fortuna/.sqlx/query-9d7448c9bbad50d6242dfc0ba7d5ad4837201a1585bd56cc9a65fe75d0fa5952.json rename to apps/fortuna/.sqlx/query-4c8c05ec08e128d847faafdd3d79fa50da70066f30b74f354e5d3a843ba6a2c0.json index f0e43099a7..2fc58d302c 100644 --- a/apps/fortuna/.sqlx/query-9d7448c9bbad50d6242dfc0ba7d5ad4837201a1585bd56cc9a65fe75d0fa5952.json +++ b/apps/fortuna/.sqlx/query-4c8c05ec08e128d847faafdd3d79fa50da70066f30b74f354e5d3a843ba6a2c0.json @@ -1,12 +1,12 @@ { "db_name": "SQLite", - "query": "UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?", + "query": "UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number =?, gas_used = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?", "describe": { "columns": [], "parameters": { - "Right": 9 + "Right": 10 }, "nullable": [] }, - "hash": "9d7448c9bbad50d6242dfc0ba7d5ad4837201a1585bd56cc9a65fe75d0fa5952" + "hash": "4c8c05ec08e128d847faafdd3d79fa50da70066f30b74f354e5d3a843ba6a2c0" } diff --git a/apps/fortuna/.sqlx/query-ba011bb5690ad6821689bec939c5303c8619b6302ef33145db3bf62259492783.json b/apps/fortuna/.sqlx/query-78be8c62d5eb764995221f927b0f166e38d6fba8eb8fddb07f50c572fd27b4e2.json similarity index 74% rename from apps/fortuna/.sqlx/query-ba011bb5690ad6821689bec939c5303c8619b6302ef33145db3bf62259492783.json rename to apps/fortuna/.sqlx/query-78be8c62d5eb764995221f927b0f166e38d6fba8eb8fddb07f50c572fd27b4e2.json index 71734d95dd..1ba9d570d2 100644 --- a/apps/fortuna/.sqlx/query-ba011bb5690ad6821689bec939c5303c8619b6302ef33145db3bf62259492783.json +++ b/apps/fortuna/.sqlx/query-78be8c62d5eb764995221f927b0f166e38d6fba8eb8fddb07f50c572fd27b4e2.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT * FROM request WHERE chain_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?", + "query": "SELECT * FROM request WHERE network_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?", "describe": { "columns": [ { @@ -9,73 +9,88 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], "parameters": { - "Right": 4 + "Right": 5 }, "nullable": [ false, @@ -88,11 +103,14 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, - "hash": "ba011bb5690ad6821689bec939c5303c8619b6302ef33145db3bf62259492783" + "hash": "78be8c62d5eb764995221f927b0f166e38d6fba8eb8fddb07f50c572fd27b4e2" } diff --git a/apps/fortuna/.sqlx/query-8cd10cd5839b81bd9538aeb10fdfd27c6e36baf5d90a4fb9e61718f021812710.json b/apps/fortuna/.sqlx/query-8cd10cd5839b81bd9538aeb10fdfd27c6e36baf5d90a4fb9e61718f021812710.json index cb9f834296..05031bec35 100644 --- a/apps/fortuna/.sqlx/query-8cd10cd5839b81bd9538aeb10fdfd27c6e36baf5d90a4fb9e61718f021812710.json +++ b/apps/fortuna/.sqlx/query-8cd10cd5839b81bd9538aeb10fdfd27c6e36baf5d90a4fb9e61718f021812710.json @@ -9,68 +9,83 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], @@ -88,10 +103,13 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, "hash": "8cd10cd5839b81bd9538aeb10fdfd27c6e36baf5d90a4fb9e61718f021812710" diff --git a/apps/fortuna/.sqlx/query-905dbc91cd5319537c5c194277d531689ac5c1338396414467496d0f50ddc3f0.json b/apps/fortuna/.sqlx/query-905dbc91cd5319537c5c194277d531689ac5c1338396414467496d0f50ddc3f0.json index 44163d3b61..0bc128fb33 100644 --- a/apps/fortuna/.sqlx/query-905dbc91cd5319537c5c194277d531689ac5c1338396414467496d0f50ddc3f0.json +++ b/apps/fortuna/.sqlx/query-905dbc91cd5319537c5c194277d531689ac5c1338396414467496d0f50ddc3f0.json @@ -9,68 +9,83 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], @@ -88,10 +103,13 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, "hash": "905dbc91cd5319537c5c194277d531689ac5c1338396414467496d0f50ddc3f0" diff --git a/apps/fortuna/.sqlx/query-a62e094cee65ae58bd12ce7d3e7df44f5aca31520d1ceced83f492945e850764.json b/apps/fortuna/.sqlx/query-a62e094cee65ae58bd12ce7d3e7df44f5aca31520d1ceced83f492945e850764.json index bf72d49ded..39ff0484e7 100644 --- a/apps/fortuna/.sqlx/query-a62e094cee65ae58bd12ce7d3e7df44f5aca31520d1ceced83f492945e850764.json +++ b/apps/fortuna/.sqlx/query-a62e094cee65ae58bd12ce7d3e7df44f5aca31520d1ceced83f492945e850764.json @@ -9,68 +9,83 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], @@ -88,10 +103,13 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, "hash": "a62e094cee65ae58bd12ce7d3e7df44f5aca31520d1ceced83f492945e850764" diff --git a/apps/fortuna/.sqlx/query-b0d9afebb3825c3509ad80e5ebab5d72360326593407518770fe537ac3da1e10.json b/apps/fortuna/.sqlx/query-b0d9afebb3825c3509ad80e5ebab5d72360326593407518770fe537ac3da1e10.json new file mode 100644 index 0000000000..2888b76e36 --- /dev/null +++ b/apps/fortuna/.sqlx/query-b0d9afebb3825c3509ad80e5ebab5d72360326593407518770fe537ac3da1e10.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO request(chain_id, network_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender, gas_limit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 12 + }, + "nullable": [] + }, + "hash": "b0d9afebb3825c3509ad80e5ebab5d72360326593407518770fe537ac3da1e10" +} diff --git a/apps/fortuna/.sqlx/query-b2baa9f9d46f873a3a7117c38ecab09f56082c5267dbf5180f39c608b6262f5a.json b/apps/fortuna/.sqlx/query-b2baa9f9d46f873a3a7117c38ecab09f56082c5267dbf5180f39c608b6262f5a.json deleted file mode 100644 index 1c7b13b136..0000000000 --- a/apps/fortuna/.sqlx/query-b2baa9f9d46f873a3a7117c38ecab09f56082c5267dbf5180f39c608b6262f5a.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "UPDATE request SET state = ?, last_updated_at = ?, info = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'", - "describe": { - "columns": [], - "parameters": { - "Right": 7 - }, - "nullable": [] - }, - "hash": "b2baa9f9d46f873a3a7117c38ecab09f56082c5267dbf5180f39c608b6262f5a" -} diff --git a/apps/fortuna/.sqlx/query-795b81369e5b039cfa38df06bd6c8da8610d84f19a294fb8d3a8370a47a3f241.json b/apps/fortuna/.sqlx/query-c9e3089b1ffd52d20cfcd89e71e051c0f351643dce9be4b84b6343909c816c22.json similarity index 77% rename from apps/fortuna/.sqlx/query-795b81369e5b039cfa38df06bd6c8da8610d84f19a294fb8d3a8370a47a3f241.json rename to apps/fortuna/.sqlx/query-c9e3089b1ffd52d20cfcd89e71e051c0f351643dce9be4b84b6343909c816c22.json index dc90b07db1..6129528669 100644 --- a/apps/fortuna/.sqlx/query-795b81369e5b039cfa38df06bd6c8da8610d84f19a294fb8d3a8370a47a3f241.json +++ b/apps/fortuna/.sqlx/query-c9e3089b1ffd52d20cfcd89e71e051c0f351643dce9be4b84b6343909c816c22.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT * FROM request WHERE sender = ? AND chain_id = ?", + "query": "SELECT * FROM request WHERE sequence = ? AND network_id = ?", "describe": { "columns": [ { @@ -9,68 +9,83 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], @@ -88,11 +103,14 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, - "hash": "795b81369e5b039cfa38df06bd6c8da8610d84f19a294fb8d3a8370a47a3f241" + "hash": "c9e3089b1ffd52d20cfcd89e71e051c0f351643dce9be4b84b6343909c816c22" } diff --git a/apps/fortuna/.sqlx/query-7d4365a9cb7c9ec16fd4ca60e1d558419954a0326b29180fa9943605813f04e6.json b/apps/fortuna/.sqlx/query-f58bdd3e0ecb30f35356c22e9ab1b3802f8eebda455efabc18d30f02d23787b7.json similarity index 78% rename from apps/fortuna/.sqlx/query-7d4365a9cb7c9ec16fd4ca60e1d558419954a0326b29180fa9943605813f04e6.json rename to apps/fortuna/.sqlx/query-f58bdd3e0ecb30f35356c22e9ab1b3802f8eebda455efabc18d30f02d23787b7.json index 1065eef059..32cbf3b9c5 100644 --- a/apps/fortuna/.sqlx/query-7d4365a9cb7c9ec16fd4ca60e1d558419954a0326b29180fa9943605813f04e6.json +++ b/apps/fortuna/.sqlx/query-f58bdd3e0ecb30f35356c22e9ab1b3802f8eebda455efabc18d30f02d23787b7.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT * FROM request WHERE sequence = ? AND chain_id = ?", + "query": "SELECT * FROM request WHERE sender = ? AND network_id = ?", "describe": { "columns": [ { @@ -9,68 +9,83 @@ "type_info": "Text" }, { - "name": "provider", + "name": "network_id", "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "provider", + "ordinal": 2, "type_info": "Text" }, { "name": "sequence", - "ordinal": 2, + "ordinal": 3, "type_info": "Integer" }, { "name": "created_at", - "ordinal": 3, + "ordinal": 4, "type_info": "Datetime" }, { "name": "last_updated_at", - "ordinal": 4, + "ordinal": 5, "type_info": "Datetime" }, { "name": "state", - "ordinal": 5, + "ordinal": 6, "type_info": "Text" }, { "name": "request_block_number", - "ordinal": 6, + "ordinal": 7, "type_info": "Integer" }, { "name": "request_tx_hash", - "ordinal": 7, + "ordinal": 8, "type_info": "Text" }, { "name": "user_random_number", - "ordinal": 8, + "ordinal": 9, "type_info": "Text" }, { "name": "sender", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" }, { "name": "reveal_block_number", - "ordinal": 10, + "ordinal": 11, "type_info": "Integer" }, { "name": "reveal_tx_hash", - "ordinal": 11, + "ordinal": 12, "type_info": "Text" }, { "name": "provider_random_number", - "ordinal": 12, + "ordinal": 13, "type_info": "Text" }, { "name": "info", - "ordinal": 13, + "ordinal": 14, + "type_info": "Text" + }, + { + "name": "gas_used", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "gas_limit", + "ordinal": 16, "type_info": "Text" } ], @@ -88,11 +103,14 @@ false, false, false, + false, + true, + true, true, true, true, - true + false ] }, - "hash": "7d4365a9cb7c9ec16fd4ca60e1d558419954a0326b29180fa9943605813f04e6" + "hash": "f58bdd3e0ecb30f35356c22e9ab1b3802f8eebda455efabc18d30f02d23787b7" } diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index 79b26d9739..99205a5472 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "7.6.1" +version = "7.6.2" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index cc3115a15a..18b1ef36d4 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "7.6.1" +version = "7.6.2" edition = "2021" [lib] diff --git a/apps/fortuna/migrations/20250502164500_init.up.sql b/apps/fortuna/migrations/20250502164500_init.up.sql index 0e433ba1a9..38bf13ea99 100644 --- a/apps/fortuna/migrations/20250502164500_init.up.sql +++ b/apps/fortuna/migrations/20250502164500_init.up.sql @@ -1,6 +1,7 @@ -- we use VARCHAR(40) for addresses and VARCHAR(64) for tx_hashes and 32 byte numbers CREATE TABLE request( chain_id VARCHAR(20) NOT NULL, + network_id INTEGER NOT NULL, provider VARCHAR(40) NOT NULL, sequence INTEGER NOT NULL, created_at DATETIME NOT NULL, @@ -14,11 +15,11 @@ CREATE TABLE request( reveal_tx_hash VARCHAR(64), provider_random_number VARCHAR(64), info TEXT, - PRIMARY KEY (chain_id, sequence, provider, request_tx_hash) + PRIMARY KEY (network_id, sequence, provider, request_tx_hash) ); CREATE INDEX idx_request_sequence ON request (sequence); -CREATE INDEX idx_request_chain_id_created_at ON request (chain_id, created_at); +CREATE INDEX idx_request_network_id_created_at ON request (network_id, created_at); CREATE INDEX idx_request_created_at ON request (created_at); CREATE INDEX idx_request_request_tx_hash ON request (request_tx_hash) WHERE request_tx_hash IS NOT NULL; CREATE INDEX idx_request_reveal_tx_hash ON request (reveal_tx_hash) WHERE reveal_tx_hash IS NOT NULL; diff --git a/apps/fortuna/migrations/20250521203448_gas.down.sql b/apps/fortuna/migrations/20250521203448_gas.down.sql new file mode 100644 index 0000000000..a031cd4767 --- /dev/null +++ b/apps/fortuna/migrations/20250521203448_gas.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE request +DROP COLUMN gas_used; +ALTER TABLE request +DROP COLUMN gas_limit; diff --git a/apps/fortuna/migrations/20250521203448_gas.up.sql b/apps/fortuna/migrations/20250521203448_gas.up.sql new file mode 100644 index 0000000000..6604e8742f --- /dev/null +++ b/apps/fortuna/migrations/20250521203448_gas.up.sql @@ -0,0 +1,5 @@ +-- U256 max value is 78 digits, so 100 is a safe upper bound +ALTER TABLE request +ADD COLUMN gas_used VARCHAR(100); +ALTER TABLE request +ADD COLUMN gas_limit VARCHAR(100) NOT NULL; diff --git a/apps/fortuna/src/api.rs b/apps/fortuna/src/api.rs index eed5cab3fa..9334cb48f1 100644 --- a/apps/fortuna/src/api.rs +++ b/apps/fortuna/src/api.rs @@ -33,6 +33,7 @@ mod ready; mod revelation; pub type ChainId = String; +pub type NetworkId = u64; #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)] pub struct RequestLabel { @@ -86,6 +87,9 @@ impl ApiState { pub struct BlockchainState { /// The chain id for this blockchain, useful for logging pub id: ChainId, + /// The network id for this blockchain + /// Obtained from the response of eth_chainId rpc call + pub network_id: u64, /// The hash chain(s) required to serve random numbers for this blockchain pub state: Arc, /// The contract that the server is fulfilling requests for. @@ -239,6 +243,7 @@ mod test { let eth_state = BlockchainState { id: "ethereum".into(), + network_id: 1, state: ETH_CHAIN.clone(), contract: eth_read.clone(), provider_address: PROVIDER, @@ -252,6 +257,7 @@ mod test { let avax_state = BlockchainState { id: "avalanche".into(), + network_id: 43114, state: AVAX_CHAIN.clone(), contract: avax_read.clone(), provider_address: PROVIDER, diff --git a/apps/fortuna/src/api/explorer.rs b/apps/fortuna/src/api/explorer.rs index 7b278acdbf..dc7c0efa04 100644 --- a/apps/fortuna/src/api/explorer.rs +++ b/apps/fortuna/src/api/explorer.rs @@ -1,6 +1,6 @@ use { crate::{ - api::{ChainId, RestError}, + api::{ApiBlockChainState, NetworkId, RestError}, history::RequestStatus, }, axum::{ @@ -16,24 +16,30 @@ use { #[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)] #[into_params(parameter_in=Query)] pub struct ExplorerQueryParams { - /// Only return logs that are newer or equal to this timestamp. + /// Only return logs that are newer or equal to this timestamp. Timestamp is in ISO 8601 format with UTC timezone. #[param(value_type = Option, example = "2023-10-01T00:00:00Z")] pub min_timestamp: Option>, - /// Only return logs that are older or equal to this timestamp. + /// Only return logs that are older or equal to this timestamp. Timestamp is in ISO 8601 format with UTC timezone. #[param(value_type = Option, example = "2033-10-01T00:00:00Z")] pub max_timestamp: Option>, /// The query string to search for. This can be a transaction hash, sender address, or sequence number. pub query: Option, - #[param(value_type = Option)] - /// The chain ID to filter the results by. - pub chain_id: Option, + /// The network ID to filter the results by. + #[param(value_type = Option)] + pub network_id: Option, + /// The maximum number of logs to return. Max value is 1000. + #[param(default = 1000)] + pub limit: Option, + /// The offset to start returning logs from. + #[param(default = 0)] + pub offset: Option, } const LOG_RETURN_LIMIT: u64 = 1000; /// Returns the logs of all requests captured by the keeper. /// -/// This endpoint allows you to filter the logs by a specific chain ID, a query string (which can be a transaction hash, sender address, or sequence number), and a time range. +/// This endpoint allows you to filter the logs by a specific network ID, a query string (which can be a transaction hash, sender address, or sequence number), and a time range. /// This is useful for debugging and monitoring the requests made to the Entropy contracts on various chains. #[utoipa::path( get, @@ -45,11 +51,25 @@ pub async fn explorer( State(state): State, Query(query_params): Query, ) -> anyhow::Result>, RestError> { - if let Some(chain_id) = &query_params.chain_id { - if !state.chains.read().await.contains_key(chain_id) { + if let Some(network_id) = &query_params.network_id { + if !state + .chains + .read() + .await + .iter() + .any(|(_, state)| match state { + ApiBlockChainState::Uninitialized => false, + ApiBlockChainState::Initialized(state) => state.network_id == *network_id, + }) + { return Err(RestError::InvalidChainId); } } + if let Some(limit) = query_params.limit { + if limit > LOG_RETURN_LIMIT || limit == 0 { + return Err(RestError::InvalidQueryString); + } + } if let Some(query) = query_params.query { if let Ok(tx_hash) = TxHash::from_str(&query) { return Ok(Json( @@ -64,7 +84,7 @@ pub async fn explorer( return Ok(Json( state .history - .get_requests_by_sender(sender, query_params.chain_id) + .get_requests_by_sender(sender, query_params.network_id) .await .map_err(|_| RestError::TemporarilyUnavailable)?, )); @@ -73,7 +93,7 @@ pub async fn explorer( return Ok(Json( state .history - .get_requests_by_sequence(sequence_number, query_params.chain_id) + .get_requests_by_sequence(sequence_number, query_params.network_id) .await .map_err(|_| RestError::TemporarilyUnavailable)?, )); @@ -84,8 +104,9 @@ pub async fn explorer( state .history .get_requests_by_time( - query_params.chain_id, - LOG_RETURN_LIMIT, + query_params.network_id, + query_params.limit.unwrap_or(LOG_RETURN_LIMIT), + query_params.offset.unwrap_or(0), query_params.min_timestamp, query_params.max_timestamp, ) diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index e2747c3737..aaa92352dc 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -160,17 +160,17 @@ impl SignablePythContractInner { } } - pub async fn from_config_and_provider( + pub fn from_config_and_provider_and_network_id( chain_config: &EthereumConfig, private_key: &str, provider: Provider, + network_id: u64, ) -> Result> { - let chain_id = provider.get_chainid().await?; let gas_oracle = EthProviderOracle::new(provider.clone(), chain_config.priority_fee_multiplier_pct); let wallet__ = private_key .parse::()? - .with_chain_id(chain_id.as_u64()); + .with_chain_id(network_id); let address = wallet__.address(); @@ -185,6 +185,20 @@ impl SignablePythContractInner { )), )) } + + pub async fn from_config_and_provider( + chain_config: &EthereumConfig, + private_key: &str, + provider: Provider, + ) -> Result> { + let network_id = provider.get_chainid().await?.as_u64(); + Self::from_config_and_provider_and_network_id( + chain_config, + private_key, + provider, + network_id, + ) + } } impl SignablePythContract { @@ -195,14 +209,20 @@ impl SignablePythContract { } impl InstrumentedSignablePythContract { - pub async fn from_config( + pub fn from_config( chain_config: &EthereumConfig, private_key: &str, chain_id: ChainId, metrics: Arc, + network_id: u64, ) -> Result { let provider = TracedClient::new(chain_id, &chain_config.geth_rpc_addr, metrics)?; - Self::from_config_and_provider(chain_config, private_key, provider).await + Self::from_config_and_provider_and_network_id( + chain_config, + private_key, + provider, + network_id, + ) } } @@ -232,6 +252,13 @@ impl InstrumentedPythContract { } } +impl PythRandom> { + pub async fn get_network_id(&self) -> Result { + let chain_id = self.client().get_chainid().await?; + Ok(chain_id) + } +} + #[async_trait] impl EntropyReader for PythRandom> { async fn get_request( diff --git a/apps/fortuna/src/command/run.rs b/apps/fortuna/src/command/run.rs index 0faeff38b6..c3bfe66e1e 100644 --- a/apps/fortuna/src/command/run.rs +++ b/apps/fortuna/src/command/run.rs @@ -216,6 +216,11 @@ async fn setup_chain_state( chain_id.clone(), rpc_metrics, )?); + let network_id: u64 = contract + .get_network_id() + .await + .map_err(|e| anyhow!("Failed to get network id: {}. Chain id: {}", &chain_id, e))? + .as_u64(); let mut provider_commitments = chain_config.commitments.clone().unwrap_or_default(); provider_commitments.sort_by(|c1, c2| { c1.original_commitment_sequence_number @@ -295,6 +300,7 @@ async fn setup_chain_state( let state = BlockchainState { id: chain_id.clone(), state: Arc::new(chain_state), + network_id, contract, provider_address: *provider, reveal_delay_blocks: chain_config.reveal_delay_blocks, diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index ffffce9afc..806b845005 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -189,9 +189,6 @@ pub struct EthereumConfig { /// at each specified delay. For example: [5, 10, 20]. #[serde(default = "default_block_delays")] pub block_delays: Vec, - - #[serde(default = "default_retry_previous_blocks")] - pub retry_previous_blocks: u64, } fn default_sync_fee_only_on_register() -> bool { @@ -202,10 +199,6 @@ fn default_block_delays() -> Vec { vec![5] } -fn default_retry_previous_blocks() -> u64 { - 100 -} - fn default_priority_fee_multiplier_pct() -> u64 { 100 } diff --git a/apps/fortuna/src/history.rs b/apps/fortuna/src/history.rs index 9afbe5b075..7c462b6e0c 100644 --- a/apps/fortuna/src/history.rs +++ b/apps/fortuna/src/history.rs @@ -1,8 +1,13 @@ use { - crate::api::ChainId, + crate::api::{ChainId, NetworkId}, anyhow::Result, chrono::{DateTime, NaiveDateTime}, - ethers::{core::utils::hex::ToHex, prelude::TxHash, types::Address}, + ethers::{ + core::utils::hex::ToHex, + prelude::TxHash, + types::{Address, U256}, + utils::keccak256, + }, serde::Serialize, serde_with::serde_as, sqlx::{migrate, Pool, Sqlite, SqlitePool}, @@ -17,6 +22,7 @@ use { pub enum RequestEntryState { Pending, Completed { + /// The block number of the reveal transaction. reveal_block_number: u64, /// The transaction hash of the reveal transaction. #[schema(example = "0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32", value_type = String)] @@ -25,9 +31,22 @@ pub enum RequestEntryState { #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")] #[serde_as(as = "serde_with::hex::Hex")] provider_random_number: [u8; 32], + /// The gas used for the reveal transaction in the smallest unit of the chain. + /// For example, if the native currency is ETH, this will be in wei. + #[schema(example = "567890", value_type = String)] + #[serde(with = "crate::serde::u256")] + gas_used: U256, + /// The combined random number generated from the user and provider contributions. + #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")] + #[serde_as(as = "serde_with::hex::Hex")] + combined_random_number: [u8; 32], }, Failed { reason: String, + /// The provider contribution to the random number. + #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")] + #[serde_as(as = "Option")] + provider_random_number: Option<[u8; 32]>, }, } @@ -37,6 +56,9 @@ pub struct RequestStatus { /// The chain ID of the request. #[schema(example = "ethereum", value_type = String)] pub chain_id: ChainId, + /// The network ID of the request. This is the response of eth_chainId rpc call. + #[schema(example = "1", value_type = u64)] + pub network_id: NetworkId, #[schema(example = "0x6cc14824ea2918f5de5c2f75a9da968ad4bd6344", value_type = String)] pub provider: Address, pub sequence: u64, @@ -48,6 +70,11 @@ pub struct RequestStatus { /// The transaction hash of the request transaction. #[schema(example = "0x5a3a984f41bb5443f5efa6070ed59ccb25edd8dbe6ce7f9294cf5caa64ed00ae", value_type = String)] pub request_tx_hash: TxHash, + /// Gas limit for the callback in the smallest unit of the chain. + /// For example, if the native currency is ETH, this will be in wei. + #[schema(example = "500000", value_type = String)] + #[serde(with = "crate::serde::u256")] + pub gas_limit: U256, /// The user contribution to the random number. #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")] #[serde_as(as = "serde_with::hex::Hex")] @@ -58,9 +85,22 @@ pub struct RequestStatus { pub state: RequestEntryState, } +impl RequestStatus { + pub fn generate_combined_random_number( + user_random_number: &[u8; 32], + provider_random_number: &[u8; 32], + ) -> [u8; 32] { + let mut concat: [u8; 96] = [0; 96]; // last 32 bytes are for the block hash which is not used here + concat[0..32].copy_from_slice(user_random_number); + concat[32..64].copy_from_slice(provider_random_number); + keccak256(concat) + } +} + #[derive(Clone, Debug, Serialize, ToSchema, PartialEq)] struct RequestRow { chain_id: String, + network_id: i64, provider: String, sequence: i64, created_at: NaiveDateTime, @@ -70,9 +110,11 @@ struct RequestRow { request_tx_hash: String, user_random_number: String, sender: String, + gas_limit: String, reveal_block_number: Option, reveal_tx_hash: Option, provider_random_number: Option, + gas_used: Option, info: Option, } @@ -81,6 +123,7 @@ impl TryFrom for RequestStatus { fn try_from(row: RequestRow) -> Result { let chain_id = row.chain_id; + let network_id = row.network_id as u64; let provider = row.provider.parse()?; let sequence = row.sequence as u64; let created_at = row.created_at.and_utc(); @@ -89,6 +132,8 @@ impl TryFrom for RequestStatus { let user_random_number = hex::FromHex::from_hex(row.user_random_number)?; let request_tx_hash = row.request_tx_hash.parse()?; let sender = row.sender.parse()?; + let gas_limit = U256::from_dec_str(&row.gas_limit) + .map_err(|_| anyhow::anyhow!("Failed to parse gas limit"))?; let state = match row.state.as_str() { "Pending" => RequestEntryState::Pending, @@ -107,19 +152,36 @@ impl TryFrom for RequestStatus { ))?; let provider_random_number: [u8; 32] = hex::FromHex::from_hex(provider_random_number)?; + let gas_used = row + .gas_used + .ok_or(anyhow::anyhow!("Gas used is missing for completed request"))?; + let gas_used = U256::from_dec_str(&gas_used) + .map_err(|_| anyhow::anyhow!("Failed to parse gas used"))?; RequestEntryState::Completed { reveal_block_number, reveal_tx_hash, provider_random_number, + gas_used, + combined_random_number: Self::generate_combined_random_number( + &user_random_number, + &provider_random_number, + ), } } "Failed" => RequestEntryState::Failed { reason: row.info.unwrap_or_default(), + provider_random_number: match row.provider_random_number { + Some(provider_random_number) => { + Some(hex::FromHex::from_hex(provider_random_number)?) + } + None => None, + }, }, _ => return Err(anyhow::anyhow!("Unknown request state: {}", row.state)), }; Ok(Self { chain_id, + network_id, provider, sequence, created_at, @@ -129,6 +191,7 @@ impl TryFrom for RequestStatus { request_tx_hash, user_random_number, sender, + gas_limit, }) } } @@ -185,15 +248,18 @@ impl History { async fn update_request_status(pool: &Pool, new_status: RequestStatus) { let sequence = new_status.sequence as i64; let chain_id = new_status.chain_id; + let network_id = new_status.network_id as i64; let request_tx_hash: String = new_status.request_tx_hash.encode_hex(); let provider: String = new_status.provider.encode_hex(); + let gas_limit = new_status.gas_limit.to_string(); let result = match new_status.state { RequestEntryState::Pending => { let block_number = new_status.request_block_number as i64; let sender: String = new_status.sender.encode_hex(); let user_random_number: String = new_status.user_random_number.encode_hex(); - sqlx::query!("INSERT INTO request(chain_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + sqlx::query!("INSERT INTO request(chain_id, network_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender, gas_limit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", chain_id, + network_id, provider, sequence, new_status.created_at, @@ -202,7 +268,9 @@ impl History { block_number, request_tx_hash, user_random_number, - sender) + sender, + gas_limit + ) .execute(pool) .await } @@ -210,29 +278,45 @@ impl History { reveal_block_number, reveal_tx_hash, provider_random_number, + gas_used, + combined_random_number: _, } => { let reveal_block_number = reveal_block_number as i64; let reveal_tx_hash: String = reveal_tx_hash.encode_hex(); let provider_random_number: String = provider_random_number.encode_hex(); - sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?", + let gas_used: String = gas_used.to_string(); + let result = sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number =?, gas_used = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?", "Completed", new_status.last_updated_at, reveal_block_number, reveal_tx_hash, provider_random_number, - chain_id, + gas_used, + network_id, sequence, provider, request_tx_hash) .execute(pool) - .await + .await; + if let Ok(query_result) = &result { + if query_result.rows_affected() == 0 { + tracing::error!("Failed to update request status to complete: No rows affected. Chain ID: {}, Sequence: {}, Request TX Hash: {}", network_id, sequence, request_tx_hash); + } + } + result } - RequestEntryState::Failed { reason } => { - sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, info = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'", + RequestEntryState::Failed { + reason, + provider_random_number, + } => { + let provider_random_number: Option = provider_random_number + .map(|provider_random_number| provider_random_number.encode_hex()); + sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, info = ?, provider_random_number = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'", "Failed", new_status.last_updated_at, reason, - chain_id, + provider_random_number, + network_id, sequence, provider, request_tx_hash) @@ -271,16 +355,17 @@ impl History { pub async fn get_requests_by_sender( &self, sender: Address, - chain_id: Option, + network_id: Option, ) -> Result> { let sender: String = sender.encode_hex(); - let rows = match chain_id { - Some(chain_id) => { + let rows = match network_id { + Some(network_id) => { + let network_id = network_id as i64; sqlx::query_as!( RequestRow, - "SELECT * FROM request WHERE sender = ? AND chain_id = ?", + "SELECT * FROM request WHERE sender = ? AND network_id = ?", sender, - chain_id, + network_id, ) .fetch_all(&self.pool) .await @@ -301,16 +386,17 @@ impl History { pub async fn get_requests_by_sequence( &self, sequence: u64, - chain_id: Option, + network_id: Option, ) -> Result> { let sequence = sequence as i64; - let rows = match chain_id { - Some(chain_id) => { + let rows = match network_id { + Some(network_id) => { + let network_id = network_id as i64; sqlx::query_as!( RequestRow, - "SELECT * FROM request WHERE sequence = ? AND chain_id = ?", + "SELECT * FROM request WHERE sequence = ? AND network_id = ?", sequence, - chain_id, + network_id, ) .fetch_all(&self.pool) .await @@ -334,8 +420,9 @@ impl History { pub async fn get_requests_by_time( &self, - chain_id: Option, + network_id: Option, limit: u64, + offset: u64, min_timestamp: Option>, max_timestamp: Option>, ) -> Result> { @@ -352,20 +439,23 @@ impl History { .unwrap(), ); let limit = limit as i64; - let rows = match chain_id { - Some(chain_id) => { - let chain_id = chain_id.to_string(); - sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE chain_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?", - chain_id, + let offset = offset as i64; + let rows = match network_id { + Some(network_id) => { + let network_id = network_id as i64; + sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE network_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?", + network_id, min_timestamp, max_timestamp, - limit).fetch_all(&self.pool).await + limit, + offset).fetch_all(&self.pool).await } None => { - sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?", + sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?", min_timestamp, max_timestamp, - limit).fetch_all(&self.pool).await + limit, + offset).fetch_all(&self.pool).await } }.map_err(|e| { tracing::error!("Failed to fetch request by time: {}", e); @@ -382,6 +472,7 @@ mod test { fn get_random_request_status() -> RequestStatus { RequestStatus { chain_id: "ethereum".to_string(), + network_id: 121, provider: Address::random(), sequence: 1, created_at: chrono::Utc::now(), @@ -391,6 +482,7 @@ mod test { user_random_number: [20; 32], sender: Address::random(), state: RequestEntryState::Pending, + gas_limit: U256::from(500_000), } } @@ -404,11 +496,16 @@ mod test { reveal_block_number: 1, reveal_tx_hash, provider_random_number: [40; 32], + gas_used: U256::from(567890), + combined_random_number: RequestStatus::generate_combined_random_number( + &status.user_random_number, + &[40; 32], + ), }; History::update_request_status(&history.pool, status.clone()).await; let logs = history - .get_requests_by_sequence(status.sequence, Some(status.chain_id.clone())) + .get_requests_by_sequence(status.sequence, Some(status.network_id)) .await .unwrap(); assert_eq!(logs, vec![status.clone()]); @@ -432,7 +529,7 @@ mod test { assert_eq!(logs, vec![status.clone()]); let logs = history - .get_requests_by_sender(status.sender, Some(status.chain_id.clone())) + .get_requests_by_sender(status.sender, Some(status.network_id)) .await .unwrap(); assert_eq!(logs, vec![status.clone()]); @@ -445,14 +542,82 @@ mod test { } #[tokio::test] + async fn test_no_transition_from_completed_to_failed() { + let history = History::new_in_memory().await.unwrap(); + let reveal_tx_hash = TxHash::random(); + let mut status = get_random_request_status(); + History::update_request_status(&history.pool, status.clone()).await; + status.state = RequestEntryState::Completed { + reveal_block_number: 1, + reveal_tx_hash, + provider_random_number: [40; 32], + gas_used: U256::from(567890), + combined_random_number: RequestStatus::generate_combined_random_number( + &status.user_random_number, + &[40; 32], + ), + }; + History::update_request_status(&history.pool, status.clone()).await; + let mut failed_status = status.clone(); + failed_status.state = RequestEntryState::Failed { + reason: "Failed".to_string(), + provider_random_number: None, + }; + History::update_request_status(&history.pool, failed_status).await; + + let logs = history + .get_requests_by_tx_hash(reveal_tx_hash) + .await + .unwrap(); + assert_eq!(logs, vec![status.clone()]); + } + + #[tokio::test] + async fn test_failed_state() { + let history = History::new_in_memory().await.unwrap(); + let mut status = get_random_request_status(); + History::update_request_status(&history.pool, status.clone()).await; + status.state = RequestEntryState::Failed { + reason: "Failed".to_string(), + provider_random_number: Some([40; 32]), + }; + History::update_request_status(&history.pool, status.clone()).await; + let logs = history + .get_requests_by_tx_hash(status.request_tx_hash) + .await + .unwrap(); + assert_eq!(logs, vec![status.clone()]); + } + + #[tokio::test] + async fn test_generate_combined_random_number() { + let user_random_number = hex::FromHex::from_hex( + "0000000000000000000000006c8ac03d388d5572f77aca84573628ee87a7a4da", + ) + .unwrap(); + let provider_random_number = hex::FromHex::from_hex( + "deeb67cb894c33f7b20ae484228a9096b51e8db11461fcb0975c681cf0875d37", + ) + .unwrap(); + let combined_random_number = RequestStatus::generate_combined_random_number( + &user_random_number, + &provider_random_number, + ); + let expected_combined_random_number: [u8; 32] = hex::FromHex::from_hex( + "1c26ffa1f8430dc91cb755a98bf37ce82ac0e2cfd961e10111935917694609d5", + ) + .unwrap(); + assert_eq!(combined_random_number, expected_combined_random_number,); + } + #[tokio::test] async fn test_history_filter_irrelevant_logs() { let history = History::new_in_memory().await.unwrap(); let status = get_random_request_status(); History::update_request_status(&history.pool, status.clone()).await; let logs = history - .get_requests_by_sequence(status.sequence, Some("not-ethereum".to_string())) + .get_requests_by_sequence(status.sequence, Some(123)) .await .unwrap(); assert_eq!(logs, vec![]); @@ -470,7 +635,7 @@ mod test { assert_eq!(logs, vec![]); let logs = history - .get_requests_by_sender(Address::zero(), Some(status.chain_id.clone())) + .get_requests_by_sender(Address::zero(), Some(status.network_id)) .await .unwrap(); assert_eq!(logs, vec![]); @@ -487,12 +652,13 @@ mod test { let history = History::new_in_memory().await.unwrap(); let status = get_random_request_status(); History::update_request_status(&history.pool, status.clone()).await; - for chain_id in [None, Some("ethereum".to_string())] { + for network_id in [None, Some(121)] { // min = created_at = max let logs = history .get_requests_by_time( - chain_id.clone(), + network_id, 10, + 0, Some(status.created_at), Some(status.created_at), ) @@ -503,8 +669,9 @@ mod test { // min = created_at + 1 let logs = history .get_requests_by_time( - chain_id.clone(), + network_id, 10, + 0, Some(status.created_at + Duration::seconds(1)), None, ) @@ -515,8 +682,9 @@ mod test { // max = created_at - 1 let logs = history .get_requests_by_time( - chain_id.clone(), + network_id, 10, + 0, None, Some(status.created_at - Duration::seconds(1)), ) @@ -526,7 +694,7 @@ mod test { // no min or max let logs = history - .get_requests_by_time(chain_id.clone(), 10, None, None) + .get_requests_by_time(network_id, 10, 0, None, None) .await .unwrap(); assert_eq!(logs, vec![status.clone()]); @@ -541,7 +709,7 @@ mod test { // wait for the writer thread to write to the db sleep(std::time::Duration::from_secs(1)).await; let logs = history - .get_requests_by_sequence(1, Some("ethereum".to_string())) + .get_requests_by_sequence(1, Some(121)) .await .unwrap(); assert_eq!(logs, vec![status]); diff --git a/apps/fortuna/src/keeper.rs b/apps/fortuna/src/keeper.rs index a8cc800d3c..b08c217aa9 100644 --- a/apps/fortuna/src/keeper.rs +++ b/apps/fortuna/src/keeper.rs @@ -67,15 +67,13 @@ pub async fn run_keeper_threads( let latest_safe_block = get_latest_safe_block(&chain_state).in_current_span().await; tracing::info!("Latest safe block: {}", &latest_safe_block); - let contract = Arc::new( - InstrumentedSignablePythContract::from_config( - &chain_eth_config, - &private_key, - chain_state.id.clone(), - rpc_metrics.clone(), - ) - .await?, - ); + let contract = Arc::new(InstrumentedSignablePythContract::from_config( + &chain_eth_config, + &private_key, + chain_state.id.clone(), + rpc_metrics.clone(), + chain_state.network_id, + )?); let keeper_address = contract.wallet().address(); let fulfilled_requests_cache = Arc::new(RwLock::new(HashSet::::new())); @@ -105,15 +103,7 @@ pub async fn run_keeper_threads( let (tx, rx) = mpsc::channel::(1000); // Spawn a thread to watch for new blocks and send the range of blocks for which events has not been handled to the `tx` channel. - spawn( - watch_blocks_wrapper( - chain_state.clone(), - latest_safe_block, - tx, - chain_eth_config.retry_previous_blocks, - ) - .in_current_span(), - ); + spawn(watch_blocks_wrapper(chain_state.clone(), latest_safe_block, tx).in_current_span()); // Spawn a thread for block processing with configured delays spawn( diff --git a/apps/fortuna/src/keeper/block.rs b/apps/fortuna/src/keeper/block.rs index 0546d5bdea..6df6e32cb6 100644 --- a/apps/fortuna/src/keeper/block.rs +++ b/apps/fortuna/src/keeper/block.rs @@ -30,6 +30,8 @@ const RETRY_INTERVAL: Duration = Duration::from_secs(5); const BLOCK_BATCH_SIZE: u64 = 100; /// How much to wait before polling the next latest block const POLL_INTERVAL: Duration = Duration::from_secs(2); +/// Retry last N blocks +const RETRY_PREVIOUS_BLOCKS: u64 = 100; #[derive(Debug, Clone)] pub struct BlockRange { @@ -194,7 +196,6 @@ pub async fn watch_blocks_wrapper( chain_state: BlockchainState, latest_safe_block: BlockNumber, tx: mpsc::Sender, - retry_previous_blocks: u64, ) { let mut last_safe_block_processed = latest_safe_block; loop { @@ -202,7 +203,6 @@ pub async fn watch_blocks_wrapper( chain_state.clone(), &mut last_safe_block_processed, tx.clone(), - retry_previous_blocks, ) .in_current_span() .await @@ -221,7 +221,6 @@ pub async fn watch_blocks( chain_state: BlockchainState, last_safe_block_processed: &mut BlockNumber, tx: mpsc::Sender, - retry_previous_blocks: u64, ) -> Result<()> { tracing::info!("Watching blocks to handle new events"); @@ -230,7 +229,7 @@ pub async fn watch_blocks( let latest_safe_block = get_latest_safe_block(&chain_state).in_current_span().await; if latest_safe_block > *last_safe_block_processed { - let mut from = latest_safe_block.saturating_sub(retry_previous_blocks); + let mut from = latest_safe_block.saturating_sub(RETRY_PREVIOUS_BLOCKS); // In normal situation, the difference between latest and last safe block should not be more than 2-3 (for arbitrum it can be 10) // TODO: add a metric for this in separate PR. We need alerts diff --git a/apps/fortuna/src/keeper/process_event.rs b/apps/fortuna/src/keeper/process_event.rs index 638741b814..823d03c896 100644 --- a/apps/fortuna/src/keeper/process_event.rs +++ b/apps/fortuna/src/keeper/process_event.rs @@ -42,6 +42,7 @@ pub async fn process_event_with_backoff( tracing::info!("Started processing event"); let mut status = RequestStatus { chain_id: chain_state.id.clone(), + network_id: chain_state.network_id, provider: event.provider_address, sequence: event.sequence_number, created_at: chrono::Utc::now(), @@ -51,6 +52,7 @@ pub async fn process_event_with_backoff( sender: event.requestor, user_random_number: event.user_random_number, state: RequestEntryState::Pending, + gas_limit, }; history.add(&status); @@ -60,6 +62,7 @@ pub async fn process_event_with_backoff( .map_err(|e| { status.state = RequestEntryState::Failed { reason: format!("Error revealing: {:?}", e), + provider_random_number: None, }; history.add(&status); anyhow!("Error revealing: {:?}", e) @@ -91,6 +94,11 @@ pub async fn process_event_with_backoff( reveal_block_number: result.receipt.block_number.unwrap_or_default().as_u64(), reveal_tx_hash: result.receipt.transaction_hash, provider_random_number: provider_revelation, + gas_used: result.receipt.gas_used.unwrap_or_default(), + combined_random_number: RequestStatus::generate_combined_random_number( + &event.user_random_number, + &provider_revelation, + ), }; history.add(&status); tracing::info!( @@ -160,6 +168,11 @@ pub async fn process_event_with_backoff( .requests_processed_failure .get_or_create(&account_label) .inc(); + status.state = RequestEntryState::Failed { + reason: format!("Error revealing: {:?}", e), + provider_random_number: Some(provider_revelation), + }; + history.add(&status); } } } diff --git a/apps/fortuna/src/lib.rs b/apps/fortuna/src/lib.rs index 8b645ca611..f6eac21356 100644 --- a/apps/fortuna/src/lib.rs +++ b/apps/fortuna/src/lib.rs @@ -5,4 +5,5 @@ pub mod config; pub mod eth_utils; pub mod history; pub mod keeper; +pub mod serde; pub mod state; diff --git a/apps/fortuna/src/serde.rs b/apps/fortuna/src/serde.rs new file mode 100644 index 0000000000..f72eebd08f --- /dev/null +++ b/apps/fortuna/src/serde.rs @@ -0,0 +1,21 @@ +pub mod u256 { + use { + ethers::types::U256, + serde::{de::Error, Deserialize, Deserializer, Serializer}, + }; + + pub fn serialize(b: &U256, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(b.to_string().as_str()) + } + + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(d)?; + U256::from_dec_str(s.as_str()).map_err(|err| D::Error::custom(err.to_string())) + } +}