---
# System prepended metadata

title: Pod Controller Design Documentation
tags: [Autonomy, jim-draft]

---

# Pod Controller Design Documentation


## Functionalities

The pod controller is a service assists its client to interact with his wallet. It owns an independent identity to form a 2-3 multisig wallet with two other public keys from its client. It overall provides:

- Identity management - Only allowed user can access the controller
- Gordian wallet management - A 2-3 multisig wallet will be created and managed using bitcoind
- Messaging client - The Service and clients communicate through messaging protocol
- Bitcoind lifecycle management - Monitor and control the bitcoind process status


## API

### WebAPI

The web API is now only for components of a pod to communicate internally.

- `GET` **/tx-notification/<txid>**


### Messaging API

All the APIs to the pod would go through signal messaging protocol. Here only lists down the description, requests and responses for each APIs.

#### bind

`bind` initiates the account binding process between a client and the controller. A pod controller needs to be bound first before it can provide full functionality.

- Request Body: None
- Response Body: A JSON object of the following keys:
    | Key | Description |
    | - | - |
    | identity | The identity of the pod controller | 
    | nonce | A random nonce | 
    | timestamp | Current timestmap | 
    | signature | A signature of `concat(nonce, timestamp)` | 


#### bind_ack

`bind_ack` is to sign a valid signature back to prove the identity of the request client. The signature is 

- Request Body: A JSON object of the following keys:
    | Key | Description |
    | - | - |
    | timestamp | Current timestmap | 
    | signature | A signature of `concat(bind_nonce, timestamp)` | 
- Response Body: JSON response
    ```
    {
      "status": "",
      "error": ""
    }
    ```

#### create_wallet

create_wallet creates a gordian wallet by a given valid descriptor.

#### bitcoind

bitcoind proxies all bitcoind RPC. The method provides a directly access to a user's bitcoind process.

#### start_bitcoind

:::info
This is a new API
:::

start_bitcoind is to start the bitcoind pod by make a request to `bitcoind-ctl`.

#### stop_bitcoind

:::info
This is a new API
:::

stop_bitcoind is to stop the bitcoind pod by make a request to `bitcoind-ctl`.

:::warning
Normally, the client would not stop its node. This API is reserved for special cases.
:::

#### get_bitcoind_status

:::info
This is a new API
:::

get_bitcoind_status returns a summarized bitcoind information.

## Flows

### Pod Registration (New)

When a pod first boots up, it will register its identity by a pre-set registration token. 

```mermaid
sequenceDiagram
    autonumber
    
    client->>+autonomy_api: Create a new pod
    autonomy_api-->>-client: OK
    autonomy_api->>+autonomy_api: Create a token for registration
    autonomy_api->>node_manager: Spawn a pod with <br> the token attached
    node_manager->>Pod: Create a pod with a token
    Pod->>Pod: Init pod and create its identity
    Pod->>+autonomy_api: Report its DID with the given token
    autonomy_api-->>-Pod: OK
    Pod->>Pod: Mark DID registered
    autonomy_api->>-autonomy_api: Update the pod mapping for a DID
    client->>+autonomy_api: Get pod ID
    autonomy_api-->>-client: Pod ID
```

:::info
For the self-owned pod-controller, a user will need to request a token from the client and set it to its pod-controller manually.
:::

### Binding

Here is a [sequential diagram](/Huid-8ulSb6Lea8brJVzGg#Binding) that illustrates the flow of the binding process.

## Lifecycle management

### Usage Statistics

By collecting data of requests behavior, we can use those dat to optimise the bitcoind lifecycle or schedule a better pod upgrading time, etc.

There are two metrics we are collecting now. They are requests counts by weekly and monthly. The pod-controller counts requests in memory and save it to its persistent storage by a given `flush_interval`. 

**Weekly Usage**

Weekly usage collects requests counts for each weekday. The data structure is represented as following:

| Hour/Weekday| Sun | Mon | Tue | Wed | Thur | Fri | Sat |
| - | - | - | - | - | - | - | - | 
| 0 | | | | | | | |
| 1 | | | | | | | |
| 2 | | | | | | | |
| . | | | | | | | |
| . | | | | | | | |
| 21 | | | | | | | |
| 22 | 5 | 7 | 5 | 4 | 6 | 4 | 8 |
| 23 | | | | | | | |

**Monthly Usage**

Monthly usage collects the daily data for the past 30 days. It collects daily data with the same format to each weekdays above. It saves  in an array. For example,

```
[ day30, day29, ... , day3, yesterday, today ]
```

:::info
For both usage data, when a new day arrive, it removes the old data and creates a fresh row of that day. This fixes the size of  This minimize the size of the usage data.
:::


#### Sequential Diagram

- [Pod Suspend and Resume Sequential Diagram](/k1cujmATThm4gA7UFy978w)


## Transaction Notification

Bitcoind provides a configuration for setting a callback command which would be invoked when a new transaction of watched wallet is comming. Instead of communicate with the Autonomy API directly, it uses `curl` to send transaction information to pod-controller.

The pod-controller decides whether to send a notification to client based on scenarios. If a notification is required, the pod-controller will prepare messages and request to the Autonomy API in order to send a notification to its owner.


```mermaid
sequenceDiagram
    autonumber
    participant bitcoind
    participant pod_controller
    participant autonomy_api
    
    bitcoind->>+pod_controller: send incoming transaction <br> for watched addresses
    pod_controller-->>-bitcoind: ok
    pod_controller->>pod_controller: Detemine whether to send notification
    
    pod_controller->>+autonomy_api: send notification through <br> /api/accounts/notification
    autonomy_api-->>-pod_controller: ok
```

### Autonomy Pod

###### tags: `jim-draft` `Autonomy`

In our cluster, the number of instances is mainly controlled by [cluster-autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler). We start from investigating the parameters of it but without any lucks. The mechanism behind it is to detect the `unschedulable` events for each pods and see if any of nodes in its pool could fulfill those pod. If it can, it will add a node into the cluster. For the scaling down, it is determined by the overall usages of resources in a cluster and is not able to add redundant instances.

Kubernetes introduces the [PrioirtyClass](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) since 1.14. The goal is to replace the unimportant pods when a cluster is out of resources but there are some key services need to be added immediately. Take advantage of this feature, we can create numbers of spare pods which take the same resources but do nothing. When an autonomy pod is added with insufficient instances, those spare pods will be removed and replaced by the pod we just add.

We first create a `PriorityClass` and set its value to `-1`. The default priority value of pods without any specified PriorityClass is `0`. That means if there are any regular pods be added those pods with the PriorityClass set will be taken down.

```go
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: spare-autonomy-pod
value: -1
globalDefault: false
description: "This priority class should be used for spare autonomy pods only."
```

Now, we can create spare pods like following:

```go
apiVersion: apps/v1
kind: Deployment
metadata:
  name: autonomy-mainnet-spare-pod
  namespace: autonomy-wallet
spec:
  selector:
    matchLabels:
      app: spare-autonomy-pod
  replicas: 10
  template:
    metadata:
      labels:
        network: mainnet
        app: spare-autonomy-pod
    spec:
      priorityClassName: spare-autonomy-pod
      nodeSelector:
        bitcoin-network: mainnet
      containers:
        - name: controller
          image: autonomy-wallet:pod-controller-0.0.1
          resources:
            requests:
              cpu: "0"
              memory: "0"
            limits:
              cpu: "0.1"
              memory: "100Mi"
          command:
            - /bin/sleep
            - 30d
```

Here we set the **replica** to 10 that means we are going to reserve 10 spare pods for autonomy pod. In the pod SPEC, we make both the **image** and the **resource requests** to be the same as the normal autonomy pod. Thus, the required images will be downloaded in advance and also make sure the cluster will reserve enough spaces for autonomy pod.

### Reference

- [https://medium.com/scout24-engineering/cluster-overprovisiong-in-kubernetes-79433cb3ed0e](https://medium.com/scout24-engineering/cluster-overprovisiong-in-kubernetes-79433cb3ed0e)
- [Spare Autonomy Pod](/48DwskFFQJSJb-mfMHwLOg)