The Nexa hard fork was successfully activated at block number 766,084 at 12:22 UTC on 31 March 2025. The majority of network participants have since migrated to the Nexa Full Node 2.0 software. All full-node wallet users are required to upgrade to version 2.0 to maintain compatibility with the current consensus rules. This release introduces a comprehensive set of protocol enhancements and scripting extensions aimed at improving developer ergonomics and significantly expanding the programmability of the Nexa blockchain.
Download Full Node 2.0 Now:
- Windows (64-bit):
https://bitcoinunlimited.info/nexa/2.0.0.0/nexa-2.0.0.0-win64-setup.exe - macOS (x86_64):
https://bitcoinunlimited.info/nexa/2.0.0.0/nexa-2.0.0.0-macos-x86.dmg - Linux:
https://www.bitcoinunlimited.info/nexa/2.0.0.0/nexa-2.0.0.0-linux64.tar.gz
Special thanks go to the Nexa developers and core contributors whose efforts made this release possible. Their continued dedication and technical expertise have been instrumental in driving the project forward. We extend our sincere gratitude to Andrea Suisani, Andrew Kallmeyer, Andrew Stone, Dagur Valberg Johannsson, Dolaned, Griffith, Jørgen Svennevik Notland, Peter Tschipper, Proteus, and vgrunner for their invaluable contributions.
Hard Fork Activation
Fork 1 has a 2-phase activation. When the MTP (Median Time Past) of a block is greater than or equal to the activation time, the fork “activates” (phase 1). The transactions in this “activation” block MUST still follow the old ruleset. Activation flushes the txpool and re-admits all pending transactions under the new rules. The next block (and all subsequent blocks), regardless of its time, are “enabled” blocks. They are evaluated using the new fork rules.
This 2-phase activation allows us to cleanly apply the new ruleset to pending transactions by giving advance warning of a “locked-in” upgrade.
Hard Fork Feature Summary
New Opcodes
- OP_MERKLEROOT
- OP_JUMP
- OP_STORE / OP_LOAD
- OP_PARSE
- Negative indexes for OP_ROLL and OP_PICK
- OP_INPUTTYPE, OP_OUTPUTTYPE, OP_INPUTVALUE
Functionality
- Read-only inputs
- Script machine limits expanded
- Expanded handling of bignums in additional opcodes
- Limited template script args hash
- Limited legacy output scripts
- Data in inputs and outputs via OP_RETURN
- Minimum block size is raised to 2MB
New Script Functionality/Opcodes
- Introspection (OP_INPUTTYPE, OP_OUTPUTTYPE, OP_INPUTVALUE)
OP_JUMP (Nexa Spec:OP_JUMP)
OP_MERKLEROOT (Nexa Spec:OP_MERKLEROOT)
OP_PARSE (Nexa Spec:OP_PARSE)
Negative indexes for OP_ROLL and OP_PICK (Nexa Spec:OP_ROLL OP_PICK)
OP_STORE / OP_LOAD (Nexa Spec:OP_STORE) and (Nexa Spec:OP_LOAD)
New Transaction Functionality
- Read-only inputs.
- Data in inputs and outputs. Append an OP_RETURN and arbitrary additional data to unlocking or locking scripts to associate that data with an input or an output. Anything after the OP_RETURN is not executed in the script. It is recommended that you serialize the additional data using script serialization so analysis programs can parse it without needing to know its contents. This extra data is accessible via transaction introspection, although it is recommended to use normal push operations to provide data to scripts.
Constraints
- The minimum block size is raised to 2MB to allow for greater volatility in transaction generation rates.
- Script Machine limits have changed.
- Template script argument hashes are limited to 0, 20, or 32 bytes. Previously, other sizes were allowed on-chain but were unspendable.
This change prevents the accidental creation of unspendable templates and reserves other sizes for future use. - Legacy (non-script template) output formats are tightly constrained.
By constraining arbitrary scripts in outputs, we minimize the size of the UTXO set and prevent various DoS and scalability attacks, issues that historically led Bitcoin to adopt the “IsStandard” design principle. - All non-standard outputs are now disallowed by consensus. Pay-to-script-template must be used for arbitrary contracts.
- TX_PUBKEY and TX_SCRIPTHASH “standard” scripts are disallowed.
- Multisig scripts are allowed if they are “standard,” meaning they must be M-of-N where N is 1, 2, or 3, and 0 < M ≤ N.
- For legacy compatibility, TX_PUBKEYHASH is allowed, but its use is discouraged. Instead, use pay-to-public-key-template.
TX_PUBKEYHASH remains available so legacy libraries can be used with Nexa with minimal modifications. - TX_NULL_DATA (OP_RETURN) is fully supported and is the recommended way to include data (that is not intended for the UTXO set) in a transaction.
Breaking Down the Hard Fork Features
Rostrum 11.0
This release includes database changes that require the index to be rebuilt. On the first run after upgrading, Rostrum will automatically trigger this reindexing process.
- A new unspent output index significantly improves the performance of unspent UTXO and balance queries.
- Many RPC calls now support new filters, including height filters that enable historical queries—for example:
“Give me all UTXOs created after height A that existed at height B.” - An HTTP endpoint has been added, allowing Electrum queries via HTTP POST requests (enable with --http).
More details:
https://gitlab.com/nexa/nexa/-/blob/dev/doc/release-notes/2.0.0.0.md
OP_MERKLEROOT and Merkle Proof Computation
OP_MERKLEROOT calculates a Merkle root given a list of elements and a Merkle proof. This instruction can be used to efficiently verify a variety of operations in Nexa Script on verifiable databases.
Syntax:
proof elementsArray N option OP_MERKLEROOT → [N adjacency] [N depth] [N index] merkleRoot
More details:
https://gitlab.com/nexa/nexa/-/blob/dev/doc/op_merkleroot.md
OP_JUMP, aka Script Loops
This opcode jumps to a location defined by the current position minus the argument to the script (measured in bytes). However, if the argument is False or a bignum/scriptnum value of 0, execution continues to the next instruction, rather than jumping.
(REQ1.0) Jumping to exactly the location after the last instruction (i.e., the end of the script code, but not beyond) is a valid way to end the script. All other validation checks (e.g., clean stack) must still pass for the script to be considered valid.
Note: Since the offset is specified in bytes, it’s possible to jump into a region of the script that contains pushed data.
For example:
PUSH 010000000000h, JUMP
This jumps backwards 1 byte, executing the last byte of the push (00) as code (which pushes 0 onto the stack), then executes JUMP again, which passes through because the top of the stack is false.
Script authors may also use constructs like 0 IF code ENDIF to create isolated code sections that can only be jumped into.
More details:
Implement Script Machine Registers: OP_STORE, OP_LOAD
Script machine registers provide random-access data storage within the script machine, rather than relying solely on sequential stack operations.
By using the two opcodes OP_LOAD and OP_STORE, scripts can store up to X variables in machine registers for easier access later, offering a more performant alternative to ROLL and PICK. Initially, up to 32 (arbitrary value) registers are allowed.
Within an OP_EXEC, the script registers are reset to empty and may be used by the called script. Upon return, the registers are restored to their original values. In other words, OP_EXEC behaves as though it runs inside a completely separate script machine.
Script registers are cleared after each script execution in both traditional and script template execution models. In push-only scripts, loading from script registers is not meaningful and will cause the script to fail.
Scripts SHOULD NOT rely on a register’s initial value being zero. They SHOULD explicitly load an initial value.
Script machine registers are implemented as:
std::array<StackItem, 32> arrRegisters;
in the script machine.
More details:
OP_PARSE
This opcode automatically parses common types of script data (notably bytecode), extracting portions of that data and placing them on the stack. It is designed to extract data from buffers that are serialized in a specific format, unlike OP_SPLIT, which extracts data from buffers of any format using a byte offset.
One of the primary tasks of UTXO-family scripts is to constrain and verify properties of locking scripts, this is the only way to enforce conditions on the effects of a transaction.
For example, suppose an author wants to create a spend clause that ensures an input can only be spent to a specific output. This example is both simple and essential to “vault”-style functionality and reversible payments. The author must write a script that accesses the output script and checks that its template hash and args hash match the expected values.
Currently, to accomplish this, the author must use introspection to access the output script and write a custom bytecode parser within the script itself to extract the needed fields. While feasible, such a parser is non-trivial, and without loops, its size grows with the number of opcodes that need to be parsed.
With OP_PARSE, however, accessing these two fields can be done in a single instruction.
Similarly, any state baton pattern benefits from OP_PARSE. In this pattern, a service provider periodically updates a piece of state stored in a UTXO, while users read or interact with that state. The updates must follow a set of agreed-upon rules, which are enforced by the constraint script.
OP_PARSE is used to extract the prior (incoming) state from the prevout of an input. The script then applies its logic to compute the expected next state. OP_PARSE is again used to extract the actual new state from one of the outputs, and OP_EQUALVERIFY checks that the expected and actual states match.
More details:
Implement Negative Indexes for OP_ROLL / OP_PICK
There is a need to insert items into the middle of the stack (as opposed to overwriting items using OP_PLACE). It was observed that OP_ROLL can be thought of as rotating a number of stack items. Insertion is effectively rotation in the opposite direction.
Supporting negative numbers captures this “reverse rotation” concept and efficiently utilizes the available opcode functionality.
More details:
Read-Only Inputs
Nexa transactions can include inputs that are not spent when the transaction is processed. To mark a transaction input as read-only, its type must be set to 1 (READONLY) instead of the normal 0 (UTXO). Read-only inputs must have both their nValue and nSequence fields set to 0. The scriptSig of a read-only input may be empty or contain a valid script.
Read-only inputs are useful for bringing in transaction data from UTXOs that do not belong to the user creating the transaction, as they do not require signing. This data can be accessed via transaction introspection, which is helpful for covenants and script templates that need consistent access to specific UTXOs.
Read-only inputs allow multiple transactions to access the same data in parallel, unlike the traditional “import and rewrite” approach, where the UTXO is destroyed and recreated.
A non-zero-length signature in the scriptSig can be used to prove ownership of a UTXO, as any non-zero-length signature must be valid for the transaction to be valid—regardless of the read-only flag. This makes asset-related operations easier, especially in cases where owners need to prove asset ownership. Dividend and token reward distributions are classic use cases.
Note: Contracts using this feature must use introspection to check that the read-only input’s scriptSig length is non-zero.
If the contract also needs to verify the quantity of an owned asset, it can read the amount from the prevout.nValue field. Even though the input’s own nValue field is 0, the original UTXO amount remains accessible.
The powers associated with a token authority UTXO can be reused by importing the authority UTXO as read-only and signing it, provided it has the BATON flag set. This approach improves efficiency and simplifies development, as it avoids the need for the “import and rewrite” pattern.
A BATON group authority can be offered to the public in a non-destructive way by creating a spend clause that verifies the input is read-only. A separate clause can be added for actually spending the UTXO to revoke public access.
However, since a non-zero scriptSig is required to enable authority powers, the constraint should enforce at least a minimal scriptSig—e.g., OP_DROP or OP_1. In practice, group owners often want to prevent non-read-only spends entirely, so a more detailed script using introspection is necessary.
Important:
It is not possible to prove signatory control over a BATON authority UTXO without activating its powers within the same transaction. Therefore, it is recommended that any use of read-only inputs to prove signatory ability over BATON authorities be signed using a xxx/ALL sighash type, to strictly control how those authorities are used.
More details:
https://gitlab.com/nexa/nexa/-/blob/dev/doc/read-only-inputs.md
Script Machine Limits Changed
Scripts may use a total of 1 MB (1024 * 1024 bytes) of stack memory. They may also use up to 8 * 1024 individual stack elements. The length of a single stack element is no longer constrained (aside from the obvious requirement that it must be smaller than the total available stack memory).
These limits are calculated by summing the maximum usage across both the stack and altstack, including the current usage of any parent scripts recursively (see OP_EXEC).
Stack elements are treated as if they contain no metadata. For example, pushing 0x1234 onto the stack is counted as 2 bytes and 1 element.
Any bignums are treated as if stored in sign-magnitude format, regardless of the actual internal format. Round both sign and magnitude up independently to the nearest byte. For instance, the number 0 is charged 2 bytes of memory, even though technically it only requires 1 bit.
Opcodes (unless explicitly noted otherwise) should be analyzed as if they first pop all required arguments, then push all results. In other words, use the most optimistic stack counting.
For example, if OP_CAT, OP_TOALTSTACK, or OP_FROMALTSTACK is executed, the maximum stack memory usage remains unchanged, even if the internal implementation (e.g., of FROMALTSTACK) temporarily copies the stack item from altstack to the main stack—resulting in a brief internal state where the item exists in both locations.
Some opcodes combine an operation and a verification, such as CHECKSIGVERIFY. Even though such opcodes are compound in effect, their stack usage MUST NOT include the intermediate value being verified.
More details:
https://gitlab.com/nexa/nexa/-/blob/dev/doc/nexa-script-machine.md
Nexa Remote Procedure Call (RPC) Commands Documentation
- dumputxoset “filename”
- evicttransaction “txid”
- getbestblockhash
- getblock hash_or_height (verbosity) (tx_count)
- getblockchaininfo
- getblockcount
- getblockhash index
You can find all available commands in the official RPC documentation:
Additional Changes
Extended Introspection Opcodes:
New opcodes added for deeper transaction inspection:
OP_INPUTTYPE, OP_OUTPUTTYPE, and OP_INPUTVALUE
https://gitlab.com/nexa/nexa/-/blob/dev/doc/fork1.md
-
Expanded Bignum Handling:
NexaScript now supports bignums in additional opcodes such as OP_IF, OP_NOTIF, OP_VERIFY, and more, enhancing scripting flexibility. -
White Paper Source Added:
The LaTeX source of the Nexa white paper is now available in the documentation folder.
https://gitlab.com/nexa/nexa/-/blob/dev/doc/nexa_whitepaper/main.tex
-
Reduced Dependency on Boost:
Internal refactoring has reduced Nexa’s reliance on the Boost library, simplifying builds and improving portability.
For more detailed information and the full changelog, please visit our GitLab:
https://gitlab.com/nexa/nexa/-/blob/dev/doc/release-notes/2.0.0.0.md