Server Client
The server clients use Hedera operator credentials or an injected Hedera SDK client to create HCS-27 topics and publish checkpoints directly to the network.
Create a ClientDirect link to Create a Client
- TypeScript
- Go
- Python
import { Client as HederaClient } from '@hashgraph/sdk';
import { HCS27Client } from '@hashgraphonline/standards-sdk';
const hederaClient = HederaClient.forTestnet();
hederaClient.setOperator(
process.env.HEDERA_ACCOUNT_ID!,
process.env.HEDERA_PRIVATE_KEY!,
);
const client = new HCS27Client({
operatorId: process.env.HEDERA_ACCOUNT_ID!,
operatorKey: process.env.HEDERA_PRIVATE_KEY!,
network: 'testnet',
client: hederaClient,
});
import (
"log"
"os"
"github.com/hashgraph-online/standards-sdk-go/pkg/hcs27"
)
client, err := hcs27.NewClient(hcs27.ClientConfig{
OperatorAccountID: os.Getenv("HEDERA_ACCOUNT_ID"),
OperatorPrivateKey: os.Getenv("HEDERA_PRIVATE_KEY"),
Network: "testnet",
})
if err != nil {
log.Fatal(err)
}
import os
from hedera import Client as HederaClient, AccountId, PrivateKey
from standards_sdk_py.hcs27 import HCS27Client
hedera_client = HederaClient.forTestnet()
hedera_client.setOperator(
AccountId.fromString(os.environ["HEDERA_ACCOUNT_ID"]),
PrivateKey.fromString(os.environ["HEDERA_PRIVATE_KEY"]),
)
client = HCS27Client(
operator_id=os.environ["HEDERA_ACCOUNT_ID"],
operator_key=os.environ["HEDERA_PRIVATE_KEY"],
network="testnet",
hedera_client=hedera_client,
)
Create a Checkpoint TopicDirect link to Create a Checkpoint Topic
- TypeScript
- Go
- Python
const topic = await client.createCheckpointTopic({
ttl: 3600,
adminKey: true,
submitKey: true,
});
console.log(topic.topicId, topic.transactionId);
import (
"context"
"fmt"
"log"
"github.com/hashgraph-online/standards-sdk-go/pkg/hcs27"
)
topicID, transactionID, err := client.CreateCheckpointTopic(context.Background(), hcs27.CreateTopicOptions{
TTLSeconds: 3600,
UseOperatorAsAdmin: true,
UseOperatorAsSubmit: true,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(topicID, transactionID)
topic = client.create_checkpoint_topic(
{
"ttl": 3600,
"useOperatorAsAdmin": True,
"useOperatorAsSubmit": True,
}
)
print(topic["topicId"], topic["transactionId"])
Publish a CheckpointDirect link to Publish a Checkpoint
- TypeScript
- Go
- Python
await client.publishCheckpoint(
topic.topicId,
{
type: 'ans-checkpoint-v1',
stream: { registry: 'ans', log_id: 'default' },
log: {
alg: 'sha-256',
leaf: 'sha256(jcs(event))',
merkle: 'rfc9162',
},
root: {
treeSize: '2',
rootHashB64u: 'mB6Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZQ',
},
prev: {
treeSize: '1',
rootHashB64u: 'pD8Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZA',
},
},
'checkpoint 2',
);
import (
"context"
"fmt"
"log"
"github.com/hashgraph-online/standards-sdk-go/pkg/hcs27"
)
result, err := client.PublishCheckpoint(context.Background(), topicID, hcs27.CheckpointMetadata{
Type: "ans-checkpoint-v1",
Stream: hcs27.StreamID{
Registry: "ans",
LogID: "default",
},
Log: &hcs27.LogProfile{
Algorithm: "sha-256",
Leaf: "sha256(jcs(event))",
Merkle: "rfc9162",
},
Root: hcs27.RootCommitment{
TreeSize: "2",
RootHashB64u: "mB6Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZQ",
},
Previous: &hcs27.PreviousCommitment{
TreeSize: "1",
RootHashB64u: "pD8Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZA",
},
}, "checkpoint 2", "")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.SequenceNumber, result.TransactionID)
result = client.publish_checkpoint(
topic["topicId"],
{
"type": "ans-checkpoint-v1",
"stream": {"registry": "ans", "log_id": "default"},
"log": {
"alg": "sha-256",
"leaf": "sha256(jcs(event))",
"merkle": "rfc9162",
},
"root": {
"treeSize": "2",
"rootHashB64u": "mB6Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZQ",
},
"prev": {
"treeSize": "1",
"rootHashB64u": "pD8Q2Wv1hR2hRGYt3Ty1m9E0m7M1hB4wS-9DqR2xFZA",
},
},
"checkpoint 2",
)
print(result["sequenceNumber"], result["transactionId"])
Overflow Metadata Uses an Inscription ReferenceDirect link to Overflow Metadata Uses an Inscription Reference
If the serialized HCS-27 payload exceeds 1024 bytes, publishCheckpoint(...) automatically stores the metadata as an inscription and publishes the smaller pointer message instead.
- TypeScript
- Go
- Python
const result = await client.publishCheckpoint(
topic.topicId,
{
type: 'ans-checkpoint-v1',
stream: { registry: 'ans', log_id: 'overflow' },
log: {
alg: 'sha-256',
leaf: 'sha256(jcs(event))-'.repeat(90),
merkle: 'rfc9162',
},
root: {
treeSize: '1',
rootHashB64u: 'A7K9uA5RUL9Ko_BIO8l7ON3W5gQw6B6yv2nM3xABjS8',
},
},
'overflow checkpoint',
);
const checkpoints = await client.getCheckpoints(topic.topicId);
console.log(checkpoints[0]?.message.metadata); // hcs://1/<topic>
import (
"context"
"fmt"
"log"
"strings"
"github.com/hashgraph-online/standards-sdk-go/pkg/hcs27"
)
_, err := client.PublishCheckpoint(context.Background(), topicID, hcs27.CheckpointMetadata{
Type: "ans-checkpoint-v1",
Stream: hcs27.StreamID{Registry: "ans", LogID: "overflow"},
Log: &hcs27.LogProfile{
Algorithm: "sha-256",
Leaf: strings.Repeat("sha256(jcs(event))-", 90),
Merkle: "rfc9162",
},
Root: hcs27.RootCommitment{
TreeSize: "1",
RootHashB64u: "A7K9uA5RUL9Ko_BIO8l7ON3W5gQw6B6yv2nM3xABjS8",
},
}, "overflow checkpoint", "")
if err != nil {
log.Fatal(err)
}
records, err := client.GetCheckpoints(context.Background(), topicID, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(records[0].Message.Metadata)) // "hcs://1/<topic>"
client.publish_checkpoint(
topic["topicId"],
{
"type": "ans-checkpoint-v1",
"stream": {"registry": "ans", "log_id": "overflow"},
"log": {
"alg": "sha-256",
"leaf": "sha256(jcs(event))-" * 90,
"merkle": "rfc9162",
},
"root": {
"treeSize": "1",
"rootHashB64u": "A7K9uA5RUL9Ko_BIO8l7ON3W5gQw6B6yv2nM3xABjS8",
},
},
"overflow checkpoint",
)
records = client.get_checkpoints(topic["topicId"])
print(records[0]["message"]["metadata"]) # hcs://1/<topic>
Read and Validate a Checkpoint ChainDirect link to Read and Validate a Checkpoint Chain
- TypeScript
- Go
- Python
const checkpoints = await client.getCheckpoints(topic.topicId);
client.validateCheckpointChain(checkpoints);
for (const checkpoint of checkpoints) {
console.log(
checkpoint.sequence,
checkpoint.effectiveMetadata.root.treeSize,
checkpoint.effectiveMetadata.root.rootHashB64u,
);
}
import (
"context"
"fmt"
"log"
"github.com/hashgraph-online/standards-sdk-go/pkg/hcs27"
)
records, err := client.GetCheckpoints(context.Background(), topicID, nil)
if err != nil {
log.Fatal(err)
}
if err := hcs27.ValidateCheckpointChain(records); err != nil {
log.Fatal(err)
}
for _, record := range records {
fmt.Println(record.Sequence, record.EffectiveMetadata.Root.TreeSize, record.EffectiveMetadata.Root.RootHashB64u)
}
records = client.get_checkpoints(topic["topicId"])
client.validate_checkpoint_chain(records)
for record in records:
print(
record["sequence"],
record["effectiveMetadata"]["root"]["treeSize"],
record["effectiveMetadata"]["root"]["rootHashB64u"],
)
Resolve an HCS-1 Reference DirectlyDirect link to Resolve an HCS-1 Reference Directly
- TypeScript
- Go
- Python
const metadataBytes = await client.resolveHCS1Reference('hcs://1/0.0.123456');
console.log(metadataBytes.toString('utf8'));
import (
"context"
"fmt"
"log"
)
payload, err := client.ResolveHCS1Reference(context.Background(), "hcs://1/0.0.123456")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(payload))
payload = client.resolveHCS1Reference("hcs://1/0.0.123456")
print(payload.decode("utf-8"))
Private Topics Created ElsewhereDirect link to Private Topics Created Elsewhere
If you create the topic outside the TypeScript SDK and still want to publish to a private submit-key topic, register the submit signer before calling publishCheckpoint(...):
client.registerTopicSubmitKey('0.0.123456', process.env.HCS27_SUBMIT_KEY!);