Development of blockchain programmability

Published 5.9.2023, updated 16d21h37m ago

The first generation of blockchain is basically just a transactional network, similar to PayPal. Value can only be unconditionally transferred from Alice to Bob. Bitcoin is most commonly used for the simple transfer of value. However, there is a possibility to write a script. Cardano and Ethereum are smart contract platforms, so alternative banking services can be created on them. Platforms allow developers to implement more complex programs and work with the states of applications. In the article, we will describe the programmability possibilities of Bitcoin, Ethereum, and Cardano. We will also shortly look at some differences between Ethereum smart contracts and Plutus scripts.

Capabilities of the first generation of blockchain

We can include Bitcoin in the first generation of blockchain. Bitcoin came up with the concept of digital scarcity. In general, blockchain enables the creation of a fixed monetary policy that is immutable or can only be changed with the consent of the majority. There may be digital coins (also called native coins) that cannot be copied within the system.

Native coins are used as part of the network's incentive model (rewards for those who decentralize and secure the network), but that is irrelevant to this article.

The existence of coins is dependent on a distributed network and decentralization.

The blockchain network can ensure the safe transfer of coins between two participants, for example from Alice to Bob, as it ensures that the same coins are not spent twice in a row (protection against double-spend of coins). This preserves digital scarcity. Furthermore, it ensures that only the real owner and no one else can spend the coins. In the blockchain network, it is not possible to cheat, freeze an account, prevent someone from using the network or spending coins, etc.

Monetary policy, digital scarcity, fairness, equality, and coin transfer are overseen by all participants of the network consensus.

Simply put, the first generation of blockchain is only capable of these essential functions. The creation of a fixed monetary policy and the unconditional transfer of digital value between two participants.

The transfer of value is unconditional. The blockchain network can only verify that Alice is the real owner of the coins and that she is the one who instructs the transfer of value to Bob's address through the transaction. If the transaction validation is successful, Bob becomes the new owner of the value (coins) once the transaction is included in the new block and the block is finalized.

In the image below you can see how a Bitcoin node verifies a common transaction. Alice sends BTC to Bob. The source code for transaction verification is defined in the Bitcoin client. The node verifies, besides other things, if the transaction has a valid digital signature that proves the ownership of BTC in the inputs, and if the transaction spends any BTC that already has been spent by another transaction and other consensus rules.

Validation of regular transactions is not dependent on the third-party program. All logic is implemented in the Bitcoin client. If it is necessary to do anything more complex beyond normal validation, such as a digital signature, it is necessary for the logic to be programmed by a third party and the protocol to enable the execution of the program.

A question arises. Does it make sense to send transactions conditionally? Does it make sense to mint new tokens that would have the same or similar properties as native coins, i.e. an unchangeable monetary policy? Or can it ever be beneficial for monetary policies to change (burning and re-minting tokens)? This would require defining the rules and enabling their validation in a decentralized manner.

Bitcoin script allows the implementation of more complex logic. It is possible to create multi-signature transactions, time-locked transactions (value can only be spent after a certain date or block height), or hash-locked transactions (value can only be spent by revealing a secret that matches a given hash).

Scripting allows users to transfer value to the state channel (Lightning Network) or define a condition for spending the value so that the transaction must be signed by a defined number of participants (for example, two out of three).

Bitcoin can define conditions for spending BTC.

Bitcoin scripting is a simple, stack-based, and non-Turing-complete language that is designed to be secure, deterministic, and easy to verify. This has several major limitations for developers.

Bitcoin scripting has a limited set of opcodes and data types, which restricts the functionality and efficiency of the scripts. It does not support loops, recursion, or stateful computation, which makes it impossible to implement some algorithms or protocols. Furthermore, it does not have access to any external data or events, which limits the interactivity and adaptability of the scripts. Bitcoin scripting does not have formal semantics or a type system, which makes it difficult to reason about the correctness and security of the scripts.

Most transactions in the Bitcoin network are standard. In 2021 the number of non-standard (script) transactions rose to 10-18% (analyses differ from each other).

It is important to repeat that Bitcoin's source code takes care of transferring values within normal transactions. No third party can intervene in the processing of normal transactions, as everything is under the control of the nodes in the distributed network (more precisely, under the control of the pools).

Scripts are always defined by a third party and are not part of the Bitcoin source code. Bitcoin is capable of processing the scripts via an interpreter (more about it later). Scripts pose certain risks, as there may be a bug in the logic of the script.

In the images in the article, the scripts will be shown in red. The interpreter and virtual machines will be shown in blue.

Anyone who creates a transaction can use Bitcoin scripts to specify how the BTC in the transaction can be spent in the future. Bitcoin scripts are deployed by including them in the inputs and outputs of a transaction. The inputs contain the scripts that unlock BTC from previous transactions, and the outputs contain the scripts that lock the BTC for future transactions.

Bitcoin scripts are processed by the Bitcoin nodes, who validate transactions. When a node receives a transaction, it checks if the scripts in the inputs and outputs are valid and follow the consensus rules. The node executes the scripts by using a stack-based interpreter.

A stack-based interpreter is a program that can execute another program written in a special language (in the case of Bitcoin, it is called just Script). The interpreter is embedded in the Bitcoin node. The interpreter works by reading the instructions of the Bitcoin script one by one (from left to right) and performing the corresponding actions. The node evaluates the scripts, and if the final result is true, the transaction is accepted. If the final result is false, or if there is an error or exception, the transaction is rejected.

The interpreter is on every Bitcoin node. If it is necessary to execute a script as part of the validation, it will happen. Note that transaction validation occurs in a decentralized manner.

In the image below you can see how the Bitcoin node verifies the script transaction. The transaction contains a script (in red) with logic for spending the value. This is logic supplied by a third party. Script interpreter (blue) is used for script validation. The output of the script evaluation determines whether the value will be spent (that is, whether ownership of the value will pass to Bob).

Ethereum is the first Turing-complete blockchain platform

Ethereum is the first decentralized platform that supports a Turing-complete and general-purpose programming language called Solidity. Solidity can be used to write smart contracts for any kind of logic or functionality. Smart contracts are self-executing programs that run on the Ethereum blockchain and can interact with other smart contracts, users, or external data sources.

One of the features of Ethereum is that it allows users to create custom tokens via smart contracts. Tokens are digital assets that can represent anything of value, such as currencies, collectibles, or rights. The first generation of blockchains only had native coins. Neither the protocol nor the scripts allowed the creation and transfer of custom tokens.

Ethereum made it possible to write complex applications that can work with native coins and a whole range of other tokens. Thanks to Ethereum, the DeFi industry was born.

Ethereum EVM is the Ethereum Virtual Machine, which is the runtime environment for smart contracts on the Ethereum network. The EVM is a stack-based, Turing-complete, and isolated virtual machine that can execute bytecode instructions that are generated from high-level programming languages such as Solidity. The EVM can access and manipulate data stored on the blockchain, such as account balances, contract state, or transaction inputs and outputs. The EVM also enforces the consensus rules and the gas system of the network, which limit the computation and storage resources used by the smart contracts.

One of the differences between Bitcoin and Ethereum in terms of complex operations is that Ethereum transactions do not contain a smart contract with spending logic, but only a reference to the smart contract.

Smart contracts are stored on the Ethereum blockchain. Smart contracts are deployed to the blockchain by sending a special type of transaction that contains the bytecode and the constructor parameters of the smart contract. The transaction will create a new address on the network that represents the smart contract. The bytecode and the state of the smart contract are stored in the blockchain under this address.

To invoke a smart contract, a user can send another type of transaction that refers to the smart contract address and provides some parameters, such as the value, gas limit, gas price, data, or function call. The transaction will trigger the execution of the smart contract code by the nodes on the network, using the Ethereum Virtual Machine (EVM).

Bitcoin scripts are stateless while Ethereum smart contracts can keep a state in the ledger. This means that Bitcoin scripts do not have any memory or storage of previous transactions or events, and can only operate on the data provided by the current transaction. Ethereum smart contracts, on the other hand, can have a persistent state that is stored on the blockchain and can be updated or accessed by the smart contract code.

The statelessness of Bitcoin scripts is a design choice that aims to ensure the security, determinism, and simplicity of the Bitcoin network. By not having a state, Bitcoin scripts avoid the problems of state corruption, inconsistency, or manipulation that may arise from malicious actors or network failures.

The statefulness of Ethereum smart contracts is a design choice that aims to enable more expressiveness, functionality, and interactivity of the Ethereum network. By having a state, Ethereum smart contracts can implement complex and dynamic logic and functionality for transactions, such as creating tokens, performing calculations, or interacting with other smart contracts. Ethereum smart contracts also allow for more interactivity and adaptability, as they can use oracles and triggers to access and react to off-chain information, such as price feeds, timestamps, or user inputs.

In the image below you can see an Ethereum transaction in which Alice sends value (tokens) to Bob. As these are custom tokens, a smart contract must be used. Alice inserts a reference to the smart contract into the transaction. The Ethereum Virtual Machine finds the smart contract in the blockchain (including its current state) and performs the required operation.

Let's look at a few differences between Solidy and EVM in contrast to Bitcoin Script and a stack-based interpreter.

Solidity offers higher expressiveness. Solidity is a high-level, object-oriented, and Turing-complete programming language that can implement complex and flexible logic and functionality for smart contracts.

Solidity code is compiled into bytecode instructions that are executed by the EVM. Bitcoin Script code is directly embedded in the inputs and outputs of transactions and interpreted by the nodes on the network.

Solidity code can access and react to external data or events, such as oracles, triggers, or user inputs, using various mechanisms such as function calls, events, or modifiers. High interactivity with the surrounding world makes it possible to replicate traditional banking services. Bitcoin Script code cannot access or respond to any external data or events, and can only operate on the data provided by the transaction itself.

Solidity and EVM can provide more expressiveness and elegance than Bitcoin Script, as they can use various features such as loops, recursion, stateful computation, higher-order functions, or lazy evaluation. Ethereum can offer more security and reliability than Bitcoin Script, as smart contracts can use formal verification and testing to ensure the correctness and safety of the smart contracts (various tools and frameworks are available).

Greater expressiveness and statefulness may require more skill and care than Bitcoin Script to write and execute smart contracts, as they may involve more complexity and potential pitfalls.

The execution of smart contracts may incur more costs than Bitcoin Script to run smart contracts, as they may consume more gas fees for computation and storage resources on the network. Ethereum may face more scalability issues than the processing of Bitcoin Script, as the execution of smart contracts may generate more network congestion and latency due to their higher demand for resources.

Most Ethereum transactions are related to the execution of smart contracts. DeFi, NFTs, and stablecoins are relevant blockchain industries that open up new possibilities. Ethereum has surpassed Bitcoin in the number of active users and transactions.

Cardano has native scripts and Plutus scripts

Cardano is a smart contract platform similar to Ethereum but with many differences. Scripting on Cardano can be done in two ways: native scripts and Plutus scripts.

Native scripts are simple scripts that can be used to lock funds, mint tokens, or delegate ADA coins. They are written in a JSON-like format and can be directly interpreted by the Cardano ledger rules. So, native scripts do not require any special tools or virtual machines to validate them.

Native scripts are one way to implement a multi-signature transaction. They are used in delegation certificates. A delegation certificate is a type of transaction that allows a stakeholder to delegate their ADA coins to a stake pool.

In the image below you can see a transaction with a native script. Note that the Cardano node does not need any virtual machine or interpreter to execute the script (unlike Bitcoin and Ethereum). They are executed natively. Alice can send a transaction with a delegation certificate in which she delegates ADA to Bob's pool. Alternatively, it may be a transaction that contains a minting script. Tokens will be minted to Bob's address (token issuer).

There are two ways to include a native script in a transaction:

  • Firstly, embedding the script as a Datum in the transaction. This means that the script is part of the transaction data and can be seen by anyone who inspects the transaction. This method is suitable for scripts that are short and simple, or that need to be changed frequently.
  • Secondly, storing the script on the blockchain via a transaction and referencing it by its hash. The script is stored as an output on the blockchain and can be identified by its unique hash. The output must have an address that is derived from the hash of the script and must have a value of at least 1 ADA to ensure that it is not pruned by the network. This method is suitable for scripts that are long and complex, or that need to be fixed and immutable.

It is possible to reference the native script that is stored on the blockchain many times in a row. As long as the script output is not spent by another transaction, it can be used as an input for multiple transactions that use the same script.

In the picture below, you can see a similar case as above, with the difference that the transaction references a native script stored in the blockchain through a hash.

Native scripts are mostly on-chain logic, as they are embedded in the transaction or stored on the blockchain. However, they can also have some off-chain logic, such as generating transactions that use native scripts as inputs or outputs. For example, you can use JavaScript, Python, or Java to create applications that generate transactions that use native scripts.

Now let's take a closer look at Plutus scripts.

Plutus scripts are more complex and expressive scripts that can implement arbitrary logic and interact with other smart contracts. They are written in Plutus (Haskell-like language), a functional programming language, and compiled into Plutus Core, a low-level language that runs on the Cardano virtual machine (CVM).

One of the key features of scripting on Cardano is the separation of on-chain code and off-chain code (the logic of the program).

On-chain logic is the logic that runs on the blockchain and validates the transactions that involve smart contracts. On-chain logic is written in Plutus Core and executed by the Cardano virtual machine. On-chain logic is immutable and transparent, meaning that it cannot be changed or hidden once it is deployed on the blockchain.

Off-chain logic is the logic that runs outside the blockchain and handles the requirements and interactions of the smart contract application. Off-chain logic is written in Haskell, a high-level language that can use the Plutus Application Framework (PAF) to access services such as nodes, wallets, oracles, etc. However, other high-level programming languages can also be used (JavaScript, Python, or Java).

Off-chain logic is mutable and private, meaning that it can be updated or modified without affecting the on-chain logic or requiring a hard fork.

Plutus scripts are smart contracts that use both on-chain and off-chain logic. The on-chain part of a Plutus script is called a validator script, which decides whether a transaction that spends an output is authorized to do so. On-chain code, as the name suggests, is validated by all nodes in the Cardano network. The off-chain part of a Plutus script is called a Plutus application backend (PAB), which generates transactions that conform to the rules of the validator script.

The separation of on-chain code and off-chain code is useful for several reasons. First, it allows developers to write smart contracts in a high-level language like Haskell, Java, Python JavaScript which is easier to read, write, and test than low-level languages like Plutus Core or Solidity. Second, it reduces the size and cost of transactions that run smart contracts, as only the essential on-chain code is included in the transaction. Third, it improves the security and scalability of smart contracts, as off-chain code can be updated and modified without affecting the on-chain code or requiring a hard fork.

The separation of on-chain code and off-chain code is good for creating flexible and robust smart contract applications. By using tools like Plutus Application Framework (PAF) and Plutus Application Backend (PAB), developers can easily create and deploy their smart contract applications locally or in a live production environment.

Plutus scripts can be part of transactions as inputs or outputs, depending on how they are used. There are two main types of Plutus scripts: validator scripts and minting policies (minting scripts).

Validator scripts are used to lock funds in a script address and validate the spending of those funds. The output of the validator is a boolean value (True means the value can be unlocked/spent). The validator is not able to take an action such as calling another script or sending some tokens somewhere. The validator only approves or rejects the unlocking of funds at the script address.

A validator script can be either embedded in the transaction as a Datum or stored on the blockchain and referenced by its hash. It's similar to native scripts. The advantage of storing the script on the blockchain is that it reduces the size of the transaction and the fees, but it also makes the script public and immutable.

Minting policies are used to control the creation and destruction of custom tokens. A minting policy can only be stored on the blockchain and referenced by its hash. This ensures that the minting policy is unique and consistent for all tokens of the same type.

The hash of the script is the policy ID of the native tokens that the script mints. When you have tokens with a particular policy ID, you can be sure that tokens have been minted by the script (minting policy) that corresponds to the policy ID.

Validator scripts have more flexibility, while minting policies have more consistency.

When the validator script is to be executed it is passed these three pieces of information as arguments:

  • Datum: It is a piece of data attached to the UTxO that the script is locking. This is typically used to carry state (application state).
  • Redeemer: It is a piece of data attached to the spending input. This is typically used to provide input to the script from the spender (input data that can unlock funds through the script).
  • Context: It is a piece of data that represents information about the spending transaction. This is used to make assertions about the way the output is being sent.

Datum is optional data. Datum is always stored alongside the UTXO. There can be multiple UTXOs at one address, so there can also be multiple Datums. All Datums at the address are equal. No specific Datum serves as the global state of the script. All UTXOs at the address are independent and their unlock is validated separately by the script.

In the image below you can see the Cardano smart contract composed of off-chain logic (backend) and on-chain logic. One application can use off-chain logic to create transactions with scripts and the same backend to create spending transactions. Bob created the transaction with a script. It contains the Plutus script (validator) and also the UTxO which is locked through the validator (the script). A UTxO can contain a Datum, which is a piece of data that can be used to store the state of the contract. Alice wants to unlock the UTxO. In order to spend the UTxO, a spending transaction that can contain a Redeemer must be created in the off-chain part of the smart contract. The Redeemer is the input for the Plutus script, which decides whether the spending transaction is valid or not.

The application logic defined in the backend allowed Bob to lock the value and Alice to unlock the value. The first transaction locks funds at the script address and the second one triggers the execution of the script to unlock funds.

Note that in the case of a script transaction (Bob's transaction), the arrow from the left side of the backend leads directly to the transaction in the right part of the image. This means that the Plutus script is not executed when the transaction is settled. The validator script locks funds at the script address and the execution is triggered by a spending transaction (Alice’s transaction). This is why the arrow leads from the spending transaction to the Cardano virtual machine.

Off-chain logic can generate script transactions that lock funds in a script address and require a specific Redeemer value to unlock them. For example, Bob can use off-chain logic to create a script transaction that locks 10 ADA in a script address and requires the Redeemer value “Hello” to unlock them. Bob can then send the script address and the Redeemer value to Alice, who can use off-chain logic to create a spending transaction that provides the correct Redeemer value and spends the 10 ADA from the script address.

Off-chain logic can use the Plutus Application Backend (PAB) to manage and handle the requirements of the application instance throughout its lifecycle. The PAB acts as an intermediary between Plutus applications, the node, the wallet backend, and end-users. The PAB also helps with building the transactions that run the Plutus scripts on the blockchain.

Plutus scripts are stateless in the sense that they do not have access to any persistent storage or global variables. However, they can use a technique called state threading to simulate stateful behavior by passing data between different script instances. The state is stored as a Datum attached to a script output, and it can be accessed by the next script input that spends that output. The Datum can be any arbitrary data type that the script can encode and decode.

State threading allows Plutus scripts to implement complex logic and interactions, such as state machines, oracles, and multi-signature schemes. However, it also requires careful design and coordination to ensure that the state is updated correctly and consistently. For example, a script may need to check that the state has not been modified by another transaction since the last time it was accessed, or that the state is within a valid range of values.

Plutus scripts are more expressive than Bitcoin scripts and Ethereum smart contracts. Haskell is a functional programming language that can implement arbitrary logic and interact with other smart contracts. The backend can be implemented in any high-level language. Solidity can also implement complex logic and interact with other smart contracts, but it has some limitations and drawbacks compared to Haskell. For example, Solidity has a higher risk of bugs and vulnerabilities due to its mutable state and low-level details. Solidity also has a higher cost of execution due to its gas model, which limits the complexity and scalability of smart contract applications on Ethereum.

Haskell is a language that supports formal verification, as it has a clear and precise syntax, a strong and static type system, and pure and deterministic semantics. Haskell also has libraries and tools that can help with formal verification, such as QuickCheck, LiquidHaskell, and Coq.

If you look at the types of transactions over a longer period of time, you will see that there are roughly 2x more transactions with a script than common transactions. Cardano has native assets. If users want to send tokens to each other, there is no need to use a script. Therefore, the amount of common transactions is relatively high compared to Ethereum (it is always necessary to use a smart contract to transfer assets).


Bitcoin is not and will not have the same capabilities as Cardano or Ethereum. Bitcoin scripts are very simple and do not allow to creation of the complex logic necessary to create an alternative financial service. Ordinals, Inscriptions, and BRC-20 are technologies that do not have the potential to overcome the capabilities of SC platforms. The Bitcoin protocol basically just writes data to the blockchain without any validation of the content. There is no third-party program execution.

In the article, we mainly focused on the description of programmability options and not too much on the comparison. We'll look at that sometime next time.


Related articles

Did you enjoy this article? Other great articles by the same author