diff options
author | Billy Tetrud <billy.tetrud@gmail.com> | 2021-08-04 23:39:34 -0700 |
---|---|---|
committer | bitcoindev <bitcoindev@gnusha.org> | 2021-08-05 06:39:55 +0000 |
commit | bac11352615eb45fe7b427b559667e7ed9385ba9 (patch) | |
tree | b78a74f9307e9e1b75d92aa8ef5fb11cd5c651e7 | |
parent | 5fcb7b755312e89db2d2144449494012dc802cae (diff) | |
download | pi-bitcoindev-bac11352615eb45fe7b427b559667e7ed9385ba9.tar.gz pi-bitcoindev-bac11352615eb45fe7b427b559667e7ed9385ba9.zip |
Re: [bitcoin-dev] Exploring: limiting transaction output amount as a function of total input value
-rw-r--r-- | 82/be7bdb33af1ba00d2118b97e6d472951e24eaa | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/82/be7bdb33af1ba00d2118b97e6d472951e24eaa b/82/be7bdb33af1ba00d2118b97e6d472951e24eaa new file mode 100644 index 000000000..517282e21 --- /dev/null +++ b/82/be7bdb33af1ba00d2118b97e6d472951e24eaa @@ -0,0 +1,921 @@ +Return-Path: <fresheneesz@gmail.com> +Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) + by lists.linuxfoundation.org (Postfix) with ESMTP id 21E23C000E + for <bitcoin-dev@lists.linuxfoundation.org>; + Thu, 5 Aug 2021 06:39:55 +0000 (UTC) +Received: from localhost (localhost [127.0.0.1]) + by smtp1.osuosl.org (Postfix) with ESMTP id 0663582BC0 + for <bitcoin-dev@lists.linuxfoundation.org>; + Thu, 5 Aug 2021 06:39:55 +0000 (UTC) +X-Virus-Scanned: amavisd-new at osuosl.org +X-Spam-Flag: NO +X-Spam-Score: -2.098 +X-Spam-Level: +X-Spam-Status: No, score=-2.098 tagged_above=-999 required=5 + tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, + DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, + HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, + SPF_PASS=-0.001] autolearn=ham autolearn_force=no +Authentication-Results: smtp1.osuosl.org (amavisd-new); + dkim=pass (2048-bit key) header.d=gmail.com +Received: from smtp1.osuosl.org ([127.0.0.1]) + by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id fRKQAJtdZe2b + for <bitcoin-dev@lists.linuxfoundation.org>; + Thu, 5 Aug 2021 06:39:52 +0000 (UTC) +X-Greylist: whitelisted by SQLgrey-1.8.0 +Received: from mail-ej1-x636.google.com (mail-ej1-x636.google.com + [IPv6:2a00:1450:4864:20::636]) + by smtp1.osuosl.org (Postfix) with ESMTPS id 220DA82AF8 + for <bitcoin-dev@lists.linuxfoundation.org>; + Thu, 5 Aug 2021 06:39:52 +0000 (UTC) +Received: by mail-ej1-x636.google.com with SMTP id o5so7770123ejy.2 + for <bitcoin-dev@lists.linuxfoundation.org>; + Wed, 04 Aug 2021 23:39:51 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; + h=mime-version:references:in-reply-to:from:date:message-id:subject:to + :cc; bh=XplVH7Pa7wHDfRBd0t0QhZCPoDY5jgHvxCS3LOEeG20=; + b=Vjw7N4ywLxLL/Wuv3hMmviv/iJS+8FR0WLGFLIk8QgL9svkL5lWuEdWtwBw376GGzD + +nJSUygx/Z1mjiX6zxt7mNXft1PsF9QfCMFYnsz+uuvpBFBJiJBP/66E65WTi3ZgqFL/ + aGy8t9Uz/qk6kkqpWgeOAmCQ5UBqaTaseofoKw7Vjrmo8hFt1hbKFiVz3GpYQYLnChPN + +DWifis+mfxnVzHNK9NPN+uZtiGVzoo/RdnB8rU6/jQnek9+oPfRNOQWsvS2fhYDxkHR + xY44upBR7hwugLVH9f+/jL1XLDMLPKNbXZu+qhQ7YTdIwM0ZTO+UE7yPzqnN9Oh9L3Wo + 60AQ== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20161025; + h=x-gm-message-state:mime-version:references:in-reply-to:from:date + :message-id:subject:to:cc; + bh=XplVH7Pa7wHDfRBd0t0QhZCPoDY5jgHvxCS3LOEeG20=; + b=rW7/RBOe0Lo89yWJAwnTp24AaiHD4X2JXgOyWfuI41BVhnTdt21Av9NRJDzwrl60H6 + LkI7mHZZ/r74SZl2wreJOT7oBRYHghuVjR/tV3tnf6Ot8PHUOoSpYVZnbIjoD1K+TwTc + PEvSgbodiosu7sy/9K40QploQ2PUn6SQzkgc4IgFB1394NOHbx6uig546FdzkfIL4ECE + d4MnEAQ/xr6EtHXZIrPPuVj06oUof0n3pU8YE1I5LCsnoMA/XdGIS3TN8YxzJ0Tg5oc6 + KHfOHqma8iDvt/cUYuJ7oG3iSFMRD8hwVXH2GFvK1k4jkv0GRTOSWg30E7BM+XxZySBI + 67nw== +X-Gm-Message-State: AOAM531vBLdkU/wA5++ftOURjCuzp66W9ksZV9Yp5ly+JM6HdXqLe6Ar + a4o0pKfRKhVa5DRG32kSuq/FIqkwwY5coTyT2ec= +X-Google-Smtp-Source: ABdhPJxeMnSp5HySDn/S3jygdMNQGCSH3d/Cl/ARIKHe6xuTUvdyib0LQyQ9MINFkcQlOlPy50QUKztnAhX9mLmyw9w= +X-Received: by 2002:a17:906:4d94:: with SMTP id + s20mr3161402eju.152.1628145590280; + Wed, 04 Aug 2021 23:39:50 -0700 (PDT) +MIME-Version: 1.0 +References: <CAJ4-pEAETy7_vOez5H32mZLg9gRpRajvoBjZyBT_v=DEqdQJvQ@mail.gmail.com> + <CAJ4-pEAxqvMc89xSp9NXXNwnpJ3NhMqE6p=dRbpYCAB3Gbb14g@mail.gmail.com> + <CAGpPWDbidOBqUXHpoteAf50WXeMi392PJZmApyT8h2Gk6n1gKQ@mail.gmail.com> + <CAJ4-pEAi-ooVMvJmeXhrS9J6-JVQ1jxy1On1NTQYpOSD49cbrw@mail.gmail.com> +In-Reply-To: <CAJ4-pEAi-ooVMvJmeXhrS9J6-JVQ1jxy1On1NTQYpOSD49cbrw@mail.gmail.com> +From: Billy Tetrud <billy.tetrud@gmail.com> +Date: Wed, 4 Aug 2021 23:39:34 -0700 +Message-ID: <CAGpPWDZNrPT9Li_neNOr3BDGdusMorWjFodNPo6YqNC3SDaf3w@mail.gmail.com> +To: Zac Greenwood <zachgrw@gmail.com> +Content-Type: multipart/alternative; boundary="0000000000008d270e05c8ca2ffb" +X-Mailman-Approved-At: Thu, 05 Aug 2021 10:16:49 +0000 +Cc: Bitcoin Protocol Discussion <bitcoin-dev@lists.linuxfoundation.org> +Subject: Re: [bitcoin-dev] Exploring: limiting transaction output amount as + a function of total input value +X-BeenThere: bitcoin-dev@lists.linuxfoundation.org +X-Mailman-Version: 2.1.15 +Precedence: list +List-Id: Bitcoin Protocol Discussion <bitcoin-dev.lists.linuxfoundation.org> +List-Unsubscribe: <https://lists.linuxfoundation.org/mailman/options/bitcoin-dev>, + <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=unsubscribe> +List-Archive: <http://lists.linuxfoundation.org/pipermail/bitcoin-dev/> +List-Post: <mailto:bitcoin-dev@lists.linuxfoundation.org> +List-Help: <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=help> +List-Subscribe: <https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev>, + <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=subscribe> +X-List-Received-Date: Thu, 05 Aug 2021 06:39:55 -0000 + +--0000000000008d270e05c8ca2ffb +Content-Type: text/plain; charset="UTF-8" + +> A maximum amount is allowed to be spent within EVERY epoch. + +It sounds like you're proposing an opcode that takes in epochStart and +epochEnd as parameters. I still don't understand why its useful to specify +those as absolute block heights. You mentioned that this enables more +straightforward validation logic, but I don't see how. Eg, if you have a +UTXO encumbered by rateLimit(epochStart = 800100, epochEnd = 800200, limit += 100k, remain = 100k), what happens if you don't spend that UTXO before +block 800200? Is the output no longer rate limited then? Or is the opcode +calculating 800200-800100 = 100 and applying a rate limit for the next +epoch? If the first, then the UTXO must be spent within one epoch to remain +rate limited. If the second, then it seems nearly identical to simply +specifying window=100 as a parameter instead of epochStart and epochEnd. + +> then there must be only a single (rate-limited) output + +This rule would make transactions tricky if you're sending money into +someone else's wallet that may be rate limited. If the requirement is that +only you yourself can send money into a rate limited wallet, then this +point is moot but it would be ideal to not have such a requirement. + +This is how I'd imagine creating an opcode like this: + +rateLimit(windowSize = 144 blocks, limit = 100k sats) + +This would define that the epoch is 1 day's worth of blocks. This would +evenly divide bitcoin's retarget period and so each window would start and +end at those dividing lines (eg the first 144 blocks of the retargetting +period, then the second, then the third, etc). + +When this output is spent, it ensures that there's a maximum of 100k sats +is sent to addresses other than the originating address. It also records +the amount spent in the current 144 block window for that address (eg by +simply recording the already-spent amount on the resulting UTXO and having +an index that allows looking up UTXOs by address and adding them up). That +way, when any output from that address is spent again, if a new 144 block +window has started, the limit is reset, but if its still within the same +window, the already-spent amounts for UTXOs from that address are added up +and subtracted from the limit, and that number is the remaining limit a +subsequent transaction needs to adhere to. + +This way, 3rd party could send transactions into an address like this, and +multiple outputs can be combined and used to spend to arbitrary outputs (up +to the rate limit of course). + +On Wed, Aug 4, 2021 at 3:48 AM Zac Greenwood <zachgrw@gmail.com> wrote: + +> > Ah I see, this is all limited to within a single epoch. +> +> No, that wouldn't be useful. A maximum amount is allowed to be spent +> within EVERY epoch. +> +> Consider an epoch length of 100 blocks with a spend limit of 200k per +> epoch. The following is allowed: +> +> epoch1 (800101 - 800200): spend 120k in block 800140. Remaining for +> epoch1: 80k; +> epoch1 (800101 - 800200): spend another 60k in block 800195. Remaining for +> epoch1: 20k; +> epoch2 (800201 - 800300): spend 160k in block 800201. Remaining for +> epoch2: 40k. +> +> Since the limit pertains to each individual epoch, it is allowed to spend +> up to the full limit at the start of any new epoch. In this example, the +> spending was as follows: +> +> 800140: 120k +> 800195: 60k +> 800201: 160k. +> +> Note that in a span of 62 blocks a total of 340k sats was spent. This may +> seem to violate the 200k limit per 100 blocks, but this is the result of +> using a per-epoch limit. This allows a maximum of 400k to be spent in 2 +> blocks llke so: 200k in the last block of an epoch and another 200k in the +> first block of the next epoch. However this is inconsequential for the +> intended goal of rate-limiting which is to enable small spends over time +> from a large amount and to prevent theft of a large amount with a single +> transaction. +> +> To explain the proposed design more clearly, I have renamed the params as +> follows: +> +> epochStart: block height of first block of the current epoch (was: h0); +> epochEnd: block height of last block of the current epoch (was: h1); +> limit: the maximum total amount allowed to be spent within the current +> epoch (was: a); +> remain: the remaining amount allowed to be spent within the current epoch +> (was: a_remaining); +> +> Also, to illustrate that the params are specific to a transaction, I will +> hence precede the param with the transaction name like so: +> tx8_limit, tx31c_remain, tx42z_epochStart, ... etc. +> +> For simplicity, only transactions with no more than one rate-limited input +> are considered, and with no more than two outputs: one rate-limited change +> output, and a normal (not rate-limited) output. +> +> Normally, a simple transaction generates two outputs: one for a payment to +> a third party and one for the change address. Again for simplicity, we +> demand that a transaction which introduces rate-limiting must have only a +> single, rate-limited output. The validation rule might be: if a transaction +> has rate-limiting params and none of its inputs are rate-limited, then +> there must be only a single (rate-limited) output (and no second or change +> output). +> +> Consider rate limiting transactions tx1 having one or more normal (non +> rate-limited) inputs: +> +> tx1 gets included at block height 800004; +> The inputs of tx1 are not rate-limited => tx1 must have only a single +> output which will become rate-limited; +> params: tx1_epochStart=800001, tx1_epochEnd=800100, tx1_limit=200k, +> tx1_remain=200k; +> => This defines that an epoch has 100 blocks and no more than 200k sats +> may be spent in any one epoch. Within the current epoch, 200k sats may +> still be spent. +> +> This transaction begins to rate-limit a set of inputs, so it has a single +> rate-limited output. +> Let's explore transactions that have the output of tx1 as their input. I +> will denote the output of tx1 as "out1". +> +> tx2a has out1 as its only input; +> tx2a spends 50k sats and gets included at block height 803050; +> tx2a specifies the following params for its change output "chg2a": +> chg2a_epochStart=803001, chg2a_epochEnd=803100; +> chg2a_limit=200k, chg2a_remain=150k. +> +> To enforce rate-limiting, the system must validate the params of the +> change output chg2a to ensure that overspending is not allowed. +> +> The above params are allowed because: +> => 1. the epoch does not become smaller than 100 blocks [(chg2a_epochEnd - +> chg2a_epochStart) >= (tx1_epochEnd - tx1_epochStart)] +> => 2. tx1_limit has not been increased (ch2a_limit <= tx1_limit) +> => 3. the amount spent (50k sats) does not exceed tx1_remain AND does not +> exceed chg2a_limit; +> => 4. chg2a_remain" is 50k sats less than chg2a_limit. +> +> A transaction may also further constrain further spending like so: +> +> tx2b has out1as its only input; +> tx2b spends 8k sats and gets included at block height 808105; +> tx2b specifies the following params for its change output "chg2b": +> chg2b_epochStart=808101, chg2b_epochEnd=808250; +> chg2b_limit=10k, chg2b_remain=0. +> +> These params are allowed because: +> => 1. the epoch does not become smaller than100 blocks. It is fine to +> increase the epoch to 150 blocks because it does not enable exceeding the +> original rate-limit; +> => 2. the limit (chg2b_limit) has been decreased to 10k sats, further +> restricting the maximum amount allowed to be spent within the current and +> any subsequent epochs; +> => 3. the amount spent (10k sats) does not exceed tx1_remain AND does not +> exceed chg2b_limit; +> => 4. chg2b_remain has been set to zero, meaning that within the current +> epoch (block height 808101 to and including 808250), tx2b cannot be used as +> a spending input to any transaction. +> +> Starting from block height 808251, a new epoch will start and the +> rate-limited output of tx2b may again be used as an input for a subsequent +> rate-limited transaction tx3b. This transaction tx3b must again be +> accompanied by params that do not violate the rate-limit as defined by the +> params of tx2b and which are stored with output out2b. So, the epoch of +> tx3b must be at minimum 150 blocks, the maximum that is allowed to be spent +> per epoch is at most 10k sats, and chg3b_remain must be decreased by at +> least the amount spent by tx3b. +> +> From the above, the rate-limiting mechanics should hopefully be clear and +> full set of validation rules could be defined in a more generalized way +> with little additional effort. +> +> Note that I conveniently avoided talking about how to represent the +> parameters within transactions or outputs, simply because I currently lack +> enough understanding to reason about this. I am hoping that others may +> offer help. +> +> Zac +> +> +> On Tue, Aug 3, 2021 at 8:12 PM Billy Tetrud <billy.tetrud@gmail.com> +> wrote: +> +>> > To enable more straightforward validation logic. +>> > within the current epoch +>> +>> Ah I see, this is all limited to within a single epoch. I think that +>> sufficiently limits the window of time in which nodes have to store +>> information for rate limited outputs. However, I don't see how specifying +>> block ranges simplifies the logic - wouldn't this complicate the logic with +>> additional user-specified constraints? It also prevents the output from +>> being able to be rate limited over the span of multiple epochs, which would +>> seem to make it a lot more difficult to use for certain types of wallets +>> (eg cold wallets). +>> +>> I think I see the logic of your 'remaining' parameter there. If you start +>> with a single rate-limited input, you can split that into many outputs, +>> only one of which have a 'remaining' balance. The rest can simply remain +>> unspendable for the rest of the epoch. That way these things don't need to +>> be tied together. However, that doesn't solve the problem of 3rd parties +>> being able to send money into the wallet. +>> +>> > I don't believe that the marginal added functionality would justify the +>> increased implementation complexity +>> +>> Perhaps, but I think there is a lot of benefit in allowing these kinds of +>> things to operate as similarly as possible to normal transactions, for one +>> because of usability reasons. If each opcode has its own quirks that are +>> not intuitively related to their purpose (eg if a rate-limited wallet had +>> no way to get a receiving address), it would confuse end-users (eg who +>> wonder how to get a receiving address and how they can ask people to send +>> money into their wallet) or require a lot of technical complexity in +>> applications (eg to support something like cooperatively connecting with +>> their wallet so that a transaction can be made that creates a new +>> single-output for the wallet). A little complexity in this opcode can save +>> a lot of external complexity here I think. +>> +>> > my understanding of Bitcoin is way too low to be able to write a BIP +>> and do the implementation +>> +>> You might be able to find people willing to help. I would be willing to +>> help write the BIP spec. I'm not the right person to help with the +>> implementation, but perhaps you could find someone else who is. Even if the +>> BIP isn't adopted, it could be a starting point or inspiration for someone +>> else to write an improved version. +>> +>> On Mon, Aug 2, 2021 at 2:32 AM Zac Greenwood <zachgrw@gmail.com> wrote: +>> +>>> [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 <zachgrw@gmail.com> 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 +>>>> +>>> + +--0000000000008d270e05c8ca2ffb +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +<div dir=3D"ltr">>=C2=A0 + +=C2=A0A maximum amount is allowed to be spent within EVERY epoch.<div><br><= +/div><div>It sounds like you're proposing an opcode that takes in epoch= +Start and epochEnd as parameters. I still don't understand why its=C2= +=A0useful to specify those as absolute block heights. You mentioned=C2=A0th= +at this enables more straightforward validation logic, but I don't see = +how. Eg, if you have a UTXO encumbered by rateLimit(epochStart =3D 800100, = +epochEnd =3D 800200, limit =3D 100k, remain =3D 100k), what happens if you = +don't spend that UTXO before block 800200? Is the output no longer rate= + limited then? Or is the opcode calculating 800200-800100 =3D 100 and apply= +ing a rate limit for the next epoch? If the first, then the UTXO must be sp= +ent within one epoch to remain rate limited. If the second, then it seems n= +early identical to simply specifying window=3D100 as a parameter instead of= + epochStart and epochEnd.</div><div><br></div><div>> then there must be = +only a single (rate-limited) output</div><div><br></div><div>This rule woul= +d make transactions tricky if you're sending money into someone else= +9;s wallet that may be rate limited. If the requirement is that only you yo= +urself can send money into a rate limited wallet, then this point is moot b= +ut it would be ideal to not have such a requirement.</div><div><br></div><d= +iv>This is how I'd imagine creating an opcode like this:</div><div><br>= +</div><div>rateLimit(windowSize =3D 144 blocks, limit =3D 100k sats)</div><= +div><br></div><div>This would define that the epoch is 1 day's worth of= + blocks. This would evenly divide bitcoin's retarget period and so each= + window would start and end at those dividing lines (eg the first 144 block= +s of the retargetting period, then the second, then the third, etc).=C2=A0<= +/div><div><br></div><div>When this output is spent, it ensures that there&#= +39;s a maximum of 100k sats is sent to addresses other than the originating= + address. It also records the amount spent in the current 144 block window = +for that address (eg by simply recording the already-spent amount on the re= +sulting UTXO and having an index that allows looking up UTXOs by address an= +d adding them up). That way, when any output from that address is spent aga= +in, if a new 144 block window has started, the limit is reset, but if its s= +till within the same window, the already-spent amounts for UTXOs from that = +address are added up and subtracted from the limit, and that number is the = +remaining limit a subsequent transaction needs to adhere to.=C2=A0</div><di= +v><br></div><div>This way, 3rd party could send transactions into an addres= +s like this, and multiple outputs can be combined and used to spend to arbi= +trary outputs (up to the rate limit of course).</div></div><br><div class= +=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Wed, Aug 4, 2021 = +at 3:48 AM Zac Greenwood <<a href=3D"mailto:zachgrw@gmail.com" target=3D= +"_blank">zachgrw@gmail.com</a>> wrote:<br></div><blockquote class=3D"gma= +il_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,2= +04,204);padding-left:1ex"><div dir=3D"ltr"><div dir=3D"ltr"><div>> Ah I = +see, this is all limited to within a single epoch.</div><div><br></div><div= +>No, that wouldn't be useful. A maximum amount is allowed to be spent w= +ithin EVERY epoch.</div><div><br></div><div>Consider an epoch length of 100= + blocks with a spend limit of 200k per epoch. The following is allowed:</di= +v><div><br></div><div>epoch1 (800101 - 800200): spend 120k in block 800140.= + Remaining for epoch1: 80k;</div><div>epoch1 (800101 - 800200): spend anoth= +er 60k in block 800195. Remaining for epoch1: 20k;</div><div>epoch2 (800201= + - 800300): spend 160k in block 800201. Remaining for epoch2: 40k.</div><di= +v><br></div><div>Since the limit pertains to each individual epoch, it is a= +llowed to spend up to the full limit at the start of any new epoch. In this= + example, the spending was as follows:</div><div><br></div><div>800140: 120= +k</div><div>800195: 60k</div><div>800201: 160k.</div><div><br></div><div>No= +te that in a span of 62 blocks a total of 340k sats was spent. This may see= +m to violate the 200k limit per 100 blocks, but this is the result of using= + a per-epoch limit. This allows a maximum of 400k to be spent in 2 blocks l= +lke so: 200k in the last block of an epoch and another 200k in the first bl= +ock of the next epoch. However this is inconsequential for the intended goa= +l of rate-limiting which is to enable small spends over time from a large a= +mount and to prevent theft of a large amount with a single transaction.</di= +v><div><br></div><div>To explain the proposed design more clearly, I have r= +enamed the params as follows:</div><div><br></div><div>epochStart: block he= +ight of first block of the current epoch (was: h0);</div><div>epochEnd: blo= +ck height of last block of the current epoch (was: h1);</div><div>limit: th= +e maximum total amount allowed to be spent within the current epoch (was: a= +);</div><div>remain: the remaining amount allowed to be spent within the cu= +rrent epoch (was: a_remaining);</div><div><br></div><div>Also, to illustrat= +e that the params are specific to a transaction, I will hence precede the p= +aram with the transaction name like so:</div><div>tx8_limit, tx31c_remain, = +tx42z_epochStart, ... etc.</div><div><br></div><div>For simplicity, only tr= +ansactions with no more than one rate-limited input are considered, and wit= +h no more than two outputs: one rate-limited change output, and a normal (n= +ot rate-limited) output.<br></div><div><br></div><div>Normally, a simple tr= +ansaction generates two outputs: one for a payment to a third party and one= + for the change address. Again for simplicity, we demand that a transaction= + which introduces rate-limiting must have only a single, rate-limited outpu= +t. The validation rule might be: if a transaction has rate-limiting params = +and none of its inputs are rate-limited, then there must be only a single (= +rate-limited) output (and no second or change output).<br></div><div><br></= +div><div><div>Consider rate limiting transactions tx1 having one or more no= +rmal (non rate-limited) inputs:</div><div><br></div><div>tx1 gets included = +at block height 800004;</div><div>The inputs of tx1 are not rate-limited = +=3D> tx1 must have only a single output which will become rate-limited;<= +/div><div>params: tx1_epochStart=3D800001, tx1_epochEnd=3D800100, tx1_limit= +=3D200k, tx1_remain=3D200k;</div><div>=3D> This defines that an epoch ha= +s 100 blocks and no more than 200k sats may be spent in any one epoch. With= +in the current epoch, 200k sats may still be spent.</div><div><br></div><di= +v>This transaction begins to rate-limit a set of inputs, so it has a single= + rate-limited output.</div></div><div>Let's explore transactions that h= +ave the output of tx1 as their input. I will denote the output of tx1 as &q= +uot;out1".</div><div><br></div><div>tx2a has out1 as its only input;</= +div><div>tx2a spends 50k sats and gets included at block height 803050;</di= +v><div>tx2a specifies the following params for its change output "chg2= +a":</div><div>chg2a_epochStart=3D803001, chg2a_epochEnd=3D803100;</div= +><div>chg2a_limit=3D200k, chg2a_remain=3D150k.</div><div><br></div><div>To = +enforce rate-limiting, the system must validate the params of the change ou= +tput chg2a to ensure that overspending is not allowed.</div><div><br></div>= +<div>The above params are allowed because:</div><div>=3D> 1. the epoch d= +oes not become smaller than 100 blocks [(chg2a_epochEnd - chg2a_epochStart)= + >=3D (tx1_epochEnd - tx1_epochStart)]</div><div>=3D> 2. tx1_limit ha= +s not been increased (ch2a_limit <=3D tx1_limit)</div><div>=3D> 3. th= +e amount spent (50k sats) does not exceed tx1_remain AND does not exceed ch= +g2a_limit;</div><div>=3D> 4. chg2a_remain" is 50k sats less than ch= +g2a_limit.</div><div><br></div><div>A transaction may also further constrai= +n further spending like so:</div><div><br></div><div>tx2b has out1as its on= +ly input;</div><div><div>tx2b spends 8k sats and gets included at block hei= +ght 808105;</div><div>tx2b specifies the following params for its change ou= +tput "chg2b":</div><div>chg2b_epochStart=3D808101, chg2b_epochEnd= +=3D808250;</div><div>chg2b_limit=3D10k, chg2b_remain=3D0.<br></div><div><br= +></div><div>These params are allowed because:<br></div><div>=3D> 1. the = +epoch does not become smaller than100 blocks. It is fine to increase the ep= +och to 150 blocks because it does not enable exceeding the original rate-li= +mit;</div><div>=3D> 2. the limit (chg2b_limit) has been decreased to 10k= + sats, further restricting the maximum amount allowed to be spent within th= +e current and any subsequent epochs;</div><div>=3D> 3. the amount spent = +(10k sats) does not exceed tx1_remain AND does not exceed chg2b_limit;</div= +><div>=3D> 4. chg2b_remain has been set to zero, meaning that within the= + current epoch (block height 808101 to and including 808250), tx2b cannot b= +e used as a spending input to any transaction.</div><div><br></div></div><d= +iv>Starting from block height 808251, a new epoch will start and the rate-l= +imited output of tx2b may again be used as an input for a subsequent rate-l= +imited transaction tx3b. This transaction tx3b must again be accompanied by= + params that do not violate the rate-limit as defined by the params of tx2b= + and which are stored with output out2b. So, the epoch of tx3b must be at m= +inimum 150 blocks, the maximum that is allowed to be spent per epoch is at = +most 10k sats, and chg3b_remain must be decreased by at least the amount sp= +ent by tx3b.</div><div><br></div><div>From the above, the rate-limiting mec= +hanics should hopefully be clear and full set of validation rules could be = +defined in a more generalized way with little additional effort.</div><div>= +<br></div><div>Note that I conveniently avoided talking about how to repres= +ent the parameters within transactions or outputs, simply because I current= +ly lack enough understanding to reason about this. I am hoping that others = +may offer help.</div><div><br></div><div>Zac</div><div><br></div></div><br>= +<div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Tue, Au= +g 3, 2021 at 8:12 PM Billy Tetrud <<a href=3D"mailto:billy.tetrud@gmail.= +com" target=3D"_blank">billy.tetrud@gmail.com</a>> wrote:<br></div><bloc= +kquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:= +1px solid rgb(204,204,204);padding-left:1ex"><div dir=3D"ltr">> To enabl= +e more straightforward validation logic.<div>> within the current epoch<= +/div><div><br></div><div>Ah I see, this is all limited to within a single e= +poch. I think that sufficiently limits the window of time in which nodes ha= +ve to store information for rate limited outputs. However, I don't see = +how specifying block ranges simplifies the logic - wouldn't this compli= +cate the logic with additional user-specified constraints? It also prevents= + the output from being able to be rate limited over the span of multiple ep= +ochs, which would seem to make it a lot more difficult to use for certain t= +ypes of wallets (eg cold wallets).=C2=A0</div><div><br></div><div>I think I= + see the logic of your 'remaining' parameter there. If you start wi= +th a single rate-limited input, you can split that into many outputs, only = +one of which have a 'remaining' balance. The rest can simply remain= + unspendable for the rest of the epoch. That way these things don't nee= +d to be tied together. However, that doesn't solve the problem of 3rd p= +arties being able to send money into the wallet.=C2=A0</div><div><br></div>= +<div>> I don't believe that the marginal added functionality would j= +ustify the increased implementation complexity</div><div><br></div><div>Per= +haps, but I think there is a lot of benefit in allowing these kinds of thin= +gs to operate as similarly as possible to normal transactions, for one beca= +use of usability reasons. If each opcode has its own quirks that are not in= +tuitively related to their purpose (eg if a rate-limited wallet had no way = +to get a receiving address), it would confuse end-users (eg who wonder how = +to get a receiving address and how they can ask people to send money into t= +heir wallet) or require a lot of technical complexity in applications (eg t= +o support something like cooperatively connecting with their wallet so that= + a transaction can be made that creates a new single-output=C2=A0for the wa= +llet). A little complexity in this opcode can save a lot of external comple= +xity here I think.=C2=A0</div><div><br></div><div>> my understanding of = +Bitcoin is way too low to be able to write a BIP and do the implementation<= +/div><div><br></div><div>You might be able to find people willing to help. = +I would be willing to help write the BIP spec. I'm not the right person= + to help with the implementation, but perhaps you could find someone else w= +ho is. Even if the BIP isn't adopted, it could be a starting point or i= +nspiration for someone else to write an improved version.=C2=A0</div></div>= +<br><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Mon= +, Aug 2, 2021 at 2:32 AM Zac Greenwood <<a href=3D"mailto:zachgrw@gmail.= +com" target=3D"_blank">zachgrw@gmail.com</a>> wrote:<br></div><blockquot= +e class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px s= +olid rgb(204,204,204);padding-left:1ex"><div dir=3D"ltr">[Note: I've mo= +ved your reply to the newly started thread]<div><br></div><div>Hi Billy,<di= +v><br></div><div>Thank you for your kind and encouraging feedback.</div><di= +v><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0= +.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I don't q= +uite 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?</blockquote><div><br></div><div>To enable= + more straightforward validation logic.</div><div><br></div><blockquote cla= +ss=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid = +rgb(204,204,204);padding-left:1ex">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 comp= +letely separate rate limits, which wouldn't work since the change outpu= +t would ignore the previous rate limit.</blockquote><div><br></div><div>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 outpu= +t is itself rate-limited such that it does not violate the rate-limiting co= +nstraints of its input.</div><div><br></div><div>In my thread-starter, I ga= +ve the below example of a rate-limited address a2 that serves as input for = +transaction t2:</div><div><br></div><div><div>a2: 99.8 sats at height=C2=A0= +800100;</div><div>Rate-limit params: h0=3D800000, h1=3D800143, a=3D500k, a_= +remaining=3D300k;</div><div><br></div><div>Transaction t2:</div><div>Includ= +ed at block height 800200</div><div>Spend: 400k=C2=A0+ fees.</div><div>Rate= +-limiting params: h0=3D800144, h1=3D800287, a=3D500k, a_remaining=3D100k.<b= +r></div><div><br></div><div>Note how transaction t2 re-specifies the rate-l= +imiting parameters. Validation must ensure that the re-specified parameters= + are within bounds, i.e., do not allow more spending per epoch than the rat= +e-limiting parameters of its input address a2. Re-specifying the rate-limit= +ing parameters offers the flexibility to further restrict spending, or to d= +isable any additional spending within the current epoch by setting a_remain= +ing to zero.</div><div><br></div><div><div>Result:</div><div>Value at desti= +nation address: 400k sats;</div><div>Rate limiting params at destination ad= +dress: none;</div><div>Value at change address a3: 99.4m sats;</div><div>Ra= +te limiting params at change address a3: h0=3D800144, h1=3D800287, a=3D500k= +, a_remaining=3D100k.</div></div></div><div><br></div><div>As a design prin= +ciple 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 how= +ever enable more sophisticated rate-limiting (such as rate-limiting per sli= +ding 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 parameter= +s 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.</div><= +div><br></div><div>I haven't gone into how to process a transaction hav= +ing 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 cou= +ld imagine complex logic to handle transactions having multiple rate-limite= +d inputs by creating multiple rate-limited change addresses. However at fir= +st glance I don't believe that the marginal added functionality would j= +ustify the increased implementation complexity.</div><div><br></div><blockq= +uote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1p= +x solid rgb(204,204,204);padding-left:1ex">=C2=A0I'd be interested in s= +eeing you write a BIP for this.</blockquote><div><br></div><div>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 funct= +ionality. Favorable feedback of the list regarding the usefulness and the t= +echnical feasibility of rate-limiting functionality would of course be an e= +ncouragement for me to descend further down the rabbit hole.</div><div><br>= +</div><div>Zac</div></div><div><br></div></div><br><div class=3D"gmail_quot= +e"><div dir=3D"ltr" class=3D"gmail_attr">On Sun, Aug 1, 2021 at 10:09 AM Za= +c Greenwood <<a href=3D"mailto:zachgrw@gmail.com" target=3D"_blank">zach= +grw@gmail.com</a>> wrote:<br></div><blockquote class=3D"gmail_quote" sty= +le=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);paddi= +ng-left:1ex"><div dir=3D"ltr"><div>[Resubmitting to list with minor edits. = +My previous submission ended up inside an existing thread, apologies.]</div= +><div><br></div><div>Hi list,</div><div><br></div><div>I'd like to expl= +ore whether it is feasible to implement new scripting capabilities in Bitco= +in that enable limiting the output amount of a transaction based on the tot= +al value of its inputs. In other words, to implement the ability to limit t= +he maximum amount that can be sent from an address.</div><div><br></div><di= +v>Two use cases come to mind:</div><div><br></div><div>UC1: enable a user t= +o add additional protection their funds by rate-limiting the amount that th= +ey are allowed to send during a certain period (measured in blocks). A typi= +cal 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 t= +he user to become aware of the theft and intervene to prevent further theft= +s.</div><div><br></div><div>UC2: exchanges may wish to rate-limit addresses= + containing large amounts of bitcoin, adding warm- or hot-wallet functional= +ity 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 key= +s that give access to the full amount.</div><div><br></div><div>In a typica= +l 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 m= +eaning "one or more" keys). One set of private keys allows only f= +or sending with rate-limiting restrictions in place, and a second set of pr= +ivate keys allowing for sending any amount without rate-limiting, effective= +ly overriding such restriction.</div><div><br></div><div>The parameters tha= +t define in what way an output is rate-limited might be defined as follows:= +</div><div><br></div><div>Param 1: a block height "h0" indicating= + the first block height of an epoch;</div><div><div>Param 2: a block height= + "h1" indicating the last block height of an epoch;</div><div>Par= +am 3: an amount "a" in satoshi indicating the maximum amount that= + is allowed to be sent in any epoch;<br></div><div>Param 4: an amount "= +;a_remaining" (in satoshi) indicating the maximum amount that is allow= +ed to be sent within the current epoch.</div></div><div><br></div><div>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 80= +0000 and ends at height 800143 (or about one day ignoring block time varian= +ce) and that the full amount of 500k is still sendable. These rate-limiting= + parameters ensure that it takes at minimum 100m / 500k =3D 200 transaction= +s and 200 x 144 blocks or about 200 days to spend the full 100m sats. As no= +ted earlier, in a typical setup a user should retain the option to transact= + the entire amount using a second (set of) private key(s).</div><div><br></= +div><div>For rate-limiting to work, any change output created by a transact= +ion from a rate-limited address must itself be rate-limited as well. For in= +stance, expanding on the above example, assume that the user spends 200k sa= +ts from a rate-limited address a1 containing 100m sats:</div><div><br></div= +><div>Start situation:</div><div>At block height 800000: rate-limited addre= +ss a1 is created;</div><div>Value of a1: 100.0m sats;</div><div>Rate limiti= +ng params of a1: h0=3D800000, h1=3D800143, a=3D500k, a_remaining=3D500k;</d= +iv><div><br></div><div>Transaction t1:</div><div>Included at block height 8= +00100;</div><div>Spend: 200k + fee;</div><div>Rate limiting params: h0=3D80= +0000, h1=3D800143, a=3D500k, a_remaining=3D300k.</div><div><br></div><div>R= +esult:</div><div>Value at destination address: 200k sats;</div><div>Rate li= +miting params at destination address: none;</div><div>Value at change addre= +ss a2: 99.8m sats;</div><div>Rate limiting params at change address a2: h0= +=3D800000, h1=3D800143, a=3D500k, a_remaining=3D300k.</div><div><br></div><= +div>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 s= +ame rate limiting parameters as the transaction that served as its input. A= +s a result, from block 800100 up until and including block 800143, a maximu= +m amount of 300k sats is allowed to be spent from the change address.</div>= +<div><br></div><div>Example continued:</div><div>a2: 99.8 sats at height=C2= +=A0800100;</div><div>Rate-limit params: h0=3D800000, h1=3D800143, a=3D500k,= + a_remaining=3D300k;</div><div><br></div><div>Transaction t2:</div><div>Inc= +luded at block height 800200</div><div>Spend: 400k=C2=A0+ fees.</div><div>R= +ate-limiting params: h0=3D800144, h1=3D800287, a=3D500k, a_remaining=3D100k= +.<br></div><div><br></div><div><div>Result:</div><div>Value at destination = +address: 400k sats;</div><div>Rate limiting params at destination address: = +none;</div><div>Value at change address a3: 99.4m sats;</div><div>Rate limi= +ting params at change address a3: h0=3D800144, h1=3D800287, a=3D500k, a_rem= +aining=3D100k.</div><div><br></div><div>Transaction t2 is allowed because i= +t falls within the next epoch (running from 800144 to 800287) so a spend of= + 400k does not violate the constraint of 500k per epoch.</div><div><br></di= +v><div>As could be seen, the rate limiting parameters are part of the trans= +action and chosen by the user (or their wallet). This means that the parame= +ters must be validated to ensure that they do not violate the intended cons= +traints.</div><div><br></div><div>For instance, this transaction should not= + be allowed:</div><div><div>a2: 99.8 sats at height=C2=A0800100;</div><div>= +Rate-limit params of a2: h0=3D800000, h1=3D800143, a=3D500k, a_remaining=3D= +300k;</div><div><br></div><div>Transaction t2a:</div><div>Included at block= + height 800200;</div><div>Spend: 400k=C2=A0+ fees;</div><div><div>Rate-limi= +t params: h0=3D800124, h1=3D800267, a=3D500k, a_remaining=3D100k.</div><div= +><br></div></div><div>This transaction t2a attempts to shift the epoch forw= +ard 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:</div></div><div><br></div><div>t1: spend 200= +k at 800100 (epoch 1: total: 200k);</div><div>t2a: spend 400k at 800200 (ep= +och 2: total: 400k);</div><div>t3a: spend 100k at 800201 (epoch 2: total: 5= +00k);</div><div>t4a: spend 500k at 800268 (epoch 2: total: 1000k, overspend= +ing for epoch 2).</div><div><br></div><div>Specifying the rate-limiting par= +ameters explicitly at every transaction allows the user to tighten the spen= +ding 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 a= +dvantage of explicitly specifying the four rate-limiting parameters with ea= +ch transaction is that it allows the system to fully validate the transacti= +on without having to consider any previous transactions within an epoch.</d= +iv><div><br></div><div>I will stop here because I would like to gauge inter= +est in this idea first before continuing work on other aspects. Two main pi= +eces of work jump to mind:</div><div><br></div><div>Define all validations;= +</div><div>Describe aggregate behaviour of multiple (rate-limited) inputs, = +proof that two rate-limited addresses cannot spend more than the sum of the= +ir individual limits.</div><font color=3D"#888888"><div><br></div><div>Zac<= +/div></font></div></div> +</blockquote></div> +</blockquote></div> +</blockquote></div></div> +</blockquote></div> + +--0000000000008d270e05c8ca2ffb-- + |