← Book map

Chapter 12 · Platform

The chain's own vocabulary

Identities, content multimaps, destinations and signatures — with the wire formats, version constants and flag tables you need to read an identity byte by byte.

The pbaas vocabulary An identity extends a principal and is composed of key ids, parent and system identity ids, a content multimap, and revocation and recovery authorities. Destinations and signatures are shared primitives. Identity extends Principal KeyID[] primaryaddresses IdentityID parent · system ContentMultiMap KvMap<array> authorities revocation · recovery SHARED PRIMITIVES TransferDestination · NoDestination · SaplingPaymentAddress · SignatureData · DataDescriptor · MMR An identity is not a blob — it is these fields, each with its own serialiser.

FIGURE 12.1 · Identity and the objects it is made of.

Principal — the base identity body

Principal is what an identity shares with any multi-signature authority: a version, a flags word, a list of primary addresses, and a minimum signature count. Its serialiser is short enough to memorise, and it is the template every Identity read follows:

toBuffer():
  if containsVersion()          writeUInt32(version)     // 4 bytes LE
  if containsFlags()            writeUInt32(flags)       // 4 bytes LE
  if containsPrimaryAddresses() writeVector(addresses)   // compactSize count, then var-slice each
  if containsMinSigs()          writeUInt32(minSigs)     // 4 bytes LE

The containsX() hooks are protected methods that subclasses override. That is the whole inheritance trick: PartialIdentity turns some of them off and serialises a contains bitmask instead of assuming every field is present.

Identity — versions and flags

ConstantValueMeaning
VERSION_VERUSID1The original VerusID body
VERSION_VAULT2Vault form
VERSION_PBAAS3Current — system-aware identity

Flags are hex constants on the identity body itself:

FlagHexEffect
IDENTITY_FLAG_REVOKED0x8000Identity is revoked
IDENTITY_FLAG_LOCKED0x2Identity is locked against updates
IDENTITY_FLAG_ACTIVECURRENCY0x1Identity is in use as a currency name
IDENTITY_FLAG_TOKENIZED_CONTROL0x4Revocation/recovery can be performed by whoever controls the token

The rest of Identity is nine fields, in serialisation order: parent, systemId, name, contentMap, contentMultiMap, revocationAuthority, recoveryAuthority, privateAddresses, unlockAfter. Note the change in this revision: snake_case property names now throw with a rename hint rather than silently serialising zeros.

ContentMultiMap — the wire format worth knowing

This is where identity data actually lives, and its layout is simple enough to debug by hand:

compactSize(keyCount)
  repeat keyCount:
    20 bytes        key hash160 (i-address payload)
    compactSize(valueCount)
      repeat valueCount:
        varSlice     either raw bytes, or a VdxfUniValue blob with its own type tag

Two rules sit on top of that layout. KvMap stores keys by their serialised form, so a TYPE_FQN key and a TYPE_I_ADDRESS key that resolve to the same i-address are considered a collision and set() throws. And PartialIdentity upgrades any plain ContentMultiMap to FqnContentMultiMap on construction, so FQN keys survive a binary round-trip instead of collapsing to addresses.

PartialIdentity — an update is a bitmask

Instead of "here is a whole identity", a partial identity says which fields participate:

BitConstantField carried
1PARTIAL_ID_CONTAINS_PARENTparent
2...CONTENT_MULTIMAPcontent multimap
4...PRIMARY_ADDRSprimary addresses
8...REVOCATIONrevocation authority
16...RECOVERYrecovery authority
32...UNLOCK_AFTERunlock height
64...SYSTEM_IDsystem
128...PRIV_ADDRSprivate addresses
256...CONTENT_MAPlegacy content map hashes
512...MINSIGSminimum signatures
1024...FLAGSidentity flags
2048...VERSIONversion

This is why an identity update can change one field without restating the others — and why a mismatched bitmask is the classic cause of an update that "applies" but changes nothing.

TransferDestination — type plus flags in one byte

Types 0–11: DEST_INVALID, PK, PKH, SH, ID, FULLID, REGISTERCURRENCY, QUANTUM, NESTEDTRANSFER, ETH, ETHNFT, RAW. Above LAST_VALID_TYPE_NO_FLAGS the same byte also carries FLAG_RESERVED1 (16), FLAG_RESERVED2 (32), FLAG_DEST_AUX (64) and FLAG_DEST_GATEWAY (128); typeNoFlags() masks them off.

writeUInt8(type)                 // type | flags
writeVarSlice(destinationBytes)
if gateway:  gatewayID(20) · gatewayCode(20) · fees(int64)
if aux:      compactSize(n) · varSlice(each aux dest)

getAddressString() only supports PKH, ID and ETH — anything else throws, which is a useful signal that you are holding a destination that needs a gateway rather than an address.

SignatureData and DataDescriptor

SignatureData is the DER container with its own context: version, systemID, hashType, signatureHash, identityID, sigType, optional vdxfKeys, vdxfKeyNames, boundHashes, and signatureAsVch. It is what VerifiableSignatureData wraps — read it when a signature parses but fails verification.

DataDescriptor describes stored data with an eight-bit presence mask:

1 encrypted data · 2 salt · 4 encryption public key · 8 incoming viewing key
0x10 symmetric key · 0x20 label · 0x40 mime type · 0x80 vdxf key

VdxfUniValue — the "I don't know the type" container

Content multimap values can be any of: raw buffer, string, CurrencyValueMap, Rating, TransferDestination, ContentMultiMapRemove, CrossChainDataRef, SignatureData, DataDescriptor, MMRDescriptor, URLRef, IdentityMultimapRef, Credential. VdxfUniValue is the union that can hold any of them, with JSON escape hatches (serializedhex, serializedbase64, message) for payloads it does not model.

MMR — proofs, not data

MMR.ts holds MerkleMountainRange, MMRLayer, MMRNode, MMRBranch, MMRProof and MerkleMountainView. You need them when verifying that an identity's data at some height matches a commitment — the proof structure, not the payload.

Reading order inside pbaas/: IdentityID → KeyID → Principal → PartialIdentity → ContentMultiMap → TransferDestination → SignatureData → DataDescriptor → VdxfUniValue → MMR.

Reading the code

src/pbaas/
├── Principal.ts           # version, flags, addresses, minSigs — the serialisation template
├── Identity.ts            # nine more fields + identity flags
├── PartialIdentity.ts     # bitmask of participating fields
├── IdentityID.ts / KeyID.ts
├── ContentMultiMap.ts     # count · 20-byte keys · arrays of var slices
├── VdxfUniValue.ts        # the 13-type union + JSON escape hatches
├── TransferDestination.ts # 12 types, 4 high-bit flags, gateway extension
├── NoDestination.ts / SaplingPaymentAddress.ts
├── SignatureData.ts       # DER + context
├── DataDescriptor.ts      # 8-bit presence mask
├── MMR.ts / MMRDescriptor.ts / CrossChainProof.ts
└── transaction/*.ts