tranche 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
core.py ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: tranche.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 02:20
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Third-party packages ###
14
+ from click import group
15
+
16
+ ### Local modules ###
17
+ from src.commands import clean, cluster, mine, nodekeys, ping_pong, remove_deprecated, setup
18
+
19
+
20
+ @group
21
+ def cli() -> None:
22
+ """tranche"""
23
+
24
+
25
+ cli.add_command(clean, "clean")
26
+ cli.add_command(cluster, "cluster")
27
+ cli.add_command(mine, "mine")
28
+ cli.add_command(nodekeys, "nodekeys")
29
+ cli.add_command(ping_pong, "ping-pong")
30
+ cli.add_command(remove_deprecated, "remove-deprecated")
31
+ cli.add_command(setup, "setup")
32
+
33
+
34
+ if __name__ == "__main__":
35
+ cli()
src/__init__.py ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/__init__.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Local modules ###
14
+ from src.commands import clean, cluster, nodekeys
15
+ from src.schemas import (
16
+ ClusterEnum,
17
+ BlockchainInfo,
18
+ ImageAlias,
19
+ MutexOption,
20
+ NewAddress,
21
+ NodeInfo,
22
+ Service,
23
+ ServiceName,
24
+ )
25
+
26
+
27
+ __all__ = [
28
+ "ClusterEnum",
29
+ "BlockchainInfo",
30
+ "ImageAlias",
31
+ "NewAddress",
32
+ "MutexOption",
33
+ "NodeInfo",
34
+ "Service",
35
+ "ServiceName",
36
+ "clean",
37
+ "cluster",
38
+ "nodekeys",
39
+ ]
40
+
41
+ __version__ = "0.1.0"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/__init__.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Local modules ###
14
+ from src.commands.clean import clean
15
+ from src.commands.cluster import cluster
16
+ from src.commands.mine import mine
17
+ from src.commands.nodekeys import nodekeys
18
+ from src.commands.ping_pong import ping_pong
19
+ from src.commands.remove_deprecated import remove_deprecated
20
+ from src.commands.setup import setup
21
+
22
+ __all__ = ["clean", "cluster", "mine", "nodekeys", "ping_pong", "remove_deprecated", "setup"]
src/commands/clean.py ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/clean.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from re import match
15
+
16
+ ### Third-party packages ###
17
+ from click import Context, command, pass_context
18
+ from docker import DockerClient, from_env
19
+ from docker.errors import NotFound
20
+ from docker.models.networks import Network
21
+ from rich.progress import track
22
+
23
+ ### Local modules ###
24
+ from src.configs import NETWORK
25
+
26
+
27
+ @command
28
+ @pass_context
29
+ def clean(context: Context) -> None:
30
+ """Remove all active "tranche-*" containers, drop network."""
31
+ client: DockerClient = from_env()
32
+ if client.ping():
33
+ for container in track(client.containers.list(), description="Remove active containers."):
34
+ if match(r"tranche-*", container.name) is not None:
35
+ container.stop()
36
+ container.remove()
37
+ try:
38
+ tranche_network: Network = client.networks.get(NETWORK)
39
+ tranche_network.remove()
40
+ except NotFound:
41
+ pass
42
+
43
+
44
+ __all__ = ["clean"]
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/cluster.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from re import match
15
+ from time import sleep
16
+ from typing import Dict, List
17
+
18
+ ### Third-party packages ###
19
+ from click import command, option
20
+ from docker import DockerClient, from_env
21
+ from docker.errors import APIError
22
+ from docker.models.containers import Container
23
+ from pydantic import TypeAdapter
24
+ from rich.progress import track
25
+
26
+ ### Local modules ###
27
+ from src.configs import CLUSTERS, IMAGES, NETWORK
28
+ from src.schemas import MutexOption, NewAddress, Service, ServiceName
29
+
30
+
31
+ @command
32
+ @option("--duo", alternatives=["uno"], cls=MutexOption, is_flag=True, type=bool)
33
+ @option("--uno", alternatives=["duo"], cls=MutexOption, is_flag=True, type=bool)
34
+ def cluster(duo: bool, uno: bool) -> None:
35
+ """Deploy cluster, either with one or two LND nodes."""
36
+ duo = duo or (not duo and not uno) # defaults to duo network
37
+ cluster: Dict[ServiceName, Service] = (CLUSTERS["duo"], CLUSTERS["uno"])[uno]
38
+ client: DockerClient = from_env()
39
+ if client.ping():
40
+ try:
41
+ client.networks.create(NETWORK, check_duplicate=True)
42
+ except APIError:
43
+ pass
44
+ for name, service in track(cluster.items(), description="Deploy specified local cluster"):
45
+ image_name: str = IMAGES[service.alias]
46
+ ports: Dict[str, str] = dict(
47
+ map(lambda item: (item[0], item[1]), [port.split(":") for port in service.ports])
48
+ )
49
+ client.containers.run(
50
+ image_name,
51
+ command=service.command,
52
+ detach=True,
53
+ environment=service.env_vars,
54
+ name=name,
55
+ network=NETWORK,
56
+ ports=ports,
57
+ )
58
+ sleep(3) # wait until lnd ready
59
+ mining_targets: List[str] = []
60
+ for container in track(client.containers.list(), description="Generate addresses"):
61
+ if match(r"tranche-lnd|tranche-ping|tranche-pong", container.name) is not None:
62
+ new_address: NewAddress = TypeAdapter(NewAddress).validate_json(
63
+ container.exec_run(
64
+ """
65
+ lncli
66
+ --macaroonpath=/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon
67
+ --rpcserver=localhost:10001
68
+ --tlscertpath=/home/lnd/.lnd/tls.cert
69
+ newaddress p2wkh
70
+ """
71
+ ).output
72
+ )
73
+ mining_targets.append(new_address.address)
74
+ bitcoind: Container = client.containers.get("tranche-bitcoind")
75
+ for address in track(mining_targets, description="Mine initial capital for parties."):
76
+ bitcoind.exec_run(
77
+ """
78
+ bitcoin-cli -regtest -rpcuser=tranche -rpcpassword=tranche generatetoaddress 101 %s
79
+ """
80
+ % address
81
+ )
82
+
83
+
84
+ __all__ = ["cluster"]
src/commands/mine.py ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/nodekeys.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from re import match
15
+ from typing import List
16
+
17
+ ### Third-party packages ###
18
+ from apscheduler.schedulers.background import BackgroundScheduler
19
+ from click import argument, command
20
+ from docker import DockerClient, from_env
21
+ from docker.errors import NotFound
22
+ from docker.models.containers import Container
23
+ from pydantic import TypeAdapter
24
+ from rich.json import JSON
25
+ from rich.layout import Layout
26
+ from rich.live import Live
27
+ from rich.panel import Panel
28
+ from rich.progress import track
29
+
30
+ ### Local modules ###
31
+ from src.schemas import BlockchainInfo, NewAddress, NodeInfo
32
+
33
+
34
+ @command
35
+ @argument("blockcount", default=1, type=int)
36
+ @argument("blocktime", default=5, type=int)
37
+ def mine(blockcount: int, blocktime: int) -> None:
38
+ """Scheduled mining with "blockcount" and "blocktime"."""
39
+ client: DockerClient = from_env()
40
+ if client.ping():
41
+ mining_targets: List[str] = []
42
+ for container in track(client.containers.list(), description="Generate mining treasuries"):
43
+ if match(r"tranche-lnd|tranche-ping|tranche-pong", container.name) is not None:
44
+ new_address: NewAddress = TypeAdapter(NewAddress).validate_json(
45
+ container.exec_run(
46
+ """
47
+ lncli
48
+ --macaroonpath=/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon
49
+ --rpcserver=localhost:10001
50
+ --tlscertpath=/home/lnd/.lnd/tls.cert
51
+ newaddress p2wkh
52
+ """
53
+ ).output
54
+ )
55
+ mining_targets.append(new_address.address)
56
+ bitcoind: Container
57
+ try:
58
+ bitcoind = client.containers.get("tranche-bitcoind")
59
+ except NotFound:
60
+ print('!! Unable to find "tranche-bitcoind" container.')
61
+ return
62
+ scheduler: BackgroundScheduler = BackgroundScheduler()
63
+ for address in mining_targets:
64
+ scheduler.add_job(
65
+ bitcoind.exec_run,
66
+ "interval",
67
+ [
68
+ """
69
+ bitcoin-cli
70
+ -regtest
71
+ -rpcuser=tranche
72
+ -rpcpassword=tranche
73
+ generatetoaddress %d %s
74
+ """
75
+ % (blockcount, address)
76
+ ],
77
+ seconds=blocktime,
78
+ )
79
+ scheduler.start()
80
+ pane: Layout = Layout()
81
+ pane.split_row(
82
+ Layout(name="bitcoind", size=30),
83
+ Layout(name="lightning", size=70),
84
+ )
85
+ with Live(pane, refresh_per_second=4, transient=True) as live:
86
+ while True:
87
+ info: BlockchainInfo = TypeAdapter(BlockchainInfo).validate_json(
88
+ bitcoind.exec_run(
89
+ """
90
+ bitcoin-cli -regtest -rpcuser=tranche -rpcpassword=tranche getblockchaininfo
91
+ """
92
+ ).output
93
+ )
94
+ pane["bitcoind"].update(Panel(JSON.from_data(info.model_dump()), title="bitcoind"))
95
+
96
+ node_infos: List[NodeInfo] = []
97
+ for container in client.containers.list():
98
+ if match("tranche-lnd|tranche-ping|tranche-pong", container.name) is not None:
99
+ node_info: NodeInfo = TypeAdapter(NodeInfo).validate_json(
100
+ container.exec_run(
101
+ """
102
+ lncli
103
+ --macaroonpath=/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon
104
+ --rpcserver=localhost:10001
105
+ --tlscertpath=/home/lnd/.lnd/tls.cert
106
+ getinfo
107
+ """
108
+ ).output
109
+ )
110
+ node_infos.append(node_info)
111
+ # if
112
+
113
+
114
+ __all__ = ["mine"]
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/nodekeys.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from re import match
15
+
16
+ ### Third-party packages ###
17
+ from click import command
18
+ from docker import DockerClient, from_env
19
+ from pydantic import TypeAdapter
20
+ from rich.progress import track
21
+
22
+ ### Local modules ###
23
+ from src.schemas import NodeInfo
24
+
25
+
26
+ @command
27
+ def nodekeys() -> None:
28
+ """Fetch nodekeys from active LND containers."""
29
+ client: DockerClient = from_env()
30
+ if client.ping():
31
+ for container in track(client.containers.list(), description="Fetch LND nodekeys."):
32
+ if match(r"tranche-lnd|tranche-ping|tranche-pong", container.name) is not None:
33
+ info: NodeInfo = TypeAdapter(NodeInfo).validate_json(
34
+ container.exec_run(
35
+ """
36
+ lncli
37
+ --macaroonpath=/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon
38
+ --rpcserver=localhost:10001
39
+ --tlscertpath=/home/lnd/.lnd/tls.cert
40
+ getinfo
41
+ """
42
+ ).output
43
+ )
44
+ print(f"<Nodekey: '{container.name}', '{info.identity_pubkey}'>")
45
+
46
+
47
+ __all__ = ["nodekeys"]
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/ping_pong.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 06:18
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Third-party packages ###
14
+ from click import command
15
+
16
+
17
+ @command
18
+ def ping_pong() -> None:
19
+ """For "duo" cluster, create channels between LND nodes."""
20
+
21
+
22
+ __all__ = ["ping_pong"]
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/remove_deprecated.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 06:24
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import Set
15
+
16
+ ### Third-party packages ###
17
+ from click import command
18
+ from docker import DockerClient, from_env
19
+ from rich.progress import track
20
+
21
+ ### Local modules ###
22
+ from src.configs import DEPRECATED
23
+
24
+
25
+ @command
26
+ def remove_deprecated() -> None:
27
+ """Remove images deprecated by workspace."""
28
+ client: DockerClient = from_env()
29
+ if client.ping():
30
+ docker_images: Set[str] = {image.tags[0] for image in client.images.list()}
31
+ for registry_id in track(DEPRECATED, description="Remove images deprecated by workspace."):
32
+ if registry_id in docker_images:
33
+ client.images.remove(registry_id)
34
+ print(f"<Image: '{ registry_id }'> removed.")
35
+ else:
36
+ print("!! Unable to connect to docker daemon.")
37
+
38
+
39
+ __all__ = ["remove_deprecated"]
src/commands/setup.py ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/commands/setup.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 06:18
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import Set
15
+
16
+ ### Third-party packages ###
17
+ from click import command
18
+ from docker import DockerClient, from_env
19
+ from rich.progress import track
20
+
21
+ ### Local modules ###
22
+ from src.configs import IMAGES
23
+
24
+
25
+ @command
26
+ def setup() -> None:
27
+ """Download docker images used by command-line interface."""
28
+ client: DockerClient = from_env()
29
+ if client.ping():
30
+ docker_images: Set[str] = {image.tags[0] for image in client.images.list()}
31
+ for registry_id in track(IMAGES.values(), description="Download required images..."):
32
+ if registry_id in docker_images:
33
+ print(f"<Image: '{ registry_id }'> already exists in local docker images.")
34
+ else:
35
+ repository, tag = registry_id.split(":")
36
+ client.images.pull(repository=repository, tag=tag)
37
+ print(f"<Image: '{ registry_id }'> downloaded.")
38
+ else:
39
+ print("!! Unable to connect to docker daemon.")
40
+
41
+
42
+ __all__ = ["setup"]
src/configs.py ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/configs.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from pydantic import TypeAdapter
15
+ from typing import Any, Dict, List, Optional
16
+ from yaml import Loader, load
17
+
18
+ ### Local modules ###
19
+ from src.schemas import ClusterEnum, ImageAlias, Service, ServiceName
20
+
21
+ CLUSTERS: Dict[ClusterEnum, Dict[ServiceName, Service]]
22
+ DEPRECATED: List[str]
23
+ IMAGES: Dict[ImageAlias, str]
24
+ NETWORK: str
25
+ with open("./src/constants.yaml", "rb") as f:
26
+ constants: Optional[Dict[str, Any]] = load(f, Loader=Loader)
27
+ if constants:
28
+ CLUSTERS = TypeAdapter(Dict[ClusterEnum, Dict[ServiceName, Service]]).validate_python(
29
+ constants["clusters"]
30
+ )
31
+ DEPRECATED = constants.get("deprecated", [])
32
+ IMAGES = TypeAdapter(Dict[ImageAlias, str]).validate_python(constants["images"])
33
+ NETWORK = constants.get("network", "tranche")
34
+
35
+ __all__ = ["CLUSTERS", "DEPRECATED", "IMAGES", "NETWORK"]
src/constants.yaml ADDED
@@ -0,0 +1,160 @@
1
+ clusters:
2
+ duo:
3
+ tranche-bitcoind:
4
+ alias: bitcoind
5
+ command:
6
+ - -server=1
7
+ - -regtest=1
8
+ - -rpcauth=tranche:79c479f35a9b741e66f2658870f86f25$9c299f4e7b2abdedb7ae64cbe5f9ab356ec7234573ba31495aa10f7ca116ab52
9
+ - -debug=1
10
+ - -zmqpubrawblock=tcp://0.0.0.0:28334
11
+ - -zmqpubrawtx=tcp://0.0.0.0:28335
12
+ - -zmqpubhashblock=tcp://0.0.0.0:28336
13
+ - -txindex=0
14
+ - -dnsseed=0
15
+ - -upnp=0
16
+ - -rpcbind=0.0.0.0
17
+ - -rpcallowip=0.0.0.0/0
18
+ - -rpcport=18443
19
+ - -rpcworkqueue=128
20
+ - -rest
21
+ - -listen=1
22
+ - -listenonion=0
23
+ - -fallbackfee=0.0002
24
+ - -blockfilterindex=1
25
+ - -peerblockfilters=1
26
+ - -datadir=/home/bitcoin/.bitcoin
27
+ - -prune=600
28
+ ports:
29
+ - 18443:18443
30
+ - 28334:28334
31
+ - 28335:28335
32
+ tranche-ping:
33
+ alias: lnd
34
+ command:
35
+ - --noseedbackup
36
+ - --trickledelay=5000
37
+ - --externalip=tranche-ping
38
+ - --tlsextradomain=tranche-ping
39
+ - --listen=0.0.0.0:9735
40
+ - --rpclisten=0.0.0.0:10001
41
+ - --restlisten=0.0.0.0:8080
42
+ - --bitcoin.active
43
+ - --bitcoin.regtest
44
+ - --bitcoin.node=bitcoind
45
+ - --bitcoind.rpchost=tranche-bitcoind
46
+ - --bitcoind.rpcuser=tranche
47
+ - --bitcoind.rpcpass=tranche
48
+ - --bitcoind.zmqpubrawblock=tcp://tranche-bitcoind:28334
49
+ - --bitcoind.zmqpubrawtx=tcp://tranche-bitcoind:28335
50
+ ports:
51
+ - 9735:9735
52
+ - 10001:10001
53
+ tranche-pong:
54
+ alias: lnd
55
+ ports:
56
+ - 9735:9736
57
+ - 10001:10002
58
+ command:
59
+ - --noseedbackup
60
+ - --trickledelay=5000
61
+ - --externalip=tranche-pong
62
+ - --tlsextradomain=tranche-pong
63
+ - --listen=0.0.0.0:9735
64
+ - --rpclisten=0.0.0.0:10001
65
+ - --restlisten=0.0.0.0:8080
66
+ - --bitcoin.active
67
+ - --bitcoin.regtest
68
+ - --bitcoin.node=bitcoind
69
+ - --bitcoind.rpchost=tranche-bitcoind
70
+ - --bitcoind.rpcuser=tranche
71
+ - --bitcoind.rpcpass=tranche
72
+ - --bitcoind.zmqpubrawblock=tcp://tranche-bitcoind:28334
73
+ - --bitcoind.zmqpubrawtx=tcp://tranche-bitcoind:28335
74
+ tranche-postgres:
75
+ alias: postgres
76
+ env_vars:
77
+ - POSTGRES_HOST_AUTH_METHOD=trust
78
+ ports:
79
+ - 5432:5432
80
+ tranche-redis:
81
+ alias: redis
82
+ ports:
83
+ - 6379:6379
84
+ uno:
85
+ tranche-bitcoind:
86
+ alias: bitcoind
87
+ command:
88
+ - -server=1
89
+ - -regtest=1
90
+ - -rpcauth=tranche:79c479f35a9b741e66f2658870f86f25$9c299f4e7b2abdedb7ae64cbe5f9ab356ec7234573ba31495aa10f7ca116ab52
91
+ - -debug=1
92
+ - -zmqpubrawblock=tcp://0.0.0.0:28334
93
+ - -zmqpubrawtx=tcp://0.0.0.0:28335
94
+ - -zmqpubhashblock=tcp://0.0.0.0:28336
95
+ - -txindex=0
96
+ - -dnsseed=0
97
+ - -upnp=0
98
+ - -rpcbind=0.0.0.0
99
+ - -rpcallowip=0.0.0.0/0
100
+ - -rpcport=18443
101
+ - -rpcworkqueue=128
102
+ - -rest
103
+ - -listen=1
104
+ - -listenonion=0
105
+ - -fallbackfee=0.0002
106
+ - -blockfilterindex=1
107
+ - -peerblockfilters=1
108
+ - -datadir=/home/bitcoin/.bitcoin
109
+ - -prune=600
110
+ ports:
111
+ - 18443:18443
112
+ - 28334:28334
113
+ - 28335:28335
114
+ tranche-lnd:
115
+ alias: lnd
116
+ command:
117
+ - --noseedbackup
118
+ - --trickledelay=5000
119
+ - --externalip=tranche-lnd
120
+ - --tlsextradomain=tranche-lnd
121
+ - --listen=0.0.0.0:9735
122
+ - --rpclisten=0.0.0.0:10001
123
+ - --restlisten=0.0.0.0:8080
124
+ - --bitcoin.active
125
+ - --bitcoin.regtest
126
+ - --bitcoin.node=bitcoind
127
+ - --bitcoind.rpchost=tranche-bitcoind
128
+ - --bitcoind.rpcuser=tranche
129
+ - --bitcoind.rpcpass=tranche
130
+ - --bitcoind.zmqpubrawblock=tcp://tranche-bitcoind:28334
131
+ - --bitcoind.zmqpubrawtx=tcp://tranche-bitcoind:28335
132
+ ports:
133
+ - 9735:9735
134
+ - 10001:10001
135
+ tranche-postgres:
136
+ alias: postgres
137
+ env_vars:
138
+ - POSTGRES_HOST_AUTH_METHOD=trust
139
+ ports:
140
+ - 5432:5432
141
+ tranche-redis:
142
+ alias: redis
143
+ ports:
144
+ - 6379:6379
145
+ deprecated:
146
+ - polarlightning/bitcoind:24.0
147
+ - polarlightning/lnd:0.16.0-beta
148
+ - polarlightning/lnd:0.16.1-beta
149
+ - polarlightning/lnd:0.16.2-beta
150
+ - polarlightning/lnd:0.16.4-beta
151
+ - polarlightning/lnd:0.17.0-beta.rc1
152
+ - polarlightning/lnd:0.17.0-beta.rc2
153
+ - polarlightning/lnd:0.17.0-beta
154
+ - polarlightning/lnd:0.17.1-beta
155
+ images:
156
+ bitcoind: polarlightning/bitcoind:25.0
157
+ lnd: polarlightning/lnd:0.17.2-beta
158
+ postgres: postgres:latest
159
+ redis: redis:latest
160
+ network: tranche
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/__init__.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Local modules ###
14
+ from src.schemas.blockchain_info import BlockchainInfo
15
+ from src.schemas.cluster_enum import ClusterEnum
16
+ from src.schemas.mutex_option import MutexOption
17
+ from src.schemas.image_alias import ImageAlias
18
+ from src.schemas.new_address import NewAddress
19
+ from src.schemas.node_info import NodeInfo
20
+ from src.schemas.service import Service, ServiceName
21
+
22
+
23
+ __all__ = [
24
+ "ClusterEnum",
25
+ "BlockchainInfo",
26
+ "ImageAlias",
27
+ "NewAddress",
28
+ "MutexOption",
29
+ "NodeInfo",
30
+ "Service",
31
+ "ServiceName",
32
+ ]
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/blockchain_info.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Third-party packages ###
14
+ from pydantic import BaseModel, StrictInt, StrictStr
15
+
16
+
17
+ class BlockchainInfo(BaseModel):
18
+ blocks: StrictInt
19
+ chain: StrictStr
20
+ size_on_disk: StrictInt
21
+ time: StrictInt
22
+
23
+
24
+ __all__ = ["BlockchainInfo"]
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/cluster_enum.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import Literal
15
+
16
+ ClusterEnum = Literal["duo", "uno"]
17
+
18
+ __all__ = ["ClusterEnum"]
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/image_alias.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import Literal
15
+
16
+ ImageAlias = Literal["bitcoind", "lnd", "postgres", "redis"]
17
+
18
+ __all__ = ["ImageAlias"]
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/mutex_option.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import Any, List, Mapping, Tuple
15
+
16
+ ### Third-party packages ###
17
+ from click import Context, Option, UsageError
18
+
19
+
20
+ class MutexOption(Option):
21
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
22
+ self.alternatives: list = kwargs.pop("alternatives")
23
+ assert self.alternatives, "'alternatives' parameter required."
24
+ kwargs["help"] = (
25
+ kwargs.get("help", "")
26
+ + f"Option is mutually exclusive with {', '.join(self.alternatives)}."
27
+ ).strip()
28
+ super(MutexOption, self).__init__(*args, **kwargs)
29
+
30
+ def handle_parse_result(
31
+ self, context: Context, options: Mapping[str, Any], arguments: List[str]
32
+ ) -> Tuple[Any, List[str]]:
33
+ current_opt: bool = self.name in options
34
+ for mutex_option in self.alternatives:
35
+ if mutex_option in options:
36
+ if current_opt:
37
+ raise UsageError(
38
+ f"Illegal usage: '{self.name}' is mutually exclusive with {mutex_option}."
39
+ )
40
+ else:
41
+ self.prompt = None
42
+ return super(MutexOption, self).handle_parse_result(context, options, arguments)
43
+
44
+
45
+ __all__ = ["MutexOption"]
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/new_address.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Third-party packages ###
14
+ from pydantic import BaseModel, StrictStr
15
+
16
+
17
+ class NewAddress(BaseModel):
18
+ address: StrictStr
19
+
20
+
21
+ __all__ = ["NewAddress"]
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/node_info.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Third-party packages ###
14
+ from pydantic import BaseModel, StrictStr
15
+
16
+
17
+ class NodeInfo(BaseModel):
18
+ identity_pubkey: StrictStr
19
+
20
+
21
+ __all__ = ["NodeInfo"]
src/schemas/service.py ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3.8
2
+ # coding:utf-8
3
+ # Copyright (C) 2022-2023 All rights reserved.
4
+ # FILENAME: ~~/src/schemas/service.py
5
+ # VERSION: 0.1.0
6
+ # CREATED: 2023-12-01 05:31
7
+ # AUTHOR: Sitt Guruvanich <aekasitt.g+github@siamintech.co.th>
8
+ # DESCRIPTION:
9
+ #
10
+ # HISTORY:
11
+ # *************************************************************
12
+
13
+ ### Standard packages ###
14
+ from typing import List, Literal
15
+
16
+ ### Third-party packages ###
17
+ from pydantic import BaseModel, StrictStr
18
+
19
+ ### Local modules ###
20
+ from src.schemas.image_alias import ImageAlias
21
+
22
+
23
+ class Service(BaseModel):
24
+ alias: ImageAlias
25
+ command: List[StrictStr] = []
26
+ env_vars: List[StrictStr] = []
27
+ ports: List[StrictStr]
28
+
29
+
30
+ ServiceName = Literal[
31
+ "tranche-bitcoind",
32
+ "tranche-lnd",
33
+ "tranche-ping",
34
+ "tranche-pong",
35
+ "tranche-postgres",
36
+ "tranche-redis",
37
+ ]
38
+
39
+ __all__ = ["Service", "ServiceName"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (C) 2022-2023, Sitt Guruvanich
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.1
2
+ Name: tranche
3
+ Version: 0.1.0
4
+ Summary: Command line interface used for generating local Lightning test environment
5
+ License: MIT
6
+ Author: Sitt Guruvanich
7
+ Author-email: aekasitt.g+github@siamintech.co.th
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: APScheduler (>=3.10.4,<4.0.0)
17
+ Requires-Dist: click (>=8.1.7,<9.0.0)
18
+ Requires-Dist: docker (>=6.1.3,<7.0.0)
19
+ Requires-Dist: pydantic (>=2.5.2,<3.0.0)
20
+ Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
21
+ Requires-Dist: rich (>=13.7.0,<14.0.0)
22
+ Requires-Dist: types-pyyaml (>=6.0.12.12,<7.0.0.0)
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Tranche
26
+
27
+ [![Bitcoin-only](https://img.shields.io/badge/bitcoin-only-FF9900?logo=bitcoin)](https://twentyone.world)
28
+ [![LN](https://img.shields.io/badge/lightning-792EE5?logo=lightning)](https://mempool.space/lightning)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
30
+
31
+ [![Tranche Banner](static/tranche-banner.svg)](static/tranche-banner.svg)
32
+
33
+ ### Known issues
34
+ ```python
35
+ docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))
36
+ ```
37
+
38
+ See the following issue for Mac OSX troubleshooting.
39
+
40
+ [docker from_env and pull is broken on mac](https://github.com/docker/docker-py/issues/3059#issuecomment-1294369344)
41
+
42
+ Recommneded fix is to run the following command:
43
+
44
+ ```sh
45
+ sudo ln -s "$HOME/.docker/run/docker.sock" /var/run/docker.sock
46
+ ```
47
+
48
+ ## License
49
+
50
+ This project is licensed under the terms of the MIT license.
@@ -0,0 +1,25 @@
1
+ core.py,sha256=Taf2Ahwh0Jsom_sbcULWrxhxLvzGqlQplha7J4lYtAs,846
2
+ src/__init__.py,sha256=ihGnKXv63GFC4ie2LO7_PQ4AwY820kTVYck8leVziVo,793
3
+ src/commands/__init__.py,sha256=VC3PPfjanvD99ctC3eNjgQpzOnKnFucZ36cLNGVFwr8,759
4
+ src/commands/clean.py,sha256=FNwKUEowWAOOWfT66FJYAi9LySlclOVTEWDVXI3YnAo,1274
5
+ src/commands/cluster.py,sha256=PhU9bBpAoJC25aK_36n815ApONLWJ7jXHFkKSwbS68Q,3272
6
+ src/commands/mine.py,sha256=MBvm63VGF9qi2g17odQEm9HjJt3oQIdF_Zv9g85WHxQ,4398
7
+ src/commands/nodekeys.py,sha256=AvSalJBwtdNSx7sLoKcfvu1H2aUuuE3HyYkW91JJLKc,1546
8
+ src/commands/ping_pong.py,sha256=Rq-zSOY6oIyglVcahHCMG5NRSSV_lIHWN94LJDvpGvg,525
9
+ src/commands/remove_deprecated.py,sha256=xMZURzbOCvwdFdhlCs3cf8Bk7z6vfyxtouVaAer3SvE,1183
10
+ src/commands/setup.py,sha256=KngvRJUsZrgupPiaxntc7LY9TdZKCJvvbdG-l0oYNH0,1340
11
+ src/configs.py,sha256=vHUNDyBiVz1jnzwd7RYvKpKS95vYFvyajXNe5Qi1eE4,1205
12
+ src/constants.yaml,sha256=T9Ak0BIw3V8kXC3aWtRyvTZB14ST9SL5eaEZhYClk3A,4637
13
+ src/schemas/__init__.py,sha256=3JEcuwGspGR-KkPF-ZEJnlfVyWP6DkNk9wbzHzmSx0A,869
14
+ src/schemas/blockchain_info.py,sha256=YOYL0gb_ok2DNooBx8jbh8uMVPi6rGM_AM7WOwPOM8k,588
15
+ src/schemas/cluster_enum.py,sha256=enbm-VpeYOXoUjImIovVJ4cXpL-AswUgJ-tXgmlQ8BI,463
16
+ src/schemas/image_alias.py,sha256=c52FAuENjkKIFCYtnCRzv042J3xSUU8Os6zl1bioHoc,486
17
+ src/schemas/mutex_option.py,sha256=3NOxMHzvegmqk9RhJTzPGbfBEZaLBmAFSDg5qXXANpw,1583
18
+ src/schemas/new_address.py,sha256=Mi2CN70_tl4PokX5p3gsommxBqExCJl7lUHYvjHlbN4,497
19
+ src/schemas/node_info.py,sha256=_vq4d8FmTcHU7gYvaYhqfavBk4djLrgB3dfHL282zEc,499
20
+ src/schemas/service.py,sha256=1nn8hTJNwvCQIjpdWTzhXErC34nuIKWZqyhs2aQ2ZfI,881
21
+ tranche-0.1.0.dist-info/LICENSE,sha256=wRkcd6H2Z5ZmlmlV59AAOAkBBPSelLaGwgy-T047U60,1078
22
+ tranche-0.1.0.dist-info/METADATA,sha256=1w-coxcbElLFVHh0UN8hYSd7XAFHLdeErbs8p_A2W_I,1860
23
+ tranche-0.1.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
24
+ tranche-0.1.0.dist-info/entry_points.txt,sha256=8Hu2Rscx-Kbq0SIv9PpNMlQt3q510h5Or0u1vry43YI,36
25
+ tranche-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.8.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ tranche=core:cli
3
+