mirror of
https://github.com/vtnerd/monero-lws.git
synced 2024-11-16 09:17:37 +00:00
Add /validate endpoint to admin REST api (#81)
This commit is contained in:
parent
3b35ce2845
commit
10dc4801d7
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":[...]}`
|
||||
* [**reject_requests**](#reject_requests): `{"type": "import"|"create", "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:
|
||||
* **token**: A string to be returned when the webhook is triggered
|
||||
* **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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -777,6 +777,7 @@ namespace lws
|
|||
{"/modify_account_status", call_admin<rpc::modify_account_>, 50 * 1024},
|
||||
{"/reject_requests", call_admin<rpc::reject_requests_>, 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_delete", call_admin<rpc::webhook_delete_>, 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));
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
switch (req.type)
|
||||
|
|
|
@ -70,6 +70,14 @@ namespace rpc
|
|||
};
|
||||
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
|
||||
{
|
||||
std::string url;
|
||||
|
@ -147,6 +155,13 @@ namespace rpc
|
|||
};
|
||||
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_
|
||||
{
|
||||
using request = webhook_add_req;
|
||||
|
@ -177,5 +192,4 @@ namespace rpc
|
|||
{ return (*this)(dest, std::move(disk)); }
|
||||
};
|
||||
constexpr const webhook_list_ webhook_list{};
|
||||
|
||||
}} // lws // rpc
|
||||
|
|
Loading…
Reference in a new issue