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
- Variables
- type Block
- func (b *Block) Accept(ctx context.Context) error
- func (b *Block) Bytes() []byte
- func (b *Block) Height() uint64
- func (b *Block) ID() ids.ID
- func (b *Block) Marshal() []byte
- func (b *Block) Parent() ids.ID
- func (b *Block) ParentID() ids.ID
- func (b *Block) Publish()
- func (b *Block) Reject(ctx context.Context) error
- func (b *Block) Status() uint8
- func (b *Block) Timestamp() time.Time
- func (b *Block) Verify(ctx context.Context) error
- func (b *Block) Write(db database.Database) error
- type Change
- type Config
- type Credential
- type CredentialReply
- type EmptyArgs
- type Factory
- type Genesis
- type HealthReply
- type IDArgs
- type Identity
- type IdentityReply
- type Issuer
- type IssuerReply
- type ListIssuersReply
- type ProofArgs
- type ProofReply
- type ResolveArgs
- type Revocation
- type Service
- func (s *Service) CreateProof(r *http.Request, args *ProofArgs, reply *ProofReply) error
- func (s *Service) GetCredential(r *http.Request, args *IDArgs, reply *CredentialReply) error
- func (s *Service) GetIdentity(r *http.Request, args *IDArgs, reply *IdentityReply) error
- func (s *Service) GetIssuer(r *http.Request, args *IDArgs, reply *IssuerReply) error
- func (s *Service) Health(r *http.Request, args *EmptyArgs, reply *HealthReply) error
- func (s *Service) ListIssuers(r *http.Request, args *EmptyArgs, reply *ListIssuersReply) error
- func (s *Service) ResolveIdentity(r *http.Request, args *ResolveArgs, reply *IdentityReply) error
- func (s *Service) SubmitCredential(r *http.Request, args *SubmitCredentialArgs, reply *SubmitReply) error
- func (s *Service) SubmitIdentity(r *http.Request, args *SubmitIdentityArgs, reply *SubmitReply) error
- func (s *Service) SubmitIssuer(r *http.Request, args *SubmitIssuerArgs, reply *SubmitReply) error
- func (s *Service) SubmitRevocation(r *http.Request, args *SubmitRevocationArgs, reply *SubmitReply) error
- func (s *Service) VerifyCredential(r *http.Request, args *IDArgs, reply *VerifyReply) error
- type SubmitCredentialArgs
- type SubmitIdentityArgs
- type SubmitIssuerArgs
- type SubmitReply
- type SubmitRevocationArgs
- type VM
- func (vm *VM) BuildBlock(ctx context.Context) (vmchain.Block, error)
- func (vm *VM) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion *vmchain.VersionInfo) error
- func (vm *VM) CreateHandlers(ctx context.Context) (map[string]http.Handler, error)
- func (vm *VM) Credential(id ids.ID) (*Credential, string, error)
- func (vm *VM) CrossChainRequest(ctx context.Context, chainID ids.ID, requestID uint32, deadline time.Time, ...) error
- func (vm *VM) CrossChainRequestFailed(ctx context.Context, chainID ids.ID, requestID uint32, appErr *warp.Error) error
- func (vm *VM) CrossChainResponse(ctx context.Context, chainID ids.ID, requestID uint32, response []byte) error
- func (vm *VM) Disconnected(ctx context.Context, nodeID ids.NodeID) error
- func (vm *VM) FeePolicy() fee.Policy
- func (vm *VM) GetBlock(ctx context.Context, blockID ids.ID) (vmchain.Block, error)
- func (vm *VM) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error)
- func (vm *VM) Gossip(ctx context.Context, nodeID ids.NodeID, msg []byte) error
- func (vm *VM) HealthCheck(ctx context.Context) (vmchain.HealthResult, error)
- func (vm *VM) Identity(id ids.ID) (*Identity, error)
- func (vm *VM) Initialize(ctx context.Context, vmInit vmcore.Init) error
- func (vm *VM) Issuer(id ids.ID) (*Issuer, error)
- func (vm *VM) Issuers() []*Issuer
- func (vm *VM) LastAccepted(ctx context.Context) (ids.ID, error)
- func (vm *VM) NewHTTPHandler(ctx context.Context) (http.Handler, error)
- func (vm *VM) ParseBlock(ctx context.Context, blockBytes []byte) (vmchain.Block, error)
- func (vm *VM) Proof(id ids.ID, disclosure []byte) (*artifacts.CredentialProof, error)
- func (vm *VM) Request(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, ...) error
- func (vm *VM) RequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *warp.Error) error
- func (vm *VM) Resolve(did string) (*Identity, error)
- func (vm *VM) Response(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error
- func (vm *VM) SetPreference(ctx context.Context, blockID ids.ID) error
- func (vm *VM) SetState(ctx context.Context, state uint32) error
- func (vm *VM) Shutdown(ctx context.Context) error
- func (vm *VM) Submit(c *Change) error
- func (vm *VM) Verify(id ids.ID) error
- func (vm *VM) Version(ctx context.Context) (string, error)
- func (vm *VM) WaitForEvent(ctx context.Context) (vmcore.Message, error)
- type VerifyReply
Constants ¶
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.
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 )
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 ¶
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 ¶
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) Marshal ¶ added in v1.7.6
Marshal encodes the block. It cannot fail — every field is a value this package produced — so it does not claim it can.
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 ¶
Reject discards the block. It wrote nothing, so there is nothing to undo; what it carried stays queued for a later block.
func (*Block) Verify ¶
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.
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 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 ¶
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.
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 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 ¶
CreateProof builds a selective-disclosure artifact for a credential.
func (*Service) GetCredential ¶
GetCredential returns a credential and its status.
func (*Service) GetIdentity ¶
GetIdentity returns an identity by id.
func (*Service) ListIssuers ¶
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 ¶
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 ¶
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 ¶
CreateHandlers implements chain.ChainVM
func (*VM) Credential ¶ added in v1.7.39
Credential returns a credential and its status as of now.
func (*VM) CrossChainRequest ¶ added in v1.7.39
func (*VM) CrossChainRequestFailed ¶ added in v1.7.39
func (*VM) CrossChainResponse ¶ added in v1.7.39
func (*VM) Disconnected ¶
Disconnected implements chain.ChainVM
func (*VM) FeePolicy ¶ added in v1.2.6
FeePolicy exposes the chain's declared fee policy for diagnostics and the boot-time Validate gate.
func (*VM) GetBlockIDAtHeight ¶
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) HealthCheck ¶
HealthCheck implements chain.ChainVM
func (*VM) Initialize ¶
Initialize implements chain.ChainVM
func (*VM) Issuers ¶ added in v1.7.39
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 ¶
LastAccepted implements chain.ChainVM
func (*VM) NewHTTPHandler ¶
NewHTTPHandler mounts the same route by path.
func (*VM) ParseBlock ¶
ParseBlock implements chain.ChainVM.
func (*VM) Proof ¶ added in v1.7.39
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) Resolve ¶ added in v1.7.39
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) SetPreference ¶
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) Submit ¶ added in v1.7.39
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
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) WaitForEvent ¶
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
VerifyReply reports whether a credential is usable now.