mirror of
https://github.com/vtnerd/monero-lws.git
synced 2025-05-12 13:52:20 +00:00
Add /validate endpoint to admin REST api
This commit is contained in:
parent
2872ba9ecb
commit
46b676b0f0
4 changed files with 122 additions and 1 deletions
|
@ -68,6 +68,7 @@ are:
|
||||||
* [**modify_account_status**](#modify_account_status): `{"status": "active"|"hidden"|"inactive", "addresses":[...]}`
|
* [**modify_account_status**](#modify_account_status): `{"status": "active"|"hidden"|"inactive", "addresses":[...]}`
|
||||||
* [**reject_requests**](#reject_requests): `{"type": "import"|"create", "addresses":[...]}`
|
* [**reject_requests**](#reject_requests): `{"type": "import"|"create", "addresses":[...]}`
|
||||||
* [**rescan**](#rescan): `{"height":..., "addresses":[...]}`
|
* [**rescan**](#rescan): `{"height":..., "addresses":[...]}`
|
||||||
|
* [**validate**](#validate): `{"spend_public_hex":..., "view_public_hex":..., "view_key_hex":...}`
|
||||||
* [**webhook_add**](#webhook_add): `{"type":"tx-confirmation", "address":"...", "url":"...", ...}` with optional fields:
|
* [**webhook_add**](#webhook_add): `{"type":"tx-confirmation", "address":"...", "url":"...", ...}` with optional fields:
|
||||||
* **token**: A string to be returned when the webhook is triggered
|
* **token**: A string to be returned when the webhook is triggered
|
||||||
* **payment_id**: 16 hex characters representing a unique identifier for a transaction
|
* **payment_id**: 16 hex characters representing a unique identifier for a transaction
|
||||||
|
@ -143,6 +144,46 @@ information from that endpoint on how to use this one.
|
||||||
This tells the scanner to rescan specific account(s) from the specified
|
This tells the scanner to rescan specific account(s) from the specified
|
||||||
height.
|
height.
|
||||||
|
|
||||||
|
### validate
|
||||||
|
This takes the spend_public, view_public, and view key all as hex, and then
|
||||||
|
does basic validation for the caller: (1) that each value is 64 hex-ascii
|
||||||
|
characters, (2) that the public keys are valid ed25519 points, and that (3)
|
||||||
|
the view_key matches the view_public.
|
||||||
|
|
||||||
|
The return value is the `address` on success, and `error` object on
|
||||||
|
validation failure:
|
||||||
|
|
||||||
|
#### Example Request
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"params": {
|
||||||
|
"view_public_hex": "3e77c1ee5a396cd6c3f68ce343882b2a09317649d6501078911fb17a1adac0b6",
|
||||||
|
"spend_public_hex": "7028d918af2cc4f1cfa499cca2ef014e57124982b99004e9630caf0b25c13954",
|
||||||
|
"view_key_hex": "80..."
|
||||||
|
},
|
||||||
|
"auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example Failure Return
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"field": "view_public_hex",
|
||||||
|
"details": "Invalid public key format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
HTTP error codes are still returned if the JSON itself is invalid.
|
||||||
|
|
||||||
|
#### Example Success Return
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"address": "9wRAu3giCtKhSsVnkZJ7LLE6zqzrmMKpPg39S8aoC7T6F6GobeDpz8TcvVfTQT3ucW82oTYKG8v3ZMAeh8SZVXWwMdvwZew"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### webhook_add
|
### webhook_add
|
||||||
This is used to track events happening in the database: (1) a new payment to
|
This is used to track events happening in the database: (1) a new payment to
|
||||||
an optional payment_id, or (2) a new account creation. This endpoint always
|
an optional payment_id, or (2) a new account creation. This endpoint always
|
||||||
|
|
|
@ -777,6 +777,7 @@ namespace lws
|
||||||
{"/modify_account_status", call_admin<rpc::modify_account_>, 50 * 1024},
|
{"/modify_account_status", call_admin<rpc::modify_account_>, 50 * 1024},
|
||||||
{"/reject_requests", call_admin<rpc::reject_requests_>, 50 * 1024},
|
{"/reject_requests", call_admin<rpc::reject_requests_>, 50 * 1024},
|
||||||
{"/rescan", call_admin<rpc::rescan_>, 50 * 1024},
|
{"/rescan", call_admin<rpc::rescan_>, 50 * 1024},
|
||||||
|
{"/validate", call_admin<rpc::validate_>, 50 * 1024},
|
||||||
{"/webhook_add", call_admin<rpc::webhook_add_>, 50 * 1024},
|
{"/webhook_add", call_admin<rpc::webhook_add_>, 50 * 1024},
|
||||||
{"/webhook_delete", call_admin<rpc::webhook_delete_>, 50 * 1024},
|
{"/webhook_delete", call_admin<rpc::webhook_delete_>, 50 * 1024},
|
||||||
{"/webhook_delete_uuid", call_admin<rpc::webhook_del_uuid_>,50 * 1024},
|
{"/webhook_delete_uuid", call_admin<rpc::webhook_del_uuid_>,50 * 1024},
|
||||||
|
|
|
@ -167,6 +167,10 @@ namespace lws { namespace rpc
|
||||||
{
|
{
|
||||||
read_addresses(source, self, WIRE_FIELD(height));
|
read_addresses(source, self, WIRE_FIELD(height));
|
||||||
}
|
}
|
||||||
|
void read_bytes(wire::reader& source, validate_req& self)
|
||||||
|
{
|
||||||
|
wire::object(source, WIRE_FIELD(spend_public_hex), WIRE_FIELD(view_public_hex), WIRE_FIELD(view_key_hex));
|
||||||
|
}
|
||||||
void read_bytes(wire::reader& source, webhook_add_req& self)
|
void read_bytes(wire::reader& source, webhook_add_req& self)
|
||||||
{
|
{
|
||||||
boost::optional<std::string> address;
|
boost::optional<std::string> address;
|
||||||
|
@ -237,6 +241,67 @@ namespace lws { namespace rpc
|
||||||
return write_addresses(dest, disk.rescan(req.height, epee::to_span(req.addresses)));
|
return write_addresses(dest, disk.rescan(req.height, epee::to_span(req.addresses)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct validate_error
|
||||||
|
{
|
||||||
|
std::string field;
|
||||||
|
std::string details;
|
||||||
|
};
|
||||||
|
|
||||||
|
void write_bytes(wire::writer& dest, const validate_error& self)
|
||||||
|
{
|
||||||
|
wire::object(dest, WIRE_FIELD(field), WIRE_FIELD(details));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect<void> output_error(wire::writer& dest, std::string field, std::string details)
|
||||||
|
{
|
||||||
|
wire::object(dest, wire::field("error", validate_error{std::move(field), std::move(details)}));
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool convert_key(wire::writer& dest, T& out, const boost::string_ref in, const boost::string_ref field)
|
||||||
|
{
|
||||||
|
if (in.size() != sizeof(out) * 2)
|
||||||
|
{
|
||||||
|
output_error(dest, std::string{field}, "Expected " + std::to_string(sizeof(out) * 2) + " characters");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(out), in))
|
||||||
|
{
|
||||||
|
output_error(dest, std::string{field}, "Invalid hex");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect<void> validate_::operator()(wire::writer& dest, const db::storage&, const request& req) const
|
||||||
|
{
|
||||||
|
db::account_address address{};
|
||||||
|
crypto::secret_key view_key{};
|
||||||
|
|
||||||
|
if (!convert_key(dest, address.spend_public, req.spend_public_hex, "spend_public_hex"))
|
||||||
|
return success(); // error is delivered in JSON as opposed to HTTP codes
|
||||||
|
if (!convert_key(dest, address.view_public, req.view_public_hex, "view_public_hex"))
|
||||||
|
return success();
|
||||||
|
if (!convert_key(dest, unwrap(unwrap(view_key)), req.view_key_hex, "view_key_hex"))
|
||||||
|
return success();
|
||||||
|
|
||||||
|
if (!crypto::check_key(address.spend_public))
|
||||||
|
return output_error(dest, "spend_public_hex", "Invalid public key format");
|
||||||
|
if (!crypto::check_key(address.view_public))
|
||||||
|
return output_error(dest, "view_public_hex", "Invalid public key format");
|
||||||
|
|
||||||
|
crypto::public_key test{};
|
||||||
|
if (!crypto::secret_key_to_public_key(view_key, test) || test != address.view_public)
|
||||||
|
return output_error(dest, "view_key_hex", "view_key and view_public do not match");
|
||||||
|
|
||||||
|
wire::object(dest, wire::field("address", db::address_string(address)));
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
expect<void> webhook_add_::operator()(wire::writer& dest, db::storage disk, request&& req) const
|
expect<void> webhook_add_::operator()(wire::writer& dest, db::storage disk, request&& req) const
|
||||||
{
|
{
|
||||||
switch (req.type)
|
switch (req.type)
|
||||||
|
|
|
@ -70,6 +70,14 @@ namespace rpc
|
||||||
};
|
};
|
||||||
void read_bytes(wire::reader&, rescan_req&);
|
void read_bytes(wire::reader&, rescan_req&);
|
||||||
|
|
||||||
|
struct validate_req
|
||||||
|
{
|
||||||
|
std::string spend_public_hex;
|
||||||
|
std::string view_public_hex;
|
||||||
|
std::string view_key_hex;
|
||||||
|
};
|
||||||
|
void read_bytes(wire::reader&, validate_req&);
|
||||||
|
|
||||||
struct webhook_add_req
|
struct webhook_add_req
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
|
@ -147,6 +155,13 @@ namespace rpc
|
||||||
};
|
};
|
||||||
constexpr const rescan_ rescan{};
|
constexpr const rescan_ rescan{};
|
||||||
|
|
||||||
|
struct validate_
|
||||||
|
{
|
||||||
|
using request = validate_req;
|
||||||
|
expect<void> operator()(wire::writer& dest, const db::storage&, const request& req) const;
|
||||||
|
};
|
||||||
|
constexpr const validate_ validate{};
|
||||||
|
|
||||||
struct webhook_add_
|
struct webhook_add_
|
||||||
{
|
{
|
||||||
using request = webhook_add_req;
|
using request = webhook_add_req;
|
||||||
|
@ -177,5 +192,4 @@ namespace rpc
|
||||||
{ return (*this)(dest, std::move(disk)); }
|
{ return (*this)(dest, std::move(disk)); }
|
||||||
};
|
};
|
||||||
constexpr const webhook_list_ webhook_list{};
|
constexpr const webhook_list_ webhook_list{};
|
||||||
|
|
||||||
}} // lws // rpc
|
}} // lws // rpc
|
||||||
|
|
Loading…
Reference in a new issue