[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 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 >