Subject: Re: [bitcoin-dev] Exploring: limiting transaction output amount as a function of total input value

From: Zac Greenwood <[email-masked]>
Date: Mon, 2 Aug 2021 11:32:36 +0200

[Note: I've moved your reply to the newly started thread]

Hi Billy,

Thank you for your kind and encouraging feedback.

I don't quite understand why you'd want to define a specific span of blocks
> for the rate limit. Why not just specify the size of the window (in blocks)
> to rate limit within, and the limit?

To enable more straightforward validation logic.

You mentioned change addresses, however, with the parameters you defined,
> there would be no way to connect together the change address with the
> original address, meaning they would have completely separate rate limits,
> which wouldn't work since the change output would ignore the previous rate
> limit.

The rate-limiting parameters must be re-specified for each rate-limited
input. So, a transaction that has a rate-limited input is only valid if its
output is itself rate-limited such that it does not violate the
rate-limiting constraints of its input.

In my thread-starter, I gave the below example of a rate-limited address a2
that serves as input for transaction t2:

a2: 99.8 sats at height 800100;
Rate-limit params: h0=800000, h1=800143, a=500k, a_remaining=300k;

Transaction t2:
Included at block height 800200
Spend: 400k + fees.
Rate-limiting params: h0=800144, h1=800287, a=500k, a_remaining=100k.

Note how transaction t2 re-specifies the rate-limiting parameters.
Validation must ensure that the re-specified parameters are within bounds,
i.e., do not allow more spending per epoch than the rate-limiting
parameters of its input address a2. Re-specifying the rate-limiting
parameters offers the flexibility to further restrict spending, or to
disable any additional spending within the current epoch by setting
a_remaining to zero.

Result:
Value at destination address: 400k sats;
Rate limiting params at destination address: none;
Value at change address a3: 99.4m sats;
Rate limiting params at change address a3: h0=800144, h1=800287, a=500k,
a_remaining=100k.

As a design principle I believe it makes sense if the system is able to
verify the validity of a transaction without having to consider any
transactions that precede its inputs. As a side-note, doing away with this
design principle would however enable more sophisticated rate-limiting
(such as rate-limiting per sliding window instead of rate-limiting per
epoch having a fixed start and end block), but while at the same time
reducing the size of per rate-limiting transaction (because it would enable
specifying the rate-limiting parameters more space-efficiently). To test
the waters and to keep things relatively simple, I chose not to go into
this enhanced form of rate-limiting.

I haven't gone into how to process a transaction having multiple
rate-limited inputs. The easiest way to handle this case is to not allow
any transaction having more than one rate-limited input. One could imagine
complex logic to handle transactions having multiple rate-limited inputs by
creating multiple rate-limited change addresses. However at first glance I
don't believe that the marginal added functionality would justify the
increased implementation complexity.

 I'd be interested in seeing you write a BIP for this.

Thank you, but sadly my understanding of Bitcoin is way too low to be able
to write a BIP and do the implementation. However I see tremendous value in
this functionality. Favorable feedback of the list regarding the usefulness
and the technical feasibility of rate-limiting functionality would of
course be an encouragement for me to descend further down the rabbit hole.

Zac

On Sun, Aug 1, 2021 at 10:09 AM Zac Greenwood <[email-masked]> wrote:

> [Resubmitting to list with minor edits. My previous submission ended up
> inside an existing thread, apologies.]
>
> Hi list,
>
> I'd like to explore whether it is feasible to implement new scripting
> capabilities in Bitcoin that enable limiting the output amount of a
> transaction based on the total value of its inputs. In other words, to
> implement the ability to limit the maximum amount that can be sent from an
> address.
>
> Two use cases come to mind:
>
> UC1: enable a user to add additional protection their funds by
> rate-limiting the amount that they are allowed to send during a certain
> period (measured in blocks). A typical use case might be a user that
> intends to hodl their bitcoin, but still wishes to occasionally send small
> amounts. Rate-limiting avoids an attacker from sweeping all the users'
> funds in a single transaction, allowing the user to become aware of the
> theft and intervene to prevent further thefts.
>
> UC2: exchanges may wish to rate-limit addresses containing large amounts
> of bitcoin, adding warm- or hot-wallet functionality to a cold-storage
> address. This would enable an exchange to drastically reduce the number of
> times a cold wallet must be accessed with private keys that give access to
> the full amount.
>
> In a typical setup, I'd envision using multisig such that the user has two
> sets of private keys to their encumbered address (with a "set" of keys
> meaning "one or more" keys). One set of private keys allows only for
> sending with rate-limiting restrictions in place, and a second set of
> private keys allowing for sending any amount without rate-limiting,
> effectively overriding such restriction.
>
> The parameters that define in what way an output is rate-limited might be
> defined as follows:
>
> Param 1: a block height "h0" indicating the first block height of an epoch;
> Param 2: a block height "h1" indicating the last block height of an epoch;
> Param 3: an amount "a" in satoshi indicating the maximum amount that is
> allowed to be sent in any epoch;
> Param 4: an amount "a_remaining" (in satoshi) indicating the maximum
> amount that is allowed to be sent within the current epoch.
>
> For example, consider an input containing 100m sats (1 BTC) which has been
> rate-limited with parameters (h0, h1, a, a_remaining) of (800000, 800143,
> 500k, 500k). These parameters define that the address is rate-limited to
> sending a maximum of 500k sats in the current epoch that starts at block
> height 800000 and ends at height 800143 (or about one day ignoring block
> time variance) and that the full amount of 500k is still sendable. These
> rate-limiting parameters ensure that it takes at minimum 100m / 500k = 200
> transactions and 200 x 144 blocks or about 200 days to spend the full 100m
> sats. As noted earlier, in a typical setup a user should retain the option
> to transact the entire amount using a second (set of) private key(s).
>
> For rate-limiting to work, any change output created by a transaction from
> a rate-limited address must itself be rate-limited as well. For instance,
> expanding on the above example, assume that the user spends 200k sats from
> a rate-limited address a1 containing 100m sats:
>
> Start situation:
> At block height 800000: rate-limited address a1 is created;
> Value of a1: 100.0m sats;
> Rate limiting params of a1: h0=800000, h1=800143, a=500k, a_remaining=500k;
>
> Transaction t1:
> Included at block height 800100;
> Spend: 200k + fee;
> Rate limiting params: h0=800000, h1=800143, a=500k, a_remaining=300k.
>
> Result:
> Value at destination address: 200k sats;
> Rate limiting params at destination address: none;
> Value at change address a2: 99.8m sats;
> Rate limiting params at change address a2: h0=800000, h1=800143, a=500k,
> a_remaining=300k.
>
> In order to properly enforce rate limiting, the change address must be
> rate-limited such that the original rate limit of 500k sats per 144 blocks
> cannot be exceeded. In this example, the change address a2 were given the
> same rate limiting parameters as the transaction that served as its input.
> As a result, from block 800100 up until and including block 800143, a
> maximum amount of 300k sats is allowed to be spent from the change address.
>
> Example continued:
> a2: 99.8 sats at height 800100;
> Rate-limit params: h0=800000, h1=800143, a=500k, a_remaining=300k;
>
> Transaction t2:
> Included at block height 800200
> Spend: 400k + fees.
> Rate-limiting params: h0=800144, h1=800287, a=500k, a_remaining=100k.
>
> Result:
> Value at destination address: 400k sats;
> Rate limiting params at destination address: none;
> Value at change address a3: 99.4m sats;
> Rate limiting params at change address a3: h0=800144, h1=800287, a=500k,
> a_remaining=100k.
>
> Transaction t2 is allowed because it falls within the next epoch (running
> from 800144 to 800287) so a spend of 400k does not violate the constraint
> of 500k per epoch.
>
> As could be seen, the rate limiting parameters are part of the transaction
> and chosen by the user (or their wallet). This means that the parameters
> must be validated to ensure that they do not violate the intended
> constraints.
>
> For instance, this transaction should not be allowed:
> a2: 99.8 sats at height 800100;
> Rate-limit params of a2: h0=800000, h1=800143, a=500k, a_remaining=300k;
>
> Transaction t2a:
> Included at block height 800200;
> Spend: 400k + fees;
> Rate-limit params: h0=800124, h1=800267, a=500k, a_remaining=100k.
>
> This transaction t2a attempts to shift the epoch forward by 20 blocks such
> that it starts at 800124 instead of 800144. Shifting the epoch forward like
> this must not be allowed because it enables spending more that the rate
> limit allows, which is 500k in any epoch of 144 blocks. It would enable
> overspending:
>
> t1: spend 200k at 800100 (epoch 1: total: 200k);
> t2a: spend 400k at 800200 (epoch 2: total: 400k);
> t3a: spend 100k at 800201 (epoch 2: total: 500k);
> t4a: spend 500k at 800268 (epoch 2: total: 1000k, overspending for epoch
> 2).
>
> Specifying the rate-limiting parameters explicitly at every transaction
> allows the user to tighten the spending limit by setting tighter limits or
> for instance by setting a_remainder to 0 if they wish to enforce not
> spending more during an epoch. A second advantage of explicitly specifying
> the four rate-limiting parameters with each transaction is that it allows
> the system to fully validate the transaction without having to consider any
> previous transactions within an epoch.
>
> I will stop here because I would like to gauge interest in this idea first
> before continuing work on other aspects. Two main pieces of work jump to
> mind:
>
> Define all validations;
> Describe aggregate behaviour of multiple (rate-limited) inputs, proof that
> two rate-limited addresses cannot spend more than the sum of their
> individual limits.
>
> Zac
>