Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Article Thumbnail

Leased Transactions: Securing Advanced Smart Contract Design

We’ve written extensively about how to write smart contracts and the structure of transactions. Today let’s dig into the hidden gem of functionality enabled by the Lease field nestled quietly within the transaction header. We’ll explore what this optional field enables, why you may choose to include it within a smart contract, and review some code examples for completeness.

Introduction

The Lease field was introduced in the Algorand 2.0 release. Leases can be used to provide security for exclusive transaction execution favoring a long time preference (such as recurring payments), to mitigate fee variability, and to enable long-running smart contracts. We’ll explore some of these concepts within this article. More technically, a confirmed transaction containing a Lease value ([32]byte) will persist the { Sender : Lease } pair on the validation node until its LastValid round expires. This creates a “lock” preventing any future transaction from using the same { Sender : Lease } pair until expiration.

The typical one-time payment or asset “send” transaction is short-lived and may not necessarily benefit from including a Lease value, but failing to define one within certain smart contract designs may leave an account vulnerable to a denial-of-service attack. Let’s take a look at why you may want to use the Lease field and when you definitely should.

Info

A quick review of Algorand transactions and smart contract validation. Every transaction includes a Header which defines the transaction Type and includes both required and optional fields used during validation. Two required fields FirstValid and LastValid define a range in which the transaction may be validated by the network (at most 1000 rounds), else discarded. On average, this equates to a “validity window” of at most about 70 minutes on MainNet. Many smart contract scenarios include a “target” validity window calculation and also add a specific Lease value within their validation logic. Combined, this enables smart contracts for periodic, recurring transactions for payments, key management and other scenarios.

Hello World

We should all be familiar with the “hello world” example providing a walkthrough of sending some Algos from Alice to Bob. This is an example of a short-lived transaction, and unlikely to benefit from assigning a Lease during normal network conditions. Using goal:

$ goal clerk send –from $ALICE –to $BOB –amount $AMOUNT

Under normal network conditions, this transaction will be confirmed in the next round (roughly 5 seconds). Bob gets his money from Alice and there are no further concerns.

However, now let’s assume the network is congested, fees are higher than normal and Alice desires to minimize her fee spend while ensuring only a single payment transaction to Bob is confirmed by the network. Alice may construct a series of transactions to Bob, each defining identical Lease, FirstValid and LastValid values but increasing Fee amounts, then broadcast them to the network.

# Define transaction fields
$ LEASE_VALUE=$(echo "Lease value (at most 32-bytes)" | xxd -p | base64)
$ FIRST_VALID=$(goal node status | grep "Last committed block:" | awk '{ print $4 }')
$ VALID_ROUNDS=1000
$ LAST_VALID=$(($FIRST_VALID+$VALID_ROUNDS))
$ FEE=1000

# Create the initial signed transaction and write it out to a file
$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
                  –-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
                  –-fee $FEE –-out $FEE.stxn --sign

Above, Alice defined values to use within her transactions. The $LEASE_VALUE must be base64 encoded and not exceed 32-bytes (commonly populated with a hash value). The $FIRST_VALID value is obtained from the network and $VALID_ROUNDS (set to maximum here) is used to calculate $LAST_VALID. Initially $FEE is set to the minimum and will be the only value modified in subsequent transactions.

Alice now broadcasts the initial transaction with goal clerk rawsend –-filename 1000.stxn but due to network congestion and high fees, goal will continue awaiting confirmation until $LAST_VALID. During the validation window Alice may construct additional nearly identical transactions with only higher fees and broadcast each one concurrently.

# Redefine ONLY the FEE value
$ FEE=$(($FEE+1000))

# Broadcast additional signed transaction
$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
                  –-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
                  –-fee $FEE

Alice will continue to increase the $FEE value with each subsequent transaction. At some point, one of the transactions will be approved, likely the one with the highest fee at that time, and the “lock” is now set for { $ALICE : $LEASE_VALUE } until $LAST_VALID. Alice is assured that none of her previously submitted pending transaction can be validated. Bob is paid just one time.

Potential Pitfalls

That was a rather simple scenario and unlikely during normal network conditions. Next, let’s uncover some security concerns Alice needs to guard against. Once Alice broadcasts her initial transaction, she must ensure all subsequent transactions utilize the exact same values for FirstValid, LastValid and Lease. Notice in the second transaction only the Fee is incremented, ensuring the other values remain static. If Alice executes the initial code block twice, the $FIRST_VALID value will be updated by querying the network presently, thus extending the validation window for $LEASE_VALUE to be evaluated.

Similarly, if the $LEASE_VALUE is changed within a static validation window, multiple transactions may be confirmed. Remember, the “lock” is a mutual exclusion on { Sender : Lease }; changing either creates a new lock.

After the validation window expires, Alice is free to reuse the $LEASE_VALUE in any new transaction. This is a common practice for recurring payments.

Smart Contract Design

Here are few smart contract use cases making use of the Lease field: delegated key registration, periodic payment and dynamic fees. There are two main design considerations for including Lease evaluation within your smart contract: fee minimization and recurring transactions. These two concepts could be combined within a single smart contract, but we will leave that to the reader to implement and share results on the Developer Forum.

Delegate Key Registration

The Delegate Key Registration walkthrough explains the functionality and use of the template for periodic key rotation using a LogicSig. The reason a Lease is beneficial in this use case is to prevent multiple transactions from being approved within a validation window which may drain the account through additional fee spends.

Periodic Payment

The Periodic Payment. It’s very similar to the Delegate Key Registration in template fields, but validates that the transaction type is a payment rather than a key registration. The tmpl_amt specifies a fixed amount for all transactions, so the Lease field is used to ensure only a single transaction may be approved within each validation window. This smart contract uses a fixed fee which leaves the recipient susceptible to denial of service if the network requires a higher fee during any of the validation windows. The next example addresses this point.

Dynamic Fee

The Dynamic Fee group. This is advantageous to the sender because of uncertainty in future fee pricing. This LogicSig uses the Lease to guard against replay attacks, similar to Delegate Key Registration above.

Conclusion

The Lease field is a powerful tool in securing your account from replay attacks and enables smart contracts for periodic recurring transactions. We have many resources to guide your development when designing secure smart contracts and many SDKs to aid implementation.