Skip to content

Commit b4ccad8

Browse files
authored
Merge pull request #4 from 0KnowledgeNetwork/feature/networks
feat: init commands for networks
2 parents 2f95b01 + b05b34f commit b4ccad8

File tree

7 files changed

+229
-10
lines changed

7 files changed

+229
-10
lines changed

.github/workflows/docker.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ on:
77
push:
88
branches:
99
- main
10-
paths:
11-
- ".github/workflows/docker.yml"
10+
paths-ignore:
11+
- '**/*.md'
1212
tags:
1313
- "v*"
1414

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ RUN pnpm run build
2727
# Stage 2: Production image
2828
FROM base AS runner
2929

30+
# install deps used by scripts
31+
RUN apk add --no-cache \
32+
bash \
33+
netcat-openbsd
34+
3035
COPY --from=builder /app/appchain/packages/chain/dist /app/appchain/packages/chain/dist
3136
COPY --from=builder /app/appchain/packages/chain/package.json /app/appchain/packages/chain/
3237
COPY --from=builder /app/appchain/package.json /app/appchain/pnpm-lock.yaml /app/appchain/
@@ -36,4 +41,6 @@ COPY --from=builder /app/appchain-agent/dist ./dist
3641
COPY --from=builder /app/appchain-agent/package.json /app/appchain-agent/pnpm-lock.yaml ./
3742
RUN --mount=type=cache,id=pnpm,target=${PNPM_HOME}/store pnpm install --prod --frozen-lockfile
3843

44+
COPY bin ./bin
45+
3946
USER node

bin/network-init.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/bin/bash
2+
3+
test -z "${2}" && echo "USAGE: ${0} <network_id> <file://build.yml> [data dir]" && exit 1
4+
5+
network_id=${1}
6+
network_build=${2}
7+
dir=${3:-/tmp}
8+
socket=${dir}/appchain.sock
9+
10+
cat <<EOF
11+
Initialize Network:
12+
data directory: '${dir}'
13+
network id: '${network_id}'
14+
network build file: '${network_build}'
15+
EOF
16+
17+
mkdir -p "${dir}"
18+
19+
node \
20+
--experimental-specifier-resolution=node \
21+
--experimental-vm-modules \
22+
--experimental-wasm-modules \
23+
--experimental-wasm-threads \
24+
--no-warnings \
25+
dist/index.js \
26+
--admin \
27+
--debug \
28+
--ipfs \
29+
--ipfs-data ${dir}/ipfs \
30+
--key ${dir}/admin.key \
31+
--listen \
32+
--nonce \
33+
--socket ${socket} \
34+
--tx-status-retries 0 \
35+
&
36+
PID=$!
37+
38+
# Wait for the socket to exist
39+
while [ ! -S ${socket} ]; do sleep 1; done
40+
41+
appchain () {
42+
echo "$@" | tee >(cat >&2) | nc -U -q 0 ${socket}
43+
sleep 0.2s
44+
}
45+
46+
appchain admin setAdmin
47+
appchain nodes setRegistrationStake 0
48+
appchain nodes openRegistration
49+
appchain networks register ${network_id} ${network_build}
50+
sleep 1s # avoid nonce collision from slower register command (IPFS)
51+
appchain networks setActive ${network_id}
52+
53+
kill ${PID}
54+
55+
rm -f ${socket}

clients/go/chainbridge/chainbridge.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ type CommandResponse struct {
5151
TX string `cbor:"tx,omitempty"`
5252
}
5353

54+
type Network struct {
55+
Identifier string `cbor:"identifier"`
56+
Parameters []byte `cbor:"parameters"`
57+
}
58+
5459
type Node struct {
5560
Administrator string `cbor:"administrator"`
5661
Identifier string `cbor:"identifier"`
@@ -65,11 +70,14 @@ var (
6570
ErrNoData = errors.New("ChainBridge: no data")
6671

6772
// command response errors returned by the appchain
68-
Err_nodes_alreadyRegistered = "Node already registered"
73+
Err_networks_alreadyRegistered = "Network already registered"
74+
Err_nodes_alreadyRegistered = "Node already registered"
6975
)
7076

7177
// appchain-agent commands; cuz compile errors are better than runtime errors
7278
var (
79+
Cmd_networks_getNetwork = "networks getNetwork %s"
80+
Cmd_networks_register = "networks register %s"
7381
Cmd_nodes_getNode = "nodes getNode %s"
7482
Cmd_nodes_register = "nodes register %s %d %d"
7583
Cmd_pki_getDocucment = "pki getDocument %d"

clients/go/cmd/client/main.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,42 @@ func sendCommand(command string, payload []byte) {
2424
}
2525
}
2626

27+
func networkRegistration() {
28+
network1 := chainbridge.Network{
29+
Identifier: "0x000",
30+
Parameters: []byte("pA: 1\npB: 2\npC: 3\n"),
31+
}
32+
log.Printf("Network in: %+v", network1)
33+
34+
// register the network
35+
command := fmt.Sprintf(
36+
chainbridge.Cmd_networks_register,
37+
network1.Identifier,
38+
)
39+
response, err := chBridge.Command(command, network1.Parameters)
40+
log.Printf("Response (%s): %+v\n", command, response)
41+
if err != nil {
42+
log.Printf("ChainBridge command error: %v", err)
43+
}
44+
if response.Error != "" {
45+
log.Printf("ChainBridge response error: %v", response.Error)
46+
}
47+
48+
// retrieve the network
49+
command = fmt.Sprintf(chainbridge.Cmd_networks_getNetwork, network1.Identifier)
50+
response, err = chBridge.Command(command, nil)
51+
if err != nil {
52+
log.Printf("ChainBridge command error: %v", err)
53+
} else {
54+
log.Printf("Response (%s): %+v\n", command, response)
55+
}
56+
var network2 chainbridge.Network
57+
if err := chBridge.DataUnmarshal(response, &network2); err != nil {
58+
log.Printf("ChainBridge data error: %v", err)
59+
}
60+
log.Printf("Network out: %+v", network2)
61+
}
62+
2763
func nodeRegistration() {
2864
node1 := chainbridge.Node{
2965
Identifier: "node-5000",
@@ -35,7 +71,7 @@ func nodeRegistration() {
3571

3672
// register the node
3773
command := fmt.Sprintf(
38-
"nodes register %s %d %d",
74+
chainbridge.Cmd_nodes_register,
3975
node1.Identifier,
4076
chainbridge.Bool2int(node1.IsGatewayNode),
4177
chainbridge.Bool2int(node1.IsServiceNode))
@@ -49,7 +85,7 @@ func nodeRegistration() {
4985
}
5086

5187
// retrieve the node
52-
command = fmt.Sprintf("nodes getNode %s", node1.Identifier)
88+
command = fmt.Sprintf(chainbridge.Cmd_nodes_getNode, node1.Identifier)
5389
response, err = chBridge.Command(command, nil)
5490
if err != nil {
5591
log.Printf("ChainBridge command error: %v", err)
@@ -89,7 +125,7 @@ func pkiMixDescriptor() {
89125
id := "node-5000"
90126

91127
// send encoded data as payload to IPFS and the appchain
92-
command := fmt.Sprintf("pki setMixDescriptor %d %s", epoch, id)
128+
command := fmt.Sprintf(chainbridge.Cmd_pki_setMixDescriptor, epoch, id)
93129
response, err := chBridge.Command(command, enc)
94130
log.Printf("Response (%s): %+v\n", command, response)
95131
if err != nil {
@@ -100,7 +136,7 @@ func pkiMixDescriptor() {
100136
}
101137

102138
// retrieve the stored data
103-
command = fmt.Sprintf("pki getMixDescriptor %d %s", epoch, id)
139+
command = fmt.Sprintf(chainbridge.Cmd_pki_getMixDescriptor, epoch, id)
104140
response, err = chBridge.Command(command, nil)
105141
if err != nil {
106142
log.Printf("ChainBridge command error: %v", err)
@@ -122,8 +158,7 @@ func pkiMixDescriptor() {
122158
}
123159

124160
func getGenesisEpoch() uint64 {
125-
command := "pki getGenesisEpoch"
126-
response, err := chBridge.Command(command, nil)
161+
response, err := chBridge.Command(chainbridge.Cmd_pki_getGenesisEpoch, nil)
127162
if err != nil {
128163
log.Printf("ChainBridge command error: %v", err)
129164
}
@@ -186,6 +221,7 @@ func main() {
186221
}
187222
wg.Wait()
188223

224+
networkRegistration()
189225
nodeRegistration()
190226

191227
log.Printf("genesisEpoch: %d", getGenesisEpoch())

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"scripts": {
99
"lint": "eslint .",
1010
"build": "tsc -p tsconfig.json",
11-
"agent": "node --experimental-specifier-resolution=node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads dist/index.js"
11+
"agent": "node --experimental-specifier-resolution=node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads --no-warnings dist/index.js"
1212
},
1313
"devDependencies": {
1414
"@types/node": "^22.4.1",

src/index.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
Balance,
1717
CID,
1818
MixDescriptor,
19+
Network,
1920
Node,
2021
TreasuryId,
2122
client,
@@ -60,6 +61,19 @@ const getPrivateKeyFromFile = async (path: string): Promise<PrivateKey> => {
6061
}
6162
};
6263

64+
const getBytesFromFile = async (path: string): Promise<Uint8Array> => {
65+
const f = path.replace(/^file:\/\//, "");
66+
return await fs.readFile(f);
67+
};
68+
69+
const putBytesToFile = async (
70+
path: string,
71+
data: Uint8Array,
72+
): Promise<void> => {
73+
const f = path.replace(/^file:\/\//, "");
74+
return await fs.writeFile(f, data);
75+
};
76+
6377
const program = new Command();
6478
program.name("cli").description("appchain cli");
6579

@@ -130,6 +144,7 @@ console.log("opts", opts);
130144
await client.start();
131145
const admin = client.runtime.resolve("Admin");
132146
const faucet = client.runtime.resolve("Faucet");
147+
const networks = client.runtime.resolve("Networks");
133148
const nodes = client.runtime.resolve("Nodes");
134149
const pki = client.runtime.resolve("Pki");
135150
const token = client.runtime.resolve("Token");
@@ -303,6 +318,104 @@ const executeCommand = async (
303318
});
304319
}
305320

321+
const commandNetworks = program
322+
.command("networks")
323+
.description("networks commands");
324+
commandNetworks
325+
.command("register <identifier> [file://]")
326+
.description("register a network <parameters := file:// OR payload>")
327+
.action(async (identifier: string, file?: string) => {
328+
if (!ipfsNode) return callback(responses.IPFS_NOT_STARTED);
329+
330+
// get network parameters from file or payload
331+
const _payload = file ? await getBytesFromFile(file) : payload;
332+
if (!_payload) return callback(responses.PAYLOAD_UNDEFINED);
333+
334+
const parametersCID = await ipfsNode.putBytes(_payload);
335+
336+
const r = await txer(async () => {
337+
await networks.register(
338+
new Network({
339+
identifier: CircuitString.fromString(identifier),
340+
parametersCID: CID.fromString(parametersCID),
341+
}),
342+
);
343+
});
344+
345+
const debug = { identifier, cid: parametersCID, tx: r.tx };
346+
callback({ id, ...r }, debug);
347+
});
348+
commandNetworks
349+
.command("getActive")
350+
.description("get the identifier of the active network(s)")
351+
.action(async () => {
352+
const nid = (await client.query.runtime.Networks.activeNetwork.get()) as
353+
| Field
354+
| undefined;
355+
if (!nid) return callback(responses.RECORD_NOT_FOUND);
356+
357+
// retrieve the string form of the network identifier
358+
const n = await client.query.runtime.Networks.networks.get(nid);
359+
if (!n) return callback(responses.RECORD_NOT_FOUND);
360+
361+
callback({ id, status: SUCCESS, data: n.identifier.toString() });
362+
});
363+
commandNetworks
364+
.command("getNetwork <identifier> [file://]")
365+
.description(
366+
'get network by id; "_" for active, optionally save params to file',
367+
)
368+
.action(async (identifier: string, file?: string) => {
369+
if (!ipfsNode) return callback(responses.IPFS_NOT_STARTED);
370+
371+
var networkID: Field;
372+
if (identifier === "_") {
373+
const nid =
374+
(await client.query.runtime.Networks.activeNetwork.get()) as
375+
| Field
376+
| undefined;
377+
if (!nid) return callback(responses.RECORD_NOT_FOUND);
378+
networkID = nid;
379+
} else {
380+
networkID = Network.getID(CircuitString.fromString(identifier));
381+
}
382+
383+
const network = (await client.query.runtime.Networks.networks.get(
384+
networkID,
385+
)) as Network | undefined;
386+
if (!network) return callback(responses.RECORD_NOT_FOUND);
387+
388+
const cid = network.parametersCID.toString();
389+
const parameters = await ipfsNode.getBytes(cid);
390+
if (file) await putBytesToFile(file, parameters);
391+
392+
const { parametersCID, ...rest } = Network.toObject(network);
393+
const data = {
394+
parameters,
395+
...rest,
396+
};
397+
398+
const debug = { identifier, cid, network: rest };
399+
callback(
400+
{
401+
id,
402+
status: SUCCESS,
403+
data: opts.socketFormat === "cbor" ? cbor.encode(data) : data,
404+
},
405+
debug,
406+
);
407+
});
408+
commandNetworks
409+
.command("setActive <identifier>")
410+
.description("set the active network")
411+
.action(async (identifier: string) => {
412+
const networkID = Network.getID(CircuitString.fromString(identifier));
413+
const r = await txer(async () => {
414+
await networks.setActiveNetwork(networkID);
415+
});
416+
callback({ id, ...r });
417+
});
418+
306419
const commandNodes = program.command("nodes").description("nodes commands");
307420
commandNodes
308421
.command("isRegistrationOpen")

0 commit comments

Comments
 (0)