> For the complete documentation index, see [llms.txt](https://canopy-network.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://canopy-network.gitbook.io/docs/app-builder/run-test-and-configure.md).

# Run, Test, and Configure

### **Running the Chain**

#### **Standard start**

From the repo root:

```shell
canopy start
```

This does three things: starts the Canopy node, calls `pluginctl.sh` to launch your plugin binary, and begins producing blocks. Your plugin connects to the FSM over a Unix socket within a few seconds of startup.

The node writes logs to stdout and to `~/.canopy/canopy.log`. Plugin logs go to a separate file:

```shell
# Follow both logs simultaneously
tail -f ~/.canopy/canopy.log &
tail -f /tmp/plugin/go-plugin.log
```

#### **Managing the plugin separately**

If you need to restart just the plugin without restarting the full node (useful when iterating on plugin logic), you can use `pluginctl.sh` directly:

```shell
cd plugin/go
./pluginctl.sh stop
make build
./pluginctl.sh start
./pluginctl.sh status   # verify it's running
```

The node will reconnect to the plugin automatically once it's back up. The standard `canopy start` approach is recommended for normal development — call `pluginctl.sh` directly only when you have a specific reason to.

#### **Stopping cleanly**

`Ctrl+C` on the `canopy start` process sends SIGTERM to both the node and the plugin. The plugin script has a 10-second graceful shutdown window before it force-kills the process.

#### **Using the data directory flag**

If you want to run multiple chains concurrently (e.g., two different app configurations), use separate data directories:

```shell
canopy start --data-dir ~/.canopy-myapp
canopy start --data-dir ~/.canopy-other
```

Each data directory gets its own `config.json`, `genesis.json`, and keystore.

***

### **The Tutorial Test Suite**

The plugin template ships with a full integration test suite in `plugin/go/tutorial/`. It demonstrates the complete flow: creating accounts, signing transactions, submitting them to the RPC, waiting for block inclusion, and verifying balances.

**Before running tests,** make sure:

1. Canopy is running locally with your plugin enabled (`canopy start`)
2. The plugin has successfully connected (you see `plugin connected: ...` in the logs)

Run the tests:

```shell
cd plugin/go/tutorial
go test -v -run TestPluginTransactions -timeout 120s
```

The 120-second timeout is intentional — each transaction needs up to \~24 seconds to land in a block, and the test runs multiple transactions in sequence.

What the test does:

1. Creates two accounts via the admin keystore RPC
2. Submits a faucet transaction to fund account 1 (mints tokens from nothing — this is a custom tx type added in the tutorial)
3. Sends tokens from account 1 to account 2
4. Sends a reward from account 2 back to account 1
5. Verifies final balances match expected values after each step

***

### **How Transaction Signing Works**

This is the most technically dense part of Canopy development, and it's where most developers hit their first wall. Understanding it fully will save you hours.

#### **The sign-bytes rule**

You sign the **protobuf-encoded bytes of the `Transaction` message, with the `signature` field set to its zero value (empty)**. You do not sign JSON. You do not sign the raw message bytes. You sign the full outer `Transaction` struct.

The `Transaction` proto message looks like this (from `proto/tx.proto`):

```protobuf
message Transaction {
  string               message_type    = 1;
  google.protobuf.Any  msg             = 2;
  Signature            signature       = 3;  // leave empty when computing sign bytes
  uint64               created_height  = 4;
  uint64               time            = 5;
  uint64               fee             = 6;
  string               memo            = 7;
  uint64               network_id      = 8;
  uint64               chain_id        = 9;
}
```

To get the sign bytes, construct the full `Transaction`, set `signature` to `nil`, marshal it with `proto.Marshal`, then sign those bytes.

#### **The signing algorithm**

Canopy supports **BLS12-381**, **ED25519**, and **SECP256k1** signatures. The key type used for a transaction is determined by the private key that was used to create the account. The tutorial uses BLS12-381 via the drand kyber library:

```go
// From plugin/go/tutorial/crypto/bls.go

// Load a private key from its hex representation
privKey, err := StringToBLS12381PrivateKey(hexPrivKey)

// Sign bytes
sig, err := privKey.Sign(signBytes)

// The resulting signature is 96 bytes (BLS12-381)
// The public key is 48 bytes (BLS12-381)
```

Validator keys generated by `canopy start` are BLS12-381 by default. If you create keys through other tooling or use a different key type, use the corresponding signing library.

#### **Full buildSignAndSendTx pattern**

Here is the complete flow from the tutorial test suite, condensed:

```go
func buildSignAndSendTx(msg proto.Message, typeURL string, privKeyHex string, pubKeyHex string, fee uint64, height uint64) error {
    // 1. Wrap the message in a protobuf Any
    anyMsg, _ := anypb.New(msg)

    // 2. Build the Transaction with signature empty
    tx := &types.Transaction{
        MessageType:   typeURL,
        Msg:           anyMsg,
        CreatedHeight: height,
        Time:          uint64(time.Now().UnixNano()),
        Fee:           fee,
        NetworkId:     1,
        ChainId:       1,
        // Signature field intentionally omitted / zero
    }

    // 3. Get sign bytes: deterministic protobuf marshal with signature = nil
    signBytes, _ := proto.Marshal(tx)

    // 4. Sign with BLS12-381
    privKey, _ := crypto.StringToBLS12381PrivateKey(privKeyHex)
    sig, _ := privKey.Sign(signBytes)

    // 5. Attach the signature
    pubKeyBytes, _ := hex.DecodeString(pubKeyHex)
    tx.Signature = &types.Signature{
        PublicKey: pubKeyBytes,
        Signature: sig,
    }

    // 6. Marshal to JSON for the HTTP body
    // Use protojson — not encoding/json — to correctly encode bytes as base64
    body, _ := protojson.Marshal(tx)

    // 7. POST to /v1/tx
    resp, _ := http.Post("http://localhost:50002/v1/tx", "application/json", bytes.NewReader(body))
    // ... handle response
}
```

Two things that catch developers off guard:

* **Use `protojson.Marshal`, not `json.Marshal`.** Standard `encoding/json` will encode `[]byte` fields as base64 but without the padding format that the Canopy RPC expects. `protojson` handles this correctly.
* **The `time` field must be set.** It's used for entropy and deduplication. Use `time.Now().UnixNano()`.

#### **The NewTransaction helper**

The Canopy library provides a `NewTransaction` helper that builds and signs the transaction struct in one call, so you don't have to wire the steps together manually:

```go
func NewTransaction(
    pk      crypto.PrivateKeyI,
    msg     lib.MessageI,
    networkId, chainId, fee, height uint64,
    memo    string,
) (lib.TransactionI, lib.ErrorI)
```

This function handles constructing the `Transaction` proto struct, computing the sign bytes, signing with the provided private key, and attaching the signature. If you're building against the Canopy library directly (rather than the standalone tutorial), prefer this helper over assembling the transaction by hand. The tutorial test suite uses the manual pattern to make each step explicit; your production tooling should use `NewTransaction`.

#### **Getting keys from the keystore**

The admin RPC exposes private keys for local development. In production these never leave the node, but for testing this is how you get the key material you need to sign:

```shell
# Via CLI
canopy admin ks-get <address or nickname> --password ""

# Via HTTP (port 50003)
curl -s -X POST http://localhost:50003/v1/admin/keystore-get \
  -H "Content-Type: application/json" \
  -d '{"addressOrNickname": "validator", "password": ""}'
```

Response includes both the public key (hex) and the encrypted private key. For local development with no password set, the raw private key is accessible directly.

***

### **Writing Your Own Tests**

The tutorial test gives you the full skeleton. For your custom transaction types, follow the same pattern:

1. Create test accounts via `keystoreNewKey`
2. Fund them (via your faucet transaction type, or a direct send from the genesis validator)
3. Call `buildSignAndSendTx` with your custom message type
4. Call `waitForTxInclusion` — polls `/v1/query/txs-by-sender` until the tx appears or timeout
5. Call `checkTxNotFailed` — checks `/v1/query/failed-txs` to confirm the tx didn't error
6. Query the expected state change and assert the result

The test is a regular Go test file. Run it with `go test` while the node is running. There's no mock or in-process test harness — the suite talks to a live local node over HTTP, which means you're testing the actual plugin behavior end-to-end.

**AI prompt:** "Using the test pattern in plugin/go/tutorial/rpc\_test.go as a reference, write a Go test function `TestPostMessage` that: creates one test account, submits a `post_message` transaction, waits for inclusion, queries the state for the stored Post object by key, and asserts the author address and content match what was submitted."

***

### **Common Configuration Changes**

All configuration lives in `~/.canopy/config.json`. Here are the fields you're most likely to tune during development.

#### **Speed up block time**

The default block time is \~22 seconds. For development, you'll want blocks faster:

```json
{
  "newHeightTimeoutMS":    1000,
  "electionTimeoutMS":      500,
  "electionVoteTimeoutMS":  500,
  "proposeTimeoutMS":       800,
  "proposeVoteTimeoutMS":  1000,
  "precommitTimeoutMS":     600,
  "precommitVoteTimeoutMS": 600,
  "commitTimeoutMS":        500
}
```

This brings block time down to roughly 5 seconds. Don't go lower than \~300ms per phase or the single-validator node can start having timing issues with itself.

#### **Increase plugin timeout**

The default `pluginTimeoutMS` is 1000ms. If your `DeliverTx` does heavy computation (unlikely but possible), increase this:

```json
{
  "pluginTimeoutMS": 5000
}
```

If your plugin regularly exceeds this timeout, Canopy will log errors and the node will stall waiting for plugin responses. This is the first thing to check if you see timeout-related errors in the node log.

#### **Log level**

```json
{
  "logLevel": "debug"
}
```

Valid values: `"debug"`, `"info"`, `"warn"`, `"error"`. Debug mode is noisy but gives you the full consensus round-trip and plugin communication trace.

#### **Ports**

If you need to run Canopy on non-default ports (e.g., two nodes on the same machine):

```json
{
  "rpcPort":    "51002",
  "adminPort":  "51003"
}
```

Update `rpcURL` and `adminRPCUrl` to match:

```json
{
  "rpcURL":       "http://localhost:51002",
  "adminRPCUrl":  "http://localhost:51003"
}
```

#### **In-memory mode**

For fast unit-style tests that don't need persistence:

```json
{
  "inMemory": true
}
```

State is not written to disk. The chain resets on each restart. Useful for test runs where you want a clean state every time.

#### **Faucet address**

If you want a dedicated faucet account that the node auto-funds on genesis:

```json
{
  "faucetAddress": "your-address-here"
}
```

***

### **Common Mistakes**

These are the issues that come up most often when building on Canopy.

**`SupportedTransactions` and `TransactionTypeUrls` are misaligned**

The arrays must be in the same order. If they're not, transactions route to the wrong handler and fail with a message cast error. Always add entries to both arrays at the same index.

**Signing JSON instead of protobuf bytes**

This is the most common integration bug. The sign bytes must be `proto.Marshal(txWithEmptySignature)`. If you sign `json.Marshal(tx)` or `protojson.Marshal(tx)`, the server will reject the transaction with a signature verification failure every time.

**`CheckTx` trying to read state**

`CheckTx` is stateless. Calling `StateRead` inside `CheckTx` will return an error because the FSM doesn't open a state context for mempool validation. If you need to validate something that requires state (e.g., "does this post ID exist"), do it in `DeliverTx` and return an error there instead.

**Address length isn't exactly 20 bytes**

Canopy addresses are derived from a SHA256 hash of the public key, truncated to 20 bytes. If you're building test transactions by hand and pass an address of any other length, `CheckTx` will reject it with code 12 (invalid address). When testing with addresses from the keystore RPC, they're already in the right format — this only bites you when constructing test data manually.

**Forgetting `proto.Marshal` is deterministic, `protojson.Marshal` is not**

Use `proto.Marshal` for sign bytes. It produces a deterministic encoding. `protojson.Marshal` (the JSON form) is what you use for the HTTP request body, but its output is not deterministic across library versions, making it unsuitable for signing.

**Plugin binary not rebuilt after contract changes**

`canopy start` launches the plugin binary that's already on disk. If you edit `contract.go` but forget to `make build` before restarting, you're running stale code. Make a habit of `make build && canopy start`.

**Block finalization takes longer than expected**

The default block time is \~22 seconds. A common mistake is querying state immediately after submitting a transaction and seeing no change, then assuming something is broken. Wait at least one full block time. In tests, always use a `waitForTxInclusion` loop with a timeout of at least 30 seconds.

**`DeliverTx` error still charges the fee**

If your `DeliverTx` returns an error, the transaction is still recorded in the block as failed and the sender is still charged the fee. Design your `CheckTx` to catch everything that would cause `DeliverTx` to fail, so bad transactions are rejected before they enter a block.

***

**Next:** [RPC Reference](/docs/app-builder/rpc-reference.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://canopy-network.gitbook.io/docs/app-builder/run-test-and-configure.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
