monero-lws/docs/administration.md
2023-09-27 16:50:12 -04:00

20 KiB

monero-lws Administration

The monero-lws-admin executable or --admin-rest-server option in the monero-lws-daemon executable can be used to administer the database used by monero-lws-daemon. Any number of monero-lws-admin instances can run concurrently with a single monero-lws-daemon instance on the same database. Administration is necessary to authorize new accounts and rescan requests submitted from the REST API. The admin executable can also be used to list the contents of the LMDB file for debugging purposes.

monero-lws-admin

The monero-lws-admin utility is structured around command-line arguments with JSON responses printed to stdout. Each administration command takes arguments by position. Every available administration command and required+optional arguments are listed when the --help flag is given to the executable.

The jq utility is recommended if using monero-lws-admin in a shell environment. The jq program can be used for indenting the output to make it more readable, and can be used to search+filter the JSON output from the command.

Admin REST API

The monero-lws-daemon can be started with 1+ --admin-rest-server parameters that specify a listening location for admin REST clients. By default, there is no admin REST server and no available admin accounts.

An admin REST server can be merged with a regular REST server if path prefixes are specified, such as --rest-server https://0.0.0.0:8443/basic --admin-rest-server https://0.0.0.0:8443/admin. This will start a server listening on one port, 8443, and requires clients to specify /basic/command or /admin/admin_command when making a request.

An admin account account can be created via monero-lws-admin create_admin only (this command is not available via REST for security purposes). The key value returned in the create_admin JSON object becomes the auth parameter in the admin REST API. A new admin account is put into the hidden state - the account is not scanned for transactions and is not available to the normal REST API, but is available to the admin REST API.

Running monero-lws-admin list_admin will display all current admin accounts, and their current state ("active", "inactive", or "hidden"). If an admin account needs to be revoked, use the modify_account command to put the account into the "inactive" state. Deleting accounts is not currently supported.

Every admin REST request must be a POST that contains a JSON object with an auth field (in default settings) and an optional params field:

{
  "auth":"...",
  "params":{...}
 }

where the params object is specified below. The auth field can be omitted if --disable-admin-auth is specified in the CLI arguments for the REST server.

Commands (of Admin REST API)

A subset of admin commands are available via admin REST API - the remainder are initially omitted for security purposes. The commands available via REST are:

where the listed object must be the params field above.

accept_requests

Accepts new account and rescan from block 0 requests in the incoming queue.

add_account

Add account for view-key scanning. An example of the JSON:

{
  "params": {
    "address": "9uTcr6T9GURRt7UADQc2rhjg5oMYBDyoQ5jgx8nAvVvs757WwDkc2vHLPJhwZfCnfVdnWNvuuKzJe8eMVTKwadYzBrYRG5j",
    "key": "deadbeef"
  },
  "auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"
}

list_accounts

Request a listing of all active accounts in the datbase. The request should looke like:

curl -v -H "Content-Type: application/json" -d '{}' http://127.0.0.1:8081/list_accounts

when auth is disabled, and when enabled:

curl -v -H "Content-Type: application/json" -d '{"auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"}' http://127.0.0.1:8081/list_accounts

The response will look something like:

{
  "active": [
    {
      "address": "9wRAu3giCtKhSsVnkZJ7LLE6zqzrmMKpPg39S8aoC7T6F6GobeDpz8TcvVfTQT3ucW82oTYKG8v3ZMAeh8SZVXWwMdvwZew",
      "scan_height": 2220875,
      "access_time": 1681244149
    }
  ]
}

list_requests

This is a listing of all pending new account requests and all requests to import from genesis block requests. When auth is disabled usage looks like:

curl -v -H "Content-Type: application/json" -d '{}' http://127.0.0.1:8081/list_requests

and with auth enabled looks like:

curl -v -H "Content-Type: application/json" -d '{"auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"}' http://127.0.0.1:8081/list_requests

modify_account_status

This can change an account status to active, inactive or hidden. The active state is the normal state - the account is being scanned and returned by the API. The inactive state is still returned by the API, but is no longer being scanned. The hidden is the current way to "delete" an account - it is not scanned nor returned by the API. Accounts cannot currently be deleted due to internal DB requirements.

reject_requests

This is the opposite of accept_requests above. See information from that endpoint on how to use this one.

rescan

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

{
  "params": {
    "view_public_hex": "3e77c1ee5a396cd6c3f68ce343882b2a09317649d6501078911fb17a1adac0b6",
    "spend_public_hex": "7028d918af2cc4f1cfa499cca2ef014e57124982b99004e9630caf0b25c13954",
    "view_key_hex": "80..."
  },
  "auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"
}

Example Failure Return

{
  "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

{
  "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 requires a URL for callback purposes.

When the event type is tx-confirmation, this endpint requires a web address for callback purposes, a primary (not integrated!) address, and finally the type ("tx-confirmation"). The event will remain in the database until one of the delete commands (webhook_delete_uuid or webhook_delete) is used to remove it.

When the event type is new-account, this endpoint requires a web address for callback purposes, and the type ("new-account"). Spurious information will be returned for this endpoint to simplify the server implementation (i.e. several fields returned in the initial call are not useful to new account creations).

The provided URL will use SSL/TLS if https:// is prefixed in the URL and will use plaintext if http:// is prefixed in the URL. If zmq is provided as the callback, notifications are performed only over the ZMQ pub socket. SSL/TLS connections will use the system certificate authority (root-CAs) by default, and will ignore all authority checks if --webhook-ssl-verification none is provided on the command line when starting monero-lws-daemon. The webhook will fail if there is a mismatch of http and https between the two servers, and will also fail if https verification is mismatched. The rule is: (1) if the callback server has SSL/TLS disabled, the webhook should use http://, (2) if the callback server has a self-signed certificate, https:// and --webhook-ssl-verification none should be used, and (3) if the callback server is using "Let's Encrypt" (or similar), then https:// with no additional command line flag should be used.

tx-confirmation

Initial Request to server

Example where admin authentication is required (--disable-admin-auth NOT set on start which is the default):

{
  "auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09",
  "params": {
    "type": "tx-confirmation",
    "url": "http://127.0.0.1:7000",  
    "payment_id": "df034c176eca3296",
    "token": "1234",
    "address": "9uTcr6T9GURRt7UADQc2rhjg5oMYBDyoQ5jgx8nAvVvs757WwDkc2vHLPJhwZfCnfVdnWNvuuKzJe8eMVTKwadYzBrYRG5j"
  }
}

Example where admin authentication is not required (--disable-admin-auth set on start):

{
  "params": {
    "type": "tx-confirmation",
    "url": "http://127.0.0.1:7000",  
    "payment_id": "df034c176eca3296",
    "token": "1234",
    "address": "9uTcr6T9GURRt7UADQc2rhjg5oMYBDyoQ5jgx8nAvVvs757WwDkc2vHLPJhwZfCnfVdnWNvuuKzJe8eMVTKwadYzBrYRG5j"
  }
}

As noted above - payment_id and token are both optional - token will default to the empty string, and payment_id will default to zero.

Initial Response from Server

The server will replay all values back to the user for confirmation. An
additional field - event_id - is also returned which contains a globally unique value (internally this is a 128-bit UUID).

Example response:

{
  "payment_id": "df034c176eca3296",
  "event_id": "fa10a4db485145f1a24dc09c19a79d43",
  "token": "1234",
  "confirmations": 1,
  "url": "http://127.0.0.1:7000"
}

If you use the debug_database command provided by the monero-lws-admin executable, the event should be listed in the webhooks_by_account_id,payment_id field of the returned JSON object. The event will remain in the database until an explicit webhook_delete_uuid is invoked.

Callback from Server

When the event "fires" due to a transaction, the provided URL is invoked with a JSON payload that looks like the below:

{
  "event": "tx-confirmation",
  "payment_id": "df034c176eca3296",
  "token": "1234",
  "confirmations": 1,
  "id": "fa10a4db485145f1a24dc09c19a79d43",
  "tx_info": {
    "id": {
      "high": 0,
      "low": 5550229
    },
    "block": 2192100,
    "index": 0,
    "amount": 4949570000,
    "timestamp": 1678324181,
    "tx_hash": "901f9a2a919b6312131537ff6117d56ce2c0dc1f1341b845d7667299e1ef892f",
    "tx_prefix_hash": "89685cb7acb836fde30fae8be5d8b884e92706df086960d0508e146979ef80dc",
    "tx_public": "54c153792e47c1da8ceb3979560c424c1928b7b4a089c1c8b3ce99c563e1d240",
    "rct_mask": "f3449407dc3721299b5309c0c336a17daeebce55165ddd447ba28bbd1f46c201",
    "payment_id": "df034c176eca3296",
    "unlock_time": 0,
    "mixin_count": 15,
    "coinbase": false
  }
}

which is the same information provided by the user API. The database will contain an entry in the webhook_events_by_account_id,type,block_id,tx_hash,output_id,payment_id,event_id field of the JSON object provided by the debug_database command. The entry will be removed when the number of confirmations has been reached.

new-account

Initial Request to server

Example where admin authentication is required (--disable-admin-auth NOT set on start which is the default):

{
  "auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09",
  "params": {
    "type": "new-account",
    "url": "http://127.0.0.1:7001",
    "token": "1234"
  }
}

Example where admin authentication is not required (--disable-admin-auth set on start):

{
  "params": {
    "type": "new-account",
    "url": "http://127.0.0.1:7001",
    "token": "1234"
  }
}

As noted above - token is optional - it will default to the empty string.

Initial Response from Server

The server will replay all values back to the user for confirmation. An additional field - event_id - is also returned which contains a globally unique value (internally this is a 128-bit UUID). The fields confirmations, and payment_id are sent to simplify the backend, and can be ignored when the type is new-account.

Example response:

{
  "payment_id": "0000000000000000",
  "event_id": "c5a735e71b1e4f0a8bfaeff661d0b38a"",
  "token": "1234",
  "confirmations": 1,
  "url": "http://127.0.0.1:7000"
}

If you use the debug_database command provided by the monero-lws-admin executable, the event should be listed in the webhooks_by_account_id,payment_id field of the returned JSON object. The event will remain in the database until an explicit webhook_delete_uuid is invoked.

Callback from Server

When the event "fires" due to a new account creation, the provided URL is invoked with a JSON payload that looks like the below:

{
  "event_id": "c5a735e71b1e4f0a8bfaeff661d0b38a",
  "token": "",
  "address": "9zGwnfWRMTF9nFVW9DNKp46aJ43CRtQBWNFvPqFVSN3RUKHuc37u2RDi2GXGp1wRdSRo5juS828FqgyxkumDaE4s9qyyi9B"
}

webhook_delete

Deletes all webhooks associated with a specific Monero primary address.

webhook_delete_uuid

Deletes all references to a specific webhook referenced by its UUID (event_id)

webhook_list

This will list every webhook that is currently "listening" for incoming transactions. If the server has auth disabled, the request is simply:

curl -v -H "Content-Type: application/json" -d '{}' http://127.0.0.1:8081/webhook_list

and with auth enabled looks like:

curl -v -H "Content-Type: application/json" -d '{"auth": "f50922f5fcd186eaa4bd7070b8072b66fea4fd736f06bd82df702e2314187d09"}' http://127.0.0.1:8081/webhook_list

which returns a JSON object that looks like:

{
  "webhooks": [
    {
      "key": {
        "user": 1,
        "type": "tx-confirmation"
      },
      "value": [
        {
          "payment_id": "9bc1a59b34253896",
          "event_id": "4dc201838af54dfe88686bea7e2b599f",
          "token": "12345",
          "confirmations": 5,
          "url": "http://127.0.0.1:8082"
        },
        {
          "payment_id": "9bc1a59b34253896",
          "event_id": "615171e477464401a1a23cdb45b3b433",
          "token": "12345",
          "confirmations": 5,
          "url": "http://127.0.0.1:8082"
        },
        {
          "payment_id": "9bc1a59b34253896",
          "event_id": "e64be3ad6d1647618fbd292be0485901",
          "token": "this is a fresh test",
          "confirmations": 1,
          "url": "http://127.0.0.1:8082/foobar"
        },
        {
          "payment_id": "9bc1a59b34253896",
          "event_id": "fe692cdf7de1453898ad453d8fabce42",
          "token": "12345",
          "confirmations": 5,
          "url": "http://127.0.0.1:8082/foobar"
        }
      ]
    }
  ]
}

Examples

Admin REST API

Default Settings

{
  "auth":"6d732245002a9499b3842c0a7f9fc6b2d657c77bd612dbefa4f7f9357d08530a",
  "params":{
    "status": "inactive",
    "addresses": ["9sAejnQ9EBR1111111111111111111111111111111111AdYmVTw2Tv6L9KYkHjJ2wd737ov8ZL5QU7CJ4zV6basGP9fyno"]
  }
 }

will put the listed address into the "inactive" state.

--disable-admin-auth Setting

{
  "params":{
    "status": "inactive",
    "addresses": ["9sAejnQ9EBR1111111111111111111111111111111111AdYmVTw2Tv6L9KYkHjJ2wd737ov8ZL5QU7CJ4zV6basGP9fyno"]
  }
 }

monero-lws-admin

List every active Monero address on a newline:

monero-lws-admin list_accounts | jq -r '.active | .[] | .address'

Auto-accept every pending account creation request:

monero-lws-admin accept_requests create $(monero-lws-admin list_requests | jq -j '.create? | .[]? | .address?+" "')

Debugging

monero-lws-admin has a debug mode that dumps everything stored in the database, except the blockchain hashes are always truncated and viewkeys are omitted by default (a command-line flag can enable viewkey output). Most of the array outputs are sorted to accelerate jq filtering and search queries.

Indexes

  • blocks_by_id - array of objects sorted by block height.
  • accounts_by_status,id - A single object where account status names are keys. Each value is an array of objects sorted by account id.
  • accounts_by_address - A single object where account addresses are keys. Each value is an object containing the status and account id for the account for lookup in accounts_by_status,id. The majority of account lookups should be done by this id (an integer).
  • accounts_by_height,id - An array of objects sorted by block height. These objects contain another array of objects sorted by account id.
  • outputs_by_account_id,block_id,tx_hash,output_id - An object where keys are account ids. Each value is an array of objects sorted by block height, transaction hash, then by output number.
  • spends_by_account_id,block_id,tx_hash,image - An object where keys are account ids. Each value is an array of objects sorted by block height, transaction hash, then by key image.
  • requests_by_type,address - An object where keys are request type, and each value is an array of objects sorted by address.

Examples

List every key-image associated with every account:

monenero-lws-admin debug_database | jq '."spends_by_account_id,block_id,tx_hash,output_id" | map_values([.[] | .image])'

will output something like:

{"1":["image1", "image2",...],"2":["image1","image2"...],...}

List every account that received XMR in a given transaction hash:

monenero-lws-admin debug_database | jq '."outputs_by_account_id,block_id,tx_hash,output_id" | map_values(select([.[] | .tx_hash == "hash"] | any)) | keys'

will output somethng like:

{"1",...}

Add total received XMR for every account:

monenero-lws-admin debug_database | jq '."outputs_by_account_id,block_id,tx_hash,output_id" | map_values([.[] | .amount] | add)'

will output something like:

{"1":6346,"2":45646}

Extending Administration in monero-lws

JSON via stdin

Some commands take sensitive information such as private view keys, and therefore reading arguments from stdin via JSON array would also be useful for those situations. This should be a relatively straightforward adaptation given the design of the positional arguments.

Administration via ZeroMQ

The LMDB database does account lookups by view-public only, so that CurveZMQ (which uses curve25519) can be used to authenticate an administration account without additional protocol overhead. The parameters to administration commands can be sent via JSON or MsgPack array since the functions already use positional arguments.