Skip to main content

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 TypeGo Type
Nullgtv.NullValue{}
ByteArraygtv.ByteArrayValue{Value: []byte}
Stringgtv.StringValue{Value: string}
Integergtv.IntegerValue{Value: int64}
Dictgtv.DictValue{Value: map[string]gtv.Value}
Arraygtv.ArrayValue{Value: []gtv.Value}
BigIntegergtv.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 typeDescription
gtv.ValueInterface for all GTV values
gtv.NullValueRepresents a null value
gtv.ByteArrayValueRepresents a byte array
gtv.StringValueRepresents a string
gtv.IntegerValueRepresents a 64-bit integer
gtv.DictValueRepresents a dictionary (map)
gtv.ArrayValueRepresents an array
gtv.BigIntegerValueRepresents a big integer
gtv.MarshalBinary()Serializes a GTV value to binary
gtv.UnmarshalBinary()Deserializes binary data to a GTV value

Merkle Package

FunctionDescription
merkle.HashVersion1Original hash version (with bug)
merkle.HashVersion2Current hash version
merkle.Hash()Computes a Merkle hash of a GTV value

GTX Package

FunctionDescription
gtx.RIDRepresents a reference identifier (32-byte hash)
gtx.NewRIDFromHex()Creates a RID from a hex string
gtx.OperationRepresents a blockchain operation
gtx.NewOperation()Creates a new operation
gtx.TransactionRepresents 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

FunctionDescription
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.TransactionStatusRepresents transaction status
postchain.TransactionStatusValueEnum for transaction status (Confirmed, Waiting, Rejected, Unknown)
postchain.TransactionInfoContains detailed information about a transaction
postchain.TxDetailRepresents transaction details
postchain.BlockDetailRepresents block details