Subject: Re: [bitcoin-dev] Scalable Semi-Trustless Asset Transfer via Single-Use-Seals and Proof-of-Publication

From: Pulat Yunusov <[email-masked]>
Date: Mon, 11 Dec 2017 22:23:21 +0000

Thank you for your post, Peter. Why is it necessary to centralize the p-o-p
sidechain and have a maintainer? It seems the Bitcoin network will secure
the most critical element, which is the witness authenticity. Wouldn't a
second decentralized network be able to perform the functions of the
maintainer so the entire system is trustless?

On Tue, Dec 5, 2017 at 5:16 AM Peter Todd via bitcoin-dev <
[email-masked]> wrote:

> I recently wrote this up for a client, and although the material has been
> covered elsewhere, I thought being a worked example it might be of
> interest,
> particularly while sidechains are being discussed again.
>
> As per (1) I've perhaps foolishly committed to making an even more fleshed
> out
> example, so peer review here before it gets to an even wider audience
> would be
> appreciated. :)
>
> 1) https://twitter.com/petertoddbtc/status/937401676042039296
>
>
> tl;dr: We can do trustless with respect to validity, trusted with respect
> to
> censorship resistance, indivisible asset transfer with less than
> 5MB/year/token
> of proof data, assuming token ownership is updated every two hours, at a
> rate
> of ~500,000 transfers per second. The scalability of this scheme is linear
> with
> respect to update interval, and logarithmic with respect to overall
> transfer
> rate.
>
>
> ## Single-Use-Seal Definition
>
> Analogous to the real-world, physical, single-use-seals used to secure
> shipping
> containers, a single-use-seal primitive is a unique object that can be
> closed
> over a message exactly once. In short, a single-use-seal is an abstract
> mechanism to prevent double-spends.
>
> A single-use-seal implementation supports two fundamental operations:
>
>     Close(l,m) -> w_l
>         Close seal l over message m, producing a witness w_l
>
>     Verify(l,w_l,m) -> bool
>         Verify that the seal l was closed over message m
>
> A single-use-seal implementation is secure if it is impossible for an
> attacker
> to cause the Verify function to return true for two distinct messages m_1,
> m_2,
> when applied to the same seal (it _is_ acceptable, although non-ideal, for
> there to exist multiple witnesses for the same seal/message pair).
>
> Practical single-use-seal implementations will also obviously require some
> way
> of generating new single-use-seals. Secondly, authentication is generally
> useful. Thus we have:
>
>     Gen(p) -> l
>         Generate a new seal bound to pubkey p
>
>     Close(l,m,s) -> w_l
>         Close seal l over message m, authenticated by signature s valid
> for pubkey p
>
> Obviously, in the above, pubkey can be replaced by any cryptographic
> identity
> scheme, such as a Bitcoin-style predicate script, zero-knowledge proof,
> etc.
>
> Finally, _some_ single-use-seal implementations may support the ability to
> prove that a seal is _open_, e.g. as of a given block height or point in
> time.
> This however is optional, and as it can be difficult to implement, it is
> suggested that seal-using protocols avoid depending on this functionality
> existing.
>
>
> ## Indivisible Token Transfer
>
> With a secure single-use-seal primitive we can build a indivisible token
> transfer system, allowing the secure transfer of a token from one party to
> another, with the seals preventing double-spends of that indivisible token.
>
> Each token is identified by its genesis seal l_0. To transfer a token, the
> most
> recent seal l_n is closed over a message committing to a new seal, l_{n+1},
> producing a witness w_{l_n} attesting to that transfer. This allows a
> recipient
> to securely verify that they have received the desired token as follows:
>
> 1. Generate a fresh, open, seal l_{n+1} that only they can close.
> 2. Ask the sender to close their seal, l_n, over the seal l_{n+1}
> 3. Verify that there exist a set of valid witnesses w_0 .. w_n, and seals
>    l_0 .. l_n, such that for each seal l_i in i = 0 .. n, Verify(l_i, w_i,
> l_{i+1})
>    returns true.
>
> Since a secure single-use-seal protocol prohibits the closure of a single
> seal
> over multiple messages, the above protocol ensures that the token can not
> be
> double-spent. Secondly, by ensuring that seal l_{n+1} can be closed by the
> recipient and only the recipient, the receipient of the token knows that
> they
> and they alone have the ability to send that token to the next owner.
>
>
> ## Divisible Asset Transfer
>
> In the case of a divisible asset, rather than transferring a single,
> unique,
> token we want to transfer a _quantity_ of an asset. We can accomplish this
> in a
> manner similar how Bitcoin's UTXO-based transactions, in which one or more
> inputs are combined in a single transaction, then split amongst zero or
> more
> outputs.
>
> We define the concept of an _output_. Each output x is associated with a
> seal l
> and value v. For each asset we define a set of _genesis outputs_, X_G,
> whose
> validity is assumed.
>
> To transfer divisible assets we further define the concepts of a _spend_
> and a
> _split_. A spend, D, is a commitment to a set of outputs x_i .. x_j; the
> value
> of a spend is simply the sum of the values of all outputs in the spend. A
> split
> commitments to a set of zero or seal/value, (l_i,v_i), tuples, with the sum
> value of the split being the sum of a values in the split.
>
> Spends and splits are used to define a _split output_. While a genesis
> output
> is simply assumed valid, a split output x is then the tuple (D,V,i),
> committing
> to a spend D, split V, and within that split, a particular output i.
>
> A split output is valid if:
>
> 1. Each output in the spend set D is a valid output.
> 2. The sum value of the spend set D is >= the sum value of the split V.
> 3. i corresponds to a valid output in the split.
> 4. There exists a set of witnesses w_i .. w_j, such that each seal in the
> spend
>    set closed over the message (D,V) (the spend and split).
>
> As with the indivisible asset transfer, a recipient can verify that an
> asset
> has been securely transferred to them by generating a fresh seal, asking
> the
> sender to create a new split output for that seal and requested output
> amount,
> and verifying that the newly created split output is in fact valid. As with
> Bitcoin transactions, in most transfers will also result in a change
> output.
>
> Note how an actual implementation can usefully use a merkle-sum-tree to
> commit
> to the split set, allowing outputs to be proven to the recipient by giving
> only
> a single branch of the tree, with other outputs pruned. This can have both
> efficiency and privacy advantages.
>
>
>
> ## Single-Use-Seal Implementation
>
> An obvious single-use-seal implementation is to simply have a trusted
> notary,
> with each seal committing to that notary's identity, and witnesses being
> cryptographic signatures produced by that notary. A further obvious
> refinement
> is to use disposable keys, with a unique private key being generated by the
> notary for each seal, and the private key being securely destroyed when the
> seal is closed.
>
> Secondly Bitcoin (or similar) transaction outputs can implement
> single-use-seals, with each seal being uniquely identified by outpoint
> (txid:n), and witnesses being transactions spending that outpoint in a
> specified way (e.g. the first output being an OP_RETURN committing to the
> message).
>
>
> ### Proof-of-Publication Ledger
>
> For a scalable, trust-minimized, single-use-seal implementation we can use
> a
> proof-of-publication ledger, where consensus over the state of the ledger
> is
> achieved with a second single-use-seal implementation (e.g. Bitcoin).
>
> Such a ledger is associated with a genesis seal, L_0, with each entry M_i
> in
> the ledger being committed by closing the most recent seal over that entry,
> producing W_i such that Verify(L_i, (L_{i+1}, M_i), W_i) returns true.
> Thus we achieve consensus over the state of the ledger as we can prove the
> contents of the ledger.
>
> Specifically, given starting point L_i we can prove that the subsequent
> ledger
> entries M_i .. M_j are valid with witnesses W_i .. W_j and seals L_{i+1}
> .. L_{j+1}.
>
> A proof-of-publication-based seal can then be constructed via the tuple
> (L_i,
> p), where L_i is one of the ledger's seals, and p is a pubkey (or
> similar). To
> close a proof-of-publication ledger seal a valid signature for that pubkey
> and
> message m is published in the ledger in entry M_j.
>
> Thus the seal witness is proof that:
>
> 1. Entry M_j contained a valid signature by pubkey p, for message m.
> 2. All prior entries M_i .. M_{j-1} (possibly an empty set) did _not_
> contain
>    valid signatures.
>
> Finally, for the purpose of scalability, instead of each ledger entry M_i
> consisting of a unstructured message, we can instead commit to a merkelized
> key:value tree, with each key being a pubkey p, and each value being an
> alleged signature (possibly invalid). Now the non-publication condition is
> proven by showing that either:
>
> 1. Tree M_i does not contain key p.
> 2. Tree M_i does contain key p, but alleged signature s is invalid.
>
> The publication condition is proven by showing that tree M_j does contain
> key
> p, and that key is associated with valid signature s.
>
> A merkelized key:value tree can prove both statements with a log2(n) sized
> proof, and thus we achieve log2(n) size scalability, with the constant
> factor
> growing by the age of the seals, the ledger update frequency, the rate at
> which
> seals are closed, and the maximum size allowed for signatures.
>
> Note how a number of simple optimizations are possible, such as preventing
> the
> creation of "spam" invalid signatures by blinding the actual pubkey with a
> nonce, ensuring only valid signatures are published, etc. Also note how it
> is
> _not_ necessary to validate all entries in the ledger form a chain: the
> single-use-seals guarantees that a particular range of ledger entries will
> be
> unique, regardless of whether all ledger history was unique.
>
> Proof-of-Publication ledgers are trustless with regard to false seal
> witnesses:
> the ledger maintainer(s) are unable to falsify a witness because they are
> unable to produce a valid signature. They are however trusted with regard
> to
> censorship: the ledger maintainer can prevent the publication of a
> signature
> and/or or withhold data necessary to prove the state of the seal.
>
>
> # Performance Figures
>
> Assume a indivisible token transfer via a PoP ledger using Bitcoin-based
> single-use-seals, with the ledger updated 12 times a day (every two hours).
> Assume each ledger update corresponds to 2^32, 4 billion, transfers.
>
> The data required to prove publication/non-publication for a given ledger
> update is less than:
>
>     lite-client BTC tx proof:                            = ~1KB
>     merkle path down k/v tree: 32 levels * 32bytes/level =  1KB
>     key/value: 32 bytes predicate hash + 1KB script sig  = ~1KB
>                                                    Total = ~3KB/ledger
> update
>
>         * 356 days/year * 12 updates/day = 13MB/year
>
> Now, those are *absolute worst case* numbers, and there's a number of ways
> that
> they can be substantially reduced such as only publishing valid
> signatures, or
> just assuming you're not being attacked constantly... Also, note how for a
> client with multiple tokens, much of the data can be shared amongst each
> token.
> But even then, being able to prove the ownership status of a token, in a
> trustless fashion, with just 13MB/year of data is an excellent result for
> many
> use-cases.
>
> With these optimizations, the marginal cost per token after the first one
> is
> just 1KB/ledger update, 4.4MB/year.
>
> --
> https://petertodd.org 'peter'[:-1]@petertodd.org
> _______________________________________________
> bitcoin-dev mailing list
> [email-masked]
> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev
>