identityvm

package
v1.7.39 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: BSD-3-Clause Imports: 26 Imported by: 0

Documentation

Overview

Package identityvm implements the I-Chain: decentralized identifiers, the issuers the chain trusts, the credentials they issue, and the revocations that withdraw them.

Index

Constants

View Source
const (
	CredentialActive  = "active"
	CredentialRevoked = "revoked"
	CredentialExpired = "expired"
)

Credential states. A credential's state is DERIVED from the revocation set and its own expiry; it is not a field that has to be kept in step with both.

View Source
const (
	Name = "identityvm"

	// MaxPendingChanges bounds the queue of changes waiting for a block.
	// Submitting is open to anyone who can pay, so without a bound the queue is
	// whatever they choose to make it.
	MaxPendingChanges = 4096
)
View Source
const KeyMode = mldsa.MLDSA65

Keys on this chain are ML-DSA-65. An identity outlives the credentials issued against it, and a signature that a quantum computer can forge is a signature over an identity someone else then controls — so the classical choice is the one that expires.

Variables

View Source
var VMID = ids.ID{'i', 'd', 'e', 'n', 't', 'i', 't', 'y', 'v', 'm'}

VMID is the unique identifier for IdentityVM (I-Chain)

Functions

This section is empty.

Types

type Block

type Block struct {
	ParentID_      ids.ID        `json:"parentId"`
	BlockHeight    uint64        `json:"height"`
	BlockTimestamp int64         `json:"timestamp"`
	Identities     []*Identity   `json:"identities,omitempty"`
	Issuers        []*Issuer     `json:"issuers,omitempty"`
	Credentials    []*Credential `json:"credentials,omitempty"`
	Revocations    []*Revocation `json:"revocations,omitempty"`

	// Cached values
	ID_ ids.ID
	// contains filtered or unexported fields
}

Block carries every state change this chain makes.

It used to carry Identities and Revocations that Verify never looked at and Publish applied anyway — so a peer's block naming {victim's id, attacker's key} took over the victim's DID, and one naming any credential revoked it. Nothing else produced those two lists, and Write persisted neither, so the takeover was invisible on disk and survived until restart.

Now every list is produced by this chain, verified here, written here and published here — and identity, issuer and revocation state reaches consensus at all, which it did not when the RPC wrote it straight to the base database on whichever node received the call.

func (*Block) Accept

func (b *Block) Accept(ctx context.Context) error

Accept applies the block. The store commits everything below in one batch, so a record that cannot be written takes the whole block with it rather than leaving the chain holding half of one.

It also decides, under that same lock, whether this block still extends the tip — which is why nothing is asked here. Asking here read the tip, released it, and only then asked for the lock, so a tip that moved in between was answered with a reading taken before it moved.

func (*Block) Bytes

func (b *Block) Bytes() []byte

Bytes returns the block's canonical encoding, computed once.

func (*Block) Height

func (b *Block) Height() uint64

Height returns the block height

func (*Block) ID

func (b *Block) ID() ids.ID

ID returns the block ID

func (*Block) Marshal added in v1.7.6

func (b *Block) Marshal() []byte

Marshal encodes the block. It cannot fail — every field is a value this package produced — so it does not claim it can.

func (*Block) Parent

func (b *Block) Parent() ids.ID

Parent is an alias for ParentID for compatibility

func (*Block) ParentID

func (b *Block) ParentID() ids.ID

ParentID returns the parent block ID

func (*Block) Publish added in v1.7.35

func (b *Block) Publish()

Publish makes the block's effects visible in memory. It runs after the commit, so nothing here can be believed and then lost.

func (*Block) Reject

func (b *Block) Reject(ctx context.Context) error

Reject discards the block. It wrote nothing, so there is nothing to undo; what it carried stays queued for a later block.

func (*Block) Status

func (b *Block) Status() uint8

Status returns the block status

func (*Block) Timestamp

func (b *Block) Timestamp() time.Time

Timestamp returns the block timestamp

func (*Block) Verify

func (b *Block) Verify(ctx context.Context) error

Verify checks the block and every record in it.

Records are checked against the state the chain holds PLUS what this block introduces before them, so a credential may name an identity the same block creates, and a second record claiming the same id is refused whichever half of the pair it is.

func (*Block) Write added in v1.7.35

func (b *Block) Write(db database.Database) error

Write records every change the block makes. All four kinds land here: the identities and revocations used to be applied in memory only, so a restart lost them and the node came back disagreeing with the block it had accepted.

type Change added in v1.7.39

type Change struct {
	Identity   *Identity
	Issuer     *Issuer
	Credential *Credential
	Revocation *Revocation
}

Change is one state change waiting for a block: exactly one of the four.

type Config

type Config struct {
	// CredentialTTL is how long a credential lasts when its issuer names no
	// lifetime, in seconds.
	CredentialTTL int64 `json:"credentialTTL"`

	// MaxClaims bounds one credential's claims.
	MaxClaims int `json:"maxClaims"`

	// MaxRecordsPerBlock bounds a block from either direction: what a proposer
	// assembles and what Verify accepts off the wire.
	MaxRecordsPerBlock int `json:"maxRecordsPerBlock"`

	// TrustedIssuers names the issuers this chain admits, by id. An empty list
	// admits any issuer that proves it holds its key. It used to be loaded and
	// never read, which is an allowlist that allows everything.
	TrustedIssuers []ids.ID `json:"trustedIssuers"`

	// AllowSelfIssue lets a credential name an issuer this chain has no record
	// of, provided the signature is by the subject's own key: an identity
	// making a claim about itself.
	AllowSelfIssue bool `json:"allowSelfIssue"`
}

Config holds IdentityVM configuration. Every field here is read.

type Credential

type Credential struct {
	ID             ids.ID                 `json:"id"`
	Type           []string               `json:"type"`
	Issuer         ids.ID                 `json:"issuer"`
	Subject        ids.ID                 `json:"subject"`
	IssuanceDate   time.Time              `json:"issuanceDate"`
	ExpirationDate time.Time              `json:"expirationDate"`
	Claims         map[string]interface{} `json:"claims"`

	// Signature is by the ISSUER's key over signable(). Without it, issuing is
	// naming an issuer, which anyone can do.
	Signature []byte `json:"signature"`
}

Credential is a verifiable claim an issuer makes about a subject.

It carries no Status: whether a credential is revoked is what the revocation set says, and whether it is expired is what its expiry and the clock say. A stored status is a third answer that has to be kept in step with both, and keeping it in step is what had GetCredential writing under a read lock.

type CredentialReply

type CredentialReply struct {
	ID         string                 `json:"id"`
	Type       []string               `json:"type,omitempty"`
	Issuer     string                 `json:"issuer"`
	Subject    string                 `json:"subject"`
	Issuance   string                 `json:"issuance"`
	Expiration string                 `json:"expiration"`
	Claims     map[string]interface{} `json:"claims,omitempty"`
	Status     string                 `json:"status"`
}

CredentialReply represents a credential in RPC responses.

type EmptyArgs added in v1.7.39

type EmptyArgs struct{}

EmptyArgs is a call that names nothing.

type Factory

type Factory = chain.Factory[VM]

Factory creates new IdentityVM instances.

type Genesis

type Genesis struct {
	Timestamp  int64       `json:"timestamp"`
	Config     *Config     `json:"config,omitempty"`
	Issuers    []*Issuer   `json:"issuers,omitempty"`
	Identities []*Identity `json:"identities,omitempty"`
	Message    string      `json:"message,omitempty"`
}

Genesis represents genesis data for IdentityVM

func ParseGenesis

func ParseGenesis(genesisBytes []byte) (*Genesis, error)

ParseGenesis parses genesis bytes. A genesis that names no timestamp is stamped 0, not "now": the timestamp is hashed into the genesis block id, so reading the wall clock here gave every node a different genesis id — a different chain — for the same genesis file, and a different one again after each restart.

type HealthReply

type HealthReply struct {
	Healthy bool              `json:"healthy"`
	Details map[string]string `json:"details"`
}

HealthReply reports what the chain holds.

type IDArgs added in v1.7.39

type IDArgs struct {
	ID string `json:"id"`
}

IDArgs names a record by id.

type Identity

type Identity struct {
	ID        ids.ID            `json:"id"`
	PublicKey []byte            `json:"publicKey"`
	Created   time.Time         `json:"created"`
	Metadata  map[string]string `json:"metadata,omitempty"`

	// Signature is by PublicKey over signable(), so registering a key means
	// holding it.
	Signature []byte `json:"signature"`
}

Identity is a decentralized identifier: a public key, and what the chain records about it.

It carries no Controllers, no Services and no Updated: nothing wrote them, and this chain has no update path for a DID document, so a field that can only ever be empty is not a field. Adding one means adding the update transaction that fills it, verified like everything else here.

func (*Identity) DID

func (i *Identity) DID() string

DID is the identifier this identity answers to, derived from its id.

type IdentityReply

type IdentityReply struct {
	ID        string            `json:"id"`
	DID       string            `json:"did"`
	PublicKey string            `json:"publicKey"`
	Created   string            `json:"created"`
	Metadata  map[string]string `json:"metadata,omitempty"`
}

IdentityReply represents an identity in RPC responses.

type Issuer

type Issuer struct {
	ID         ids.ID    `json:"id"`
	Name       string    `json:"name"`
	PublicKey  []byte    `json:"publicKey"`
	Types      []string  `json:"types"`
	TrustLevel int       `json:"trustLevel"`
	CreatedAt  time.Time `json:"createdAt"`

	Signature []byte `json:"signature"`
}

Issuer is a party the chain lets issue credentials.

type IssuerReply

type IssuerReply struct {
	ID         string   `json:"id"`
	Name       string   `json:"name"`
	PublicKey  string   `json:"publicKey"`
	Types      []string `json:"types,omitempty"`
	TrustLevel int      `json:"trustLevel"`
	CreatedAt  string   `json:"createdAt"`
}

IssuerReply represents an issuer in RPC responses.

type ListIssuersReply

type ListIssuersReply struct {
	Issuers []IssuerReply `json:"issuers"`
}

ListIssuersReply carries every issuer the chain holds, in id order.

type ProofArgs added in v1.7.39

type ProofArgs struct {
	ID         string `json:"id"`
	Disclosure string `json:"disclosure"` // base64
}

ProofArgs asks for a selective-disclosure artifact.

type ProofReply added in v1.7.39

type ProofReply struct {
	CredentialID     string `json:"credentialId"`
	IssuerDID        string `json:"issuerDid"`
	SubjectDID       string `json:"subjectDid"`
	CredType         string `json:"credType,omitempty"`
	ClaimsCommitment string `json:"claimsCommitment"`
	IssuedAt         string `json:"issuedAt"`
	ExpiresAt        string `json:"expiresAt"`
}

ProofReply carries the artifact.

type ResolveArgs added in v1.7.39

type ResolveArgs struct {
	DID string `json:"did"`
}

ResolveArgs names an identity by DID.

type Revocation added in v1.7.39

type Revocation struct {
	CredentialID ids.ID    `json:"credentialId"`
	RevokedBy    ids.ID    `json:"revokedBy"`
	RevokedAt    time.Time `json:"revokedAt"`
	Reason       string    `json:"reason,omitempty"`

	// Signature is by RevokedBy's key over signable(). RevokedBy used to be a
	// bare id compared against the credential's issuer and subject, both of
	// which GetCredential publishes — so revoking someone's credential was
	// pasting their id into the request.
	Signature []byte `json:"signature"`
}

Revocation withdraws a credential. One credential has at most one.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides RPC access to the IdentityVM.

A mutating call SUBMITS a signed record. It does not create one: the caller holds the key, so the caller signs, and the chain checks. The service used to build the record itself from a public key the caller named — which is why registering an issuer, and revoking anyone's credential, took nothing but the request.

func (*Service) CreateProof

func (s *Service) CreateProof(r *http.Request, args *ProofArgs, reply *ProofReply) error

CreateProof builds a selective-disclosure artifact for a credential.

func (*Service) GetCredential

func (s *Service) GetCredential(r *http.Request, args *IDArgs, reply *CredentialReply) error

GetCredential returns a credential and its status.

func (*Service) GetIdentity

func (s *Service) GetIdentity(r *http.Request, args *IDArgs, reply *IdentityReply) error

GetIdentity returns an identity by id.

func (*Service) GetIssuer

func (s *Service) GetIssuer(r *http.Request, args *IDArgs, reply *IssuerReply) error

GetIssuer returns an issuer by id.

func (*Service) Health

func (s *Service) Health(r *http.Request, args *EmptyArgs, reply *HealthReply) error

Health reports what the chain holds, which is always an answer.

func (*Service) ListIssuers

func (s *Service) ListIssuers(r *http.Request, args *EmptyArgs, reply *ListIssuersReply) error

ListIssuers returns every issuer, in id order.

func (*Service) ResolveIdentity

func (s *Service) ResolveIdentity(r *http.Request, args *ResolveArgs, reply *IdentityReply) error

ResolveIdentity returns the identity a DID names.

func (*Service) SubmitCredential added in v1.7.39

func (s *Service) SubmitCredential(r *http.Request, args *SubmitCredentialArgs, reply *SubmitReply) error

SubmitCredential queues a new credential.

func (*Service) SubmitIdentity added in v1.7.39

func (s *Service) SubmitIdentity(r *http.Request, args *SubmitIdentityArgs, reply *SubmitReply) error

SubmitIdentity queues a new identity.

func (*Service) SubmitIssuer added in v1.7.39

func (s *Service) SubmitIssuer(r *http.Request, args *SubmitIssuerArgs, reply *SubmitReply) error

SubmitIssuer queues a new issuer.

func (*Service) SubmitRevocation added in v1.7.39

func (s *Service) SubmitRevocation(r *http.Request, args *SubmitRevocationArgs, reply *SubmitReply) error

SubmitRevocation queues a revocation.

func (*Service) VerifyCredential

func (s *Service) VerifyCredential(r *http.Request, args *IDArgs, reply *VerifyReply) error

VerifyCredential reports whether a credential is recorded, unrevoked and unexpired. A refusal is an ANSWER, not an error: the caller asked a question and "no, because it is revoked" is the answer to it.

type SubmitCredentialArgs added in v1.7.39

type SubmitCredentialArgs struct {
	Type       []string               `json:"type,omitempty"`
	Issuer     string                 `json:"issuer"`
	Subject    string                 `json:"subject"`
	Issuance   int64                  `json:"issuance"`   // UnixNano
	Expiration int64                  `json:"expiration"` // UnixNano; 0 means the chain's default
	Claims     map[string]interface{} `json:"claims,omitempty"`
	Signature  string                 `json:"signature"` // base64
	Fee        uint64                 `json:"fee"`
}

SubmitCredentialArgs issues a credential, signed by its issuer.

type SubmitIdentityArgs added in v1.7.39

type SubmitIdentityArgs struct {
	PublicKey string            `json:"publicKey"` // base64
	Signature string            `json:"signature"` // base64
	Created   int64             `json:"created"`   // UnixNano
	Metadata  map[string]string `json:"metadata,omitempty"`
	Fee       uint64            `json:"fee"`
}

SubmitIdentityArgs registers a DID: a public key and a signature by it.

type SubmitIssuerArgs added in v1.7.39

type SubmitIssuerArgs struct {
	Name       string   `json:"name"`
	PublicKey  string   `json:"publicKey"` // base64
	Signature  string   `json:"signature"` // base64
	Types      []string `json:"types,omitempty"`
	TrustLevel int      `json:"trustLevel"`
	CreatedAt  int64    `json:"createdAt"` // UnixNano
	Fee        uint64   `json:"fee"`
}

SubmitIssuerArgs registers an issuer.

type SubmitReply added in v1.7.39

type SubmitReply struct {
	ID string `json:"id"`
}

SubmitReply names what was accepted into the queue.

type SubmitRevocationArgs added in v1.7.39

type SubmitRevocationArgs struct {
	CredentialID string `json:"credentialId"`
	RevokedBy    string `json:"revokedBy"`
	RevokedAt    int64  `json:"revokedAt"` // UnixNano
	Reason       string `json:"reason,omitempty"`
	Signature    string `json:"signature"` // base64
	Fee          uint64 `json:"fee"`
}

SubmitRevocationArgs withdraws a credential, signed by its issuer or its subject.

type VM

type VM struct {
	// contains filtered or unexported fields
}

VM implements the IdentityVM for decentralized identity

func (*VM) BuildBlock

func (vm *VM) BuildBlock(ctx context.Context) (vmchain.Block, error)

BuildBlock implements chain.ChainVM. Reading the tip and registering the block on it happen in one step, so nothing can be accepted in between and leave the proposal hanging off a parent that is no longer the tip.

func (*VM) Connected

func (vm *VM) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion *vmchain.VersionInfo) error

Connected implements chain.ChainVM

func (*VM) CreateHandlers

func (vm *VM) CreateHandlers(ctx context.Context) (map[string]http.Handler, error)

CreateHandlers implements chain.ChainVM

func (*VM) Credential added in v1.7.39

func (vm *VM) Credential(id ids.ID) (*Credential, string, error)

Credential returns a credential and its status as of now.

func (*VM) CrossChainRequest added in v1.7.39

func (vm *VM) CrossChainRequest(ctx context.Context, chainID ids.ID, requestID uint32, deadline time.Time, request []byte) error

func (*VM) CrossChainRequestFailed added in v1.7.39

func (vm *VM) CrossChainRequestFailed(ctx context.Context, chainID ids.ID, requestID uint32, appErr *warp.Error) error

func (*VM) CrossChainResponse added in v1.7.39

func (vm *VM) CrossChainResponse(ctx context.Context, chainID ids.ID, requestID uint32, response []byte) error

func (*VM) Disconnected

func (vm *VM) Disconnected(ctx context.Context, nodeID ids.NodeID) error

Disconnected implements chain.ChainVM

func (*VM) FeePolicy added in v1.2.6

func (vm *VM) FeePolicy() fee.Policy

FeePolicy exposes the chain's declared fee policy for diagnostics and the boot-time Validate gate.

func (*VM) GetBlock

func (vm *VM) GetBlock(ctx context.Context, blockID ids.ID) (vmchain.Block, error)

GetBlock implements chain.ChainVM

func (*VM) GetBlockIDAtHeight

func (vm *VM) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error)

GetBlockIDAtHeight answers from the height index the store writes in the same commit as the block itself, so the index can never name a block the chain did not accept.

func (*VM) Gossip added in v1.7.39

func (vm *VM) Gossip(ctx context.Context, nodeID ids.NodeID, msg []byte) error

func (*VM) HealthCheck

func (vm *VM) HealthCheck(ctx context.Context) (vmchain.HealthResult, error)

HealthCheck implements chain.ChainVM

func (*VM) Identity added in v1.7.39

func (vm *VM) Identity(id ids.ID) (*Identity, error)

Identity returns an identity by id.

func (*VM) Initialize

func (vm *VM) Initialize(ctx context.Context, vmInit vmcore.Init) error

Initialize implements chain.ChainVM

func (*VM) Issuer added in v1.7.39

func (vm *VM) Issuer(id ids.ID) (*Issuer, error)

Issuer returns an issuer by id.

func (*VM) Issuers added in v1.7.39

func (vm *VM) Issuers() []*Issuer

Issuers returns every issuer the chain holds, in id order. Map order is not an order, and an RPC that answers differently each call is one a client cannot page through.

func (*VM) LastAccepted

func (vm *VM) LastAccepted(ctx context.Context) (ids.ID, error)

LastAccepted implements chain.ChainVM

func (*VM) NewHTTPHandler

func (vm *VM) NewHTTPHandler(ctx context.Context) (http.Handler, error)

NewHTTPHandler mounts the same route by path.

func (*VM) ParseBlock

func (vm *VM) ParseBlock(ctx context.Context, blockBytes []byte) (vmchain.Block, error)

ParseBlock implements chain.ChainVM.

func (*VM) Proof added in v1.7.39

func (vm *VM) Proof(id ids.ID, disclosure []byte) (*artifacts.CredentialProof, error)

Proof builds a selective-disclosure artifact for a credential. It presents what the chain holds; the disclosure proof itself is the caller's.

func (*VM) Request added in v1.7.39

func (vm *VM) Request(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error

Request implements the app protocol, of which this chain has none.

func (*VM) RequestFailed added in v1.7.39

func (vm *VM) RequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *warp.Error) error

func (*VM) Resolve added in v1.7.39

func (vm *VM) Resolve(did string) (*Identity, error)

Resolve returns the identity a DID names. The DID is derived from the id, so this is a lookup rather than the scan of every identity it used to be — which returned whichever of two identical DIDs Go's map iteration reached first.

func (*VM) Response added in v1.7.39

func (vm *VM) Response(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error

func (*VM) SetPreference

func (vm *VM) SetPreference(ctx context.Context, blockID ids.ID) error

SetPreference records the block the engine wants the next one built on. Dropping it meant Propose always built on the accepted tip, so a node with two blocks in flight re-proposed a height it had already proposed.

func (*VM) SetState

func (vm *VM) SetState(ctx context.Context, state uint32) error

SetState implements chain.ChainVM

func (*VM) Shutdown

func (vm *VM) Shutdown(ctx context.Context) error

Shutdown implements chain.ChainVM

func (*VM) Submit added in v1.7.39

func (vm *VM) Submit(c *Change) error

Submit queues one state change for a block. Nothing here writes state: a change becomes state when a block carrying it is accepted, on every node, which is what "the chain agrees" means. CreateIdentity, RegisterIssuer and RevokeCredential used to write straight to the base database on whichever node received the call, so no two nodes held the same identity set — and a block naming an issuer verified on the node that registered it and nowhere else.

func (*VM) Verify added in v1.7.39

func (vm *VM) Verify(id ids.ID) error

Verify reports whether a credential is usable now: recorded, not revoked, not expired.

It used to also accept a "ZK proof" whose only test was that it was not empty, which is a length check standing in for a verdict. What makes a credential this chain's is the issuer's signature over it, and check() refuses a block carrying one without it.

func (*VM) Version

func (vm *VM) Version(ctx context.Context) (string, error)

Version implements chain.ChainVM

func (*VM) WaitForEvent

func (vm *VM) WaitForEvent(ctx context.Context) (vmcore.Message, error)

WaitForEvent blocks until there is a change to build a block from, or the VM stops. Waiting only on the context would mean BuildBlock is never called and the chain never leaves genesis, however much is submitted.

type VerifyReply added in v1.7.39

type VerifyReply struct {
	Valid  bool   `json:"valid"`
	Reason string `json:"reason,omitempty"`
}

VerifyReply reports whether a credential is usable now.

Directories

Path Synopsis
cmd
plugin command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL