Go client
The Go client offers a range of built-in functions and utilities designed to simplify interaction with decentralized applications (dApps) created using the Postchain blockchain framework, commonly referred to as Chromia.
The Go client library provides tools for interacting with the Chromia blockchain platform. It includes functionality for: - Serializing and deserializing data using the GTV (Generic Transfer Value) format. - Creating, signing, and submitting transactions using the GTX (Generic Transaction) format. - Computing Merkle tree hashes for data verification. - Communicating with Postchain nodes via REST API.
Installation
To install the Go client, run the following command in the terminal:
go get gitlab.com/chromaway/ft4-go-client
Generic Transfer Value (GTV)
GTV is a flexible data serialization format used by Chromia. It uses ASN.1 DER encoding rules for data representation and supports these types:
GTV Type | Go Type |
---|---|
Null | gtv.NullValue{} |
ByteArray | gtv.ByteArrayValue{Value: []byte} |
String | gtv.StringValue{Value: string} |
Integer | gtv.IntegerValue{Value: int64} |
Dict | gtv.DictValue{Value: map[string]gtv.Value} |
Array | gtv.ArrayValue{Value: []gtv.Value} |
BigInteger | gtv.BigIntegerValue{Value: *big.Int} |
Working with GTV Values
Below you can find code examples that highlight how you can use the Go library to: - Create GTV values, arrays, and dictionaries. - Serialize to binary. - Deserialize from binary.
// Create GTV values
nullValue := gtv.NullValue{}
stringValue := gtv.StringValue{Value: "Hello, Chromia!"}
intValue := gtv.IntegerValue{Value: 42}
// Create a GTV array
arrayValue := gtv.ArrayValue{
Value: []gtv.Value{
stringValue,
intValue,
},
}
// Create a GTV dictionary
dictValue := gtv.DictValue{
Value: map[string]gtv.Value{
"name": gtv.StringValue{Value: "Chromia"},
"count": gtv.IntegerValue{Value: 123},
},
}
// Serialize to binary
data, err := dictValue.MarshalBinary()
if err != nil {
// Handle error
}
// Deserialize from binary
value, err := gtv.UnmarshalBinary(data)
if err != nil {
// Handle error
}
Markle
The Merkle package implements a Merkle tree-based hashing system for GTV data structures. This is used for transaction verification and to compute transaction RIDs.
The library supports two hash versions:
- Version 1: The original algorithm (contains a bug causing hash collisions).
- Version 2: The current version, without the bug.
Computing a Merkle Hash
The example below shows how to create and compute the Merkle hash:
// Create a GTV value
value := gtv.DictValue{
Value: map[string]gtv.Value{
"key": gtv.StringValue{Value: "value"},
},
}
// Compute the Merkle hash using version 2
hash := merkle.Hash(value, merkle.HashVersion2)
Generic Transaction (GTX)
GTX (Generic Transaction) is a format used to encode signed transactions in GTV. The structure is as follows:
[
[
blockchainRID,
[
[ "operation1", [arg1, arg2, arg3, ...] ],
[ "operation2", [arg1, arg2, arg3, ...] ],
...
],
[ signerPubKey1, signerPubKey2, ... ]
],
[ signature1, signature2, .. ]
]
The blockchain RID, signer public keys, and signatures are encoded as byteArray
. The signatures are generated using
ECDSA with the secp256k1 curve.
Creating and Signing a Transaction
The example below highlights the process of creating and signing a transaction:
// Create a blockchain RID
blockchainRID, err := gtx.NewRIDFromHex("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
if err != nil {
// Handle error
}
// Create operations
operations := []gtx.Operation{
gtx.NewOperation("transfer", []gtv.Value{
gtv.StringValue{Value: "recipient_account"},
gtv.IntegerValue{Value: 100},
}),
}
// Create signers (public keys)
signers := [][]byte{
// Your public key here
}
// Create a new transaction
tx := gtx.NewTransaction(blockchainRID, operations, signers, 2) // Using Merkle hash version 2
// Sign the transaction
err = tx.Sign(publicKey, privateKey)
if err != nil {
// Handle error
}
// Check if fully signed
if tx.IsFullySigned() {
// Transaction is ready to be submitted
}
Postchain Client
The Postchain client provides functionality for interacting with Postchain blockchain nodes via the REST API. It allows you to: - Get blockchain information - Query the blockchain - Submit transactions - Check transaction status
Examples
Connecting to blockchain
// Parse node URL
nodeURL, err := url.Parse("https://node1.example.com")
if err != nil {
// Handle error
}
// Create a client
client := postchain.NewClient([]*url.URL{nodeURL})
// Get blockchain RID from IID (instance ID)
blockchainRID, err := client.GetBlockchainRID(123)
if err != nil {
// Handle error
}
// Get blockchain features
features, err := client.GetFeatures(blockchainRID)
if err != nil {
// Handle error
}
// Get Merkle hash version used by the blockchain
merkleHashVersion, err := client.DetectMerkleHashVersion(blockchainRID)
if err != nil {
// Handle error
}
Creating and Sending Transactions
// Create a transaction
tx := gtx.NewTransaction(blockchainRID, operations, signers, merkleHashVersion)
// Sign the transaction
err = tx.Sign(publicKey, privateKey)
if err != nil {
// Handle error
}
// Get the transaction RID
txRID := tx.TxRID()
// Submit the transaction
err = client.PostTransaction(blockchainRID, tx)
if err != nil {
// Handle error
}
// Wait for confirmation
status := client.AwaitConfirmation(blockchainRID, txRID, 20, 500*time.Millisecond)
if status.Status == postchain.Confirmed {
// Transaction confirmed
} else if status.Status == postchain.Rejected {
// Transaction rejected
fmt.Println("Rejection reason:", status.RejectReason)
}
Querying the Blockchain
// Query without parameters
result, err := client.Query(blockchainRID, "get_account_balance", gtv.DictValue{Value: map[string]gtv.Value{}})
if err != nil {
// Handle error
}
// Query with parameters
params := gtv.DictValue{
Value: map[string]gtv.Value{
"account_id": gtv.StringValue{Value: "user123"},
},
}
result, err = client.Query(blockchainRID, "get_account_balance", params)
if err != nil {
// Handle error
}
// Process the result
if balance, ok := result.(gtv.IntegerValue); ok {
fmt.Println("Balance:", balance.Value)
}
Retrieving Transactions and Blocks
// Get a transaction by its RID
tx, err := client.GetTransaction(blockchainRID, txRID)
if err != nil {
// Handle error
}
fmt.Println("Transaction operations:", len(tx.Operations))
// Get detailed information about a transaction
txInfo, err := client.GetTransactionInfo(blockchainRID, txRID)
if err != nil {
// Handle error
}
fmt.Println("Transaction in block:", txInfo.BlockHeight)
fmt.Println("Transaction timestamp:", txInfo.Timestamp)
// Get confirmation proof for a transaction
proof, err := client.GetConfirmationProof(blockchainRID, txRID)
if err != nil {
// Handle error
}
fmt.Println("Confirmation proof size:", len(proof))
// Get a block by height
block, err := client.BlockAtHeight(blockchainRID, 42)
if err != nil {
// Handle error
}
if block != nil {
fmt.Println("Block height:", block.Height)
fmt.Println("Number of transactions:", len(block.Transactions))
fmt.Println("Block timestamp:", block.Timestamp)
}
// Get a block by RID
blockRID, _ := gtx.NewRIDFromHex("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
block, err = client.BlockByRID(blockchainRID, blockRID)
if err != nil {
// Handle error
}
if block != nil {
fmt.Println("Block RID:", block.RID.ToHex())
fmt.Println("Previous block RID:", block.PrevBlockRID.ToHex())
}
Working with GTV Data
// Create a complex GTV structure
userData := gtv.DictValue{
Value: map[string]gtv.Value{
"name": gtv.StringValue{Value: "Alice"},
"age": gtv.IntegerValue{Value: 30},
"is_active": gtv.ByteArrayValue{Value: []byte{1}}, // Boolean true
"scores": gtv.ArrayValue{
Value: []gtv.Value{
gtv.IntegerValue{Value: 95},
gtv.IntegerValue{Value: 87},
gtv.IntegerValue{Value: 92},
},
},
},
}
// Serialize to binary
data, err := userData.MarshalBinary()
if err != nil {
// Handle error
}
// Deserialize from binary
value, err := gtv.UnmarshalBinary(data)
if err != nil {
// Handle error
}
// Access values
if dict, ok := value.(gtv.DictValue); ok {
if name, ok := dict.Value["name"].(gtv.StringValue); ok {
fmt.Println("Name:", name.Value)
}
if scores, ok := dict.Value["scores"].(gtv.ArrayValue); ok {
fmt.Println("Number of scores:", len(scores.Value))
}
}
API Reference
GTV Package
Data type | Description |
---|---|
gtv.Value | Interface for all GTV values |
gtv.NullValue | Represents a null value |
gtv.ByteArrayValue | Represents a byte array |
gtv.StringValue | Represents a string |
gtv.IntegerValue | Represents a 64-bit integer |
gtv.DictValue | Represents a dictionary (map) |
gtv.ArrayValue | Represents an array |
gtv.BigIntegerValue | Represents a big integer |
gtv.MarshalBinary() | Serializes a GTV value to binary |
gtv.UnmarshalBinary() | Deserializes binary data to a GTV value |
Merkle Package
Function | Description |
---|---|
merkle.HashVersion1 | Original hash version (with bug) |
merkle.HashVersion2 | Current hash version |
merkle.Hash() | Computes a Merkle hash of a GTV value |
GTX Package
Function | Description |
---|---|
gtx.RID | Represents a reference identifier (32-byte hash) |
gtx.NewRIDFromHex() | Creates a RID from a hex string |
gtx.Operation | Represents a blockchain operation |
gtx.NewOperation() | Creates a new operation |
gtx.Transaction | Represents a blockchain transaction |
gtx.NewTransaction() | Creates a new transaction |
gtx.Transaction.Sign() | Signs a transaction |
gtx.Transaction.IsFullySigned() | Checks if a transaction is fully signed |
gtx.Transaction.TxRID() | Gets the transaction RID |
Postchain Package
Function | Description |
---|---|
postchain.NewClient() | Creates a new Postchain client |
postchain.Client.GetBlockchainRID() | Gets a blockchain RID from an IID |
postchain.Client.GetFeatures() | Gets blockchain features |
postchain.Client.DetectMerkleHashVersion() | Detects the Merkle hash version |
postchain.Client.Query() | Executes a query |
postchain.Client.PostTransaction() | Submits a transaction |
postchain.Client.CheckTxStatus() | Checks transaction status |
postchain.Client.AwaitConfirmation() | Waits for transaction confirmation |
postchain.Client.GetTransaction() | Retrieves a transaction from the blockchain |
postchain.Client.GetTransactionInfo() | Retrieves detailed information about a transaction |
postchain.Client.GetConfirmationProof() | Retrieves confirmation proof for a transaction |
postchain.Client.BlockAtHeight() | Retrieves a block from the blockchain by height |
postchain.Client.BlockByRID() | Retrieves a block from the blockchain by RID |
postchain.TransactionStatus | Represents transaction status |
postchain.TransactionStatusValue | Enum for transaction status (Confirmed, Waiting, Rejected, Unknown) |
postchain.TransactionInfo | Contains detailed information about a transaction |
postchain.TxDetail | Represents transaction details |
postchain.BlockDetail | Represents block details |