# IronWeave Integration Guide

Build applications on top of the IronWeave fabic. This guide covers everything you need to connect to a node, submit transactions, read data, and integrate IronWeave into your application stack.

***

## Quick Start

The fastest way to interact with an IronWeave node is via HTTP/JSON — no client libraries required:

```bash
# Check if a chain exists by querying its head
curl -k -X POST https://[ironweave-validator-node]/ironweave/api/core/chain/head/get \
  -H "Content-Type: application/json" \
  -d '{
    "chainAddress": {
      "addressType": "ADDRESS_TYPE_EC",
      "value": "BASE64_ENCODED_PUBLIC_KEY"
    },
    "returnBlock": true
  }'

# Read a specific block by its hash
curl -k -X POST https://[ironweave-validator-node]/ironweave/api/core/block/read \
  -H "Content-Type: application/json" \
  -d '{
    "blockId": {
      "hashType": "HASH_TYPE_SHA3_256",
      "value": "BASE64_ENCODED_BLOCK_HASH"
    }
  }'
```

For production integrations, use a gRPC client with the IronWeave proto files for type safety, better performance, and streaming support.

***

## OpenAPI Documentation

The OpenAPI documentation can be found here: [API Documentation](https://50.47.36.227/ironweave/docs/core/v3)

***

## Getting the Proto Files

IronWeave's API is defined in Protocol Buffers. You need these files to generate typed client code:

| File                 | Purpose                                                         |
| -------------------- | --------------------------------------------------------------- |
| `core_service.proto` | The three core RPCs: SubmitTransaction, GetChainHead, ReadBlock |
| `common.proto`       | Shared types: Block, Transaction, Hash, Address, Signature      |
| `extra_types.proto`  | Attachment type for file/blob references                        |
| `crypto_zk.proto`    | Zero-knowledge proof types for privacy-preserving transfers     |

All proto files use the `io.ironweave.v3` package and are located in the `Protos/io/ironweave/v3/` directory of the source distribution. They also depend on `google/api/annotations.proto` and `google/api/http.proto` for HTTP transcoding annotations.

***

## Generating Client Code

### Python

```bash
pip install grpcio grpcio-tools

python -m grpc_tools.protoc \
  --proto_path=Protos \
  --python_out=generated/ \
  --grpc_python_out=generated/ \
  Protos/io/ironweave/v3/core_service.proto \
  Protos/io/ironweave/v3/common.proto
```

### Java

```bash
protoc \
  --proto_path=Protos \
  --java_out=generated/java \
  --grpc-java_out=generated/java \
  Protos/io/ironweave/v3/core_service.proto \
  Protos/io/ironweave/v3/common.proto
```

Or with Gradle:

```groovy
protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.25.1" }
    plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.61.0" } }
    generateProtoTasks {
        all()*.plugins { grpc {} }
    }
}
```

### Rust (with tonic)

In your `build.rs`:

```rust
fn main() {
    tonic_build::configure()
        .build_server(false)
        .compile(
            &[
                "proto/io/ironweave/v3/core_service.proto",
                "proto/io/ironweave/v3/common.proto",
            ],
            &["proto"],
        )
        .unwrap();
}
```

### C# / .NET

Add the proto files to your `.csproj` — the `Grpc.Tools` NuGet package generates client code at build time:

```xml
<ItemGroup>
  <PackageReference Include="Google.Protobuf" Version="3.*" />
  <PackageReference Include="Grpc.Net.Client" Version="2.*" />
  <PackageReference Include="Grpc.Tools" Version="2.*" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
  <Protobuf Include="Protos/io/ironweave/v3/core_service.proto" GrpcServices="Client" />
  <Protobuf Include="Protos/io/ironweave/v3/common.proto" GrpcServices="None" />
</ItemGroup>
```

### Go

```bash
protoc \
  --proto_path=Protos \
  --go_out=generated/go --go_opt=paths=source_relative \
  --go-grpc_out=generated/go --go-grpc_opt=paths=source_relative \
  Protos/io/ironweave/v3/core_service.proto \
  Protos/io/ironweave/v3/common.proto
```

### TypeScript / JavaScript (with grpc-js)

```bash
npm install @grpc/grpc-js @grpc/proto-loader

# Or generate static types with protoc
npx grpc_tools_node_protoc \
  --proto_path=Protos \
  --js_out=import_style=commonjs,binary:generated/ \
  --grpc_out=grpc_js:generated/ \
  --ts_out=generated/ \
  Protos/io/ironweave/v3/core_service.proto \
  Protos/io/ironweave/v3/common.proto
```

***

## Connecting to a Node

The bootstrap endpoint is `https://50.47.36.227/ironweave/api/bootstrap`. This endpoint can be used to retrieve a list of validator node endpoints. All nodes support HTTP/1.1, HTTP/2, and HTTP/3.

### C\#

```csharp
using Grpc.Net.Client;
using Weave.V3;

// Development (self-signed certificates)
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback =
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("[weave-validator-node]", new GrpcChannelOptions
{
    HttpHandler = handler
});

var client = new IronWeaveCore.IronWeaveCoreClient(channel);
```

### Python

```python
import grpc
from io.ironweave.v3 import core_service_pb2_grpc

# Development (insecure)
channel = grpc.insecure_channel("[weave-validator-node]")
stub = core_service_pb2_grpc.IronWeaveCoreStub(channel)

# Production (TLS)
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel("[weave-validator-node]", credentials)
stub = core_service_pb2_grpc.IronWeaveCoreStub(channel)
```

### Java

```java
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.ironweave.v3.IronWeaveCoreGrpc;

// Development (plaintext)
ManagedChannel channel = ManagedChannelBuilder
    .forAddress("localhost", 8088)
    .usePlaintext()
    .build();

IronWeaveCoreGrpc.IronWeaveCoreBlockingStub client =
    IronWeaveCoreGrpc.newBlockingStub(channel);

// Production (TLS)
ManagedChannel tlsChannel = ManagedChannelBuilder
    .forAddress("your-node.example.com", 8088)
    .useTransportSecurity()
    .build();
```

### Rust

```rust
use tonic::transport::Channel;

pub mod ironweave {
    tonic::include_proto!("io.ironweave.v3");
}
use ironweave::iron_weave_core_client::IronWeaveCoreClient;

let channel = Channel::from_static("[weave-validator-node]")
    .connect()
    .await?;

let mut client = IronWeaveCoreClient::new(channel);
```

### Go

```go
import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    pb "your-module/generated/go/io/ironweave/v3"
)

// Development
conn, err := grpc.Dial("[weave-validator-node]", grpc.WithTransportCredentials(insecure.NewCredentials()))
client := pb.NewIronWeaveCoreClient(conn)
```

***

## Core Concepts

### Chains and Addresses

IronWeave uses a **multi-chain** architecture. Every participant has their own chain of blocks, identified by their public key address. When a transaction involves multiple participants, a single shared block is written and referenced on each participant's chain.

```
Chain A:  [Genesis] -> [Block 1] -> [Block 2] \               / [Block 4]
                                                [Shared Block]
Chain B:  [Genesis] -> [Block 1] -------------/               \ [Block 3]
```

An **Address** is a compressed public key (33 bytes for EC/secp256r1). It serves as both the identity of a participant and the identifier for their chain.

### Transactions and Blocks

A **Transaction** is the unit of work you submit. It contains:

* A unique ID (UUIDv7 recommended)
* One or more **Participants** — the chains this transaction touches
* A **Payload** — your application data (arbitrary bytes with a type label)
* A gas limit

When the node processes a transaction, it produces a **Block** — the immutable record stored or referenced (in the case of shared blocks) on each participant's chain. Blocks contain the transaction data plus metadata: validator signatures, timestamps, back-references to previous blocks, and a block hash (SHA3-256) that serves as its unique ID.

**Block types** you will encounter:

| Type         | Description                                        |
| ------------ | -------------------------------------------------- |
| `KNOT`       | Standard transaction block — contains your payload |
| `GENESIS`    | First block on a new chain                         |
| `MERGE`      | Chain merge block (no payload)                     |
| `DIGEST`     | Summary/checkpoint block (no payload)              |
| `ASSIGNMENT` | Validator assignment block                         |

### Transaction Lifecycle

{% stepper %}
{% step %}

### Build

Build a `Transaction` with participants, payload, and gas limit.
{% endstep %}

{% step %}

### Serialize

Serialize the transaction to bytes and **sign** those bytes with your private key.
{% endstep %}

{% step %}

### Submit

Submit via `SubmitTransaction` with the serialized bytes and signature.
{% endstep %}

{% step %}

### Validate

The node validates the transaction (checks participants, signatures, gas).
{% endstep %}

{% step %}

### Consensus

Validators agree on the block via DBFT.
{% endstep %}

{% step %}

### Write the block

A `Block` is written to every participant's chain with cross-chain back-references.
{% endstep %}

{% step %}

### Response

The response returns the new block to your client.
{% endstep %}
{% endstepper %}

Transaction processing is asynchronous — the `SubmitTransaction` response confirms the transaction was accepted, and the block is available to read once consensus completes.

### Gas

Each transaction specifies a `gas_limit` — the maximum gas the initiator is willing to spend. Gas is allocated to validators who participate in consensus, recorded as `GasAllocation` entries in the block header.

***

## API Reference

IronWeave exposes three core RPCs. All are available as both gRPC methods and HTTP/JSON endpoints.

### SubmitTransaction

Submit a transaction for inclusion in the ledger.

**gRPC:** `IronWeaveCore.SubmitTransaction`\
**HTTP:** `POST /ironweave/api/core/transaction/submit`

#### Request

| Field         | Type        | Description                                                                                        |
| ------------- | ----------- | -------------------------------------------------------------------------------------------------- |
| `transaction` | `bytes`     | Serialized `Transaction` protobuf (not a nested message — raw bytes enable signature verification) |
| `signature`   | `Signature` | Ed25519 signature over the transaction bytes                                                       |

#### Response

| Field    | Type        | Description                                  |
| -------- | ----------- | -------------------------------------------- |
| `result` | `RpcResult` | Success/failure with result code and message |
| `block`  | `Block`     | The created block (on success)               |

#### Validation Rules

* At least one participant is required.
* Exactly one participant must have `is_initiator = true`.
* Payload must not be empty.
* The signature must be valid for the transaction bytes.

#### C\#

```csharp
using Google.Protobuf;
using Weave.V3;

// Build the transaction
var transaction = new Transaction
{
    TransactionId = UUID.FromGuid(Guid.NewGuid()),
    Category = new Category { Value = { 1 } },
    GasLimit = 6,
    Payload = new Payload
    {
        Type = "application/json",
        Data = ByteString.CopyFromUtf8("{\"action\": \"transfer\", \"amount\": 100}"),
        Encoding = "utf-8"
    }
};

transaction.Participants.Add(new Participant
{
    Address = new Address
    {
        AddressType = Address.Types.AddressType.Ec,
        Value = ByteString.CopyFrom(myPublicKeyBytes)  // 33-byte compressed key
    },
    IsInitiator = true
});

transaction.Participants.Add(new Participant
{
    Address = new Address
    {
        AddressType = Address.Types.AddressType.Ec,
        Value = ByteString.CopyFrom(recipientPublicKeyBytes)
    },
    IsInitiator = false
});

// Serialize, sign, and submit
var txBytes = transaction.ToByteArray();
var signatureBytes = SignWithEd25519(txBytes, privateKey); // your signing implementation

var response = await client.SubmitTransactionAsync(new SubmitTransactionRequest
{
    Transaction = ByteString.CopyFrom(txBytes),
    Signature = new Signature
    {
        SignatureType = Signature.Types.SignatureType.Ed25519,
        SignerAddress = new Address
        {
            AddressType = Address.Types.AddressType.Ec,
            Value = ByteString.CopyFrom(myPublicKeyBytes)
        },
        Value = ByteString.CopyFrom(signatureBytes)
    }
});

if (response.Result.Success)
{
    Console.WriteLine($"Block created: {response.Block.BlockId}");
}
```

#### Java

```java
import com.google.protobuf.ByteString;
import io.ironweave.v3.Common.*;
import io.ironweave.v3.CoreService.*;
import java.util.UUID;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

// Build participants
Address initiatorAddress = Address.newBuilder()
    .setAddressType(Address.AddressType.ADDRESS_TYPE_EC)
    .setValue(ByteString.copyFrom(myPublicKeyBytes))
    .build();

Address recipientAddress = Address.newBuilder()
    .setAddressType(Address.AddressType.ADDRESS_TYPE_EC)
    .setValue(ByteString.copyFrom(recipientPublicKeyBytes))
    .build();

// Build the transaction
UUID txId = UUID.randomUUID();
ByteBuffer uuidBuffer = ByteBuffer.allocate(16);
uuidBuffer.order(ByteOrder.BIG_ENDIAN);
uuidBuffer.putLong(txId.getMostSignificantBits());
uuidBuffer.putLong(txId.getLeastSignificantBits());

Transaction transaction = Transaction.newBuilder()
    .setTransactionId(Common.UUID.newBuilder()
        .setValue(ByteString.copyFrom(uuidBuffer.array()))
        .build())
    .addParticipants(Participant.newBuilder()
        .setAddress(initiatorAddress)
        .setIsInitiator(true)
        .build())
    .addParticipants(Participant.newBuilder()
        .setAddress(recipientAddress)
        .setIsInitiator(false)
        .build())
    .setCategory(Category.newBuilder().addValue(1).build())
    .setPayload(Payload.newBuilder()
        .setType("application/json")
        .setData(ByteString.copyFromUtf8("{\"action\": \"transfer\", \"amount\": 100}"))
        .setEncoding("utf-8")
        .build())
    .setGasLimit(6)
    .build();

// Serialize, sign, and submit
byte[] txBytes = transaction.toByteArray();
byte[] signatureBytes = signWithEd25519(txBytes, privateKey);

SubmitTransactionRequest request = SubmitTransactionRequest.newBuilder()
    .setTransaction(ByteString.copyFrom(txBytes))
    .setSignature(Signature.newBuilder()
        .setSignatureType(Signature.SignatureType.SIGNATURE_TYPE_ED25519)
        .setSignerAddress(initiatorAddress)
        .setValue(ByteString.copyFrom(signatureBytes))
        .build())
    .build();

SubmitTransactionResponse response = client.submitTransaction(request);

if (response.getResult().getSuccess()) {
    System.out.println("Block created: " + response.getBlock().getBlockId());
}
```

#### Python

```python
import uuid
from io.ironweave.v3 import common_pb2, core_service_pb2

transaction = common_pb2.Transaction(
    transaction_id=common_pb2.UUID(value=uuid.uuid4().bytes),
    category=common_pb2.Category(value=[1]),
    gas_limit=6,
    payload=common_pb2.Payload(
        type="application/json",
        data=b'{"action": "transfer", "amount": 100}',
        encoding="utf-8",
    ),
)
transaction.participants.append(
    common_pb2.Participant(
        address=common_pb2.Address(
            address_type=common_pb2.Address.ADDRESS_TYPE_EC,
            value=my_public_key_bytes,
        ),
        is_initiator=True,
    )
)
transaction.participants.append(
    common_pb2.Participant(
        address=common_pb2.Address(
            address_type=common_pb2.Address.ADDRESS_TYPE_EC,
            value=recipient_public_key_bytes,
        ),
        is_initiator=False,
    )
)

tx_bytes = transaction.SerializeToString()
signature_bytes = sign_ed25519(tx_bytes, private_key)

response = stub.SubmitTransaction(
    core_service_pb2.SubmitTransactionRequest(
        transaction=tx_bytes,
        signature=common_pb2.Signature(
            signature_type=common_pb2.Signature.SIGNATURE_TYPE_ED25519,
            signer_address=common_pb2.Address(
                address_type=common_pb2.Address.ADDRESS_TYPE_EC,
                value=my_public_key_bytes,
            ),
            value=signature_bytes,
        ),
    )
)

if response.result.success:
    print(f"Block created: {response.block.block_id}")
```

#### Rust

```rust
use ironweave::{
    iron_weave_core_client::IronWeaveCoreClient,
    Address, Category, Participant, Payload,
    Signature, SubmitTransactionRequest, Transaction, Uuid,
    address::AddressType, signature::SignatureType,
};
use prost::Message;
use uuid::Uuid as RustUuid;

let tx_id = RustUuid::now_v7();
let transaction = Transaction {
    transaction_id: Some(Uuid { value: tx_id.as_bytes().to_vec() }),
    participants: vec![
        Participant {
            address: Some(Address {
                address_type: AddressType::Ec as i32,
                value: my_public_key_bytes.to_vec(),
            }),
            payload_key: None,
            is_initiator: true,
        },
        Participant {
            address: Some(Address {
                address_type: AddressType::Ec as i32,
                value: recipient_public_key_bytes.to_vec(),
            }),
            payload_key: None,
            is_initiator: false,
        },
    ],
    category: Some(Category { value: vec![1] }),
    payload: Some(Payload {
        r#type: "application/json".into(),
        data: b"{\"action\": \"transfer\", \"amount\": 100}".to_vec(),
        encoding: Some("utf-8".into()),
        encryption: None,
    }),
    gas_limit: 6,
};

let tx_bytes = transaction.encode_to_vec();
let signature_bytes = sign_ed25519(&tx_bytes, &private_key);

let request = SubmitTransactionRequest {
    transaction: tx_bytes,
    signature: Some(Signature {
        signature_type: SignatureType::Ed25519 as i32,
        signer_address: Some(Address {
            address_type: AddressType::Ec as i32,
            value: my_public_key_bytes.to_vec(),
        }),
        value: signature_bytes,
    }),
};

let response = client.submit_transaction(request).await?.into_inner();

if response.result.as_ref().map_or(false, |r| r.success) {
    println!("Block created: {:?}", response.block.unwrap().block_id);
}
```

#### Go

```go
import (
    "github.com/google/uuid"
    pb "your-module/generated/go/io/ironweave/v3"
    "google.golang.org/protobuf/proto"
)

txId, _ := uuid.NewV7()
transaction := &pb.Transaction{
    TransactionId: &pb.UUID{Value: txId[:]},
    Participants: []*pb.Participant{
        {
            Address:     &pb.Address{AddressType: pb.Address_ADDRESS_TYPE_EC, Value: myPublicKeyBytes},
            IsInitiator: true,
        },
        {
            Address:     &pb.Address{AddressType: pb.Address_ADDRESS_TYPE_EC, Value: recipientPublicKeyBytes},
            IsInitiator: false,
        },
    },
    Category: &pb.Category{Value: []uint32{1}},
    Payload: &pb.Payload{
        Type:     "application/json",
        Data:     []byte(`{"action": "transfer", "amount": 100}`),
        Encoding: proto.String("utf-8"),
    },
    GasLimit: 6,
}

txBytes, _ := proto.Marshal(transaction)
signatureBytes := signEd25519(txBytes, privateKey)

resp, err := client.SubmitTransaction(ctx, &pb.SubmitTransactionRequest{
    Transaction: txBytes,
    Signature: &pb.Signature{
        SignatureType: pb.Signature_SIGNATURE_TYPE_ED25519,
        SignerAddress:  &pb.Address{AddressType: pb.Address_ADDRESS_TYPE_EC, Value: myPublicKeyBytes},
        Value:          signatureBytes,
    },
})

if err == nil && resp.Result.Success {
    fmt.Printf("Block created: %v\n", resp.Block.BlockId)
}
```

### GetChainHead

Retrieve the current head (latest block) of a chain. Can return just the block ID for a lightweight check, or the full block.

**gRPC:** `IronWeaveCore.GetChainHead`\
**HTTP:** `POST /ironweave/api/core/chain/head/get`

#### Request

| Field           | Type      | Description                                              |
| --------------- | --------- | -------------------------------------------------------- |
| `chain_address` | `Address` | The public key address identifying the chain             |
| `return_block`  | `bool`    | `true` for the full block, `false` for just the block ID |

#### Response

| Field      | Type        | Description                                  |
| ---------- | ----------- | -------------------------------------------- |
| `result`   | `RpcResult` | Success/failure status                       |
| `block_id` | `Hash`      | Head block ID (when `return_block = false`)  |
| `block`    | `Block`     | Full head block (when `return_block = true`) |

The response uses a `oneof` — exactly one of `block_id` or `block` is set on success. For a new chain with no blocks, the result returns an empty block ID.

#### Errors

* **NOT\_FOUND** — the chain address does not exist on this node.
* **INTERNAL** — block ID was found but block data could not be retrieved.

#### C\#

```csharp
var response = await client.GetChainHeadAsync(new GetChainHeadRequest
{
    ChainAddress = new Address
    {
        AddressType = Address.Types.AddressType.Ec,
        Value = ByteString.CopyFrom(chainPublicKeyBytes)
    },
    ReturnBlock = true
});

if (response.Result.Success)
{
    Block headBlock = response.Block;
    Console.WriteLine($"Head block type: {headBlock.BlockType}");
    Console.WriteLine($"Timestamp: {headBlock.Header.Timestamp}");
}
```

#### Java

```java
GetChainHeadRequest request = GetChainHeadRequest.newBuilder()
    .setChainAddress(Address.newBuilder()
        .setAddressType(Address.AddressType.ADDRESS_TYPE_EC)
        .setValue(ByteString.copyFrom(chainPublicKeyBytes))
        .build())
    .setReturnBlock(true)
    .build();

GetChainHeadResponse response = client.getChainHead(request);

if (response.getResult().getSuccess()) {
    Block headBlock = response.getBlock();
    System.out.println("Head block type: " + headBlock.getBlockType());
    System.out.println("Timestamp: " + headBlock.getHeader().getTimestamp());
}
```

#### Python

```python
response = stub.GetChainHead(
    core_service_pb2.GetChainHeadRequest(
        chain_address=common_pb2.Address(
            address_type=common_pb2.Address.ADDRESS_TYPE_EC,
            value=chain_public_key_bytes,
        ),
        return_block=True,
    )
)

if response.result.success:
    print(f"Head block type: {response.block.block_type}")
    print(f"Timestamp: {response.block.header.timestamp}")
```

#### Rust

```rust
use ironweave::{GetChainHeadRequest, Address, address::AddressType};
use ironweave::get_chain_head_response::BlockOrId;

let request = GetChainHeadRequest {
    chain_address: Some(Address {
        address_type: AddressType::Ec as i32,
        value: chain_public_key_bytes.to_vec(),
    }),
    return_block: true,
};

let response = client.get_chain_head(request).await?.into_inner();

if response.result.as_ref().map_or(false, |r| r.success) {
    match response.block_or_id {
        Some(BlockOrId::Block(block)) => {
            println!("Head block type: {:?}", block.block_type);
            if let Some(header) = &block.header {
                println!("Timestamp: {}", header.timestamp);
            }
        }
        Some(BlockOrId::BlockId(hash)) => {
            println!("Head block ID: {:?}", hash);
        }
        None => {}
    }
}
```

#### Go

```go
resp, err := client.GetChainHead(ctx, &pb.GetChainHeadRequest{
    ChainAddress: &pb.Address{
        AddressType: pb.Address_ADDRESS_TYPE_EC,
        Value:       chainPublicKeyBytes,
    },
    ReturnBlock: true,
})

if err == nil && resp.Result.Success {
    block := resp.GetBlock()
    fmt.Printf("Head block type: %v\n", block.BlockType)
    fmt.Printf("Timestamp: %d\n", block.Header.Timestamp)
}
```

### ReadBlock

Retrieve a specific block by its hash.

**gRPC:** `IronWeaveCore.ReadBlock`\
**HTTP:** `POST /ironweave/api/core/block/read`

#### Request

| Field      | Type   | Description                         |
| ---------- | ------ | ----------------------------------- |
| `block_id` | `Hash` | SHA3-256 hash identifying the block |

#### Response

| Field    | Type        | Description                      |
| -------- | ----------- | -------------------------------- |
| `result` | `RpcResult` | Success/failure status           |
| `block`  | `Block`     | The requested block (on success) |

#### Errors

* **NOT\_FOUND** — no block exists with the given hash.
* **INTERNAL** — storage error while reading the block.

#### C\#

```csharp
var response = await client.ReadBlockAsync(new ReadBlockRequest
{
    BlockId = new Hash
    {
        HashType = Hash.Types.HashType.Sha3256,
        Value = ByteString.CopyFrom(blockHashBytes)
    }
});

if (response.Result.Success)
{
    Block block = response.Block;
    Console.WriteLine($"Block type: {block.BlockType}");
    Console.WriteLine($"Payload: {block.Data.Data.ToStringUtf8()}");

    foreach (var backRef in block.Header.BackReferences)
    {
        Console.WriteLine($"  Chain {backRef.Address} -> previous block seq {backRef.Sequence}");
    }
}
```

#### Java

```java
ReadBlockRequest request = ReadBlockRequest.newBuilder()
    .setBlockId(Hash.newBuilder()
        .setHashType(Hash.HashType.HASH_TYPE_SHA3_256)
        .setValue(ByteString.copyFrom(blockHashBytes))
        .build())
    .build();

ReadBlockResponse response = client.readBlock(request);

if (response.getResult().getSuccess()) {
    Block block = response.getBlock();
    System.out.println("Block type: " + block.getBlockType());
    System.out.println("Payload: " + block.getData().getData().toStringUtf8());

    for (BackReference ref : block.getHeader().getBackReferencesList()) {
        System.out.println("  Chain " + ref.getAddress() + " -> previous block seq " + ref.getSequence());
    }
}
```

#### Python

```python
response = stub.ReadBlock(
    core_service_pb2.ReadBlockRequest(
        block_id=common_pb2.Hash(
            hash_type=common_pb2.Hash.HASH_TYPE_SHA3_256,
            value=block_hash_bytes,
        ),
    )
)

if response.result.success:
    block = response.block
    print(f"Block type: {block.block_type}")
    print(f"Payload: {block.data.data.decode('utf-8')}")

    for back_ref in block.header.back_references:
        print(f"  Chain {back_ref.address} -> previous block seq {back_ref.sequence}")
```

#### Rust

```rust
use ironweave::{ReadBlockRequest, Hash, hash::HashType};

let request = ReadBlockRequest {
    block_id: Some(Hash {
        hash_type: HashType::Sha3256 as i32,
        value: block_hash_bytes.to_vec(),
    }),
};

let response = client.read_block(request).await?.into_inner();

if response.result.as_ref().map_or(false, |r| r.success) {
    if let Some(block) = response.block {
        println!("Block type: {:?}", block.block_type);
        if let Some(data) = &block.data {
            println!("Payload: {}", String::from_utf8_lossy(&data.data));
        }
        if let Some(header) = &block.header {
            for back_ref in &header.back_references {
                println!("  Chain {:?} -> previous block seq {}", back_ref.address, back_ref.sequence);
            }
        }
    }
}
```

#### Go

```go
resp, err := client.ReadBlock(ctx, &pb.ReadBlockRequest{
    BlockId: &pb.Hash{
        HashType: pb.Hash_HASH_TYPE_SHA3_256,
        Value:    blockHashBytes,
    },
})

if err == nil && resp.Result.Success {
    block := resp.Block
    fmt.Printf("Block type: %v\n", block.BlockType)
    fmt.Printf("Payload: %s\n", string(block.Data.Data))

    for _, ref := range block.Header.BackReferences {
        fmt.Printf("  Chain %v -> previous block seq %d\n", ref.Address, ref.Sequence)
    }
}
```

***

## Using HTTP/JSON Instead of gRPC

Every gRPC endpoint is also available as an HTTP/JSON endpoint via gRPC-JSON transcoding. This is useful for quick testing, lightweight integrations, or languages without good gRPC support.

### Endpoint Mapping

| Operation            | HTTP Method | URL                                      |
| -------------------- | ----------- | ---------------------------------------- |
| Submit a transaction | POST        | `/ironweave/api/core/transaction/submit` |
| Get chain head       | POST        | `/ironweave/api/core/chain/head/get`     |
| Read a block         | POST        | `/ironweave/api/core/block/read`         |

### JSON Field Name Conventions

JSON transcoding uses **camelCase** names (standard protobuf JSON mapping):

| Protobuf Field   | JSON Field      |
| ---------------- | --------------- |
| `block_id`       | `blockId`       |
| `chain_address`  | `chainAddress`  |
| `return_block`   | `returnBlock`   |
| `hash_type`      | `hashType`      |
| `address_type`   | `addressType`   |
| `signature_type` | `signatureType` |
| `signer_address` | `signerAddress` |
| `is_initiator`   | `isInitiator`   |
| `gas_limit`      | `gasLimit`      |
| `result_code`    | `resultCode`    |

### Bytes Encoding

All `bytes` fields are encoded as **Base64** strings in JSON.

### Examples

**Read a block:**

```bash
curl -k -X POST https://[weave-validator-node]/ironweave/api/core/block/read \
  -H "Content-Type: application/json" \
  -d '{
    "blockId": {
      "hashType": "HASH_TYPE_SHA3_256",
      "value": "BASE64_ENCODED_BLOCK_HASH"
    }
  }'
```

**Get chain head (full block):**

```bash
curl -k -X POST https://[weave-validator-node]/ironweave/api/core/chain/head/get \
  -H "Content-Type: application/json" \
  -d '{
    "chainAddress": {
      "addressType": "ADDRESS_TYPE_EC",
      "value": "BASE64_ENCODED_PUBLIC_KEY"
    },
    "returnBlock": true
  }'
```

**Submit a transaction:**

```bash
curl -k -X POST https://[weave-validator-node]/ironweave/api/core/transaction/submit \
  -H "Content-Type: application/json" \
  -d '{
    "transaction": "BASE64_ENCODED_SERIALIZED_TRANSACTION",
    "signature": {
      "signatureType": "SIGNATURE_TYPE_ED25519",
      "signerAddress": {
        "addressType": "ADDRESS_TYPE_EC",
        "value": "BASE64_ENCODED_PUBLIC_KEY"
      },
      "value": "BASE64_ENCODED_SIGNATURE"
    }
  }'
```

***

## Authentication

### Development

In debug/development builds, certificate validation is disabled. Any client can connect freely.

### Production

Production deployments require **mutual TLS (mTLS)** with client certificates:

* Clients must present an ECDSA X.509 certificate.
* The public key in the certificate must match the `signer_address` on submitted transaction signatures.
* Requests without a valid client certificate receive `UNAUTHENTICATED`.
* Requests where the signer address doesn't match the certificate receive `PERMISSION_DENIED`.

***

## Error Handling

Every API response includes an `RpcResult`:

```json
{
  "result": {
    "success": true,
    "resultCode": 0,
    "message": ""
  }
}
```

Check `result.success` first. On failure, `result.message` contains a human-readable explanation and `result.resultCode` provides a machine-readable code.

### gRPC Status Codes

In addition to `RpcResult`, the service sets standard gRPC status codes for transport-level errors:

| Status Code         | Meaning                                  |
| ------------------- | ---------------------------------------- |
| `OK`                | Operation succeeded                      |
| `NOT_FOUND`         | Chain or block does not exist            |
| `INVALID_ARGUMENT`  | Transaction validation failed            |
| `INTERNAL`          | Server-side error (storage, locking)     |
| `UNAUTHENTICATED`   | Missing client certificate (production)  |
| `PERMISSION_DENIED` | Signer address doesn't match certificate |
| `UNAVAILABLE`       | Node is starting up or in maintenance    |

### Handling Errors in Code

#### C\#

```csharp
try
{
    var response = await client.SubmitTransactionAsync(request);
    if (!response.Result.Success)
    {
        Console.WriteLine($"Error [{response.Result.ResultCode}]: {response.Result.Message}");
    }
}
catch (Grpc.Core.RpcException ex)
{
    Console.WriteLine($"gRPC error [{ex.StatusCode}]: {ex.Status.Detail}");
}
```

#### Java

```java
try {
    SubmitTransactionResponse response = client.submitTransaction(request);
    if (!response.getResult().getSuccess()) {
        System.out.println("Error [" + response.getResult().getResultCode() + "]: "
            + response.getResult().getMessage());
    }
} catch (StatusRuntimeException e) {
    System.out.println("gRPC error [" + e.getStatus().getCode() + "]: "
        + e.getStatus().getDescription());
}
```

#### Python

```python
try:
    response = stub.SubmitTransaction(request)
    if not response.result.success:
        print(f"Error [{response.result.result_code}]: {response.result.message}")
except grpc.RpcError as e:
    print(f"gRPC error [{e.code()}]: {e.details()}")
```

#### Rust

```rust
match client.submit_transaction(request).await {
    Ok(response) => {
        let inner = response.into_inner();
        if let Some(result) = &inner.result {
            if !result.success {
                eprintln!("Error [{}]: {}", result.result_code,
                    result.message.as_deref().unwrap_or("unknown"));
            }
        }
    }
    Err(status) => {
        eprintln!("gRPC error [{:?}]: {}", status.code(), status.message());
    }
}
```

#### Go

```go
resp, err := client.SubmitTransaction(ctx, request)
if err != nil {
    st, ok := status.FromError(err)
    if ok {
        fmt.Printf("gRPC error [%v]: %s\n", st.Code(), st.Message())
    }
} else if !resp.Result.Success {
    fmt.Printf("Error [%d]: %s\n", resp.Result.ResultCode, resp.Result.Message)
}
```

***

## Integration Patterns

### Writing Data to the Ledger

The basic write flow: build a transaction, serialize it, sign it, and submit.

#### C\#

```csharp
// 1. Build your application payload
var payloadJson = "{\"type\": \"invoice\", \"amount\": 1500, \"currency\": \"USD\"}";

// 2. Wrap it in a transaction
var transaction = new Transaction
{
    TransactionId = UUID.FromGuid(Guid.NewGuid()),
    Category = new Category { Value = { 1 } },
    GasLimit = 6,
    Payload = new Payload
    {
        Type = "application/json",
        Data = ByteString.CopyFromUtf8(payloadJson),
        Encoding = "utf-8"
    }
};

// 3. Add participants (your chain + any counterparties)
transaction.Participants.Add(new Participant { Address = myAddress, IsInitiator = true });
transaction.Participants.Add(new Participant { Address = counterpartyAddress, IsInitiator = false });

// 4. Serialize and sign
var txBytes = transaction.ToByteArray();
var sig = SignWithEd25519(txBytes, myPrivateKey);

// 5. Submit
var response = await client.SubmitTransactionAsync(new SubmitTransactionRequest
{
    Transaction = ByteString.CopyFrom(txBytes),
    Signature = new Signature
    {
        SignatureType = Signature.Types.SignatureType.Ed25519,
        SignerAddress = myAddress,
        Value = ByteString.CopyFrom(sig)
    }
});

// 6. Store the block ID for future lookups
var blockId = response.Block.BlockId;
```

#### Java

```java
// 1. Build your application payload
byte[] payloadData = "{\"type\": \"invoice\", \"amount\": 1500, \"currency\": \"USD\"}"
    .getBytes(StandardCharsets.UTF_8);

// 2. Build the transaction
Transaction transaction = Transaction.newBuilder()
    .setTransactionId(Common.UUID.newBuilder()
        .setValue(ByteString.copyFrom(uuidBytes))
        .build())
    .addParticipants(Participant.newBuilder()
        .setAddress(myAddress).setIsInitiator(true).build())
    .addParticipants(Participant.newBuilder()
        .setAddress(counterpartyAddress).setIsInitiator(false).build())
    .setCategory(Category.newBuilder().addValue(1).build())
    .setPayload(Payload.newBuilder()
        .setType("application/json")
        .setData(ByteString.copyFrom(payloadData))
        .setEncoding("utf-8")
        .build())
    .setGasLimit(6)
    .build();

// 3. Serialize and sign
byte[] txBytes = transaction.toByteArray();
byte[] sig = signWithEd25519(txBytes, myPrivateKey);

// 4. Submit
SubmitTransactionResponse response = client.submitTransaction(
    SubmitTransactionRequest.newBuilder()
        .setTransaction(ByteString.copyFrom(txBytes))
        .setSignature(Signature.newBuilder()
            .setSignatureType(Signature.SignatureType.SIGNATURE_TYPE_ED25519)
            .setSignerAddress(myAddress)
            .setValue(ByteString.copyFrom(sig))
            .build())
        .build());

// 5. Store the block ID for future lookups
Hash blockId = response.getBlock().getBlockId();
```

#### Python

```python
# 1. Build your application payload
payload_data = json.dumps({"type": "invoice", "amount": 1500, "currency": "USD"}).encode()

# 2. Wrap it in a transaction
transaction = common_pb2.Transaction(
    transaction_id=common_pb2.UUID(value=uuid.uuid7().bytes),
    category=common_pb2.Category(value=[1]),
    gas_limit=6,
    payload=common_pb2.Payload(
        type="application/json",
        data=payload_data,
        encoding="utf-8",
    ),
)

# 3. Add participants (your chain + any counterparties)
transaction.participants.append(
    common_pb2.Participant(address=my_address, is_initiator=True)
)
transaction.participants.append(
    common_pb2.Participant(address=counterparty_address, is_initiator=False)
)

# 4. Serialize and sign
tx_bytes = transaction.SerializeToString()
sig = sign_ed25519(tx_bytes, my_private_key)

# 5. Submit
response = stub.SubmitTransaction(
    core_service_pb2.SubmitTransactionRequest(
        transaction=tx_bytes,
        signature=common_pb2.Signature(
            signature_type=common_pb2.Signature.SIGNATURE_TYPE_ED25519,
            signer_address=my_address,
            value=sig,
        ),
    )
)

# 6. Store the block ID for future lookups
block_id = response.block.block_id
```

#### Rust

```rust
// 1. Build the transaction with application payload
let transaction = Transaction {
    transaction_id: Some(Uuid { value: uuid::Uuid::now_v7().as_bytes().to_vec() }),
    participants: vec![
        Participant { address: Some(my_address.clone()), payload_key: None, is_initiator: true },
        Participant { address: Some(counterparty_address.clone()), payload_key: None, is_initiator: false },
    ],
    category: Some(Category { value: vec![1] }),
    payload: Some(Payload {
        r#type: "application/json".into(),
        data: br#"{"type": "invoice", "amount": 1500, "currency": "USD"}"#.to_vec(),
        encoding: Some("utf-8".into()),
        encryption: None,
    }),
    gas_limit: 6,
};

// 2. Serialize and sign
let tx_bytes = transaction.encode_to_vec();
let sig = sign_ed25519(&tx_bytes, &my_private_key);

// 3. Submit
let response = client.submit_transaction(SubmitTransactionRequest {
    transaction: tx_bytes,
    signature: Some(Signature {
        signature_type: SignatureType::Ed25519 as i32,
        signer_address: Some(my_address.clone()),
        value: sig,
    }),
}).await?.into_inner();

// 4. Store the block ID for future lookups
let block_id = response.block.unwrap().block_id;
```

#### Go

```go
// 1. Build the transaction with application payload
txId, _ := uuid.NewV7()
transaction := &pb.Transaction{
    TransactionId: &pb.UUID{Value: txId[:]},
    Participants: []*pb.Participant{
        {Address: myAddress, IsInitiator: true},
        {Address: counterpartyAddress, IsInitiator: false},
    },
    Category: &pb.Category{Value: []uint32{1}},
    Payload: &pb.Payload{
        Type:     "application/json",
        Data:     []byte(`{"type": "invoice", "amount": 1500, "currency": "USD"}`),
        Encoding: proto.String("utf-8"),
    },
    GasLimit: 6,
}

// 2. Serialize and sign
txBytes, _ := proto.Marshal(transaction)
sig := signEd25519(txBytes, myPrivateKey)

// 3. Submit
resp, _ := client.SubmitTransaction(ctx, &pb.SubmitTransactionRequest{
    Transaction: txBytes,
    Signature: &pb.Signature{
        SignatureType: pb.Signature_SIGNATURE_TYPE_ED25519,
        SignerAddress:  myAddress,
        Value:          sig,
    },
})

// 4. Store the block ID for future lookups
blockId := resp.Block.BlockId
```

**Key points:**

* The `payload.type` field is application-defined — use it to distinguish different record types in your application (e.g., `"invoice"`, `"receipt"`, `"transfer"`).
* The `payload.data` field is opaque bytes — IronWeave stores it as-is without interpreting the content.
* You can optionally encrypt payload data and use the `payload.encryption` field to indicate the scheme (e.g., `"aes-256-gcm"`). Use `participant.payload_key` to distribute encrypted decryption keys to each participant.

### Reading and Verifying Data

#### C\#

```csharp
var head = await client.GetChainHeadAsync(new GetChainHeadRequest
{
    ChainAddress = targetAddress,
    ReturnBlock = true
});

if (head.Result.Success)
{
    var payload = head.Block.Data.Data.ToStringUtf8();
    Console.WriteLine($"Latest record: {payload}");

    foreach (var sig in head.Block.NetworkSignatures)
    {
        Console.WriteLine($"Validated by: {sig.SignerAddress}");
    }
}
```

#### Java

```java
GetChainHeadResponse head = client.getChainHead(
    GetChainHeadRequest.newBuilder()
        .setChainAddress(targetAddress)
        .setReturnBlock(true)
        .build());

if (head.getResult().getSuccess()) {
    String payload = head.getBlock().getData().getData().toStringUtf8();
    System.out.println("Latest record: " + payload);

    for (Signature sig : head.getBlock().getNetworkSignaturesList()) {
        System.out.println("Validated by: " + sig.getSignerAddress());
    }
}
```

#### Python

```python
head = stub.GetChainHead(
    core_service_pb2.GetChainHeadRequest(
        chain_address=target_address,
        return_block=True,
    )
)

if head.result.success:
    payload = json.loads(head.block.data.data.decode("utf-8"))
    print(f"Latest record: {payload}")

    for sig in head.block.network_signatures:
        print(f"Validated by: {sig.signer_address}")
```

#### Rust

```rust
let head = client.get_chain_head(GetChainHeadRequest {
    chain_address: Some(target_address.clone()),
    return_block: true,
}).await?.into_inner();

if head.result.as_ref().map_or(false, |r| r.success) {
    if let Some(BlockOrId::Block(block)) = head.block_or_id {
        if let Some(data) = &block.data {
            let payload = String::from_utf8_lossy(&data.data);
            println!("Latest record: {}", payload);
        }
        for sig in &block.network_signatures {
            println!("Validated by: {:?}", sig.signer_address);
        }
    }
}
```

#### Go

```go
head, _ := client.GetChainHead(ctx, &pb.GetChainHeadRequest{
    ChainAddress: targetAddress,
    ReturnBlock:  true,
})

if head.Result.Success {
    fmt.Printf("Latest record: %s\n", string(head.GetBlock().Data.Data))

    for _, sig := range head.GetBlock().NetworkSignatures {
        fmt.Printf("Validated by: %v\n", sig.SignerAddress)
    }
}
```

### Walking a Chain History

Use back-references to traverse a chain's block history.

#### C\#

```csharp
async IAsyncEnumerable<Block> WalkChain(
    IronWeaveCore.IronWeaveCoreClient client, Address chainAddress, int maxBlocks = 100)
{
    var head = await client.GetChainHeadAsync(new GetChainHeadRequest
    {
        ChainAddress = chainAddress,
        ReturnBlock = true
    });

    if (!head.Result.Success) yield break;

    var block = head.Block;
    var count = 0;

    while (block != null && count < maxBlocks)
    {
        yield return block;
        count++;

        var prevRef = block.Header.BackReferences
            .FirstOrDefault(r => r.Address.Value == chainAddress.Value);

        if (prevRef == null || prevRef.Sequence == 0) break;

        var prev = await client.ReadBlockAsync(new ReadBlockRequest { BlockId = prevRef.BlockId });
        if (!prev.Result.Success) break;

        block = prev.Block;
    }
}
```

#### Java

```java
List<Block> walkChain(IronWeaveCoreGrpc.IronWeaveCoreBlockingStub client,
                      Address chainAddress, int maxBlocks) {
    List<Block> blocks = new ArrayList<>();

    GetChainHeadResponse head = client.getChainHead(
        GetChainHeadRequest.newBuilder()
            .setChainAddress(chainAddress)
            .setReturnBlock(true)
            .build());

    if (!head.getResult().getSuccess()) return blocks;

    Block block = head.getBlock();

    while (block != null && blocks.size() < maxBlocks) {
        blocks.add(block);

        BackReference prevRef = block.getHeader().getBackReferencesList().stream()
            .filter(r -> r.getAddress().equals(chainAddress))
            .findFirst().orElse(null);

        if (prevRef == null || prevRef.getSequence() == 0) break;

        ReadBlockResponse prev = client.readBlock(
            ReadBlockRequest.newBuilder().setBlockId(prevRef.getBlockId()).build());

        if (!prev.getResult().getSuccess()) break;
        block = prev.getBlock();
    }

    return blocks;
}
```

#### Python

```python
def walk_chain(stub, chain_address, max_blocks=100):
    """Yield all blocks on a chain, newest first."""
    head = stub.GetChainHead(
        core_service_pb2.GetChainHeadRequest(
            chain_address=chain_address,
            return_block=True,
        )
    )

    if not head.result.success:
        return

    block = head.block
    count = 0

    while block and count < max_blocks:
        yield block
        count += 1

        prev_ref = None
        for ref in block.header.back_references:
            if ref.address == chain_address:
                prev_ref = ref
                break

        if prev_ref is None or prev_ref.sequence == 0:
            break  # Reached genesis

        prev = stub.ReadBlock(
            core_service_pb2.ReadBlockRequest(block_id=prev_ref.block_id)
        )

        if not prev.result.success:
            break

        block = prev.block
```

#### Rust

```rust
async fn walk_chain(
    client: &mut IronWeaveCoreClient<Channel>,
    chain_address: &Address,
    max_blocks: usize,
) -> Vec<Block> {
    let mut blocks = Vec::new();

    let head = client.get_chain_head(GetChainHeadRequest {
        chain_address: Some(chain_address.clone()),
        return_block: true,
    }).await;

    let mut block = match head {
        Ok(resp) => match resp.into_inner().block_or_id {
            Some(BlockOrId::Block(b)) => Some(b),
            _ => None,
        },
        Err(_) => None,
    };

    while let Some(b) = block.take() {
        blocks.push(b.clone());
        if blocks.len() >= max_blocks { break; }

        let prev_ref = b.header.as_ref()
            .and_then(|h| h.back_references.iter()
                .find(|r| r.address.as_ref() == Some(chain_address)));

        let prev_ref = match prev_ref {
            Some(r) if r.sequence > 0 => r,
            _ => break,
        };

        let prev = client.read_block(ReadBlockRequest {
            block_id: prev_ref.block_id.clone(),
        }).await;

        block = prev.ok().and_then(|r| r.into_inner().block);
    }

    blocks
}
```

#### Go

```go
func walkChain(ctx context.Context, client pb.IronWeaveCoreClient,
    chainAddress *pb.Address, maxBlocks int) []*pb.Block {

    var blocks []*pb.Block

    head, err := client.GetChainHead(ctx, &pb.GetChainHeadRequest{
        ChainAddress: chainAddress,
        ReturnBlock:  true,
    })
    if err != nil || !head.Result.Success {
        return blocks
    }

    block := head.GetBlock()

    for block != nil && len(blocks) < maxBlocks {
        blocks = append(blocks, block)

        var prevRef *pb.BackReference
        for _, ref := range block.Header.BackReferences {
            if proto.Equal(ref.Address, chainAddress) {
                prevRef = ref
                break
            }
        }
        if prevRef == nil || prevRef.Sequence == 0 {
            break
        }

        prev, err := client.ReadBlock(ctx, &pb.ReadBlockRequest{BlockId: prevRef.BlockId})
        if err != nil || !prev.Result.Success {
            break
        }
        block = prev.Block
    }

    return blocks
}
```

### Multi-Party Transactions

When a transaction has multiple participants, a block is written to each participant's chain. This is how IronWeave links data across chains.

#### C\#

```csharp
// A transaction between three parties
transaction.Participants.Add(new Participant
{
    Address = sellerAddress,
    IsInitiator = true  // exactly one initiator
});
transaction.Participants.Add(new Participant
{
    Address = buyerAddress,
    IsInitiator = false
});
transaction.Participants.Add(new Participant
{
    Address = escrowAddress,
    IsInitiator = false
});

// After submission, the block appears on all three chains.
// Each chain's copy has back-references linking them together.
```

#### Java

```java
// A transaction between three parties
Transaction transaction = Transaction.newBuilder()
    .setTransactionId(transactionId)
    .addParticipants(Participant.newBuilder()
        .setAddress(sellerAddress).setIsInitiator(true).build())
    .addParticipants(Participant.newBuilder()
        .setAddress(buyerAddress).setIsInitiator(false).build())
    .addParticipants(Participant.newBuilder()
        .setAddress(escrowAddress).setIsInitiator(false).build())
    .setCategory(category)
    .setPayload(payload)
    .setGasLimit(6)
    .build();

// After submission, the block appears on all three chains.
```

#### Python

```python
# A transaction between three parties
transaction.participants.append(
    common_pb2.Participant(address=seller_address, is_initiator=True)
)
transaction.participants.append(
    common_pb2.Participant(address=buyer_address, is_initiator=False)
)
transaction.participants.append(
    common_pb2.Participant(address=escrow_address, is_initiator=False)
)

# After submission, the block appears on all three chains.
# Each chain's copy has back-references linking them together.
```

#### Rust

```rust
// A transaction between three parties
let transaction = Transaction {
    transaction_id: Some(transaction_id),
    participants: vec![
        Participant { address: Some(seller_address), payload_key: None, is_initiator: true },
        Participant { address: Some(buyer_address), payload_key: None, is_initiator: false },
        Participant { address: Some(escrow_address), payload_key: None, is_initiator: false },
    ],
    category: Some(category),
    payload: Some(payload),
    gas_limit: 6,
};

// After submission, the block appears on all three chains.
```

#### Go

```go
// A transaction between three parties
transaction := &pb.Transaction{
    TransactionId: transactionId,
    Participants: []*pb.Participant{
        {Address: sellerAddress, IsInitiator: true},
        {Address: buyerAddress, IsInitiator: false},
        {Address: escrowAddress, IsInitiator: false},
    },
    Category: category,
    Payload:  payload,
    GasLimit: 6,
}

// After submission, the block appears on all three chains.
// Each chain's copy has back-references linking them together.
```

***

## Data Types Reference

All types are defined in Protocol Buffers in the `io.ironweave.v3` package.

### Hash

Block identifier — a cryptographic hash.

```protobuf
message Hash {
  HashType hash_type = 1;  // SHA3_256
  bytes    value     = 2;  // 32 bytes
}
```

### Address

Participant/chain identifier — a compressed public key.

```protobuf
message Address {
  AddressType address_type = 1;  // EC (elliptic curve) or PQC (post-quantum)
  bytes       value        = 2;  // 33 bytes for EC (secp256r1 compressed)
}
```

When serialized to text, addresses use URL-safe Base64 encoding.

### Signature

```protobuf
message Signature {
  SignatureType signature_type = 1;  // ED25519
  Address      signer_address  = 2;  // Public key of the signer
  bytes        value           = 3;  // Signature bytes
}
```

### Transaction

```protobuf
message Transaction {
  UUID                 transaction_id = 1;  // UUIDv7 recommended
  repeated Participant participants   = 2;  // Involved chains
  Category             category       = 3;
  Payload              payload        = 4;  // Your application data
  uint64               gas_limit      = 5;
}
```

### Payload

```protobuf
message Payload {
  string          type       = 1;  // Application-defined (e.g., "application/json")
  bytes           data       = 2;  // Your data
  optional string encoding   = 3;  // e.g., "utf-8"
  optional string encryption = 4;  // e.g., "aes-256-gcm"
}
```

### Block

```protobuf
message Block {
  Hash                block_id                   = 1;
  repeated Signature  network_signatures         = 2;  // Validator signatures
  Header              header                     = 3;
  BlockType           block_type                 = 4;
  Payload             data                       = 5;  // Transaction payload
  Signature           primary_validator_signature = 6;
}
```

### Header

```protobuf
message Header {
  HeaderVersion          version             = 1;
  repeated UUID          transaction_ids     = 2;
  Signature              initiator_signature = 3;
  repeated Participant   participants        = 4;
  repeated BackReference back_references     = 5;
  uint64                 timestamp           = 6;  // Unix nanoseconds
  Category               category            = 7;
  uint64                 gas_limit           = 8;
  uint64                 nonce               = 9;
  repeated GasAllocation gas_allocations     = 10;
}
```

### BackReference

Links a block to the previous block on a participant's chain.

```protobuf
message BackReference {
  Address address  = 1;  // Chain address
  uint64  sequence = 2;  // Block index in this chain
  Hash    block_id = 3;  // Previous block hash
}
```

### Participant

```protobuf
message Participant {
  Address        address      = 1;  // Chain address
  optional bytes payload_key  = 2;  // Encrypted key for payload decryption
  bool           is_initiator = 3;  // True for the transaction initiator
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ironweave.io/developer-guides/ironweave-integration-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
