mirror of
https://github.com/vtnerd/monero-lws.git
synced 2025-04-06 21:57:29 +00:00
Fix spends output, and add docs
This commit is contained in:
parent
d8bfdb8a9d
commit
afdb5d4d0b
6 changed files with 126 additions and 21 deletions
59
docs/zmq.md
59
docs/zmq.md
|
@ -19,6 +19,10 @@ option. Users are still required to "subscribe" to topics:
|
|||
with their new height and block hash.
|
||||
* `msgpack-minimal-scanned:` A msgpack object of a list of user primary
|
||||
addresses with their new height and block hash.
|
||||
* `json-full-spend_hook': A JSON object of a webhook spend event that has
|
||||
recently triggerd (identical output as webhook).
|
||||
* `msgpack-full-spend_hook`: A msgpack object of a single new account
|
||||
creation that has recently triggered (identical output as webhook).
|
||||
|
||||
|
||||
### `json-full-payment_hook`/`msgpack-full-payment_hook`
|
||||
|
@ -100,7 +104,6 @@ where matching is done by string prefixes.
|
|||
|
||||
> `index` is a counter used to detect dropped messages.
|
||||
|
||||
|
||||
### `json-minimal-scanned`/`msgpack-minimal-scanned`
|
||||
These topics receive PUB messages when a thread has finished scanning 1+
|
||||
accounts. The last block height and hash is sent.
|
||||
|
@ -115,9 +118,61 @@ json-minimal-scanned:{
|
|||
"addresses": [
|
||||
"9xkhhJSa7ZhS5sAcTix6ozL14RwdgxbV7JZVFW4rCghN7GidutaykfxDHfgW45UPiCTXncuvZ91GNSGgxs3b2Cin9TU8nP3"
|
||||
]
|
||||
|
||||
> `index` is a counter used to detect dropped messages.
|
||||
|
||||
### `json-full-spend_hook`/`msgpack-full-spend_hook`
|
||||
These topics receive PUB messages when a webhook ([`webhook_add`](administration.md)),
|
||||
event is triggered for a spend (`tx-spend`). If the specified URL is
|
||||
`zmq`, then notifications are only done over the ZMQ-PUB socket, otherwise the
|
||||
notification is sent over ZMQ-PUB socket AND the specified URL. Invoking
|
||||
`webhook_add` with a `payment_id` or `confirmation` results in a NOP because
|
||||
both fields are unused for spends. This event is only triggered on
|
||||
confirmation==1 (`confirmation` field on `webhook_add`s have no effect, and
|
||||
mempool spends are not scanned). The intent is to notify the user of unexpected
|
||||
spend operations. The end user will need to use `tx_info.input.image`,
|
||||
`tx_info.source.index`, and `tx_info.source.tx_public` to determine if the
|
||||
output was actually spent or being used as a decoy.
|
||||
|
||||
Example of the "raw" output from ZMQ-SUB side:
|
||||
|
||||
```json
|
||||
json-full-spend_hook:{
|
||||
"index": 0,
|
||||
"event": {
|
||||
"event": "tx-spend",
|
||||
"token": "spend-xmr",
|
||||
"event_id": "7ff047aa74e14f4aa978469bc0eec8ec",
|
||||
"tx_info": {
|
||||
"input": {
|
||||
"height": 2464207,
|
||||
"tx_hash": "97d4e66c4968b16fec7662adc9f8562c49108d3c5e7030c4d6dd32d97fb62540",
|
||||
"image": "b0fe7acd9e17bb8b9ac2daae36d4cb607ac60ed8a101cc9b2e1f74016cf80b24",
|
||||
"source": {
|
||||
"high": 0,
|
||||
"low": 6246316
|
||||
},
|
||||
"timestamp": 1711902214,
|
||||
"unlock_time": 0,
|
||||
"mixin_count": 15,
|
||||
"sender": {
|
||||
"maj_i": 0,
|
||||
"min_i": 0
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"id": {
|
||||
"high": 0,
|
||||
"low": 6246316
|
||||
},
|
||||
"amount": 10000000000,
|
||||
"mixin": 15,
|
||||
"index": 0,
|
||||
"tx_public": "426ccd6d39535a1ee8636d14978581e580fcea35c8d3843ceb32eb688a0197f7"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> `index` is a counter used to detect dropped messages
|
||||
|
||||
|
|
|
@ -423,6 +423,22 @@ namespace db
|
|||
);
|
||||
}
|
||||
|
||||
static void write_bytes(wire::writer& dest, const output::spend_meta_& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
WIRE_FIELD_ID(0, id),
|
||||
wire::field<1>("amount", self.amount),
|
||||
wire::field<2>("mixin", self.mixin_count),
|
||||
wire::field<3>("index", self.index),
|
||||
WIRE_FIELD_ID(4, tx_public)
|
||||
);
|
||||
}
|
||||
|
||||
static void write_bytes(wire::writer& dest, const webhook_tx_spend::tx_info_& self)
|
||||
{
|
||||
wire::object(dest, WIRE_FIELD_ID(0, input), WIRE_FIELD_ID(1, source));
|
||||
}
|
||||
|
||||
void write_bytes(wire::writer& dest, const webhook_tx_spend& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
|
|
|
@ -390,7 +390,11 @@ namespace db
|
|||
{
|
||||
webhook_key key;
|
||||
webhook_value value;
|
||||
spend tx_info;
|
||||
struct tx_info_
|
||||
{
|
||||
spend input;
|
||||
output::spend_meta_ source;
|
||||
} tx_info;
|
||||
};
|
||||
void write_bytes(wire::writer&, const webhook_tx_spend&);
|
||||
|
||||
|
|
|
@ -2574,9 +2574,10 @@ namespace db
|
|||
return success();
|
||||
}
|
||||
|
||||
expect<void> check_spends(std::vector<webhook_tx_spend>& out, MDB_cursor& webhooks_cur, const lws::account& user)
|
||||
expect<void> check_spends(std::vector<webhook_tx_spend>& out, MDB_cursor& webhooks_cur, MDB_cursor& outputs_cur, const lws::account& user)
|
||||
{
|
||||
const webhook_key hook_key{user.id(), webhook_type::tx_spend};
|
||||
const account_id user_id = user.id();
|
||||
const webhook_key hook_key{user_id, webhook_type::tx_spend};
|
||||
MDB_val key = lmdb::to_val(hook_key);
|
||||
MDB_val value{};
|
||||
|
||||
|
@ -2588,7 +2589,6 @@ namespace db
|
|||
{
|
||||
if (err != MDB_NOTFOUND)
|
||||
return {lmdb::error(err)};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2598,8 +2598,25 @@ namespace db
|
|||
out.reserve(user.spends().size());
|
||||
for (const spend& s : user.spends())
|
||||
{
|
||||
key = lmdb::to_val(user_id);
|
||||
value = lmdb::to_val(s.link.height);
|
||||
err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_GET_BOTH_RANGE);
|
||||
|
||||
expect<output::spend_meta_> meta{common_error::kInvalidArgument};
|
||||
for (;;)
|
||||
{
|
||||
if (err)
|
||||
return {lmdb::error(err)};
|
||||
meta = outputs.get_value<MONERO_FIELD(output, spend_meta)>(value);
|
||||
if (!meta)
|
||||
return meta.error();
|
||||
if (meta->id == s.source)
|
||||
break;
|
||||
err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_PREV_DUP);
|
||||
}
|
||||
|
||||
out.push_back(
|
||||
webhook_tx_spend{hook_key, *hook, s}
|
||||
webhook_tx_spend{hook_key, *hook, {s, *meta}}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2780,7 +2797,7 @@ namespace db
|
|||
out.confirm_pubs, *webhooks_cur, *outputs_cur, *events_cur, user->id(), block_id(first_new), block_id(last_update + 1)
|
||||
)
|
||||
);
|
||||
MONERO_CHECK(check_spends(out.spend_pubs, *webhooks_cur, *user));
|
||||
MONERO_CHECK(check_spends(out.spend_pubs, *webhooks_cur, *outputs_cur, *user));
|
||||
|
||||
++out.accounts_updated;
|
||||
} // ... for every account being updated ...
|
||||
|
|
|
@ -320,6 +320,10 @@ namespace lws { namespace rpc
|
|||
if (req.address)
|
||||
return {error::bad_webhook};
|
||||
break;
|
||||
case db::webhook_type::tx_spend:
|
||||
if (!req.address)
|
||||
return {error::bad_webhook};
|
||||
break;
|
||||
default:
|
||||
return {error::bad_webhook};
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace
|
|||
crypto::rand<crypto::hash>()
|
||||
},
|
||||
crypto::rand<crypto::key_image>(),
|
||||
lws::db::output_id{0, 200},
|
||||
lws::db::output_id{0, 100},
|
||||
std::uint64_t(2000),
|
||||
std::uint64_t(0),
|
||||
std::uint32_t(16),
|
||||
|
@ -273,14 +273,17 @@ LWS_CASE("db::storage::*_webhook")
|
|||
|
||||
lws::account full_account = lws::db::test::make_account(account, view);
|
||||
full_account.updated(last_block.id);
|
||||
add_spend(full_account, lws::db::block_id(105));
|
||||
EXPECT(add_out(full_account, last_block.id, 500));
|
||||
add_spend(full_account, last_block.id);
|
||||
const auto outs = full_account.outputs();
|
||||
const auto spends = full_account.spends();
|
||||
EXPECT(outs.size() == 1);
|
||||
EXPECT(spends.size() == 1);
|
||||
|
||||
const auto updated = db.update(last_block.id, chain, {std::addressof(full_account), 1}, nullptr);
|
||||
const auto updated = db.update(last_block.id, chain, {std::addressof(full_account), 1});
|
||||
EXPECT(!updated.has_error());
|
||||
EXPECT(updated->accounts_updated == 1);
|
||||
EXPECT(updated->confirm_pubs.empty());
|
||||
EXPECT(updated->confirm_pubs.size() == 3);
|
||||
EXPECT(updated->spend_pubs.size() == 1);
|
||||
|
||||
EXPECT(updated->spend_pubs[0].key.user == lws::db::account_id(1));
|
||||
|
@ -291,15 +294,21 @@ LWS_CASE("db::storage::*_webhook")
|
|||
EXPECT(updated->spend_pubs[0].value.second.token == "the_token_spend");
|
||||
EXPECT(updated->spend_pubs[0].value.second.confirmations == 0);
|
||||
|
||||
EXPECT(updated->spend_pubs[0].tx_info.link == spends[0].link);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.image == spends[0].image);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.timestamp == spends[0].timestamp);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.unlock_time == spends[0].unlock_time);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.mixin_count == spends[0].mixin_count);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.length == spends[0].length);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.payment_id == spends[0].payment_id);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.sender.maj_i == lws::db::major_index(1));
|
||||
EXPECT(updated->spend_pubs[0].tx_info.sender.min_i == lws::db::minor_index(0));
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.link == spends[0].link);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.image == spends[0].image);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.timestamp == spends[0].timestamp);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.unlock_time == spends[0].unlock_time);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.mixin_count == spends[0].mixin_count);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.length == spends[0].length);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.payment_id == spends[0].payment_id);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.sender.maj_i == lws::db::major_index(1));
|
||||
EXPECT(updated->spend_pubs[0].tx_info.input.sender.min_i == lws::db::minor_index(0));
|
||||
|
||||
EXPECT(updated->spend_pubs[0].tx_info.source.id == outs[0].spend_meta.id);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.source.amount == outs[0].spend_meta.amount);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.source.mixin_count == outs[0].spend_meta.mixin_count);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.source.index == outs[0].spend_meta.index);
|
||||
EXPECT(updated->spend_pubs[0].tx_info.source.tx_public == outs[0].spend_meta.tx_public);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue