Atoms
An atom is one of two fundamental data types in CLVM. Put simply, atom values represent a sequence of bytes.
There are multiple types of atoms:
- Atom - has no semantic meaning
- Bytes - explicitly treated as bytes
- Bytes32 - a
Bytes
value with a length of 32 - PublicKey - a BLS12-381 public key with a length of 48 bytes
- Signature - a BLS12-381 signature with a length of 96 bytes
- K1PublicKey - a secp256k1 public key with a length of 33 bytes
- K1Signature - a secp256k1 signature with a length of 64 bytes
- R1PublicKey - a secp256r1 public key with a length of 33 bytes
- R1Signature - a secp256r1 signature with a length of 64 bytes
- Int - a signed integer of arbitrary size
- Bool - either
true
orfalse
Atom
The plain Atom
type should be used when you don't care about the semantics of the value. Any other atom type can be assigned to Atom
without requiring a cast, whereas a cast is required to go from Atom
to anything else.
In most cases, you should use a concrete atom type like Bytes
instead of Atom
, since it more clearly expresses the intent and prevents making mistakes.
Bytes
The Bytes
type provides similar semantics to byte arrays or strings in other languages. If you add multiple Bytes
values together, they will be concatenated. Other mathematical operations will yield a type error unless you cast to Int
first. You should use Bytes
when you don't know (or haven't checked) the length of the value.
There are a few ways to construct a value that is compatible with Bytes
:
- Hex literals -
0xCAFEF00D
- UTF-8 encoded string literals -
"Hello, world!"
nil
You can also cast other atom types to Bytes
when needed.
Bytes32
The Bytes32
type represents any Bytes
value with a length of 32.
If a hex literal or string literal produces a value that is exactly 32 bytes, it will be compatible with this type automatically. However, if you have an atom with an unknown length, you will need to check the type first:
assert value is Bytes32;
This will emit code at runtime to check that the value has a length of 32.
Bytes32
can only guarantee the length of trusted values. See the Security page for more details.
PublicKey
The PublicKey
type represents a BLS12-381 public key (also commonly referred to as a G1 element).
An atom must be 48 bytes long to be compatible with PublicKey
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or BLS opcodes, as long as it's not directly concatenated with other bytes.
Signature
The Signature
type represents a BLS12-381 signature (also commonly referred to as a G2 element).
An atom must be 96 bytes long to be compatible with Signature
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or BLS opcodes, as long as it's not directly concatenated with other bytes.
K1PublicKey
The K1PublicKey
type represents a secp256k1 public key.
An atom must be 33 bytes long to be compatible with K1PublicKey
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or SECP opcodes, as long as it's not directly concatenated with other bytes.
K1Signature
The K1Signature
type represents a secp256k1 signature.
An atom must be 64 bytes long to be compatible with K1Signature
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or SECP opcodes, as long as it's not directly concatenated with other bytes.
R1PublicKey
The R1PublicKey
type represents a secp256r1 public key.
An atom must be 33 bytes long to be compatible with R1PublicKey
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or SECP opcodes, as long as it's not directly concatenated with other bytes.
R1Signature
The R1Signature
type represents a secp256r1 signature.
An atom must be 64 bytes long to be compatible with R1Signature
, although there is typically no harm done by not verifying the length at runtime before using it in conditions or SECP opcodes, as long as it's not directly concatenated with other bytes.
Int
The Int
type represents any integer value, positive or negative. You can use arithmetic and comparison operators with integers.
You can construct a value that is compatible with Int
by using integer literals such as 42
.
You can also cast other atom types to Int
when needed.
Bool
Bool
is simply a union of the literal values true
and false
. You can use logical operators with boolean values.
Representation
Because the various atom types differ solely by semantic meaning, there can be overlap in their CLVM representation. This is important to know when you need to differentiate between different values at runtime.
For example, the values nil
, false
, 0
, ""
, and []
all have the same runtime representation (even though each of them are semantically different and would require a cast to one another).
Value Types
In most languages, the type of literal values is general enough to cover all other values. For example, 42
would have the type Int
. This is for a few reasons:
- It allows for reassignment to variables whose type is inferred
- It allows multiple branches of conditional expressions to have the same type
- It prevents the type of object fields from being too strict
However, in Rue it's not possible to reassign to a variable, and we can use union types to alleviate some of the frustration that would be caused by a type being too specific.
Thus, every literal value has a corresponding type - for example, 42
has the type 42
. This can be surprising in some situations, but it's also important to make the language work.
It allows for more efficient type checking, disambiguation of values at runtime, and preventing certain logic errors at compile time.