Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

daemon, wallet: new pay for RPC use system #5357

Merged
merged 3 commits into from
Oct 25, 2019

Conversation

moneromooo-monero
Copy link
Collaborator

No description provided.

@moneromooo-monero
Copy link
Collaborator Author

BTW one the main things to tweak are the costs defined in src/rpc/core_rpc_commands_defs.h, the #define COST_PER_... ones. They should be roughly proportional to the amount of resources expended by the server.

@moneromooo-monero
Copy link
Collaborator Author

Plus RPC error reporting changes, that'll probably get squashed into the main commit later.

@moneromooo-monero moneromooo-monero force-pushed the share-rpc branch 9 times, most recently from b03341a to 791e54f Compare March 31, 2019 12:23
@moneromooo-monero
Copy link
Collaborator Author

The client now spots when a top block changes after a RPC so it can update and mine on the new block straight away.

@moneromooo-monero moneromooo-monero force-pushed the share-rpc branch 3 times, most recently from 44cdf8d to f1c1750 Compare April 6, 2019 16:31
payments.insert(nonce);

if (info.hashing_blob.size() < 43)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this magic number 43 come from?

Copy link
Collaborator Author

@moneromooo-monero moneromooo-monero Jun 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's to match:

#define VARIANT1_CHECK() \
  do if (length < 43) \
  { \
    fprintf(stderr, "Cryptonight variant 1 needs at least 43 bytes of data"); \
    _exit(1); \
  } while(0)

The nonce is at bytes 39-43. Though technically I think some of the data before it is variable size (the timestamp IIRC), though will not change size for quite a while.

{
try
{
// first try reading in portable mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment looks like a leftover because I don't see any attempt to read next in non-portable mode

MWARNING("Failed to create data directory: " << directory);
return false;
}
std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it customary in the Monero codebase not to bother with boost::filesystem to concat cleanly because all OS in question understand "/" as separator (even, somewhat surprisingly, Windows)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know actually. I'll look up the docs for how that works.

#pragma once

#define COST_PER_BLOCK 0.1
#define COST_PER_TX_RELAY 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first sight that looks pretty expensive but I guess that there is a reasoning behind it, right? Because unlike for the other, much cheaper operations, network I/O is involved and considered "costly"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also tx verification. All these costs are preliminary really.

@@ -121,11 +122,14 @@ namespace cryptonote
uint64_t start_height;
bool prune;
bool no_miner_tx;
std::string client;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an awful lot of structs that now get a new client field, and it all looks kind of un-elegant, but after brainstorming I was unable to come up with a alternative, better scheme: All that came to mind was either more complicated to implement or even threatened compatibility.

Why does COMMAND_RPC_SUBMIT_RAW_TX::request_t lack a client field?

I was musing that maybe it could be a good idea to add client to all structs, just out of principle and for symmetry. You could be sure then that no call "escaped" that should be able to carry a cost, and you could even introduce schemes like a very small "base cost" for bothering the daemon at all with an RPC call, even if only get_height.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does client need to be specified in each RPC? Why don't we just associate a client at the beginning of a connection (via authentication) or after an open_wallet request and keep that associated to the session?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUBMIT_RAW_TX is not a RPC method. It's a mymonero RPC. It's been added there for reasons. I have a patch moving it to a mymonero specific header on github.

I suppose that now that we have SSL, we could cache the "I have checked this is an authorized client" in the connection structure and not recheck it every RPC. Sounds like something for later though.

@moneromooo-monero
Copy link
Collaborator Author

Rebased to latest master, and misc fixes from comments above.

uint64_t balance = 0;
for (const auto &entry: res.entries)
{
tools::msg_writer() << entry.client << " " << entry.balance << "/" << entry.credits_total
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a little header or help text line would be nice, to make clear the order of the info, something like client id, balance, total credits, (nonces good / stale / bad / dupe)

case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB: return "Wrong block blob";
case CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED: return "Block not accepted";
case CORE_RPC_ERROR_CODE_CORE_BUSY: return "Core is busy";
case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE: return "Wronmg block blob size";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little typo "Wronmg"

@@ -0,0 +1,354 @@
// Copyright (c) 2018, The Monero Project
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New files probably need here 2018-2019 as years, or 2019 right away

error_message = "Payment too low";
info.credits = std::max(info.credits, PENALTY_FOR_BAD_HASH * m_credits_per_hash_found) - PENALTY_FOR_BAD_HASH * m_credits_per_hash_found;
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I miss something here, but right now I don't understand why a nonce that is not correct is reported as a payment that is too low. If that's done merely to avoid yet another extra error code, I am not sure it's a good idea: Maybe people should get reported quite clearly that they submit bad nonces.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the same thing, isn't it ? If you send a hash below the threshold, it's bad.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "conventional" standalone miners, there is a danger that your miner does not use the correct PoW algorithm and calculates completely wrong hashes because of this, so even if a hash has "the right number of zeroes in the right place" it's still not valid. I was worrying about that happening and people not finding the problem because the message "Payment too low" does not give a hint into the right direction.

But maybe this whole scenario is moot because we use Monero's own PoW code that will hardly ever produce completely wrong hashes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll change the message to give hints.

bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is RETURN_ON_RPC_RESPONSE_ERROR not yet used here, but it should be used?


namespace
{
// credits to yrp (https://stackoverflow.com/questions/87372-check-if-a-class-has-a-member-function-of-a-given-signature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link rot: It's now https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature (note the "/" instead of "-" after the number) ...

I don't think I will ever understand that trick :)

}
}
else
message_writer() << tr("Mining for payment");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe nothing wrong and I just don't understand the situation: Hashrate is 0 (or negative) but were are mining nevertheless, as per message? How so?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's not been enough time to get an average (2 seconds).

@moneromooo-monero
Copy link
Collaborator Author

Currently it doesn't seem to be possible to use multiple threads for share-rpc mining, which would be naturally expected by users since the daemon solo mining mode supports it. I suppose it'll be added later if it's longed for hard enough?

I can do it later.

From looking at the minimal changes made to the wallet RPC, I suppose its support for the share-rpc system (e.g. start/stop mining, check status) is left for future work?

I did not think it was needed there. It would seem unlikely someone runs a RPC wallet but no daemon. If it's needed, I can do it later too.

@@ -5114,27 +5116,23 @@ bool simple_wallet::check_daemon_rpc_prices(const std::string &daemon_url, uint3
{
try
{
const auto nodes = m_wallet->get_public_nodes(false);
for (const auto &node: nodes)
auto i = m_claimed_cph.find(daemon_url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't m_claimed_cph going to be empty here if public_nodes was not called prior to calling set_daemon?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be, yes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, there's no check, since you have nothing to check with.

@moneromooo-monero
Copy link
Collaborator Author

Squashed and rebased.

@moneromooo-monero
Copy link
Collaborator Author

Now ported to randomx

@moneromooo-monero
Copy link
Collaborator Author

Rebased, and ported to the latest randomx changes.

Comment on lines +373 to +514
res.status = CORE_RPC_STATUS_OK;
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it OK to return with the other fields in res such as start_height and current_height left uninitialized?

@@ -869,6 +880,9 @@ namespace tools
uint64_t get_last_block_reward() const { return m_last_block_reward; }
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }

struct rpc_node_data_t { epee::net_utils::network_address address; float credits_per_hash; bool white; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've commented previously that this struct isn't used anywhere. No response?

Comment on lines 725 to 726
KV_SERIALIZE(credits)
KV_SERIALIZE(top_hash)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are already handled by KV_SERIALIZE_PARENT(rpc_access_response_base)

{
uint32_t out_peers;
std::string status;

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(out_peers)
KV_SERIALIZE(status)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded

KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(in_peers)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;

struct response_t
struct response_t: public rpc_response_base
{
uint32_t in_peers;
std::string status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded

blob = m_rpc_payment_blob;
height = m_rpc_payment_height;
cookie = m_rpc_payment_cookie;
return boost::optional<std::string>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @vtnerd suggested, I also prefer boost::none. Also, return boost::optional<std::string>("..."); can just be simply written as return "..."; which I prefer for conciseness.

uint32_t cookie;
cryptonote::blobdata blob;
crypto::hash seed_hash, next_seed_hash;
return get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_rpc_payment_info returns true if the querying process was successful; whether the daemon requires payment or not is held in payment_required

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a && at the end.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I missed that :)

unsigned int n_hashes = 0;
cryptonote::blobdata hashing_blob;
crypto::hash seed_hash, next_seed_hash;
int cn_variant;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded

"auto-mine-for-rpc-payment-threshold <float>\n "
" Whether to automatically start mining for RPC payment if the daemon requires it.\n"
"credits-target <unsigned int>\n"
" The RPC payment credits balance to target."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention the special value 0 being interpreted as the default 50000?

Comment on lines +1601 to +1604
if (next_height != seed_height)
next_seed_hash = m_core.get_block_id_by_height(next_height);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaves next_seed_hash uninitialized when next_height == seed_height which seems problematic for later:

      if (seed_hash != next_seed_hash)
        res.next_seed_hash = string_tools::pod_to_hex(next_seed_hash);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT all of them are run if the block version is >= RX_BLOCK_VERSION. I'll add a null_hash anyway though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT all of them are run if the block version is >= RX_BLOCK_VERSION.

I'm confused, I'm talking about when version >= RX_BLOCK_VERSION. What I meant originally is a suggestion to change something like:

     {
       uint64_t next_height;
       crypto::rx_seedheights(height, &seed_height, &next_height);
       seed_hash = m_core.get_block_id_by_height(seed_height);
       if (next_height != seed_height)
         next_seed_hash = m_core.get_block_id_by_height(next_height);
+      else
+        next_seed_hash = crypto::null_hash; // or seed_hash
     }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I had totally missed it, thanks.

@stoffu
Copy link
Contributor

stoffu commented Oct 24, 2019

I see a slight problem in the commit message:

For example, in the command line above, 0.2 credits per hash.

It should be 250 / 1000 = 0.25.

@moneromooo-monero
Copy link
Collaborator Author

No widget to reply to that particular comment, so:

As @vtnerd suggested, I also prefer boost::none. Also, return boost::optionalstd::string("..."); can just be simply written as return "..."; which I prefer for conciseness.

/home/user/src/bitmonero/src/wallet/node_rpc_proxy.cpp: In member function ‘boost::optional<std::__cxx11::basic_string > tools::NodeRPCProxy::get_rpc_payment_info(bool, bool&, uint64_t&, uint64_t&, uint64_t&, cryptonote::blobdata&, uint64_t&, uint64_t&, crypto::hash&, crypto::hash&, uint32_t&)’:
/home/user/src/bitmonero/src/wallet/node_rpc_proxy.cpp:285:14: error: could not convert ‘"Invalid hashing blob"’ from ‘const char [21]’ to ‘boost::optional<std::__cxx11::basic_string >’
285 | return "Invalid hashing blob";

@stoffu
Copy link
Contributor

stoffu commented Oct 25, 2019

/home/user/src/bitmonero/src/wallet/node_rpc_proxy.cpp:285:14: error: could not convert ‘"Invalid hashing blob"’ from ‘const char [21]’ to ‘boost::optional<std::__cxx11::basic_string >’
285 | return "Invalid hashing blob";

I commented without testing it myself. Obviously it'd need to be return std::string("..."). But I see that the current coding style is used throughout this cpp file, so I'm OK with either way.

"auto-mine-for-rpc-payment-threshold <float>\n "
" Whether to automatically start mining for RPC payment if the daemon requires it.\n"
"credits-target <unsigned int>\n"
" The RPC payment credits balance to target (0 for default " BOOST_STRINGIZE(CREDITS_TARGET) ")."));
Copy link
Contributor

@stoffu stoffu Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but is it OK to have a macro call within tr(...)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, it'll end up within the translatable string, I'll change.

Daemons intended for public use can be set up to require payment
in the form of hashes in exchange for RPC service. This enables
public daemons to receive payment for their work over a large
number of calls. This system behaves similarly to a pool, so
payment takes the form of valid blocks every so often, yielding
a large one off payment, rather than constant micropayments.

This system can also be used by third parties as a "paywall"
layer, where users of a service can pay for use by mining Monero
to the service provider's address. An example of this for web
site access is Primo, a Monero mining based website "paywall":
https://github.com/selene-kovri/primo

This has some advantages:
 - incentive to run a node providing RPC services, thereby promoting the availability of third party nodes for those who can't run their own
 - incentive to run your own node instead of using a third party's, thereby promoting decentralization
 - decentralized: payment is done between a client and server, with no third party needed
 - private: since the system is "pay as you go", you don't need to identify yourself to claim a long lived balance
 - no payment occurs on the blockchain, so there is no extra transactional load
 - one may mine with a beefy server, and use those credits from a phone, by reusing the client ID (at the cost of some privacy)
 - no barrier to entry: anyone may run a RPC node, and your expected revenue depends on how much work you do
 - Sybil resistant: if you run 1000 idle RPC nodes, you don't magically get more revenue
 - no large credit balance maintained on servers, so they have no incentive to exit scam
 - you can use any/many node(s), since there's little cost in switching servers
 - market based prices: competition between servers to lower costs
 - incentive for a distributed third party node system: if some public nodes are overused/slow, traffic can move to others
 - increases network security
 - helps counteract mining pools' share of the network hash rate
 - zero incentive for a payer to "double spend" since a reorg does not give any money back to the miner

And some disadvantages:
 - low power clients will have difficulty mining (but one can optionally mine in advance and/or with a faster machine)
 - payment is "random", so a server might go a long time without a block before getting one
 - a public node's overall expected payment may be small

Public nodes are expected to compete to find a suitable level for
cost of service.

The daemon can be set up this way to require payment for RPC services:

  monerod --rpc-payment-address 4xxxxxx \
    --rpc-payment-credits 250 --rpc-payment-difficulty 1000

These values are an example only.

The --rpc-payment-difficulty switch selects how hard each "share" should
be, similar to a mining pool. The higher the difficulty, the fewer
shares a client will find.
The --rpc-payment-credits switch selects how many credits are awarded
for each share a client finds.
Considering both options, clients will be awarded credits/difficulty
credits for every hash they calculate. For example, in the command line
above, 0.25 credits per hash. A client mining at 100 H/s will therefore
get an average of 25 credits per second.
For reference, in the current implementation, a credit is enough to
sync 20 blocks, so a 100 H/s client that's just starting to use Monero
and uses this daemon will be able to sync 500 blocks per second.

The wallet can be set to automatically mine if connected to a daemon
which requires payment for RPC usage. It will try to keep a balance
of 50000 credits, stopping mining when it's at this level, and starting
again as credits are spent. With the example above, a new client will
mine this much credits in about half an hour, and this target is enough
to sync 500000 blocks (currently about a third of the monero blockchain).

There are three new settings in the wallet:

 - credits-target: this is the amount of credits a wallet will try to
reach before stopping mining. The default of 0 means 50000 credits.

 - auto-mine-for-rpc-payment-threshold: this controls the minimum
credit rate which the wallet considers worth mining for. If the
daemon credits less than this ratio, the wallet will consider mining
to be not worth it. In the example above, the rate is 0.25

 - persistent-rpc-client-id: if set, this allows the wallet to reuse
a client id across runs. This means a public node can tell a wallet
that's connecting is the same as one that connected previously, but
allows a wallet to keep their credit balance from one run to the
other. Since the wallet only mines to keep a small credit balance,
this is not normally worth doing. However, someone may want to mine
on a fast server, and use that credit balance on a low power device
such as a phone. If left unset, a new client ID is generated at
each wallet start, for privacy reasons.

To mine and use a credit balance on two different devices, you can
use the --rpc-client-secret-key switch. A wallet's client secret key
can be found using the new rpc_payments command in the wallet.
Note: anyone knowing your RPC client secret key is able to use your
credit balance.

The wallet has a few new commands too:

 - start_mining_for_rpc: start mining to acquire more credits,
regardless of the auto mining settings
 - stop_mining_for_rpc: stop mining to acquire more credits
 - rpc_payments: display information about current credits with
the currently selected daemon

The node has an extra command:

 - rpc_payments: display information about clients and their
balances

The node will forget about any balance for clients which have
been inactive for 6 months. Balances carry over on node restart.
Lists nodes exposing their RPC port for public use
luigi1111 added a commit that referenced this pull request Oct 25, 2019
b3a9a4d add a quick early out to get_blocks.bin when up to date (moneromooo-monero)
2899379 daemon, wallet: new pay for RPC use system (moneromooo-monero)
ffa4602 simplewallet: add public_nodes command (moneromooo-monero)
@luigi1111 luigi1111 merged commit ffa4602 into monero-project:master Oct 25, 2019
quangvu3 added a commit to quangvu3/sumokoin that referenced this pull request Oct 28, 2019
Daemons intended for public use can be set up to require payment
in the form of hashes in exchange for RPC service. This enables
public daemons to receive payment for their work over a large
number of calls. This system behaves similarly to a pool, so
payment takes the form of valid blocks every so often, yielding
a large one off payment, rather than constant micropayments.

This system can also be used by third parties as a "paywall"
layer, where users of a service can pay for use by mining Sumokoin
to the service provider's address. An example of this for web
site access is Primo, a Sumokoin mining based website "paywall":
https://github.com/selene-kovri/primo

This has some advantages:
- incentive to run a node providing RPC services, thereby
promoting the availability of third party nodes for those who
can't run their own
- incentive to run your own node instead of using a third
party's, thereby promoting decentralization
- decentralized: payment is done between a client and server,
with no third party needed
- private: since the system is "pay as you go", you don't need
to identify yourself to claim a long lived balance
- no payment occurs on the blockchain, so there is no extra
transactional load
- one may mine with a beefy server, and use those credits from
a phone, by reusing the client ID (at the cost of some privacy)
- no barrier to entry: anyone may run a RPC node, and your
expected revenue depends on how much work you do
- Sybil resistant: if you run 1000 idle RPC nodes, you don't
magically get more revenue
- no large credit balance maintained on servers, so they have
no incentive to exit scam
- you can use any/many node(s), since there's little cost in
switching servers
- market based prices: competition between servers to lower
costs
- incentive for a distributed third party node system: if
some public nodes are overused/slow, traffic can move to others
- increases network security
- helps counteract mining pools' share of the network hash rate
- zero incentive for a payer to "double spend" since a reorg
does not give any money back to the miner

And some disadvantages:
- low power clients will have difficulty mining (but one can
optionally mine in advance and/or with a faster machine)
- payment is "random", so a server might go a long time
without a block before getting one
- a public node's overall expected payment may be small

Public nodes are expected to compete to find a suitable level for
cost of service.

The daemon can be set up this way to require payment for RPC services:

```
  sumokoind --restricted-rpc --rpc-payment-address Sumoxxxxxx \
    --rpc-payment-credits 250 --rpc-payment-difficulty 1000
```

These values are an example only.

The `--rpc-payment-difficulty` switch selects how hard each "share" should
be, similar to a mining pool. The higher the difficulty, the fewer
shares a client will find.
The `--rpc-payment-credits` switch selects how many credits are awarded
for each share a client finds.
Considering both options, clients will be awarded credits/difficulty
credits for every hash they calculate. For example, in the command line
above, 0.25 credits per hash. A client mining at 100 H/s will therefore
get an average of 25 credits per second.
For reference, in the current implementation, a credit is enough to
sync 20 blocks, so a 100 H/s client that's just starting to use Sumokoin
and uses this daemon will be able to sync 500 blocks per second.

The wallet can be set to automatically mine if connected to a daemon
which requires payment for RPC usage. It will try to keep a balance
of 50000 credits, stopping mining when it's at this level, and starting
again as credits are spent. With the example above, a new client will
mine this much credits in about half an hour, and this target is enough
to sync 500000 blocks.

There are three new settings in the wallet:

- credits-target: this is the amount of credits a wallet will try to
reach before stopping mining. The default of 0 means 50000 credits.

- auto-mine-for-rpc-payment-threshold: this controls the minimum
credit rate which the wallet considers worth mining for. If the
daemon credits less than this ratio, the wallet will consider mining
to be not worth it. In the example above, the rate is 0.25

- persistent-rpc-client-id: if set, this allows the wallet to reuse
a client id across runs. This means a public node can tell a wallet
that's connecting is the same as one that connected previously, but
allows a wallet to keep their credit balance from one run to the
other. Since the wallet only mines to keep a small credit balance,
this is not normally worth doing. However, someone may want to mine
on a fast server, and use that credit balance on a low power device
such as a phone. If left unset, a new client ID is generated at
each wallet start, for privacy reasons.

To mine and use a credit balance on two different devices, you can
use the `--rpc-client-secret-key` switch. A wallet's client secret key
can be found using the new rpc_payments command in the wallet.
Note: anyone knowing your RPC client secret key is able to use your
credit balance.

The wallet has a few new commands too:

- start_mining_for_rpc: start mining to acquire more credits,
regardless of the auto mining settings
- stop_mining_for_rpc: stop mining to acquire more credits
- rpc_payments: display information about current credits with
the currently selected daemon

The node has an extra command:

- rpc_payments: display information about clients and their
balances

The node will forget about any balance for clients which have
been inactive for 6 months. Balances carry over on node restart.

Ref: monero-project/monero#5357
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants