{"type":"rich","version":"1.0","author_name":"npub1lpkmxpl2zhk0w30vtdz7s64ml9644k785eggmjsjgs7wman3szzqac74n4","author_url":"https://nostr.ae/npub1lpkmxpl2zhk0w30vtdz7s64ml9644k785eggmjsjgs7wman3szzqac74n4","provider_name":"njump","provider_url":"https://nostr.ae","html":"📅 Original date posted:2022-11-08\n📝 Original message:Hi list,\n\nI have been working on some notes to describe an approach that uses\ncovenants in order to enable general smart contracts in bitcoin. You can\nfind them here:\n\n    https://merkle.fun\n\nThe approach has a number of desirable features:\n\n- small impact to layer 1;\n- not application-specific, very general;\n- it fits well into P2TR;\n- it does not require new cryptographic assumptions, nor any construction\nthat has not withstood the test of time.\n\nThis content was presented at the BTCAzores unconference, where it received\nthe name of MATT − short for Merkleize All The Things.\nIn fact, no other cryptographic primitive is required, other than Merkle\ntrees.\n\nI believe this construction gets close to answering the question of how\nsmall a change on bitcoin's layer 1 would suffice to enable arbitrary smart\ncontracts.\n\nIt is not yet at the stage where a formal proposal can be made, therefore\nthe proposed specs are only for illustrative purposes.\n\nThe same content is reformatted below for the mailing list.\n\nLooking forward to hearing about your comments and improvements.\nSalvatore Ingala\n\n\n==========================================\n\n\n# General smart contracts in bitcoin via covenants\n\nCovenants are UTXOs that are encumbered with restrictions on the outputs of\nthe transaction spending the UTXO. More formally, we can define a covenant\nany UTXO such that at least one of its spending conditions is valid only if\none or more of the outputs’ scriptPubKey satisfies certain restrictions.\n\nGenerally, covenant proposals also add some form of introspection (that is,\nthe ability for Script to access parts of the inputs/outputs, or the\nblockchain history).\n\nIn this note, we want to explore the possibilities unleashed by the\naddition of a covenant with the following properties:\n\n- introspection limited to a single hash attached to the UTXO (the\n“covenant data”), and input/output amounts;\n- pre-commitment to every possible future script (but not their data);\n- few simple opcodes operating with the covenant data.\n\nWe argue that such a simple covenant construction is enough to extend the\npower of bitcoin’s layer 1 to become a universal settlement layer for\narbitrary computation.\n\nMoreover, the covenant can elegantly fit within P2TR transactions, without\nany substantial increase for the workload of bitcoin nodes.\n\nA preliminary version of these notes was presented and discussed at the\nBTCAzores Unconference [1], on 23rd September 2022.\n\n\n# Preliminaries\n\nWe can think of a smart contract as a “program” that updates a certain\nstate according to predetermined rules (which typically include access\ncontrol by authorizing only certain public keys to perform certain\nactions), and that can possibly lock/unlock some coins of the underlying\nblockchain according to the same rules.\n\nThe exact definition will be highly dependent on the properties of the\nunderlying blockchain.\n\nIn bitcoin, the only state upon which all the nodes reach consensus is the\nUTXO set; other blockchains might have other data structures as part of the\nconsensus, like a key-value store that can be updated as a side effect of\ntransaction execution.\n\nIn this section we explore the following concepts in order to set the\nframework for a definition of smart contracts that fits the structure of\nbitcoin:\n\n- the contract’s state: the “memory” the smart contract operates on;\n- state transitions: the rules to update the contract’s state;\n- covenants: the technical means that can allow contracts to function in\nthe context of a bitcoin UTXO.\n\nIn the following, an on-chain smart contract is always represented as a\nsingle UTXO that implicitly embeds the contract’s state and possibly\ncontrols some coins that are “locked” in it. More generally, one could\nthink of smart contracts that are represented in a set of multiple UTXOs;\nwe leave the exploration of generalizations of the framework to future\nresearch.\n\n## State\n\nAny interesting “state” of a smart contract can ultimately be encoded as a\nlist, where each element is either a bit, a fixed-size integers, or an\narbitrary byte string.\n\nWhichever the choice, it does not really affect what kinds of computations\nare expressible, as long as one is able to perform some basic computations\non those elements.\n\nIn the following, we will assume without loss of generality that\ncomputations happen on a state which is a list of fixed length S = [s_1,\ns_2, …, s_n], where each s_i is a byte string.\n\n### Merkleized state\n\nBy constructing a Merkle tree that has the (hashes of) the elements of S in\nthe leaves, we can produce a short commitment h_S to the entire list S with\nthe following properties (that hold for a verifier that only knows h_S):\n\n- a (log n)-sized proof can prove the value of an element s_i;\n- a (log n + |x|)-sized proof can prove the new commitment h_S’, where S’\nis a new list obtained by replacing the value of a certain leaf with x.\n\nThis allows to compactly commit to a RAM, and to prove correctness of RAM\nupdates.\n\nIn other words, a stateful smart contract can represent an arbitrary state\nin just a single hash, for example a 32-byte SHA256 output.\n\n### State transitions and UTXOs\n\nWe can conveniently represent a smart contract as a finite state machine\n(FSM), where exactly one node can be active at a given time. Each node has\nan associated state as defined above, and a set of transition rules that\ndefine:\n\n- who can use the rule;\n- what is the next active node in the FSM;\n- what is the state of the next active node.\n\nIt is then easy to understand how covenants can conveniently represent and\nenforce the smart contracts in this framework:\n\n- The smart contract is instantiated by creating a UTXO encumbered with a\ncovenant; the smart contract is in the initial node of the FSM.\n- The UTXO’s scriptPubKey specifies the current state and the valid\ntransitions.\n- The UTXO(s) produced after a valid transition might or might not be\nfurther encumbered, according to the rules.\n\nTherefore, what is necessary in order to enable this framework in bitcoin\nScript is a covenant that allows the enforcement of such state transitions,\nby only allowing outputs that commit to a valid next node (and\ncorresponding state) in the FSM.\n\nIt is not difficult to show that arbitrary computation is possible over the\ncommitted state, as long as relatively simple arithmetic or logical\noperations are available over the state.\n\nRemark: using an acyclic FSM does not reduce the expressivity of the smart\ncontracts, as any terminating computation on bounded-size inputs which\nrequires cycles can be unrolled into an acyclic one.\n\n### Merkleized state transitions\n\nSimilarly to how using Merkle trees allows to succinctly represent\narbitrary data with a short, 32-byte long summary, the same trick allows to\nsuccinctly represent arbitrary state transitions (the smart contract’s\ncode) with a single 32-byte hash. Each of the possible state transitions is\nencoded as a Script which is put in a leaf of a Merkle tree; the Merkle\nroot of this tree is a commitment to all the possible state transitions.\nThis is exactly what the taptree achieves in Taproot (see BIP-0341 [2]).\n\nLater sections in this document will suggest a possible way of how both the\ncontract’s state and valid transition rules could be represented in UTXOs.\n\n## On-chain computation?!\n\nShould the chain actually do computation?\n\nIf naively designed, the execution of a contract might require a large\nnumber of transactions, which is not feasible.\n\nWhile the covenant approach does indeed enable a chain of transactions to\nperform arbitrary computation, simple economic considerations will push\nprotocol designers to perform any non-trivial computation off-chain, and\ninstead use the blockchain consensus only to verify the computation; or, if\npossible, skip the verification altogether.\n\nThe fundamental fact that a blockchain’s layer 1 never actually needs to\nrun complex programs in order to enable arbitrary complex smart contracting\nwas observed in the past, for example in a 2016 post by Greg Maxwell [3].\n\nVitalik Buterin popularized the concept of \"functionality escape velocity\"\n[4] to signify the minimum amount of functionality required on layer 1 in\norder to enable anything else to be built on top (that is, on layer 2 and\nbeyond).\n\nIn the following section, we will argue that a simple covenant construction\nsuffices to achieve the functionality escape velocity in the UTXO model.\n\n\n# Commitments to computation and fraud challenges\n\nIn this section, we explore how a smart contract that requires any\nnon-trivial computation f : X --\u003e Y (that is too expensive or not feasible\nwith on-chain Script state transitions) can be implemented with the simple\ncovenants described in the previous section.\n\nThe ideas in this section appeared in literature; the reader is referred to\nthe references for a more comprehensive discussion.\n\nWe want to be able to build contracts that allow conditions of the type\n\"f(x) = y\"; yet, we do not want layer 1 to be forced to perform any\nexpensive computation.\n\nIn the following, we assume for simplicity that Alice and Bob are the only\nparticipants of the covenant, and they both locked some funds bond_A and\nbond_B (respectively) inside the covenant’s UTXO.\n\n1. Alice posts the statement “f(x) = y”.\n2. After a challenge period, if no challenge occurs, Alice is free to\ncontinue and unlock the funds; the statement is true.\n3. At any time before the challenge period expires, Bob can start a\nchallenge: “actually, f(x) = z”.\n\nIn case of a challenge, Alice and Bob enter a challenge resolution\nprotocol, arbitrated by layer 1; the winner takes the other party’s bond\n(details and the exact game theory vary based on the type of protocol the\nchallenge is part of; choosing the right amount of bonds is crucial for\nprotocol design).\n\nThe remainder of this section sketches an instantiation of the challenge\nprotocol.\n\n## The bisection protocol for arbitrary computation\n\nIn this section, we sketch the challenge protocol for an arbitrary\ncomputation f : X --\u003e Y.\n\n### Computation trace\n\nGiven the function f, it is possible to decompose the entire computation in\nsimple elementary steps, each performing a simple, atomic operation. For\nexample, if the domain of x and y is that of binary strings of a fixed\nlength, it is possible to create a boolean circuit that takes x and\nproduces y; in practice, some form of assembly-like language operating on a\nRAM might be more efficient and fitting for bitcoin Script.\n\nIn the following, we assume each elementary operation is operating on a\nRAM, encoded in the state via Merkle trees as sketched above. Therefore,\none can represent all the steps of the computation as triples tri = (st_i,\nop_i, st_{i + 1}), where st_i is the state (e.g. a canonical Merkle tree of\nthe RAM) before the i-th operation, st_{i + 1} is the state after, and op_i\nis the description of the operation (implementation-specific; it could be\nsomething like “add a to b and save the result in c).\n\nFinally, a Merkle tree M_T is constructed that has as leaves the values of\nthe individual computation steps T = {tr_0, tr_1, …, tr_{N - 1}} if the\ncomputation requires N steps, producing the Merkle root h_T. The height of\nthe Merkle tree is log N. Observe that each internal node commits to the\nportion of the computation trace corresponding to its own subtree.\n\nLet’s assume that the Merkle tree commitments for internal nodes are\nfurther augmented with the states st_{start} and st_{end}, respectively the\nstate before the operation of in the leftmost leaf of the subtree, and\nafter the rightmost leaf of the subtree.\n\n### Bisection protocol\n\nThe challenge protocol begins with Alice posting what she claims is the\ncomputation trace h_A, while Bob disagrees with the trace h_B != h_A;\ntherefore, the challenge starts at the root of M_T, and proceeds in steps\nin order to find a leaf where Alice and Bob disagree (which is guaranteed\nto exist, hence the disagreement). Note that the arbitration mechanism\nknows f, x and y, but not the correct computation trace hash h_T.\n\n(Bisection phase): While the challenge is at a non-leaf node of M_T, Alice\nand Bob take turns to post the two hashes corresponding to the left and\nright child of their claimed computation trace hash; moreover, they post\nthe start/end state for each child node. The protocol enforces that Alice’s\ntransaction is only valid if the posted hashes h_{l; A} and h_{r; A}, and\nthe declared start/end state for each child are consistent with the\ncommitment in the current node.\n\n(Arbitration phase): If the protocol has reached the i-th leaf node, then\neach party reveals (st_i, op_i, st_{i + 1}); in fact, only the honest party\nwill be able to reveal correct values, therefore the protocol can\nadjudicate the winner.\n\nRemark: there is definitely a lot of room for optimizations; it is left for\nfuture work to find the optimal variation of the approach; moreover,\ndifferent challenge mechanisms could be more appropriate for different\nfunctions f.\n\n### Game theory (or why the chain will not see any of this)\n\nWith the right economic incentives, protocol designers can guarantee that\nplaying a losing game always loses money compared to cooperating.\nTherefore, the challenge game is never expected to be played on-chain. The\nsize of the bonds need to be appropriate to disincentivize griefing attacks.\n\n### Implementing the bisection protocol's state transitions\n\nIt is not difficult to see that the entire challenge-response protocol\nabove can be implemented using the simple state transitions described above.\n\nBefore a challenge begins, the state of the covenant contains the value of\nx, y and the computation trace computed by Alice. When starting the\nchallenge, Bob also adds its claim for the correct computation trace, and\nthe covenant enters the bisection phase.\n\nDuring the bisaction phase, the covenant contains the claimed computation\ntrace for that node of the computation protocol, according to each party.\nIn turns, each party has to reveal the corresponding computation trace for\nboth the children of the current node; the transaction is only valid if the\nhash of the current node can be computed correctly from the information\nprovided by each party about the child nodes. The protocol repeats on one\nof the two child nodes on whose computation trace the two parties disagree\n(which is guaranteed to exist). If a leaf of M_T is reached, the covenant\nenters the final arbitration phase.\n\nDuring the arbitration phase (say at the i-th leaf node of M_T), any party\ncan win the challenge by providing correct values for tr_i = (st_i, op_i,\nst_{i + 1}). Crucially, only one party is able to provide correct values,\nand Script can verify that indeed the state moves from st_i to st_{i + 1}\nby executing op_i. The challenge is over.\n\nAt any time, the covenant allows one player to automatically win the\nchallenge after a certain timeout if the other party (who is expected to\n“make his move”) does not spend the covenant. This guarantees that the\nprotocol can always find a resolution.\n\n### Security model\n\nAs for other protocols (like the lightning network), a majority of miners\ncan allow a player to win a challenge by censoring the other player’s\ntransactions. Therefore, the bisection protocol operates under the honest\nminer majority assumption. This is acceptable for many protocols, but it\nshould certainly be taken into account during protocol design.\n\n\n# MATT covenants\n\nWe argued that the key to arbitrary, fully general smart contracts in the\nUTXO model is to use Merkle trees, at different levels:\n\n1. succinctly represent arbitrary state with a single hash. Merkleize the\nstate!\n2. succinctly represent the possible state transitions with a single hash.\nMerkleize the Script!\n3. succinctly represent arbitrary computations with a single hash.\nMerkleize the execution!\n\n(1) and (2) alone allow contracts with arbitrary computations; (3) makes\nthem scale.\n\n   Merkleize All The Things!\n\nIn this section we sketch a design of covenant opcodes that are\ntaproot-friendly and could easily be added in a soft fork to the existing\nSegWitv1 Script.\n\n## Embedding covenant data in P2TR outputs\n\nWe can take advantage of the double-commitment structure of taproot outputs\n(that is, committing to both a public key and a Merkle tree of scripts) to\ncompactly encode both the covenant and the state transition rules inside\ntaproot outputs.\n\nThe idea is to replace the internal pubkey Q with a key Q’ obtained by\ntweaking Q with the covenant data (the same process that is used to commit\nto the root of the taptree). More precisely, if d is the data committed to\nthe covenant, the covenant-data-augmented internal key Q’ is defined as:\n\n    Q’ = Q + int(hashTapCovenantData(Q || h_{data}))G\n\nwhere h_{data} is the sha256-hash of the covenant data. It is then easy to\nprove that the point is constructed in this way, by repeating the\ncalculation.\n\nIf there is no useful key path spend, similarly to what is suggested in\nBIP-0341 [5] for the case of scripts with no key path spends, we can use\nthe NUMS point:\n    H =\nlift_x(0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0).\n\nTODO: please double check if the math above is sound.\n\n## Changes to Script\n\nThe following might be some minimal new opcodes to add for taproot\ntransactions in order to enable the construction above. This is a very\npreliminary proposal, and not yet complete nor correct.\n\n- OP_SHA256CAT: returns the SHA256 hash of the concatenation of the second\nand the first (top) element of the stack. (redundant if OP_CAT is enabled,\neven just on operands with total length up to 64 bytes)\n- OP_CHECKINPUTCOVENANTVERIFY: let x, d be the two top elements of the\nstack; behave like OP_SUCCESS if any of x and d is not exactly 32 bytes;\notherwise, check that the x is a valid x-only pubkey, and the internal\npubkey P is indeed obtained by tweaking lift_x(x) with d.\n- OP_INSPECTNUMINPUTS, OP_INSPECTNUMOUTPUTS, OP_INSPECTINPUTVALUE and\nOP_INSPECTOUTPUTVALUE - opcodes to push number on the stack of\ninputs/outputs and their amounts.\n- OP_CHECKOUTPUTCOVENANTVERIFY: given a number out_i and three 32-byte hash\nelements x, d and taptree on top of the stack, verifies that the out_i-th\noutput is a P2TR output with internal key computed as above, and tweaked\nwith taptree. This is the actual covenant opcode.\n\nTODO:\n\n- Many contracts need parties to provide additional data; simply passing it\nvia the witness faces the problem that it could be malleated. Therefore, a\nway of passing signed data is necessary. One way to address this problem\ncould be to add a commitment to the data in the annex, and add an opcode to\nverify such commitment. Since the annex is covered by the signature, this\nremoves any malleability. Another option is an OP_CHECKSIGFROMSTACK opcode,\nbut that would cost an additional signature check.\n- Bitcoin numbers in current Script are not large enough for amounts.\n\nOther observations:\n\n- OP_CHECKINPUTCOVENANTVERIFY and OP_CHECKOUTPUTCOVENANTVERIFY could have a\nmode where x is replaced with a NUMS pubkey, for example if the first\noperand is an empty array of bytes instead of a 32 byte pubkey; this saves\nabout 31 bytes when no internal pubkey is needed (so about 62 bytes for a\ntypical contract transition using both opcodes)\n- Is it worth adding other introspection opcodes, for example\nOP_INSPECTVERSION, OP_INSPECTLOCKTIME? See Liquid's Tapscript Opcodes [6].\n- Is there any malleability issue? Can covenants “run” without signatures,\nor is a signature always to be expected when using spending conditions with\nthe covenant encumbrance? That might be useful in contracts where no\nsignature is required to proceed with the protocol (for example, any party\ncould feed valid data to the bisection protocol above).\n- Adding some additional opcodes to manipulate stack elements might also\nbring performance improvements in applications (but not strictly necessary\nfor feasibility).\n\nRemark: the additional introspection opcodes available in Blockstream\nLiquid [6] do indeed seem to allow MATT covenants; in fact, the opcodes\nOP_CHECKINPUTCOVENANTVERIFY and OP_CHECKOUTPUTCOVENANTVERIFY could be\nreplaced by more general opcodes like the group {OP_TWEAKVERIFY,\nOP_INSPECTINPUTSCRIPTPUBKEY, OP_PUSHCURRENTINPUTINDEX,\nOP_INSPECTOUTPUTSCRIPTPUBKEY }.\n\n### Variant: bounded recursivity\n\nIn the form described above, the covenant essentially allows fully\nrecursive constructions (an arbitrary depth of the covenant execution tree\nis in practice equivalent to full recursion).\n\nIf recursivity is not desired, one could modify the covenants in a way that\nonly allows a limited depth: a counter could be attached to the covenant,\nwith the constraint that the counter must be decreased for\nOP_CHECKOUTPUTCOVENANTVERIFY. That would still allow arbitrary fraud proofs\nas long as the maximum depth is sufficient.\n\nHowever, that would likely reduce its utility and prevent certain\napplications where recursivity seems to be a requirement.\n\nThe full exploration of the design space is left for future research.\n\n\n# Applications\n\nThis section explores some of the potential use cases of the techniques\npresented above. The list is not exhaustive.\n\nGiven the generality of fraud proofs, some variant of every kind of smart\ncontracts or layer two construction should be possible with MATT covenants,\nalthough the additional requirements (for example the capital lockup and\nthe challenge period delays) needs to be accurately considered; further\nresearch is necessary to assess for what applications the tradeoffs are\nacceptable.\n\n## State channels\n\nA state channel is a generalization of a payment channel where,\nadditionally to the balance at the end of each channel, some additional\nstate is stored. The state channel also specifies what are the rules on how\nto update the channel’s state.\n\nFor example, two people might play a chess game, where the state encodes\nthe current configuration of the board. The valid state transitions\ncorrespond to the valid moves; and, once the game is over, the winner takes\na specified amount of the channel’s money.\n\nWith eltoo-style updates, such a game could be played entirely off-chain,\nas long as both parties are cooperating (by signing the opponent’s state\nupdate).\n\nThe role of the blockchain is to guarantee that the game can be moved\nforward and eventually terminated in case the other party does not\ncooperate.\n\nIn stateful blockchain, this is simply achieved by publishing the latest\nstate (Merkleized or not) and then continuing the entire game on-chain.\nThis is expensive, especially if the state transitions require some complex\ncomputation.\n\nAn alternative that avoids moving computations on-chain is the use of a\nchallenge-response protocol, as sketched above.\n\nSimilarly to the security model of lightning channels, an honest party can\nalways win a challenge under the honest-majority of miners. Therefore, it\nis game-theoretically losing to attempt cheating in a channel.\n\n## CoinPool\n\nMultiparty state channels are possible as well; therefore, constructions\nlike CoinPool [7] should be possible, enabling multiple parties to share a\nsingle UTXO.\n\n## Zero knowledge proofs in L2 protocols\n\nProtocols based on ZK-proofs require the blockchain to be the verifier; the\nverifier is a function that takes a zero-knowledge proof and returns\ntrue/false based on its correctness.\n\nInstead of an OP_STARK operator in L1, one could think of compiling the\nOP_STARK as the function f in the protocol above.\n\nNote that covenants with a bounded “recursion depth” are sufficient to\nexpress OP_STARK, which in turns imply the ability to express arbitrary\nfunctions within contracts using the challenge protocol.\n\nOne advantage of this approach is that no new cryptographic assumptions are\nadded to bitcoin’s layer 1 even if OP_STARK does require it; moreover, if a\ndifferent or better OP_STARK2 is discovered, the innovation can reach layer\n2 contracts without any change needed in layer 1.\n\n## Optimistic rollups\n\nJohn Light recently posted a research report on how Validity Rollups could\nbe added to bitcoin’s layer 1 [8]. While no exact proposal is pushed\nforward, the suggested changes required might include a combination of\nrecursive covenants, and specific opcodes for validity proof verification.\n\nFraud proofs are the core for optimistic rollups; exploring the possibility\nof implementing optimistic rollups with MATT covenants seems a promising\ndirection. Because of the simplicity of the required changes to Script,\nthis might answer some of the costs and risks analyzed in the report, while\nproviding many of the same benefits. Notably, no novel cryptography needs\nto become part of bitcoin’s layer 1.\n\nOptimistic Rollups would probably require a fully recursive version of the\ncovenant (while fraud proofs alone are possible with a limited recursion\ndepth).\n\n\n# Acknowledgments\n\nAntoine Poinsot suggested an improvement to the original proposed covenant\nopcodes, which were limited to taproot outputs without a valid key-path\nspend.\n\nThe author would also like to thank catenocrypt, Antoine Riard, Ruben\nSomsen and the participants of the BTCAzores unconference for many useful\ndiscussions and comments on early versions of this proposal.\n\n\n# References\n\nThe core idea of the bisection protocol appears to have been independently\nrediscovered multiple times. In blockchain research, it is at the core of\nfraud proof constructions with similar purposes, although not focusing on\nbitcoin or covenants; see for example:\n\n- Harry Kalodner et al. “Arbitrum: Scalable, private smart contracts.” −\n27th USENIX Security Symposium. 2018.\nhttps://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf\n- Jason Teutsch and Christian Reitwiessner. “A scalable verification\nsolution for blockchains” − TrueBit protocol. 2017.\nhttps://people.cs.uchicago.edu/~teutsch/papers/truebit.pdf\n\nThe same basic idea was already published prior to blockchain use cases;\nsee for example:\n\nRan Canetti, Ben Riva, and Guy N. Rothblum. “Practical delegation of\ncomputation using multiple servers.” − Proceedings of the 18th ACM\nconference on Computer and communications security. 2011.\nhttp://diyhpl.us/~bryan/papers2/bitcoin/Practical%20delegation%20of%20computation%20using%20multiple%20servers.pdf\n\n\n\n# Footnotes\n\n[1] - https://btcazores.com\n[2] - https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\n[3] -\nhttps://bitcointalk.org/index.php?topic=1427885.msg14601127#msg14601127\n[4] - https://vitalik.ca/general/2019/12/26/mvb.html\n[5] -\nhttps://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs\n[6] -\nhttps://github.com/ElementsProject/elements/blob/master/doc/tapscript_opcodes.md\n[7] - https://coinpool.dev/v0.1.pdf\n[8] - https://bitcoinrollups.org\n-------------- next part --------------\nAn HTML attachment was scrubbed...\nURL: \u003chttp://lists.linuxfoundation.org/pipermail/bitcoin-dev/attachments/20221108/d6f2d8a3/attachment-0001.html\u003e"}
