From 17b5bb220d8f8e9e7a768e1e0a556b8696fb84b7 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Tue, 20 Feb 2024 10:20:30 -0800 Subject: [PATCH] refactor!: Storing receipts and RAVs internal values to DB (#122) Instead of storing a JSON-serialized version of the receipt and RAV structs. Should make it easier to reconstruct receipts and RAVs in non-Rust software. BREAKING CHANGE: Receipts and RAVs DB schemas have changed. Signed-off-by: Alexis Asseman --- ...4be6e47ee6452751af47df3ad6907d163fc65.json | 18 --- ...f952e980b713a79b197f1fe17ea1c6c9d07ec.json | 23 ---- ...64d6deac6be53dc3c5fe63b0e732198f22040.json | 18 +++ ...619ea4591595cf92516ccd36bf7a4461e53b6.json | 30 ----- ...cbc51b1353880d211de44325a62c2b1f94405.json | 44 +++++++ ...f29bed709159e76d5d6ecd4370ac10d521cff.json | 30 +++++ ...1dad6228922688b46a295548ad605e81bfc49.json | 30 ----- ...eee486bfc78faac6376100e4b3fbe54cdbeb2.json | 20 --- ...7dbf78225c58f68e5f1b581f4f13c8678268.json} | 7 +- ...53136baee1406f48f61db2b7bc504825a6fc3.json | 22 +++- ...49a223428e96d7c8af1f3ed68632ead2af548.json | 54 ++++++++ ...e1fe8c4569cc8ee35d8594af8ee0c4bd06287.json | 18 +++ ...da79d651c366e54ed95687e546be1906dc48a.json | 16 --- ...623b86576e4338009ec2fb4cd1e4b64e9f368.json | 16 --- ...ed6080ba673e4dda0ed07fe008095d43d049b.json | 41 ++++++ ...4a96ec4b8b54f04355188eb7c16703e41e7b3.json | 19 +++ Cargo.lock | 49 +++---- common/Cargo.toml | 2 + common/src/tap_manager.rs | 19 ++- migrations/20230912220523_tap_receipts.up.sql | 10 +- migrations/20230915230734_tap_ravs.up.sql | 9 +- tap-agent/Cargo.toml | 4 +- tap-agent/src/tap/rav_storage_adapter.rs | 82 +++++++++--- tap-agent/src/tap/receipt_storage_adapter.rs | 124 +++++++++++++++++- tap-agent/src/tap/sender_account.rs | 54 ++++++-- tap-agent/src/tap/sender_allocation.rs | 9 +- tap-agent/src/tap/test_utils.rs | 39 +++--- 27 files changed, 559 insertions(+), 248 deletions(-) delete mode 100644 .sqlx/query-0245e3c8b1c93a2a4f86e2a7e684be6e47ee6452751af47df3ad6907d163fc65.json delete mode 100644 .sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json create mode 100644 .sqlx/query-28b9daeb3f6162c0218eb8aab2f64d6deac6be53dc3c5fe63b0e732198f22040.json delete mode 100644 .sqlx/query-40d9eaa41f7e38e91e850a6d77d619ea4591595cf92516ccd36bf7a4461e53b6.json create mode 100644 .sqlx/query-439265d98b8301eec00664222eccbc51b1353880d211de44325a62c2b1f94405.json create mode 100644 .sqlx/query-46dab6110aad21d2d87b4da3ea6f29bed709159e76d5d6ecd4370ac10d521cff.json delete mode 100644 .sqlx/query-47f757bea4815b78fca6bc9b20a1dad6228922688b46a295548ad605e81bfc49.json delete mode 100644 .sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json rename .sqlx/{query-0d0b4c9a450ef82c4fdd5903afc6f7f8b921e2316f1137476ed2e5265cba5e1b.json => query-74656664f9b27c28a8726800d7e57dbf78225c58f68e5f1b581f4f13c8678268.json} (50%) create mode 100644 .sqlx/query-a1b0c7034cfd7c70154bf71977549a223428e96d7c8af1f3ed68632ead2af548.json create mode 100644 .sqlx/query-a25a742980e7995a16f7bb6bb6de1fe8c4569cc8ee35d8594af8ee0c4bd06287.json delete mode 100644 .sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json delete mode 100644 .sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json create mode 100644 .sqlx/query-eb167fed852786fdbeabb636b40ed6080ba673e4dda0ed07fe008095d43d049b.json create mode 100644 .sqlx/query-ed054cb84e667373b57cbb62dbd4a96ec4b8b54f04355188eb7c16703e41e7b3.json diff --git a/.sqlx/query-0245e3c8b1c93a2a4f86e2a7e684be6e47ee6452751af47df3ad6907d163fc65.json b/.sqlx/query-0245e3c8b1c93a2a4f86e2a7e684be6e47ee6452751af47df3ad6907d163fc65.json deleted file mode 100644 index 29a15220e..000000000 --- a/.sqlx/query-0245e3c8b1c93a2a4f86e2a7e684be6e47ee6452751af47df3ad6907d163fc65.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_receipts (allocation_id, signer_address, timestamp_ns, value, receipt)\n VALUES ($1, $2, $3, $4, $5)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "Numeric", - "Numeric", - "Json" - ] - }, - "nullable": [] - }, - "hash": "0245e3c8b1c93a2a4f86e2a7e684be6e47ee6452751af47df3ad6907d163fc65" -} diff --git a/.sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json b/.sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json deleted file mode 100644 index e935e7b7e..000000000 --- a/.sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT rav\n FROM scalar_tap_ravs\n WHERE allocation_id = $1 AND sender_address = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "rav", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar" - ] - }, - "nullable": [ - false - ] - }, - "hash": "276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec" -} diff --git a/.sqlx/query-28b9daeb3f6162c0218eb8aab2f64d6deac6be53dc3c5fe63b0e732198f22040.json b/.sqlx/query-28b9daeb3f6162c0218eb8aab2f64d6deac6be53dc3c5fe63b0e732198f22040.json new file mode 100644 index 000000000..a749c539f --- /dev/null +++ b/.sqlx/query-28b9daeb3f6162c0218eb8aab2f64d6deac6be53dc3c5fe63b0e732198f22040.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_ravs (sender_address, signature, allocation_id, timestamp_ns, value_aggregate)\n VALUES ($1, $2, $3, $4, $5)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bytea", + "Bpchar", + "Numeric", + "Numeric" + ] + }, + "nullable": [] + }, + "hash": "28b9daeb3f6162c0218eb8aab2f64d6deac6be53dc3c5fe63b0e732198f22040" +} diff --git a/.sqlx/query-40d9eaa41f7e38e91e850a6d77d619ea4591595cf92516ccd36bf7a4461e53b6.json b/.sqlx/query-40d9eaa41f7e38e91e850a6d77d619ea4591595cf92516ccd36bf7a4461e53b6.json deleted file mode 100644 index 3cf83c820..000000000 --- a/.sqlx/query-40d9eaa41f7e38e91e850a6d77d619ea4591595cf92516ccd36bf7a4461e53b6.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id, receipt\n FROM scalar_tap_receipts\n WHERE allocation_id = $1 AND signer_address IN (SELECT unnest($2::text[]))\n AND $3::numrange @> timestamp_ns\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "receipt", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Bpchar", - "TextArray", - "NumRange" - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "40d9eaa41f7e38e91e850a6d77d619ea4591595cf92516ccd36bf7a4461e53b6" -} diff --git a/.sqlx/query-439265d98b8301eec00664222eccbc51b1353880d211de44325a62c2b1f94405.json b/.sqlx/query-439265d98b8301eec00664222eccbc51b1353880d211de44325a62c2b1f94405.json new file mode 100644 index 000000000..f308aece5 --- /dev/null +++ b/.sqlx/query-439265d98b8301eec00664222eccbc51b1353880d211de44325a62c2b1f94405.json @@ -0,0 +1,44 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT signature, allocation_id, timestamp_ns, nonce, value\n FROM scalar_tap_receipts\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "signature", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "allocation_id", + "type_info": "Bpchar" + }, + { + "ordinal": 2, + "name": "timestamp_ns", + "type_info": "Numeric" + }, + { + "ordinal": 3, + "name": "nonce", + "type_info": "Numeric" + }, + { + "ordinal": 4, + "name": "value", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "439265d98b8301eec00664222eccbc51b1353880d211de44325a62c2b1f94405" +} diff --git a/.sqlx/query-46dab6110aad21d2d87b4da3ea6f29bed709159e76d5d6ecd4370ac10d521cff.json b/.sqlx/query-46dab6110aad21d2d87b4da3ea6f29bed709159e76d5d6ecd4370ac10d521cff.json new file mode 100644 index 000000000..fad6cb903 --- /dev/null +++ b/.sqlx/query-46dab6110aad21d2d87b4da3ea6f29bed709159e76d5d6ecd4370ac10d521cff.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH rav AS (\n SELECT \n timestamp_ns \n FROM \n scalar_tap_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND signer_address IN (SELECT unnest($3::text[]))\n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "max", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "sum", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "TextArray" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "46dab6110aad21d2d87b4da3ea6f29bed709159e76d5d6ecd4370ac10d521cff" +} diff --git a/.sqlx/query-47f757bea4815b78fca6bc9b20a1dad6228922688b46a295548ad605e81bfc49.json b/.sqlx/query-47f757bea4815b78fca6bc9b20a1dad6228922688b46a295548ad605e81bfc49.json deleted file mode 100644 index caad47371..000000000 --- a/.sqlx/query-47f757bea4815b78fca6bc9b20a1dad6228922688b46a295548ad605e81bfc49.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND signer_address IN (SELECT unnest($3::text[]))\n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "max", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "sum", - "type_info": "Numeric" - } - ], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "TextArray" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "47f757bea4815b78fca6bc9b20a1dad6228922688b46a295548ad605e81bfc49" -} diff --git a/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json b/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json deleted file mode 100644 index 69a9fe5fa..000000000 --- a/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT receipt\n FROM scalar_tap_receipts\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "receipt", - "type_info": "Json" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2" -} diff --git a/.sqlx/query-0d0b4c9a450ef82c4fdd5903afc6f7f8b921e2316f1137476ed2e5265cba5e1b.json b/.sqlx/query-74656664f9b27c28a8726800d7e57dbf78225c58f68e5f1b581f4f13c8678268.json similarity index 50% rename from .sqlx/query-0d0b4c9a450ef82c4fdd5903afc6f7f8b921e2316f1137476ed2e5265cba5e1b.json rename to .sqlx/query-74656664f9b27c28a8726800d7e57dbf78225c58f68e5f1b581f4f13c8678268.json index 7fb87cc87..dcea5f9b6 100644 --- a/.sqlx/query-0d0b4c9a450ef82c4fdd5903afc6f7f8b921e2316f1137476ed2e5265cba5e1b.json +++ b/.sqlx/query-74656664f9b27c28a8726800d7e57dbf78225c58f68e5f1b581f4f13c8678268.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_receipts (\n allocation_id, signer_address, timestamp_ns, value, receipt\n )\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id\n ", + "query": "\n INSERT INTO scalar_tap_receipts (signer_address, signature, allocation_id, timestamp_ns, nonce, value)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING id\n ", "describe": { "columns": [ { @@ -12,15 +12,16 @@ "parameters": { "Left": [ "Bpchar", + "Bytea", "Bpchar", "Numeric", "Numeric", - "Json" + "Numeric" ] }, "nullable": [ false ] }, - "hash": "0d0b4c9a450ef82c4fdd5903afc6f7f8b921e2316f1137476ed2e5265cba5e1b" + "hash": "74656664f9b27c28a8726800d7e57dbf78225c58f68e5f1b581f4f13c8678268" } diff --git a/.sqlx/query-7ff23083cf2afaf2c6b9693736053136baee1406f48f61db2b7bc504825a6fc3.json b/.sqlx/query-7ff23083cf2afaf2c6b9693736053136baee1406f48f61db2b7bc504825a6fc3.json index f50105eed..3192a3f76 100644 --- a/.sqlx/query-7ff23083cf2afaf2c6b9693736053136baee1406f48f61db2b7bc504825a6fc3.json +++ b/.sqlx/query-7ff23083cf2afaf2c6b9693736053136baee1406f48f61db2b7bc504825a6fc3.json @@ -5,21 +5,31 @@ "columns": [ { "ordinal": 0, - "name": "allocation_id", + "name": "sender_address", "type_info": "Bpchar" }, { "ordinal": 1, - "name": "sender_address", - "type_info": "Bpchar" + "name": "signature", + "type_info": "Bytea" }, { "ordinal": 2, - "name": "rav", - "type_info": "Json" + "name": "allocation_id", + "type_info": "Bpchar" }, { "ordinal": 3, + "name": "timestamp_ns", + "type_info": "Numeric" + }, + { + "ordinal": 4, + "name": "value_aggregate", + "type_info": "Numeric" + }, + { + "ordinal": 5, "name": "final", "type_info": "Bool" } @@ -31,6 +41,8 @@ ] }, "nullable": [ + false, + false, false, false, false, diff --git a/.sqlx/query-a1b0c7034cfd7c70154bf71977549a223428e96d7c8af1f3ed68632ead2af548.json b/.sqlx/query-a1b0c7034cfd7c70154bf71977549a223428e96d7c8af1f3ed68632ead2af548.json new file mode 100644 index 000000000..70c68969c --- /dev/null +++ b/.sqlx/query-a1b0c7034cfd7c70154bf71977549a223428e96d7c8af1f3ed68632ead2af548.json @@ -0,0 +1,54 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, signature, allocation_id, timestamp_ns, nonce, value\n FROM scalar_tap_receipts\n WHERE allocation_id = $1 AND signer_address IN (SELECT unnest($2::text[]))\n AND $3::numrange @> timestamp_ns\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "signature", + "type_info": "Bytea" + }, + { + "ordinal": 2, + "name": "allocation_id", + "type_info": "Bpchar" + }, + { + "ordinal": 3, + "name": "timestamp_ns", + "type_info": "Numeric" + }, + { + "ordinal": 4, + "name": "nonce", + "type_info": "Numeric" + }, + { + "ordinal": 5, + "name": "value", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "TextArray", + "NumRange" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "a1b0c7034cfd7c70154bf71977549a223428e96d7c8af1f3ed68632ead2af548" +} diff --git a/.sqlx/query-a25a742980e7995a16f7bb6bb6de1fe8c4569cc8ee35d8594af8ee0c4bd06287.json b/.sqlx/query-a25a742980e7995a16f7bb6bb6de1fe8c4569cc8ee35d8594af8ee0c4bd06287.json new file mode 100644 index 000000000..f4d7040ed --- /dev/null +++ b/.sqlx/query-a25a742980e7995a16f7bb6bb6de1fe8c4569cc8ee35d8594af8ee0c4bd06287.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_ravs (sender_address, signature, allocation_id, timestamp_ns, value_aggregate)\n VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (allocation_id, sender_address)\n DO UPDATE SET signature = $2, timestamp_ns = $4, value_aggregate = $5\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bytea", + "Bpchar", + "Numeric", + "Numeric" + ] + }, + "nullable": [] + }, + "hash": "a25a742980e7995a16f7bb6bb6de1fe8c4569cc8ee35d8594af8ee0c4bd06287" +} diff --git a/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json b/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json deleted file mode 100644 index feb6782fd..000000000 --- a/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_ravs (\n allocation_id, sender_address, rav\n )\n VALUES ($1, $2, $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "Json" - ] - }, - "nullable": [] - }, - "hash": "b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a" -} diff --git a/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json b/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json deleted file mode 100644 index bd28ab7b4..000000000 --- a/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_ravs (allocation_id, sender_address, rav)\n VALUES ($1, $2, $3)\n ON CONFLICT (allocation_id, sender_address)\n DO UPDATE SET rav = $3\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "Json" - ] - }, - "nullable": [] - }, - "hash": "bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368" -} diff --git a/.sqlx/query-eb167fed852786fdbeabb636b40ed6080ba673e4dda0ed07fe008095d43d049b.json b/.sqlx/query-eb167fed852786fdbeabb636b40ed6080ba673e4dda0ed07fe008095d43d049b.json new file mode 100644 index 000000000..ba282e693 --- /dev/null +++ b/.sqlx/query-eb167fed852786fdbeabb636b40ed6080ba673e4dda0ed07fe008095d43d049b.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT signature, allocation_id, timestamp_ns, value_aggregate\n FROM scalar_tap_ravs\n WHERE allocation_id = $1 AND sender_address = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "signature", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "allocation_id", + "type_info": "Bpchar" + }, + { + "ordinal": 2, + "name": "timestamp_ns", + "type_info": "Numeric" + }, + { + "ordinal": 3, + "name": "value_aggregate", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "eb167fed852786fdbeabb636b40ed6080ba673e4dda0ed07fe008095d43d049b" +} diff --git a/.sqlx/query-ed054cb84e667373b57cbb62dbd4a96ec4b8b54f04355188eb7c16703e41e7b3.json b/.sqlx/query-ed054cb84e667373b57cbb62dbd4a96ec4b8b54f04355188eb7c16703e41e7b3.json new file mode 100644 index 000000000..f67f92032 --- /dev/null +++ b/.sqlx/query-ed054cb84e667373b57cbb62dbd4a96ec4b8b54f04355188eb7c16703e41e7b3.json @@ -0,0 +1,19 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_receipts (signer_address, signature, allocation_id, timestamp_ns, nonce, value)\n VALUES ($1, $2, $3, $4, $5, $6)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bytea", + "Bpchar", + "Numeric", + "Numeric", + "Numeric" + ] + }, + "nullable": [] + }, + "hash": "ed054cb84e667373b57cbb62dbd4a96ec4b8b54f04355188eb7c16703e41e7b3" +} diff --git a/Cargo.lock b/Cargo.lock index b8a6220a9..6aa158aad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -832,7 +832,6 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "serde", ] [[package]] @@ -1946,9 +1945,9 @@ dependencies = [ [[package]] name = "ethers" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5344eea9b20effb5efeaad29418215c4d27017639fd1f908260f59cbbd226e" +checksum = "6c7cd562832e2ff584fa844cd2f6e5d4f35bbe11b28c7c9b8df957b2e1d0c701" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1962,9 +1961,9 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c405f24ea3a517899ba7985385c43dc4a7eb1209af3b1e0a1a32d7dcc7f8d09" +checksum = "35dc9a249c066d17e8947ff52a4116406163cf92c7f0763cb8c001760b26403f" dependencies = [ "ethers-core", "once_cell", @@ -1974,9 +1973,9 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0111ead599d17a7bff6985fd5756f39ca7033edc79a31b23026a8d5d64fa95cd" +checksum = "43304317c7f776876e47f2f637859f6d0701c1ec7930a150f169d5fbe7d76f5a" dependencies = [ "const-hex", "ethers-contract-abigen", @@ -1993,9 +1992,9 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51258120c6b47ea9d9bec0d90f9e8af71c977fbefbef8213c91bfed385fe45eb" +checksum = "f9f96502317bf34f6d71a3e3d270defaa9485d754d789e15a8e04a84161c95eb" dependencies = [ "Inflector", "const-hex", @@ -2017,9 +2016,9 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e7a0f1197cee2b62dc89f63eff3201dbf87c283ff7e18d86d38f83b845483" +checksum = "452ff6b0a64507ce8d67ffd48b1da3b42f03680dcf5382244e9c93822cbbf5de" dependencies = [ "Inflector", "const-hex", @@ -2033,9 +2032,9 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f03e0bdc216eeb9e355b90cf610ef6c5bb8aca631f97b5ae9980ce34ea7878d" +checksum = "aab3cef6cc1c9fd7f787043c81ad3052eff2b96a3878ef1526aa446311bdbfc9" dependencies = [ "arrayvec", "bytes", @@ -2063,9 +2062,9 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abbac2c890bdbe0f1b8e549a53b00e2c4c1de86bb077c1094d1f38cdf9381a56" +checksum = "16d45b981f5fa769e1d0343ebc2a44cfa88c9bc312eb681b676318b40cef6fb1" dependencies = [ "chrono", "ethers-core", @@ -2079,9 +2078,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681ece6eb1d10f7cf4f873059a77c04ff1de4f35c63dd7bccde8f438374fcb93" +checksum = "145211f34342487ef83a597c1e69f0d3e01512217a7c72cc8a25931854c7dca0" dependencies = [ "async-trait", "auto_impl", @@ -2106,9 +2105,9 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d6c0c9455d93d4990c06e049abf9b30daf148cf461ee939c11d88907c60816" +checksum = "fb6b15393996e3b8a78ef1332d6483c11d839042c17be58decc92fa8b1c3508a" dependencies = [ "async-trait", "auto_impl", @@ -2143,9 +2142,9 @@ dependencies = [ [[package]] name = "ethers-signers" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb1b714e227bbd2d8c53528adb580b203009728b17d0d0e4119353aa9bc5532" +checksum = "b3b125a103b56aef008af5d5fb48191984aa326b50bfd2557d231dc499833de3" dependencies = [ "async-trait", "coins-bip32", @@ -2162,9 +2161,9 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "2.0.11" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64f710586d147864cff66540a6d64518b9ff37d73ef827fee430538265b595f" +checksum = "d21df08582e0a43005018a858cc9b465c5fff9cf4056651be64f844e57d1f55f" dependencies = [ "cfg-if", "const-hex", @@ -3103,6 +3102,7 @@ dependencies = [ "async-trait", "autometrics 0.6.0", "axum", + "bigdecimal 0.4.2", "build-info", "env_logger", "ethers", @@ -3116,6 +3116,7 @@ dependencies = [ "lazy_static", "lru", "once_cell", + "open-fastrlp", "prometheus", "regex", "reqwest", @@ -3149,12 +3150,14 @@ dependencies = [ "dotenvy", "enum-as-inner", "ethereum-types", + "ethers", "ethers-signers", "eventuals", "graphql-http", "indexer-common", "jsonrpsee 0.20.2", "lazy_static", + "open-fastrlp", "reqwest", "serde", "serde_json", diff --git a/common/Cargo.toml b/common/Cargo.toml index f119a1962..f0b16db0b 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -46,6 +46,8 @@ tracing = "0.1.40" tower = "0.4.13" tower_governor = "0.1.0" tokio-util = "0.7.10" +open-fastrlp = "0.1.4" +bigdecimal = "0.4.2" [dev-dependencies] env_logger = "0.9.0" diff --git a/common/src/tap_manager.rs b/common/src/tap_manager.rs index dc9cf4162..218fc7872 100644 --- a/common/src/tap_manager.rs +++ b/common/src/tap_manager.rs @@ -4,8 +4,10 @@ use alloy_primitives::hex::ToHex; use alloy_sol_types::Eip712Domain; use anyhow::anyhow; +use bigdecimal::num_bigint::BigInt; use ethers_core::types::U256; use eventuals::Eventual; +use open_fastrlp::Encodable; use sqlx::postgres::PgListener; use sqlx::{types::BigDecimal, PgPool}; use std::collections::HashSet; @@ -128,17 +130,24 @@ impl TapManager { ); } + let encoded_signature = { + let mut buf: Vec = Vec::with_capacity(72); + receipt.signature.encode(&mut buf); + buf + }; + // TODO: consider doing this in another async task to avoid slowing down the paid query flow. sqlx::query!( r#" - INSERT INTO scalar_tap_receipts (allocation_id, signer_address, timestamp_ns, value, receipt) - VALUES ($1, $2, $3, $4, $5) + INSERT INTO scalar_tap_receipts (signer_address, signature, allocation_id, timestamp_ns, nonce, value) + VALUES ($1, $2, $3, $4, $5, $6) "#, - allocation_id.encode_hex::(), receipt_signer.encode_hex::(), + encoded_signature, + allocation_id.encode_hex::(), BigDecimal::from(receipt.message.timestamp_ns), - BigDecimal::from_str(&receipt.message.value.to_string())?, - serde_json::to_value(receipt).map_err(|e| anyhow!(e))? + BigDecimal::from(receipt.message.nonce), + BigDecimal::from(BigInt::from(receipt.message.value)), ) .execute(&self.pgpool) .await diff --git a/migrations/20230912220523_tap_receipts.up.sql b/migrations/20230912220523_tap_receipts.up.sql index 1f303901d..022ad48f2 100644 --- a/migrations/20230912220523_tap_receipts.up.sql +++ b/migrations/20230912220523_tap_receipts.up.sql @@ -1,11 +1,13 @@ CREATE TABLE IF NOT EXISTS scalar_tap_receipts ( id BIGSERIAL PRIMARY KEY, -- id being SERIAL is important for the function of tap-agent - allocation_id CHAR(40) NOT NULL, signer_address CHAR(40) NOT NULL, + + -- Values below are the individual fields of the EIP-712 receipt + signature BYTEA NOT NULL, + allocation_id CHAR(40) NOT NULL, timestamp_ns NUMERIC(20) NOT NULL, - -- signature CHAR(130) NOT NULL, - value NUMERIC(39) NOT NULL, - receipt JSON NOT NULL + nonce NUMERIC(20) NOT NULL, + value NUMERIC(39) NOT NULL ); CREATE FUNCTION scalar_tap_receipt_notify() diff --git a/migrations/20230915230734_tap_ravs.up.sql b/migrations/20230915230734_tap_ravs.up.sql index 5b66e30b1..e8e0ba5a6 100644 --- a/migrations/20230915230734_tap_ravs.up.sql +++ b/migrations/20230915230734_tap_ravs.up.sql @@ -1,7 +1,12 @@ CREATE TABLE IF NOT EXISTS scalar_tap_ravs ( - allocation_id CHAR(40) NOT NULL, sender_address CHAR(40) NOT NULL, - rav JSON NOT NULL, + + -- Values below are the individual fields of the EIP-712 RAV + signature BYTEA NOT NULL, + allocation_id CHAR(40) NOT NULL, + timestamp_ns NUMERIC(20) NOT NULL, + value_aggregate NUMERIC(39) NOT NULL, + final BOOLEAN DEFAULT FALSE NOT NULL, PRIMARY KEY (allocation_id, sender_address) ); diff --git a/tap-agent/Cargo.toml b/tap-agent/Cargo.toml index e17b46065..f37025c2b 100644 --- a/tap-agent/Cargo.toml +++ b/tap-agent/Cargo.toml @@ -13,7 +13,7 @@ alloy-primitives = "0.6" alloy-sol-types = "0.6" anyhow = "1.0.72" async-trait = "0.1.72" -bigdecimal = { version = "0.4.2", features = ["serde"] } +bigdecimal = "0.4.2" clap = { version = "4.4.3", features = ["derive", "env"] } confy = "0.5.1" dotenvy = "0.15.7" @@ -47,6 +47,8 @@ tracing-subscriber = { version = "0.3", features = [ "json", ] } enum-as-inner = "0.6.0" +open-fastrlp = "0.1.4" +ethers = "2.0.13" [dev-dependencies] ethers-signers = "2.0.8" diff --git a/tap-agent/src/tap/rav_storage_adapter.rs b/tap-agent/src/tap/rav_storage_adapter.rs index 3a464f19d..140dae6ed 100644 --- a/tap-agent/src/tap/rav_storage_adapter.rs +++ b/tap-agent/src/tap/rav_storage_adapter.rs @@ -1,11 +1,18 @@ // Copyright 2023-, GraphOps and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; use alloy_primitives::hex::ToHex; use anyhow::Result; use async_trait::async_trait; +use bigdecimal::num_bigint::{BigInt, ToBigInt}; +use bigdecimal::ToPrimitive; +use ethers::types::Signature; +use open_fastrlp::{Decodable, Encodable}; +use sqlx::types::BigDecimal; use sqlx::PgPool; use tap_core::adapters::rav_storage_adapter::RAVStorageAdapter as RAVStorageAdapterTrait; +use tap_core::receipt_aggregate_voucher::ReceiptAggregateVoucher; use tap_core::tap_manager::SignedRAV; use thegraph::types::Address; use thiserror::Error; @@ -28,18 +35,21 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { type AdapterError = AdapterError; async fn update_last_rav(&self, rav: SignedRAV) -> Result<(), Self::AdapterError> { + let mut signature_bytes: Vec = Vec::with_capacity(72); + rav.signature.encode(&mut signature_bytes); + let _fut = sqlx::query!( r#" - INSERT INTO scalar_tap_ravs (allocation_id, sender_address, rav) - VALUES ($1, $2, $3) + INSERT INTO scalar_tap_ravs (sender_address, signature, allocation_id, timestamp_ns, value_aggregate) + VALUES ($1, $2, $3, $4, $5) ON CONFLICT (allocation_id, sender_address) - DO UPDATE SET rav = $3 + DO UPDATE SET signature = $2, timestamp_ns = $4, value_aggregate = $5 "#, - self.allocation_id.encode_hex::(), self.sender.encode_hex::(), - serde_json::to_value(rav).map_err(|e| AdapterError::AdapterError { - error: e.to_string() - })? + signature_bytes, + self.allocation_id.encode_hex::(), + BigDecimal::from(rav.message.timestamp_ns), + BigDecimal::from(BigInt::from(rav.message.value_aggregate)), ) .execute(&self.pgpool) .await @@ -50,9 +60,9 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { } async fn last_rav(&self) -> Result, Self::AdapterError> { - let latest_rav = sqlx::query!( + let row = sqlx::query!( r#" - SELECT rav + SELECT signature, allocation_id, timestamp_ns, value_aggregate FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, @@ -61,17 +71,55 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { ) .fetch_optional(&self.pgpool) .await - .map(|r| r.map(|r| r.rav)) .map_err(|e| AdapterError::AdapterError { error: e.to_string(), })?; - match latest_rav { - Some(latest_rav) => { - Ok( - serde_json::from_value(latest_rav).map_err(|e| AdapterError::AdapterError { - error: e.to_string(), - }), - )? + + match row { + Some(row) => { + let signature = Signature::decode(&mut row.signature.as_slice()).map_err(|e| { + AdapterError::AdapterError { + error: format!( + "Error decoding signature while retrieving RAV from database: {}", + e + ), + } + })?; + let allocation_id = Address::from_str(&row.allocation_id).map_err(|e| { + AdapterError::AdapterError { + error: format!( + "Error decoding allocation_id while retrieving RAV from database: {}", + e + ), + } + })?; + let timestamp_ns = row + .timestamp_ns + .to_u64() + .ok_or(AdapterError::AdapterError { + error: "Error decoding timestamp_ns while retrieving RAV from database" + .to_string(), + })?; + let value_aggregate = row + .value_aggregate + // Beware, BigDecimal::to_u128() actually uses to_u64() under the hood. + // So we're converting to BigInt to get a proper implementation of to_u128(). + .to_bigint() + .and_then(|v| v.to_u128()) + .ok_or(AdapterError::AdapterError { + error: "Error decoding value_aggregate while retrieving RAV from database" + .to_string(), + })?; + + let rav = ReceiptAggregateVoucher { + allocation_id, + timestamp_ns, + value_aggregate, + }; + Ok(Some(SignedRAV { + message: rav, + signature, + })) } None => Ok(None), } diff --git a/tap-agent/src/tap/receipt_storage_adapter.rs b/tap-agent/src/tap/receipt_storage_adapter.rs index 64cb03924..d167ab169 100644 --- a/tap-agent/src/tap/receipt_storage_adapter.rs +++ b/tap-agent/src/tap/receipt_storage_adapter.rs @@ -4,14 +4,21 @@ use std::{ num::TryFromIntError, ops::{Bound, RangeBounds}, + str::FromStr, }; use alloy_primitives::hex::ToHex; use async_trait::async_trait; +use bigdecimal::{num_bigint::ToBigInt, ToPrimitive}; +use ethers::types::Signature; use eventuals::Eventual; use indexer_common::escrow_accounts::EscrowAccounts; +use open_fastrlp::Decodable; use sqlx::{postgres::types::PgRange, types::BigDecimal, PgPool}; -use tap_core::adapters::receipt_storage_adapter::ReceiptStorageAdapter as ReceiptStorageAdapterTrait; +use tap_core::{ + adapters::receipt_storage_adapter::ReceiptStorageAdapter as ReceiptStorageAdapterTrait, + tap_receipt::Receipt, +}; use tap_core::{ tap_manager::SignedReceipt, tap_receipt::{ReceiptCheck, ReceivedReceipt}, @@ -112,7 +119,7 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { let records = sqlx::query!( r#" - SELECT id, receipt + SELECT id, signature, allocation_id, timestamp_ns, nonce, value FROM scalar_tap_receipts WHERE allocation_id = $1 AND signer_address IN (SELECT unnest($2::text[])) AND $3::numrange @> timestamp_ns @@ -127,7 +134,47 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { .into_iter() .map(|record| { let id: u64 = record.id.try_into()?; - let signed_receipt: SignedReceipt = serde_json::from_value(record.receipt)?; + let signature = Signature::decode(&mut record.signature.as_slice()) + .map_err(|e| AdapterError::AdapterError { + error: format!( + "Error decoding signature while retrieving receipt from database: {}", + e + ), + })?; + let allocation_id = Address::from_str(&record.allocation_id).map_err(|e| { + AdapterError::AdapterError { + error: format!( + "Error decoding allocation_id while retrieving receipt from database: {}", + e + ), + } + })?; + let timestamp_ns = record + .timestamp_ns + .to_u64() + .ok_or(AdapterError::AdapterError { + error: "Error decoding timestamp_ns while retrieving receipt from database" + .to_string(), + })?; + let nonce = record.nonce.to_u64().ok_or(AdapterError::AdapterError { + error: "Error decoding nonce while retrieving receipt from database".to_string(), + })?; + // Beware, BigDecimal::to_u128() actually uses to_u64() under the hood... + // So we're converting to BigInt to get a proper implementation of to_u128(). + let value = record.value.to_bigint().and_then(|v| v.to_u128()).ok_or(AdapterError::AdapterError { + error: "Error decoding value while retrieving receipt from database".to_string(), + })?; + + let signed_receipt = SignedReceipt { + message: Receipt { + allocation_id, + timestamp_ns, + nonce, + value, + }, + signature, + }; + let received_receipt = ReceivedReceipt::new(signed_receipt, id, &self.required_checks); Ok((id, received_receipt)) @@ -203,6 +250,51 @@ mod test { use sqlx::PgPool; use tap_core::tap_receipt::get_full_list_of_checks; + /// Insert a single receipt and retrieve it from the database using the adapter. + /// The point here it to test the deserialization of large numbers. + #[sqlx::test(migrations = "../migrations")] + async fn insert_and_retrieve_single_receipt(pgpool: PgPool) { + let escrow_accounts = Eventual::from_value(EscrowAccounts::new( + HashMap::from([(SENDER.1, 1000.into())]), + HashMap::from([(SENDER.1, vec![SIGNER.1])]), + )); + + let storage_adapter = ReceiptStorageAdapter::new( + pgpool, + *ALLOCATION_ID_0, + SENDER.1, + get_full_list_of_checks(), + escrow_accounts.clone(), + ); + + let received_receipt = create_received_receipt( + &ALLOCATION_ID_0, + &SIGNER.0, + u64::MAX, + u64::MAX, + u128::MAX, + 1, + ) + .await; + + // Storing the receipt + store_receipt(&storage_adapter.pgpool, received_receipt.signed_receipt()) + .await + .unwrap(); + + let retrieved_receipt = storage_adapter + .retrieve_receipts_in_timestamp_range(.., None) + .await + .unwrap()[0] + .1 + .clone(); + + let received_receipt_json = serde_json::to_value(received_receipt).unwrap(); + let retrieved_receipt_json = serde_json::to_value(retrieved_receipt).unwrap(); + + assert_eq!(received_receipt_json, retrieved_receipt_json); + } + /// This function compares a local receipts vector filter by timestamp range (we assume that the stdlib /// implementation is correct) with the receipts vector retrieved from the database using /// retrieve_receipts_in_timestamp_range. @@ -321,7 +413,7 @@ mod test { // Retrieving all receipts in DB (including irrelevant ones) let records = sqlx::query!( r#" - SELECT receipt + SELECT signature, allocation_id, timestamp_ns, nonce, value FROM scalar_tap_receipts "# ) @@ -335,7 +427,29 @@ mod test { let recovered_received_receipt_set: Vec = records .into_iter() .map(|record| { - let signed_receipt: SignedReceipt = serde_json::from_value(record.receipt).unwrap(); + let signature = Signature::decode(&mut record.signature.as_slice()).unwrap(); + let allocation_id = Address::from_str(&record.allocation_id).unwrap(); + let timestamp_ns = record.timestamp_ns.to_u64().unwrap(); + let nonce = record.nonce.to_u64().unwrap(); + // Beware, BigDecimal::to_u128() actually uses to_u64() under the hood... + // So we're converting to BigInt to get a proper implementation of to_u128(). + let value = record + .value + .to_bigint() + .map(|v| v.to_u128()) + .unwrap() + .unwrap(); + + let signed_receipt = SignedReceipt { + message: Receipt { + allocation_id, + timestamp_ns, + nonce, + value, + }, + signature, + }; + let reveived_receipt = ReceivedReceipt::new(signed_receipt, 0, &get_full_list_of_checks()); serde_json::to_value(reveived_receipt).unwrap() diff --git a/tap-agent/src/tap/sender_account.rs b/tap-agent/src/tap/sender_account.rs index 1fd8150be..3bee1f6c7 100644 --- a/tap-agent/src/tap/sender_account.rs +++ b/tap-agent/src/tap/sender_account.rs @@ -412,12 +412,18 @@ impl Drop for SenderAccount { #[cfg(test)] mod tests { - use alloy_primitives::hex::ToHex; + use bigdecimal::{num_bigint::ToBigInt, ToPrimitive}; + use ethers::types::Signature; use indexer_common::subgraph_client::DeploymentDetails; + use open_fastrlp::Decodable; use serde_json::json; + use std::str::FromStr; use tap_aggregator::server::run_server; - use tap_core::tap_manager::SignedRAV; + use tap_core::{ + eip_712_signed_message::EIP712SignedMessage, + receipt_aggregate_voucher::ReceiptAggregateVoucher, + }; use wiremock::{ matchers::{body_string_contains, method}, Mock, MockServer, ResponseTemplate, @@ -748,7 +754,7 @@ mod tests { // Get the latest RAV from the database. let latest_rav = sqlx::query!( r#" - SELECT rav + SELECT signature, allocation_id, timestamp_ns, value_aggregate FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, @@ -757,12 +763,24 @@ mod tests { ) .fetch_optional(&pgpool) .await - .map(|r| r.map(|r| r.rav)) + .unwrap() .unwrap(); - let latest_rav = latest_rav - .map(|r| serde_json::from_value::(r).unwrap()) - .unwrap(); + let latest_rav = EIP712SignedMessage { + message: ReceiptAggregateVoucher { + allocation_id: Address::from_str(&latest_rav.allocation_id).unwrap(), + timestamp_ns: latest_rav.timestamp_ns.to_u64().unwrap(), + // Beware, BigDecimal::to_u128() actually uses to_u64() under the hood... + // So we're converting to BigInt to get a proper implementation of to_u128(). + value_aggregate: latest_rav + .value_aggregate + .to_bigint() + .map(|v| v.to_u128()) + .unwrap() + .unwrap(), + }, + signature: Signature::decode(&mut latest_rav.signature.as_slice()).unwrap(), + }; // Check that the latest RAV value is correct. assert!(latest_rav.message.value_aggregate >= trigger_value); @@ -827,7 +845,7 @@ mod tests { // Get the latest RAV from the database. let latest_rav = sqlx::query!( r#" - SELECT rav + SELECT signature, allocation_id, timestamp_ns, value_aggregate FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, @@ -836,12 +854,24 @@ mod tests { ) .fetch_optional(&pgpool) .await - .map(|r| r.map(|r| r.rav)) + .unwrap() .unwrap(); - let latest_rav = latest_rav - .map(|r| serde_json::from_value::(r).unwrap()) - .unwrap(); + let latest_rav = EIP712SignedMessage { + message: ReceiptAggregateVoucher { + allocation_id: Address::from_str(&latest_rav.allocation_id).unwrap(), + timestamp_ns: latest_rav.timestamp_ns.to_u64().unwrap(), + // Beware, BigDecimal::to_u128() actually uses to_u64() under the hood... + // So we're converting to BigInt to get a proper implementation of to_u128(). + value_aggregate: latest_rav + .value_aggregate + .to_bigint() + .map(|v| v.to_u128()) + .unwrap() + .unwrap(), + }, + signature: Signature::decode(&mut latest_rav.signature.as_slice()).unwrap(), + }; // Check that the latest RAV value is correct. diff --git a/tap-agent/src/tap/sender_allocation.rs b/tap-agent/src/tap/sender_allocation.rs index 24078481d..1ac9d216a 100644 --- a/tap-agent/src/tap/sender_allocation.rs +++ b/tap-agent/src/tap/sender_allocation.rs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use std::sync::Mutex as StdMutex; -use std::{str::FromStr, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use alloy_primitives::hex::ToHex; use alloy_sol_types::Eip712Domain; use anyhow::{anyhow, ensure, Result}; +use bigdecimal::num_bigint::BigInt; use eventuals::Eventual; use indexer_common::{escrow_accounts::EscrowAccounts, prelude::SubgraphClient}; use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; @@ -146,7 +147,7 @@ impl SenderAllocation { r#" WITH rav AS ( SELECT - rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns + timestamp_ns FROM scalar_tap_ravs WHERE @@ -323,7 +324,9 @@ impl SenderAllocation { self.allocation_id.encode_hex::(), self.sender.encode_hex::(), BigDecimal::from(received_receipt.signed_receipt().message.timestamp_ns), - BigDecimal::from_str(&received_receipt.signed_receipt().message.value.to_string())?, + BigDecimal::from(BigInt::from( + received_receipt.signed_receipt().message.value + )), serde_json::to_value(received_receipt)? ) .execute(&self.pgpool) diff --git a/tap-agent/src/tap/test_utils.rs b/tap-agent/src/tap/test_utils.rs index d0cf8b7db..9f2b895b6 100644 --- a/tap-agent/src/tap/test_utils.rs +++ b/tap-agent/src/tap/test_utils.rs @@ -6,9 +6,10 @@ use std::str::FromStr; use alloy_primitives::hex::ToHex; use alloy_sol_types::{eip712_domain, Eip712Domain}; use anyhow::Result; +use bigdecimal::num_bigint::BigInt; use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; use lazy_static::lazy_static; -use serde_json; +use open_fastrlp::Encodable; use sqlx::{types::BigDecimal, PgPool}; use tap_core::receipt_aggregate_voucher::ReceiptAggregateVoucher; use tap_core::tap_manager::{SignedRAV, SignedReceipt}; @@ -95,22 +96,27 @@ pub async fn create_rav( } pub async fn store_receipt(pgpool: &PgPool, signed_receipt: SignedReceipt) -> Result { + let encoded_signature = { + let mut buf: Vec = Vec::with_capacity(72); + signed_receipt.signature.encode(&mut buf); + buf + }; + let record = sqlx::query!( r#" - INSERT INTO scalar_tap_receipts ( - allocation_id, signer_address, timestamp_ns, value, receipt - ) - VALUES ($1, $2, $3, $4, $5) + INSERT INTO scalar_tap_receipts (signer_address, signature, allocation_id, timestamp_ns, nonce, value) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING id "#, - signed_receipt.message.allocation_id.encode_hex::(), signed_receipt .recover_signer(&TAP_EIP712_DOMAIN_SEPARATOR) .unwrap() .encode_hex::(), + encoded_signature, + signed_receipt.message.allocation_id.encode_hex::(), BigDecimal::from(signed_receipt.message.timestamp_ns), - BigDecimal::from_str(&signed_receipt.message.value.to_string())?, - serde_json::to_value(signed_receipt)? + BigDecimal::from(signed_receipt.message.nonce), + BigDecimal::from(BigInt::from(signed_receipt.message.value)), ) .fetch_one(pgpool) .await?; @@ -121,16 +127,19 @@ pub async fn store_receipt(pgpool: &PgPool, signed_receipt: SignedReceipt) -> Re } pub async fn store_rav(pgpool: &PgPool, signed_rav: SignedRAV, sender: Address) -> Result<()> { - sqlx::query!( + let mut signature_bytes: Vec = Vec::with_capacity(72); + signed_rav.signature.encode(&mut signature_bytes); + + let _fut = sqlx::query!( r#" - INSERT INTO scalar_tap_ravs ( - allocation_id, sender_address, rav - ) - VALUES ($1, $2, $3) + INSERT INTO scalar_tap_ravs (sender_address, signature, allocation_id, timestamp_ns, value_aggregate) + VALUES ($1, $2, $3, $4, $5) "#, - signed_rav.message.allocation_id.encode_hex::(), sender.encode_hex::(), - serde_json::to_value(signed_rav).unwrap(), + signature_bytes, + signed_rav.message.allocation_id.encode_hex::(), + BigDecimal::from(signed_rav.message.timestamp_ns), + BigDecimal::from(BigInt::from(signed_rav.message.value_aggregate)), ) .execute(pgpool) .await?;