diff --git a/elip-0000.mediawiki b/elip-0000.mediawiki new file mode 100644 index 0000000..141530d --- /dev/null +++ b/elip-0000.mediawiki @@ -0,0 +1,227 @@ +
+ ELIP: ??? + Layer: Applications + Title: Issued Asset Fees + Author: Tom Trevethan+ +==Introduction== + +===Abstract=== + +This document proposes a modification to Elements to enable transaction fees to be paid in issued assets in addition to the+ Comments-Summary: No comments yet. + Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-0??? + Status: Draft + Type: Standards Track + Created: 2025-09-02 + License: BSD-3-Clause +
policyAsset (or pegged asset). To enable this, the specific issued assets that will be accepted for fees and the corresponding fee rate (relative to the enforced policyAsset rate) must be agreed and configured by all block creators and also communicated to users and implemented in wallets. To enable the use of issued asset fees requires changes to policy rules for transaction relay mempool acceptance and ordering, and changes to block creation to enable issued asset fees to be paid to specified destinations in coinbase outputs. Issued asset fee acceptance and payout in the coinbase transaction is valid within the current consensus rules and so none of the features described here require any consensus fork to deploy. In addition to these changes to policy and block creation, this document also proposes features to enable both publication of accepted assets and fee rates on-chain for use by wallets, and a method to update the fee asset policy applied by nodes remotely via an authenticated on-chain transaction.
+
+===Copyright===
+
+This document is licensed under the 3-clause BSD license.
+
+===Motivation===
+
+In Elements, transaction fees are required in order to prevent denial of service attacks and to prioritize transactions for limited block space and are specified by an explicit unblinded output (signified by an empty scriptPubKey). In the current Elements implementation, policy rules for transaction relay and mempool acceptance require that the assetID of this fee output be equal to the policyAsset. The current consensus rules however allow for any assetID for the fee asset (so long as the transaction is otherwise valid). The current consensus rules also allow for coinbase outputs for any asset with values equal to or less than the total fee outputs for that asset for all transactions in a block. However the current Elements code will only create a block with a single spendable coinbase output for the total of policyAsset fees.
+
+These current restrictions limit the usefulness of the Elements platform for transacting in issued assets, as policyAsset is always then required for the payment of transaction fees. For example, for a Bitcoin sidechain (e.q. Liquid) there are many issued assets for tokenized currencies (e.g. USDT) which many users will use for making payments. However they must obtain an amount of the policyAsset (e.g. LBTC) before they can transact, and the fees will be priced in BTC instead of USD. This leads to a poor user experience when transacting in tokenized stablecoins, as a new user will be required to purchase LBTC before they are able to spend USDT that they have received in their wallet. In addition, the value of transaction fee required will not be explicit in terms of USDT and depend on the current USD value of BTC. By enabling fees to be paid in specified issued assets and at specified rates, user wallets can transact in an issued asset like USDT seamlessly, with predictable and transparent fees.
+
+In addition to enabling the issued asset transaction fees to be accepted, and payment of these fees in the coinbase of new blocks, there needs to be a method for wallets and users to know which assets are accepted by block creators and at what rates. The simplest and most robust way of achieving this is with a publication of the accepted assets and rates by block creators on chain, in the coinbase transaction - this removes the requirement for separate servers to publish and relay this data.
+
+Finally, a method is required to configure which assets and corresponding fee rates will be applied to policy on relaying and block creation nodes. These can be set with direct access to the node, either via the node configuration or the RPC interface. However, the accepted assets and fee rates may need to be updated frequently, and it may be impractical and have security implications to have direct access to functionary and bridge nodes to perform these updates via the RPC interface. Therefore, a method to perform this update remotely via an on-chain transaction is also required. Individual nodes can then be configured with a 'controller' script that authenticates changes to the asset fee policy without requiring direct access to any individual nodes.
+
+==Design==
+
+===Overview===
+
+Each node has complete control over which assets and at what fee rates to accept as policy. However, in practice all block creators and relaying nodes should agree to apply the same asset fee policy in order to provide a reliable service for users. The assets and fee rates accepted by any node can be set as part of the node configuration, specifying the assetID, the relative rate multiplier (and optionally the coinbase destination for the fee collection by the block creator) before initialization, or this can be set dynamically at runtime via a new RPC. In addition, a new RPC will be added to query the current assets, rate multiplier (and destinations) currently being applied to policy by any node.
+
+For fee outputs in an issued asset, the fee rate (in units/vbyte) will be scaled to determine an 'effective rate'. This effective rate is then used in place of the policyAsset fee rate for the purpose of meeting relay and mempool minimums and for mempool ordering.
+
+E.g. An issued asset is USDT. The current accepted exchange rate between USDT and LBTC (the policyAsset) is $114,171.23 to 1 LBTC. The issued asset fee rate multiplier for this ratio is then set to 1/114,171.23 = 0.00000876 (rounded to a precision of 1e-8). For a transaction that uses the USDT asset for the fee, the fee rate (in units/vbyte) multiplied by this number must be greater than or equal to the minimum fee rate (in sat/vbyte) in LBTC applied to policy.
+If the current minimum relay fee rate is 1 sat/vbyte in LBTC, then the minimum fee rate required in USDT would be 1/0.00000876 = 114156 units/vbyte (rounded up to the nearest integer). To relay this transaction, a client will determine the fee rate in the USDT asset, multiply it by 0.00000876 to calculate the effective rate and then verify it is greater than or equal to 1 sat/vbyte.
+
+===Specification===
+
+====Issued asset fees====
+
+Accepted assets, rate multipliers (and optional destination) are stored in a vector assetFeeRates of CAssetFeeRate objects.
+This object contains the CAsset asset ID, the int64_t fee rate multiplier and optional CScript fee destination.
+
+For each supported issued asset, there is a specified fee rate multiplier: issued_asset_fee_rate_multiplier (a float). This is represented internally as a fixed precision int64_t number issued_asset_fee_rate_multiplier_int64 (the number of decimal places supported is specified by the int64_t parameter ASSET_MULTIPLIER_SCALE_FACTOR (default 100000000) which is applied to all asset multipliers.
+
+
+issued_asset_fee_rate_multiplier_int64 = static_cast(std::ceil(issued_asset_fee_rate_multiplier * ASSET_RATE_SCALE_FACTOR))
+
+
+This value scales the calculated the fee rate of a transaction that has an issued asset fee to give an effective_fee_rate.
+
+
+effective_fee_rate = (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) / ASSET_MULTIPLIER_SCALE_FACTOR
+
+
+Where asset_fee_rate is the fee rate calculated using the transaction vsize and the amount of the issued asset fee output. For every transaction in the mempool that has an issued asset fee, the effective_fee_rate is used in place of the policyAsset fee rate for the purposes of mempool ordering and minimum mempool fee rate calculation.
+
+The value of issued_asset_fee_rate_multiplier_int64 must be between 1 and MAX_ASSET_MULTIPLIER
+
+By default MAX_ASSET_MULTIPLIER = 10^15. Scaled as a fixed precision number, this enables values of issued_asset_fee_rate_multiplier between 0.00000001 and 10,000,000.
+
+The value of (asset_fee_rate * issued_asset_fee_rate_multiplier_int64) cannot exceed the maximum allowed value of int64_t. If it does, then the effective_fee_rate is set to maximum allowable value of std::numeric_limits::max() / ASSET_RATE_SCALE_FACTOR = 92233720368 (which is 922.3372 LBTC/vbyte).
+
+To calculate the value of an issued asset fee output required for a transaction of size vsize where the current minimum policyAsset fee rate is policy_asset_rate:
+
+
+fee_amount = vsize * policy_asset_rate * (1 + ((ASSET_RATE_SCALE_FACTOR - 1) / issued_asset_fee_rate_multiplier_int64))
+
+
+The fee asset and rate multiplier can be configured for an individual node with the option:
+
+-setfeeassetrate=: set the assetID:multiplier-script applied to relay and mempool policy for this node.
+
+The multiplier is supplied as a floating point number (issued_asset_fee_rate_multiplier) which is converted to fixed point integer by ceiling multiplication with ASSET_MULTIPLIER_SCALE_FACTOR.
+
+This can be repeated for additional fee assets and rates. The maximum number of issued asset fee multipliers allowed is set by MAX_ISSUED_ASSET_FEE_SIZE (default 10).
+
+A new accepted fee asset can be added on demand at runtime with the RPC, or an existing asset updated with:
+
+updatefeeassetrate add new assetID, multiplier, (optional) destination script to apply to node policy or update the rates of existing configured assets dynamically.
+
+getassetfeerate get all assetID, multiplier, script applied by this client.
+
+Note: Consensus enforces that any individual fee output, and the total sum of fee values in a block do not exceed MAX_MONEY. Issued asset fees could in theory exceed this, as individual assets might have much larger issuance amounts than MAX_MONEY. Therefore, currently in BlockAssembler the inclusion of issued asset fee paying transactions in a block will be limited so that the total value of fee outputs will not exceed MAX_MONEY when generating a new block.
+
+====Fee rate publication====
+
+Asset fee rates multipliers (and optional destination) encoded in additional coinbase OP_RETURN outputs as follows:
+
+* 4 bytes: AFEE
+* 32 bytes: asset ID
+* 16 bytes: fee rate multiplier (uint_64 encoded)
+* (up to) 35 bytes: fee destination script
+
+The maximum number of additional outputs is limited to MAX_ISSUED_ASSET_FEE_SIZE. The maximum size for transactions included in a generated block must be reduced by the additional space taken by the additional coinbase outputs.
+
+Config option:
+
+-writefeeassetrate=n Write issued asset fee rate multipliers accepted as policy to the coinbase of created blocks with interval n (default: 0).
+
+If n is 0 then fee assets and rate multipliers are not written.
+
+If n > 0 then any accepted assets and fee rates set via RPC or config are published every n blocks.
+Any fee rates updated by setassetfeerate or controller script are not applied to policy until the end of an epoch of n blocks.
+
+A new RPC getsignerfeerates retrieves the latest published accepted assets and rate multipliers.
+getassetfeerate has a single boolean argument to either retrieve the latest updated list of assets and fee rate multipliers, or the list applied in the current epoch.
+
+On node restart, the cached rates are updated from the latest publication.
+
+====Controller fee asset rate updates====
+
+Feature for issued asset fees that enables an external controller to set issued asset fee policy via an onchain transaction, with asset fee rate multipliers encoded in OP_RETURN outputs.
+Any node can be configured to apply issued asset fee policy that is set by a controller (this does not need to be a single entity, but can be defined by any multisig script). This is set with a config option:
+
+-confeeassetrate=scriptPubKey
+
+where scriptPubKey is the script that is used by the controller(s) to set the asset and rate multipliers in a transaction.
+-conassetfeerate cannot be set at the same time as -setassetfeerate (this will generate an error).
+A valid controller transaction must spend from an output where the scriptPubKey is also the same controller script (this is used as an authentication mechanism).
+
+The format for the OP_RETURN encoding is that same as that used for the fee rate publication.
+
+==Backwards Compatibility==
+
+No issues for backwards compatibility - all existing transactions with policy asset fees are unaffected by these features. No changes to consensus rules are required.
+
+==Test Vectors==
+
+* Configure issued asset with fee rate of issued_asset_fee_rate_multiplier = 0.5
+
+-setfeeassetrate=e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0:0.5+ +* Get configured fee asset rates + +
getassetfeerate+ +Result: + +
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.5'), 'script': ''}]
+
+* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee multiplier of 0.5:
+
+** vsize: 257
+
+** Fee: 514
+
+0200000001016f90831e415a738df732b62dd0b76e87bbe489096e43c892bf3dcbbfd173778d0000000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002faf08000160014c5439a67eaa99a308719b544f327a942ae02e6a701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000002f967de0016001467c8c91e8357e1e24d1721c4535a99608e47beb701a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee801000000000000020200006c0000000000024730440220031c1aa256e40a89cf622374e1ef281870a6abb6f588411800fe885f5195a49e02204f3c37cfe2318db87ac7ce4d7da70682e3477653f6222383ab72ea259a3b1fae0121029896a2fe9c710b8b71488b90038d3974144dc21c9c8750459162dc37d141459300000000000000+ +* Update issued asset fee rate multiplier to 0.1 with RPC + +
updatefeeassetrate e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0 0.1+ +* Get configured fee asset rates + +
getassetfeerates+ +Result: + +
[{'asset': 'e86ed2437731d8ebb6bf457ee95a7f50e0bdfb41d319c69af046c52c7f25aba0', 'rate': Decimal('0.1'), 'script': ''}]
+
+* Transaction with 1 input and 2 outputs (+ 1 fee output) with asset fee at rate multiplier 0.1:
+
+** vsize: 257
+
+** Fee: 2570
+
+0200000001015ce5df8217dfa572d4539d0ae05178be26c27dbc43af35f6877f0160c8d329360100000000feffffff0301a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000001312d0000160014fc060cafa93c7f4d5e0963ba08c55540c28361bf01a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000001c830d4001600141f79c72bcdad5c20c17337c1f22c3b41f156c53d01a0ab257f2cc546f09ac619d341fbbde0507f5ae97e45bfb6ebd8317743d26ee8010000000000000a0a00004100000000000247304402205bc1f97638e5b1932ba6df702e938bd1cfdc6da46a4db8511fe172f821f102c502206a6fbe66d81e89bd78b4157d58af15e8c940e0c0d797cbe91535fb31683e013601210291d9c10cf976e932db8ffe0f2bf1d9c1574ea3affe2e0f98ff15e1b328eb860300000000000000+ +* Configuration for fee asset rate publication: + +
-writefeeassetrate=100+ +
updatefeeassetrate b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22 1.86+
updatefeeassetrate af6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3 3.781+ +Coinbase transaction: + +
0200000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0401230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeaf6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3000000008d1ba84001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22000000001689592001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000012a05f200001976a9149d8419680d03a0ac4133beb9e097d5c9ff7babbc88ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000000000000000266a24aa21a9ed5aa9aac025ce73291c124e2ccfd44ae60837d1f20a8573318427075444287de800000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000+ +Output 1: + +
afeeaf6e22da7b20de2da56c6726c4a036c73f777c8fb5bfc356892c01a9a3dd58d3000000008d1ba840+ +Output 2: + +
afeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000016895920+ +* Controller transaction + +
+controler_script = "00145956bc071791295bfd5161ddb770aa536289b3e3" +controller_addr = "ert1qt9ttcpchjy54hl23v8wmwu922d3gnvlrz7sqx7" +private_key = "cP3N3Z3rMTo4L2gNRDWmzo8nGNyVNnWwFX9DRVPqwcPnMWE8Rn33" ++ +Configuration + +
-confeeassetrate=00145956bc071791295bfd5161ddb770aa536289b3e3+ +Fee Asset setting: + +
asset_id = b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22+ +rate = 1.0 + +Encoded fee asset hex string: + +
afeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000005f5e100+ +Controller transaction: + +
020000000001500d712c870c96c0bec05f41d765f4f5f203bd8d5b7ea6ef333b53b8c2c2d164000000006a47304402207f1b2c580ee052b8d0da06a139cd1e910ec93c8b11585d692646e6267b8354c4022045caab622e3adbae204263dfdc86e341fd9f3c013cbf2234fdea2c8ee2ba0d81012103ba4a2b1f401eb59e1e6b104f8043ce41b38b65bd24c10edb3df8863b0241e5afffffffff0301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000012a046b60001600145956bc071791295bfd5161ddb770aa536289b3e301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000000002c6a2aafeeb92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a220000000005f5e10001230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000000000186a0000000000000+ +
getassetfeerate+ +
[{'asset': 'b92b2e64772d8edc7703534fc5028906ab1468a7497af9e8620999132af96a22', 'rate': 1, 'script': ''}]
+
+