The contract API¶
Every method a contract author can call, grouped by what it is for. Names and signatures are those of @activeledger/activecontracts.
Base classes¶
Your contract is the export default class of its file and extends one of these.
| Class | Adds | Use it when |
|---|---|---|
Standard |
The three steps and everything below | Most contracts |
Event |
this.event for raising events |
You want to raise events on commit |
PostProcess |
An extra postProcess step after commit |
You need work done once the data is saved |
PostProcessEvent |
Both of the above | You need both |
The steps you write¶
All return a promise. Resolve to continue, reject to refuse. A rejected string, or a thrown Error, becomes the reason the caller sees in $summary.errors of the reply.
| Method | Required | Called when |
|---|---|---|
verify(signatureless: boolean): Promise<boolean> |
No | First, on every node. signatureless is true for a self-signed transaction. Without a verify, the transaction passes this step. |
vote(): Promise<boolean> |
Yes | After verify, when the transaction has $i inputs. Rejecting makes this node vote no. |
commit(possibleTerritoriality?: boolean): Promise<any> |
Yes | When enough nodes voted yes. possibleTerritoriality is true on the node named by the transaction's $territoriality, when it set one. |
reconcile(): Promise<any> |
No | When this node voted no but the network reached consensus without it. Resolve true once you have brought the streams into line. |
postProcess(territoriality: boolean, who: string): Promise<any> |
With PostProcess |
After commit. territoriality is true on the node named by the transaction's $territoriality; who is that node's reference. |
shutdown(): Promise<any> |
No | When the engine discards the contract instance after the transaction. |
For a read-only transaction, the engine calls the method named by $entry, or read, instead of vote and commit, and returns its result to the caller in $responses. Read-only means $i is left out of $tx entirely (an empty $i: {} still runs vote), and the message carries "$sigs": {}. Streams the caller lists under $r are available through getReadOnlyStream(name). If the contract has no method of that name, nothing comes back and the reply has no $responses. See Entry points for the shape of the message.
What the contract can see¶
| Member | Type | What it holds |
|---|---|---|
this.transactions |
LedgerTransaction |
The $tx of the transaction: $namespace, $contract, $entry?, $expire?, $i, $o, $r? |
this.umid |
string |
The transaction's unique id |
this.ActiveLogger |
Logging: info, debug, warn, error, trace, fatal, each taking a message or an object and a message. Also importable from the package. |
|
this.ActiveCrypto |
Hashing and key tools, for example ActiveCrypto.Hash.getHash(data, "sha256") |
In $i and $o, each key is either a stream id or a label, in which case the value carries $stream with the id. getActivityStreams understands both.
Streams in the transaction¶
Methods on the contract itself for finding and creating streams.
| Method | Returns | What it does |
|---|---|---|
getActivityStreams() |
{ [id]: Activity } |
Every input and output stream of the transaction |
getActivityStreams(stream) |
Activity |
One stream by id. You may pass the $i or $o entry itself; if it has $stream, that id is used. An id that is not in the transaction creates a new stream of that name instead. |
newActivityStream(name, deterministic?) |
Activity |
Creates a stream. Its id is the sha256 of the transaction id and name, so every node makes the same id. Pass deterministic to use that seed instead, which makes the id reproducible outside the ledger; a seed that was used before is refused at commit. |
getReadOnlyStream(name) |
any or false |
The state of a stream listed in $r under name, without _id and _rev. Marked deprecated in favour of the next method, but it is synchronous, which is what a read method needs. |
getAnyStreamReadOnly(streamId) |
Promise<IStream> |
Fetches any stream's state from the ledger, read-only |
getMofSignatures(m) |
boolean |
True when the transaction carries at least m signatures |
hasAuthorityStake(minimum, activity) |
boolean |
True when the signers of activity hold at least minimum stake between them |
getContractData<T>() |
T |
Data kept for the contract itself, across transactions |
setContractData<T>(data) |
void |
Replaces that data |
Stream names beginning activeledger.default. are reserved; creating one outside the default namespace throws.
Working with one stream¶
Activity is one stream. Everything here applies to the object returned by getActivityStreams or newActivityStream.
| Method | Returns | What it does |
|---|---|---|
getId() |
string |
The stream id (getName() is the same) |
getState() |
IState |
A copy of the stream's data, without _id and _rev |
setState(state) |
void |
Merges state into the stream's data. The write is saved at commit. A namespace field is dropped unless the contract is in the default namespace. |
hasSignature() |
boolean |
True when this transaction carries a signature for this stream |
getAuthorities() |
ILedgerAuthority[] |
The keys allowed to sign for this stream |
getAuthority(type?) |
string \| undefined |
The first authority's public key, or its key type when type is true. Deprecated. |
setAuthorities(authority \| authority[]) |
void |
Adds or renews authorities. Only on an input stream or a stream created in this transaction. Re-adding a public key replaces its entry. Refused if it would leave no authority without an expire. |
setAuthority(pubKey, type = "rsa") |
void |
Replaces all authorities with one key at stake 100. Same limits. Deprecated. |
deleteAuthorities(pubKey \| pubKey[] \| filter) |
void |
Removes authorities by public key, list of keys, or a function that returns true for each to remove. Refused if nothing would be left, or nothing permanent. |
setContractLock(script \| script[]) |
boolean |
Only the named contract(s) may write this stream. New streams only. |
setNamespaceLock(namespace \| namespace[]) |
boolean |
Only contracts in the named namespace(s) may write this stream. New streams only. |
getCng(buffer?) |
number |
A number derived from the stream's revisions. Not random; the same on every node. |
An authority (ILedgerAuthority) has public, type and stake, and optionally hash, label, metadata, umid and expire (an ISO date after which the key may no longer sign).
Volatile data¶
Each stream can hold a second document of data that is kept outside consensus.
| Method | Returns | What it does |
|---|---|---|
getVolatile() |
Promise<IVolatile> |
A copy of the stream's volatile data, fetched from this node |
setVolatile(volatile) |
void |
Merges into the volatile data. Saved at commit on this node. |
Warning
Volatile data belongs to one node only. It is never shared with other nodes and never synced. Use it for a cache or a local note, never for anything another node must also know.
Replying to the caller¶
| Method | What it does |
|---|---|
returnToRemote(data) |
Sends data back in the reply's $responses list. One entry per node that ran the contract. |
getRemoteAddr() |
The address the transaction arrived from |
findRemoteAddr(address \| address[]) |
True when the transaction arrived from one of the given addresses |
Events¶
Available on Event and PostProcessEvent contracts through this.event.
| Method | What it does |
|---|---|
this.event.emit(name, data) |
Records an event with the contract, the current step and data. Events are stored on the node and can be followed as they happen; see The HTTP endpoints. |
Time¶
| Method | What it does |
|---|---|
this.setTimeout(ms) |
Asks for ms more milliseconds before the current step is timed out. Capped by the node's contractMaxTimeout. |
Hybrids¶
A contract can also run on a hybrid node, which follows the network rather than taking part in consensus. These methods exist for that; Contracts on hybrids explains when and how to use them.
| Method | On | What it is for |
|---|---|---|
isSupernode() |
contract | True on a network node, false on a hybrid |
adoptFromSupernode(shape, options?) |
Activity |
Declares what the network nodes write to this stream, so a hybrid can accept it |
adoptStreamsFromSupernode(streams, shape, options?) |
contract | The same for a list of candidate streams |
Less common¶
| Method | What it does |
|---|---|
getInterNodeComms() |
Messages the other nodes' contract instances sent during their vote |
setThisInterNodeComms(data) |
The message this node's instance sends to the others |
clearAllInterNodeComms() |
Asks every node to clear those messages |
isExecutingOn(host) |
True when this node is host (as host:port) |
throw(location) |
Passes the transaction on to another ledger at location |
attemptDecrypt(data, safeMode = true) |
Decrypts data secured for this node's keys. With safeMode on, which is the default, the contract may no longer change state, authorities or locks, or create streams, because each node would decrypt something different. |
Using inter-node communications or isSupernode() makes the contract depend on the network nodes when it runs on a hybrid; see Contracts on hybrids.