Developing secure Bitcoin contracts with BitML

05/18/2019 ∙ by Nicola Atzei, et al. ∙ Università di Trento Imperial College London Universita Cagliari 0

We present a toolchain for developing and verifying smart contracts that can be executed on Bitcoin. The toolchain is based on BitML, a recent domain-specific language for smart contracts with a computationally sound embedding into Bitcoin. Our toolchain automatically verifies relevant properties of contracts, among which liquidity, ensuring that funds do not remain frozen within a contract forever. A compiler is provided to translate BitML contracts into sets of standard Bitcoin transactions: executing a contract corresponds to appending these transactions to the blockchain. We assess our toolchain through a benchmark of representative contracts.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1. Introduction

In the last five years much outstanding research has been devoted to showing how to exploit Bitcoin to execute smart contracts — computer protocols which allow for exchanging cryptocurrency according to complex pre-agreed rules (Andrychowicz et al., 2014c, b, a, 2016; Banasik et al., 2016; Bartoletti and Zunino, 2017; Bentov and Kumaresan, 2014; Kumaresan and Bentov, 2014; Kumaresan et al., 2015; Kumaresan and Bentov, 2016; Kumaresan et al., 2016; Miller and Bentov, 2017). Despite the wide variety of use cases witnessed by these works, no tool support has been provided yet to facilitate the development of Bitcoin contracts. Today, this task requires to devise complex protocols which, besides using the standard cryptographic primitives, can read and append transactions on the Bitcoin blockchain. Creating a new protocol requires a significant effort to establish its correctness and security: this is an error-prone task, usually performed manually, with the risk of overlooking some corner cases. Crafting the transactions used by these protocols is burdensome as well, since it requires to struggle with low-level, poorly documented features of Bitcoin, like e.g. its stack-based scripting language.

In this paper we consider BitML, a recent high-level language for smart contracts, featuring a computationally sound embedding into Bitcoin (Bartoletti and Zunino, 2018), and a sound and complete verification technique of relevant trace properties (Bartoletti and Zunino, 2019). BitML can express many of the smart contracts appeared in the literature (Bartoletti et al., 2018; Atzei et al., 2018a), and execute them by appending suitable transactions to the Bitcoin blockchain. The computational soundness of the embedding guarantees that security properties at the level of the BitML semantics are preserved at the level of Bitcoin transactions, even in the presence of adversaries. Still, BitML lives in a theoretical limbo, as no tool support exists yet to develop contracts and deploy them on the Bitcoin blockchain.

Contributions

We develop a toolchain for writing and verifying BitML contracts, and for deploying them on Bitcoin. More specifically, our main contributions can be summarised as follows:

  1. A BitML embedding in Racket (Flatt, 2012), which allows for programming BitML contracts within the DrRacket IDE.

  2. A security analyzer which can check arbitrary LTL properties of BitML contracts. In particular, the analysis can decide liquidity, a landmark property of smart contracts111A paradigmatic case of non-liquid contract was the Ethereum Parity Wallet (par, 2017b). An attacker managed to kill a library called by the wallet, irreversibly freezing . requiring that the funds within a contract do not remain frozen forever.

  3. A compiler from BitML contracts to standard Bitcoin transactions. The computational soundness result in (Bartoletti and Zunino, 2018) ensures that attacks to compiled contracts are also observable at the BitML level. Therefore, the properties verified by our security analyzer also hold for compiled contracts.

  4. A collection of BitML contracts, which we use as a benchmark to evaluate our toolchain. This collection contains some of the most complex contracts ever developed for Bitcoin, e.g. financial services, auctions, timed commitments, lotteries, and a variety of other gambling games. We use our benchmarks to discuss the expressiveness and the limitations of Bitcoin as a smart contracts platform.

The architecture of our toolchain is displayed in Figure 1. The development workflow is the following:

write the BitML contract, and specify the required properties. Optionally, specify some constraints on the participants’ strategies, e.g. to partially define the behaviour of the honest participants;

verify that the contract satisfies the required properties through the security analyzer;

compile the contract to Bitcoin transactions;

execute the contract, by appending these transactions to the Bitcoin blockchain according to the chosen strategy. We remark that the last step can be performed on the Bitcoin main network, without requiring any extensions or customizations.

All the components of our toolchain are open-source222https://github.com/bitml-lang, as well as the contracts in our benchmark. A tutorial is available online333https://blockchain.unica.it/bitml, including references to our experiments on the Bitcoin testnet.

BitMLon DrRacket

Properties +Strategies

Contract

Abstract BitMLsemantics

Modelchecker

Queryresult

BitML toBalzac

Balzac toBitcoin

Bitcointransactions

Security Analyzer

Compiler
Figure 1. Toolchain architecture.

2. Designing BitML contracts

BitML contracts allow two or more participants to exchange their bitcoins (    B    ) according to a given logic. A contract consists of two parts: a precondition, describing requirements that participants must fulfil to stipulate the contract, and a process, which specifies the execution logic of the contract. Here, rather than providing the syntax and semantics of BitML (see (Bartoletti and Zunino, 2018) for a formalization), we illustrate it through a simple but paradigmatic example, the mutual timed commitment contract (Andrychowicz et al., 2014c). This contract involves two participants (named below A and B) each one choosing a secret and depositing a certain amount of cryptocurrency (say, ). The goal of the contract is to ensure that each participant will either learn the other participant’s secret, or otherwise receive the other participant’s deposit as a compensation. The contract gives some time to the participants to reveal their secrets. If a participant reveals her secret in time, then she can get her deposit back; otherwise, after the time is up, the other participant can withdraw that deposit.

In our tool, we can specify this contract as follows:


(participant A ”029ccced”) ; A’s public key
(participant B ”022caf30”) ; B’s public key
(contract
  (pre
    (deposit A 1 ”1a34…6f38@0”) ; tx output id (1BTC)
    (secret  A a ”628fde71”)   ; hash of A’s secret
    (deposit B 1 ”19e7…85ff@2”) ; tx output id (1BTC)
    (secret  B b ”9d48bb35”))  ; hash of B’s secret
  (choice
    (reveal (a) (choice
                  (reveal (b) (split
                                (1 -> (withdraw A”))
                                (1 -> (withdraw B”))))
                  (after 100050 (withdraw A”))))
    (after 100000 (withdraw B”)))

The first two lines create aliases for the participant names, specifying their public keys. The contract preconditions are in the pre part: each participant must specify the identifier of a transaction output, and the hash of the chosen secret. The transaction output must be unspent, must contain the required , and must be redeemable using the participant’s private key. The hash is used during the contract execution: when the participant provides a value, claiming that it is the chosen secret, the hash of this value is required to be equal to the one in the precondition.

The contract logic is specified after the preconditions. The top-level choice defines two alternative branches of the contract. The first branch can only be taken if A reveals her secret (named a); when this happens, the contract continues with the innermost choice. The second branch can only be taken after a timeout, specified as the block at height 100000, and it allows B to redeem all the funds deposited within the contract (i.e., ) by executing withdraw “B”. So, to avoid losing her deposit, A is incentivized to reveal her secret in time. Similarly, the innermost choice is used to incentivize B to reveal his secret before the block at height 100050. If B reveals, then the split subcontract is executed: this divides the balance of the contract in two parts of each, allowing the participants to withdraw their deposits back.

The language is defined exploiting the Racket macro system, which is used to rewrite BitML syntactic constructs to Racket code. This approach benefits from the Racket language ecosystem, and allows us to write BitML contracts in the DrRacket IDE. Indeed, our toolchain integrates within the DrRacket IDE the contract editor, the security analyzer and the BitML compiler. The implementation of BitML in Racket extends the idealized version of BitML in (Bartoletti and Zunino, 2018) to make the language usable in practice. For instance, it introduces special deposits of type fee, which are automatically spread over all the transactions obtained by the compiler. We also implement static checks for a number of errors that could prevent the correct execution of contracts, e.g. committing secrets with the same hash, double spending a transaction output, etc.

3. Verifying BitML contracts

The tool verifies various forms of liquidity, requiring that no funds (or funds up-to a certain amount) are frozen forever within a contract. Further, the tool can verify arbitrary LTL formulae, where state predicates can specify, e.g., the funds owned by participants, the provided authorizations, and the revealed secrets. By default, the tool verifies the required property against all possible behaviours of each participant: for instance, if a contract contains reveal a, the verifier considers both the case where the secret is revealed and the one where it is not. Authorizations are handled similarly, by considering both cases. However, in most cases, a participant wishes to verify a contract with respect to a given behaviour for herself, making no assumptions on the other participants’ behaviour (unless some other participants are considered trusted, in which case it would make sense to fix a behaviour also for them). For instance, a participant A may want to give her authorization to perform a given branch only after participant B has revealed his secret. The tool allows for constraining the behaviour of participants, specifying the conditions upon which secrets are revealed and authorizations are provided. Actions which can be performed by everyone, like withdraw and split, cannot be constrained.

For instance, we can verify that the mutual timed commitment contract is liquid whatever strategies are chosen by participants. The query check-liquid correctly answers true, since:

  • if A does not reveal, then anyone (after the block at height 100000) can perform withdraw “B”, which transfers the whole contract balance to B;

  • if A reveals but B does not reveal, then anyone (after the block at height 100050) can perform withdraw “A”, which transfers the whole contract balance to A;

  • if both A and B reveal, then anyone can perform split, which transfers the balance in equal parts to A and B.

Note that if we remove the after branch at line 16, the contract is no longer liquid. However, it becomes liquid when A’s strategy is to reveal the secret. We can verify that this holds through the query check-liquid (strategy ”A” (do-reveal a)). Liquidity is lost again if A chooses to reveal only after B has revealed, i.e. when her strategy is ”A” (do-reveal a) if (”B” (do-reveal b)).

Besides liquidity, we can check specific LTL properties of contracts through the command check-query. E.g., in the mutual timed commitment we can verify that, after A reveals, she will eventually get back at least her deposit. In LTL, this property is formalised as the following formula, where satoshi = 1    B    :

[](a revealed =¿ ¡¿A has-deposit¿= 100000000 satoshi)

We also verify that if A reveals the secret, then eventually either B reveals, or A will get B’s deposit, too. The LTL query is the following:

[](a revealed =¿ ¡¿(b revealed “/ A has-deposit¿= 200000000 satoshi))

Our verification technique is based on model-checking the state space of BitML contracts. Since this state space is infinite, before running the model-checker we reduce it to a finite-state one, by exploiting the abstraction in (Bartoletti and Zunino, 2019). This abstraction resolves the three sources of infiniteness of the concrete semantics of BitML: the passing of time, the advertisement/stipulation of contracts, and the off-contract bitcoin transfers. To obtain a finite-state system, the abstraction:

quotients time in a finite number of time intervals,

disables the advertisement of new contracts, and

limits the off-contract operations to those for transferring funds to contracts and for destroying them. This abstraction is shown in (Bartoletti and Zunino, 2019) to enjoy a strict correspondence with the concrete BitML semantics: namely, each concrete step of the contract under analysis is mimicked by an abstract step, and vice versa.

Our tool implements the abstract BitML semantics in Maude, a model-checking framework based on rewriting logic (Clavel et al., 2001). Maude is particularly convenient for this purpose: we use its equational logic to express structural equivalence between BitML terms, and its conditional rewriting rules to encode the abstract semantics of BitML. In this way, we naturally obtain an executable abstract semantics of BitML. Once a BitML contract in translated in Maude, we use the Maude LTL model-checker (Eker et al., 2002) to verify the required security properties, under the strategies specified by the user. The various forms of liquidity are also translated to corresponding LTL formulae. The computational soundness of the BitML compiler guarantees that the properties verified by the model checker are preserved when executing the contract on Bitcoin.

4. Compiling BitML to Bitcoin

Our compiler operates in two phases: first, it translates BitML contracts into Balzac444https://github.com/balzac-lang/balzac, an abstraction layer over Bitcoin transactions based on the formal model of (Atzei et al., 2018b); then, it translates Balzac transactions into standard Bitcoin transactions.

The compiler from BitML to Balzac implements the algorithm in (Bartoletti and Zunino, 2018), extending it with transaction fees. In particular, the compiler guarantees that each transaction contains enough fees to be publishable in the blockchain.

The compiler from Balzac to Bitcoin produces standard Bitcoin transactions555https://bitcoin.org/en/developer-guide#standard-transactions: this is crucial since non-standard ones are discarded by the Bitcoin network. To this purpose, Balzac produces standard output scripts of the form “Pay to Public Key Hash” (P2PKH) or “Pay to Script Hash” (P2SH). P2PKH is used for encoding signature verification (e.g., to redeem the deposit obtained by a withdraw), while P2SH is used for complex redeeming conditions (e.g., to check that the revealed secret matches the committed hash). Since Bitcoin requires that all the values pushed by standard scripts fit within 520 bytes, our compiler checks that this constraint is satisfied for each generated script. Balzac outputs serialized raw transactions, which can be directly broadcast to the Bitcoin network.

5. Evaluation

To evaluate our toolchain, we use a benchmark of representative use cases, including financial contracts (Seijas and Thompson, 2018; Biryukov et al., 2017), auctions, lotteries (Andrychowicz et al., 2016; Miller and Bentov, 2017) and gambling games666https://github.com/bitml-lang/bitml-compiler/tree/master/examples/benchmarks. For each contract in the benchmark, we display in Table 1 the number of involved participants, the number of transactions obtained by the compiler, and the verification time for checking liquidity777For uniformity, in the performance evaluation we focus on liquidity (other queries to verify the functional correctness of the contracts in Table 1 are on the repository). We carry out our experiments on a PC equipped with a hexa-core Intel Core i7-7800X CPU @ 3.50GHz, and 64GB of RAM.. The participants’ strategies are constrained only as needed to ensure liquidity: in most cases, we do not put any constraints at all. For the contracts which involve predicates on secrets (e.g., all the lotteries), in principle one would need to check liquidity against all the possible choices of secrets. To make verification feasible, since each contract only checks a finite set of predicates, we partition the infinite choices of secrets into a finite set of regions, and sample one choice from each region. In this way, the liquidity check is performed a finite number of times, ensuring that the verifier explores every reachable state of the contract. For instance, in the 4-players lottery we explore regions, which explains the 67 hours needed to verify its liquidity.888Another feature which significantly affects the verification time is the fact that we are considering all the possible strategies of all the participants.

Contract N T V
Mutual timed commitment 2 15 83ms
Mutual timed commitment 3 34 103ms
Mutual timed commitment 4 75 454ms
Mutual timed commitment 5 164 13s
Escrow (early fees) 3 12 8s
Escrow (late fees) 3 11 3.4s
Zero Coupon Bond 3 8 86ms
Coupon Bond 3 18 1.3s
Future 3 5 + 80ms +
Option 3 14 + 90ms +
Lottery ( collateral) 2 15 427ms
Lottery ( collateral) 2 8 142ms
Lottery ( collateral) 4 587 67h
Rock-Paper-Scissors 2 23 781ms
Morra game 2 40 674ms
Shell game 2 23 27s
Auction (2 turns) 2 42 3.3s
Table 1. Benchmarks for the BitML toolchain.

The only work against which we can compare the performance of our tool is (Andrychowicz et al., 2014b), which models Bitcoin contracts in Uppaal, a model-checking framework based on Timed Automata. The most complex contract modelled in (Andrychowicz et al., 2014b) is the mutual timed commitment with 2 participants: this requires s to be verified in Uppaal, while our tool verifies the same property in ms. This speedup is due to the higher abstraction level of BitML over (Andrychowicz et al., 2014b), which operates at the (lower) level of Bitcoin transactions.

One of the main difficulties that we have encountered in developing contracts is that some complex BitML specifications can not be compiled to Bitcoin, because Bitcoin has a 520-byte limit on the size of each value pushed to the evaluation stack999https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki#520-byte-limitation-on-serialized-script-size. In some cases, we managed to massage the BitML contract so to make its compilation respect the 520-byte constraint. For instance, a common pattern that easily violates the 520-byte constraint is the following:


(choice (revealif (b) (pred (p0)) (C0))
        (revealif (b) (pred (p1)) (C1))
        (after T      (C2)))

The choice is compiled into a transaction whose redeem script encodes the disjunction of three logical conditions, corresponding to the three branches of the choice. Depending on the predicates p0 and p1, and on the number of participants in the contract, this script may violate the 520-byte constraint. A workaround is to rewrite the pattern above into the following one:


(choice (revealif (b) (pred (p0)) (C0))
        (after T (tau (choice
                      (revealif (b) (pred (p1)) (C1))
                      (after T1     (C2))))))

In this case the compilation includes two transactions, corresponding to the two choices. The scripts of these transactions encode the disjunction of two logical conditions, corresponding to the two branches of the choices. Using this workaround we have managed to compile the 4-players lottery into standard transactions, at the price of increasing the number of transactions (587 for the standard version vs. 138 for the nonstandard one). Similar techniques (e.g. simplification of predicates) allowed us to compile all the contracts in Table 1 into standard Bitcoin transactions.

In general, the 520-byte constraint intrinsically limits the expressiveness of Bitcoin contracts: for instance, since public keys are 33 bytes long, a contract which needs to simultaneously verify 15 signatures can not be implemented using standard transactions.

6. Conclusions

While the business of smart contracts has flourished on platforms like Ethereum and Cardano, it never caught on Bitcoin. One of the main reasons is that, unlike the other platforms, Bitcoin has neither high-level contract languages, nor related development and verification tools. A downside of using platforms with expressive, Turing-complete languages, is that they may expose contracts to a wider attack surface: indeed, a series of language-induced vulnerabilities of Ethereum contracts (Atzei et al., 2017) has caused losses of hundreds of millions of USD (DAO, 2016; par, 2017a, b). Although recent works on the analysis of Ethereum contracts have produced tools to detect some of these vulnerabilities (Luu et al., 2016; Tsankov et al., 2018; Grishchenko et al., 2018b; Hildenbrandt et al., 2018; Park et al., 2018; Grishchenko et al., 2018a; Bhargavan et al., 2016; Sergey et al., 2018), their precision is subject to the inherent limitations derived by the Turing-completeness of the underlying languages. By contrast, leveraging on the simpler model of computation given by BitML, in this paper we have introduced a toolchain with a sound and complete verification technique.

Although our benchmarks witness a rich variety of contracts expressible in BitML, there is room for improvement. First, note that BitML is not Bitcoin-complete, i.e. there are contracts which are executable in Bitcoin but not expressible in BitML. The main sources of this incompleteness are three:

all the transactions obtained by the compiler must be signed before stipulation by all the involved participants (only the signatures for authorizations can be provided at run-time);

all the signatures used in transactions have type SIGHASH_ALL, while SIGHASH_ANYONECANPAY and SIGHASH_SINGLE are not used;

off-chain interactions are limited to revealing secrets and providing authorizations. The first constraint is required to ensure that honest participants can always perform, at the Bitcoin level, the moves enabled in the corresponding BitML contract, regardless of the behaviour of the others. In this respect, BitML follows the standard assumption101010To the best of our knowledge, in the setting of smart contracts only TypeCoin (Crary and Sullivan, 2015) assumes cooperation, allowing dishonest participants to make a contract deadlock. that participants are non-cooperative

, i.e. at any moment after stipulation they can stop interacting. Yet, cooperation can be incentivized within the contract, by punishing misbehaviour with penalties, like e.g. in the timed commitment of 

Section 2. As a consequence of the design choices above, contracts with a dynamically-defined set of players (e.g., crowdfunding), or an unbounded number of iterations (e.g., micro-payment channels), are not expressible in BitML.

The limitations of BitML (and of Bitcoin) could be overcome in various ways. For instance, using Bitcoin “as-is”, it would be possible to relax constraint (iii) above, so to allow e.g. zero-knowledge off-chain protocols. This would enable to extend BitML with primitives to express contingent payments contracts, where participants trade solutions of a class of NP problems (Banasik et al., 2016; Maxwell, 2016). Similarly, by relaxing constraint (i), we could extend BitML to enable dynamic stipulation of subcontracts, requiring that all the involved participants provide their signatures at run-time. This would allow to model e.g. micro-payment channels in BitML. Together with the use of SIGHASH_ANYONECANPAY (relaxing constraint (ii)), this would also allow for modelling crowdfunding contracts. As before, this extension could be implemented without modifying Bitcoin.

Other extensions of BitML would require extensions of Bitcoin. For instance, covenants (Möser et al., 2016; O’Connor and Piekarska, 2017) would allow for implementing arbitrary finite-state machines. Controlled input malleability would allow to efficiently implement tournaments in multi-player gambling games, like e.g. lotteries (Bartoletti and Zunino, 2017). This can also be achieved through a new opcode that checks if the redeeming transaction belongs to a given set (Miller and Bentov, 2017). Contingent payments without zero-knowledge proofs can be achieved by exploiting a new opcode that checks the validity of key pairs (Delgado-Segura et al., 2017). A new opcode which checks signatures for arbitrary messages would allow for expressing general fair multiparty computations (Kumaresan et al., 2015). Further, fair and robust multiparty computations can be achieved using more complex transactions (Kiayias et al., 2016). A more radical approach would be to replace the Bitcoin scripting language with a more expressive one. For instance, Simplicity (O’Connor, 2017) would allow for expressing any function on finite domains.

References