This document describes the blockchain and consensus changes required to implement Group Tokenization on Bitcoin Cash. It is intended for implementers, and generally does not explain why certain decisions were made. It assumes that you understand the general architecture and purpose of Group tokens. If, while you are reading this document, you need greater understanding of a topic, please refer to the main Group Tokenization proposal document here: https://docs.google.com/document/d/1X-yrqBJNj6oGPku49krZqTMGNNEWnUJBRFjX7fJXvTs/edit?usp=sharing
Notes on reading this document:
Group Tokenization fits its changes within the existing transaction format by prefixing transaction output scripts with Group data. This makes the changes required to implement group tokens very small and self contained1.
Within a transaction, grouped outputs follow the existing output serialization format, except that the output script is formatted as follows. Following normal convention <> indicates a data push. Also [] indicates optional arbitrary additional script code:
<GroupID> <QuantityOrFlags> OP_GROUP [any normal constraint script]
OP_GROUP may appear anywhere within a script.
OP_GROUP is a new opcode defined as the byte 0xee (REQ1.1.0).
Execution of the OP_GROUP opcode MUST pop 2 objects from the main stack (REQ1.1.1).
If there are fewer than 2 objects on the stack, execution MUST fail (REQ1.1.2).
Optimizing script machines may recognize the push,push,OP_GROUP pattern and skip execution. However, such a machine MUST behave exactly as if those instructions were executed including failing the script due to bounds checking (both during the pushes and the OP_GROUP) and properly accounting for these op codes in any tallies (REQ1.1.3).
The following is an example “C” implementation of the OP_GROUP instruction:
case OP_GROUP: // OP_GROUP just pops its args during script evaluation
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
popstack(stack);
break;
All existing transaction validation rules are unchanged.
A UTXO is defined as “grouped” if it contains a serialized script following the format specified in section 1.2. An OP_GROUP instruction that appears in any other place in the serialized script does not make a UTXO “grouped”.
In other words, for Group semantics to apply to a UTXO, the “real” script must be prefixed by OP_GROUP instruction and its arguments. OP_GROUP is therefore semantically an attribute of the UTXO that is actually used outside of the script interpreter. Inside the interpreter, its has no meaning except to pop 2 elements off of the stack. It is structured in this manner to minimize changes to the transaction format, and therefore changes required by the BCH ecosystem. A non-tokenized, un-upgraded light wallet will understand the transaction and will even be able to spend non-grouped UTXOs from a transaction that has grouped UTXOs. But it will not understand the Grouped UTXOs. This is a feature that prevents unupgraded light wallets from accidentally spending potentially valuable tokens for their dust BCH.
If the “QuantityOrFlags” most significant bit is clear (the number is positive), this field should be interpreted as a quantity. If the MSB is set, this is an “authority baton UTXO” and this field should be interpreted as Flags (described below).
AUTHORITY = 1<<63, This is an authority UTXO, not a “normal” quantity holding UTXO
MINT = 1ULL << 62, Can mint tokens
MELT = 1ULL << 61, Can melt (destroy) tokens
BATON = 1ULL << 60, Can create authority outputs
RESCRIPT = 1ULL << 59, Can change the output script
SUBGROUP = 1ULL << 58, Can operate on subgroups
ACTIVE_FLAG_BITS = AUTHORITY | MINT | MELT | BATON | RESCRIPT | SUBGROUP
ALL_FLAG_BITS = (0xffffULL << (64 - 16))
RESERVED_FLAG_BITS = ACTIVE_FLAG_BITS & ~ALL_FLAG_BITS
Reserved flag bits 57 to 48 are reserved for future hard fork group features and must be zero. Bits 0 through 47 are used as a nonce in the Group genesis UTXO, but must be zero in other authority UTXOs.
Group Identifiers are 32 byte values2. The last 2 bytes of a group identifier4 indicate properties of the group as follows:
COVENANT = bit 0, This group enforces the structure of output scripts
HOLDS_BCH = bit 1, This Group holds BCH instead of tokens
GROUP_RESERVED_BITS = bits 2 through 15, MUST be 0.
For example, if the “groupId” is a byte array, the COVENANT bit is groupId[31]&1.
Subgroups are groups with a parent group. Parent authorities can execute operations on subgroups. Subgroup Identifiers are the byte concatenation of the 32 byte parent group identifier and arbitrary additional bytes, limited by the maximum stack element size. Subgroups do not have their own flags; they have the same flags at the same location as their parent group.
Therefore to determine the parent group identifier from a subgroup identifier, simply take the first 32 bytes.
Certain properties are enforced across the inputs and outputs of a transaction. These properties only apply to “grouped” inputs and outputs. If a transaction has no grouped inputs or outputs an optimized validator may return VALID without further analysis.
This section enforces the following general rules:
This is not a lot of rules and the resulting algorithms are not complex. However, the following sections specify the exact rules in great detail, covering every tiny edge condition since any differences might cause a consensus failure. So upon the first read, these sections may make the rules seem daunting. But actually, this document is much larger than a quality implementation which can fit into a single function and a few helpers. Just keep your mind on the overall picture stated in the previous paragraph and work through the rules slowly. A good approach is to loop through outputs then inputs collecting data on each group. Then loop through the collected groups, verifying the rules based on data collected. If you come upon an edge condition that seems unspecified make an in-code note that will result in a compilation error. DO NOT GUESS OR SKIP! Contact an author of this paper and we will clarify the requirement.
Notes on reading these rules:
For example, the phrase “input xxx authority” (e.g “input mint authority”) means “an input group authority with the xxx Flag bit set”, or to be extremely pedantic, “a transaction input whose prevout UTXO scriptpubkey begins with <GroupID> <QuantityOrFlags> OP_GROUP, and whose GroupID field matches the one we are currently working on and whose QuantityOrFlags field has the AUTHORITY (63) and MINT (62) bits set”.
Likewise, the phrase “sum of the output Quantities” should be pedantically understood as “the sum of the Quantity fields in the non-authority-baton outputs whose GroupID field matches the one we are currently working on”.
Note: These deserialization rules mean that the only way to specify an Authority Baton UTXO is when B has a length of 8 bytes.
Note: If OP_GROUP appears as a proper script prefix, both these rules and the script machine rules apply. If OP_GROUP is used in any other place within the script, only the script machine rules are applied. This means that its valid to have an OP_GROUP in some other location within a script whose arguments have any length. Even though this makes specification more complex, it makes validation simpler.
Construct 4 64 bit bitmaps (per group that appears in the transaction’s inputs and outputs): perms, batonPerms, subgroupPerms and subgroupBatonPerms. Initialize them to 0.
Note that the SUBGROUP authority bit means that the other permission bits in this authority apply to both this group and any of its subgroups, not exclusively to subgroups. Authorities can be targeted exclusively to a subgroup via the “baton” system by creating a child authority with the subgroup’s ID.
This formulation means that a transaction can contain only create one new group. Also, no other outputs in this transaction can use this group or subgroups of this group (if implementations set all the permissions running flags to 0 this property will be enforced by other rules). Isolating the genesis UTXO in this manner is an arbitrary limitation intended to improve implementation consistency by reducing edge cases.
It is possible for a subgroup to have no input UTXOs but have outputs because the parent group authority might operate on the subgroup. An incorrect implementation might misinterpret this as an invalid group genesis output. As described above, using batonPerms is one way to realize that such an output is not a genesis. Another (normative) way is that the GroupId for any subgroup must be greater than 32 bytes.
Note that the genesis authority Flags field may not have all permission bits set. The group creator may want to restrict certain permissions from genesis.
Note that if a covenanted group is created without a RESCRIPT authority, the first MINT transaction can never be valid because there is no input script from which to pull the covenant. The genesis transaction is valid, but this group is probably useless.
If the GroupId HOLDS_BCH flag is set, Quantity MUST be 0. Copy the BCH amount field in every non-authority input and output (grouped) UTXO into the respective Quantity field (REQ3.2.3.1). (This enables grouped BCH, rather than grouped tokens)
The sum of the input Quantities MUST not exceed the maximum int64_t value (REQ3.2.3.2). (note this is a signed 64 bit integer or 0x7FFFF FFFF FFFF FFFF)
The sum of the output Quantities MUST not exceed the maximum int64_t value (REQ3.2.3.3). (note this is a signed 64 bit integer or 0x7FFFF FFFF FFFF FFFF)
The sum of the input Quantities MUST equal the sum of the output Quantities, unless the group’s perms has the MINT or MELT bit set. (REQ3.2.3.4)
If the group’s perms has the MINT bit set, the sum of the output Quantities MUST be >= the sum of the input Quantities. (REQ3.2.3.5)
If the group’s perms has the MELT bit set, the sum of the output Quantities MUST be <= the sum of the output Quantities. (REQ3.2.3.6)
Note that if the group HOLDS_BCH, the MINT and MELT operations are not actually creating or destroying BCH. Instead these operations act as entry or exit points, allowing BCH to flow into or out of the group. Conservation of BCH in the transaction is guaranteed because the existing logic that balances BCH in a transaction is untouched.
Note that the total ordinality of the group’s tokens may exceed a signed 64 bit integer. In this case a single transaction cannot manipulate all of them…
Covenants allow spending constraints to be carried from parent to child (to grandchild, etc) transactions. Any group with the COVENANT bit set enforces covenants by requiring that grouped output scripts match the first grouped prevout script. To specify an initial script, or offer a script upgrade to holders, an authority with the RESCRIPT bit set can be spent. This causes the covenant enforcement to be ignored, allowing any output script to be used.
Formally, for every grouped output, if the group has the COVENANT bits set and the group.perm’s RESCRIPT bit is clear, all output “scriptpubkey” scripts MUST equal the first grouped non-authority input’s prevout “scriptpubkey” script, beginning after the first OP_GROUP instruction (REQ3.2.4.1). Note that it is equivalent to compare the cryptographic hashes of these script suffixes rather than the script itself. If this object (the first grouped non-authority) does not exist return INVALID (REQ3.2.4.2).
(This enforces that covenanted groups use the same constraint script as the first input from that same group3. Efficient implementations should discover this script while iterating through the inputs during a previous step)
The P2SH script type has hard-coded extra-script behavior which is that upon completion the second-to-top stack item (the redeem script) is subsequently executed. The Grouped P2SH script type (described in chapter 4) MUST behave analogously5. (REQ3.2.5.1)
Two new script types SHOULD be recognized as standard. These are simply the P2PKH and P2SH script types with the OP_GROUP prefix:
Grouped P2PKH (REQ4.1)
<groupid> <quantityorflags> OP_GROUP OP_DUP OP_HASH160 <pubkeyhash> OP_EQUALVERIFY OP_CHECKSIG
Grouped P2SH (REQ4.2)
<groupid> <quantityorflags> OP_GROUP OP_HASH160 <scripthash> OP_EQUAL