Skip to content

Commit

Permalink
minimal implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
fuxingloh committed Mar 12, 2024
1 parent 3ee8adc commit 815f569
Show file tree
Hide file tree
Showing 79 changed files with 7,736 additions and 399 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ coverage
# tsconfig.json
packages/*/**/*.d.ts
packages/*/**/*.d.ts.map
packages/*/**/*.js
packages/*/**/*.js

# Karfia
.karfia
1 change: 1 addition & 0 deletions .idea/karfia.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

374 changes: 5 additions & 369 deletions LICENSE

Large diffs are not rendered by default.

40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
This is an experimental project to explore testing
and deploying blockchain nodes at scale with an emphasis on local development and testing with batteries
included for shipping to the cloud
(or on-premise).
> This is an **experimental** project to explore testing
> and deploying blockchain nodes at scale with an emphasis on local development and testing with batteries
> included for shipping to the cloud.
Karfia is an open-source framework to define, test, deploy,
and scale blockchain nodes on container-orchestration platforms.

It packages complex blockchain nodes into a single definition that can be easily deployed
and managed on Container-capable platforms such as Kubernetes,
Docker Compose, and Testcontainers.

## Motivation

> Karfia (Καρφί, pronounced kar-fee) in Greek refers to a nail;
Not so long ago, we had a single binary called Bitcoin Core that we could easily run with a single command.
Having access to this single binary equated to having access to the node, wallet, and miner.
It was simple and easy to use.
If you had the binary and enough resources, you could run a full node that could do "everything".

Then came the era of Bitcoin forks, new binaries, and new commands.
Running a node is no longer as simple as typing `bitcoind` into the terminal.
You need to know which binary to run, which command to use, and which flag to set.

Soon enough, additional tools and capabilities were added to the mix.
You need to run a separate binary for the wallet, another for the miner, another for the indexer,
another for the consensus, another for the explorer, another for the API, and another for the RPC.

As the complexity grew, it alienated the democratization of the blockchain.
Running a node and participating in the network is no longer easy.
Most developers today, even those familiar with the blockchain,
rely on third-party providers to provide them with connectivity to the network.

Karfia aims to solve this by restoring the simplicity of participating in the network,
regardless of purpose, scale, complexity, and tenancy,
to accelerate the adoption of blockchain technology.
File renamed without changes.
83 changes: 83 additions & 0 deletions definitions/bip122-000000000019d6689c085ae165831e93/bitcoind.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"$schema": "./node_modules/karfia-definition/index.json",
"id": "bip122:000000000019d6689c085ae165831e93/bitcoind:25.1",
"caip2": "bip122:000000000019d6689c085ae165831e93",
"name": "Bitcoin Mainnet",
"environment": {
"RPC_USER": {
"type": "RandomBytes",
"length": 16,
"encoding": "hex"
},
"RPC_PASSWORD": {
"type": "RandomBytes",
"length": 16,
"encoding": "hex"
}
},
"containers": {
"bitcoind": {
"image": "docker.io/kylemanna/bitcoind@sha256:1492fa0306cb7eb5de8d50ba60367cff8d29b00b516e45e93e05f8b54fa2970e",
"source": "https://github.com/kylemanna/docker-bitcoind",
"endpoints": {
"p2p": {
"port": 8333
},
"rpc": {
"port": 8332,
"protocol": "HTTP JSON-RPC 2.0",
"authorization": {
"type": "HttpBasic",
"username": {
"key": "RPC_USER"
},
"password": {
"key": "RPC_PASSWORD"
}
},
"probes": {
"readiness": {
"method": "getblockchaininfo",
"params": [],
"match": {
"result": {
"type": "object",
"properties": {
"blocks": {
"type": "number"
}
},
"required": ["blocks"]
}
}
}
}
}
},
"resources": {
"cpu": 1,
"memory": 2048
},
"environment": {
"DISABLE_WALLET": "1",
"RPCUSER": {
"key": "RPC_USER"
},
"RPCPASSWORD": {
"key": "RPC_PASSWORD"
}
},
"volumes": {
"persistent": {
"paths": ["/bitcoin/.bitcoin"],
"size": {
"initial": "600G",
"from": "2024-01-01",
"growth": "20G",
"rate": "monthly"
}
}
}
}
}
}
200 changes: 200 additions & 0 deletions definitions/bip122-000000000019d6689c085ae165831e93/bitcoind.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { KarfiaAgentContainer, KarfiaTestContainer, KarfiaTestcontainers } from 'karfia-testcontainers';
import waitForExpect from 'wait-for-expect';

import definition from './bitcoind.json';

let testcontainers: KarfiaTestcontainers;

beforeAll(async () => {
testcontainers = await KarfiaTestcontainers.start(definition);
});

afterAll(async () => {
await testcontainers.stop();
});

describe('bitcoind', () => {
let bitcoind: KarfiaTestContainer;

beforeAll(() => {
bitcoind = testcontainers.getContainer('bitcoind');
});

it('should get rpc port', async () => {
const port = bitcoind.getHostPort('rpc');
expect(port).toStrictEqual(expect.any(Number));
});

it('should rpc getblockchaininfo', async () => {
const response = await bitcoind.rpc({
method: 'getblockchaininfo',
});

expect(response.status).toStrictEqual(200);

expect(await response.json()).toMatchObject({
result: {
bestblockhash: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
chain: 'main',
},
});
});

it('should rpc getblockcount', async () => {
const response = await bitcoind.rpc({
method: 'getblockcount',
});

expect(response.status).toStrictEqual(200);

expect(await response.json()).toMatchObject({
result: 0,
});
});

it('should rpc getblock', async () => {
const response = await bitcoind.rpc({
method: 'getblock',
params: ['000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', 2],
});

expect(response.status).toStrictEqual(200);

expect(await response.json()).toEqual({
error: null,
id: expect.any(Number),
result: {
bits: '1d00ffff',
chainwork: '0000000000000000000000000000000000000000000000000000000100010001',
confirmations: 1,
difficulty: 1,
hash: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
height: 0,
mediantime: 1231006505,
merkleroot: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
nTx: 1,
nonce: 2083236893,
size: 285,
strippedsize: 285,
time: 1231006505,
tx: [
{
hash: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
hex: '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000',
locktime: 0,
size: 204,
txid: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
version: 1,
vin: [
{
coinbase:
'04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73',
sequence: 4294967295,
},
],
vout: [
{
n: 0,
scriptPubKey: {
asm: '04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG',
desc: 'pk(04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f)#vlz6ztea',
hex: '4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac',
type: 'pubkey',
},
value: 50,
},
],
vsize: 204,
weight: 816,
},
],
version: 1,
versionHex: '00000001',
weight: 1140,
},
});
});

describe.skip('synchronization', () => {
// Takes too long to run and is not deterministic
beforeAll(async () => {
await waitForExpect(async () => {
const response = await bitcoind.rpc({
method: 'getblockcount',
params: [],
});

const result = ((await response.json()) as any).result;
expect(result).toBeGreaterThan(1);
}, 30000);
});

it('should bitcoind.rpc getblock(1)', async () => {
const response = await bitcoind.rpc({
method: 'getblock',
params: ['00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048'],
});

expect(response.status).toStrictEqual(200);
expect(await response.json()).toEqual({});
});
});
});

describe('karfia-agent', () => {
let agent: KarfiaAgentContainer;

beforeAll(() => {
agent = testcontainers.getKarfiaAgent();
});

it('should get karfia-agent/deployment', async () => {
const result = await agent.getDeployment();
expect(result).toMatchObject({
deploymentId: testcontainers.getDeploymentId(),
definitionId: definition.id,
caip2: definition.caip2,
name: definition.name,
});
});

it('should get karfia-agent/definition', async () => {
const result = await agent.getDefinition();
const expected = {
...definition,
$schema: undefined,
};
delete expected.$schema;
expect(result).toMatchObject(expected);
});

it('should get karfia-agent/probes/startup', async () => {
const response = await agent.probe('startup');
expect(response.status).toStrictEqual(200);
expect(await response.json()).toMatchObject({
ok: true,
});
});

it('should get karfia-agent/probes/liveness', async () => {
const response = await agent.probe('liveness');
expect(response.status).toStrictEqual(200);
expect(await response.json()).toMatchObject({
ok: true,
});
});

it('should get karfia-agent/probes/readiness', async () => {
const response = await agent.probe('readiness');
expect(response.status).toStrictEqual(200);
expect(await response.json()).toMatchObject({
containers: {
bitcoind: {
ok: true,
},
},
ok: true,
});
});
});
28 changes: 28 additions & 0 deletions definitions/bip122-000000000019d6689c085ae165831e93/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@karfia/bip122-000000000019d6689c085ae165831e93",
"version": "0.0.0",
"private": false,
"license": "MPL 2.0",
"scripts": {
"lint": "eslint .",
"test": "jest"
},
"lint-staged": {
"*": [
"prettier --write --ignore-unknown"
],
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
},
"jest": {
"preset": "@workspace/jest-preset"
},
"devDependencies": {
"@workspace/jest-preset": "workspace:*",
"@workspace/tsconfig": "workspace:*",
"karfia-definition": "workspace:*",
"karfia-testcontainers": "workspace:*"
}
}
File renamed without changes.
Loading

0 comments on commit 815f569

Please sign in to comment.