mirror of
https://github.com/yzernik/squeaknode.git
synced 2026-08-13 12:33:25 +02:00
Compare commits
44 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6af6126c39 | ||
|
|
7fdb9d996c | ||
|
|
bfc746ce0d | ||
|
|
90c0b93a90 | ||
|
|
0181eafe74 | ||
|
|
7c67145f61 | ||
|
|
a92ad92042 | ||
|
|
a024057f1b | ||
|
|
93da9c0c97 | ||
|
|
e805751bdc | ||
|
|
2cabb8197a | ||
|
|
e66377a3c9 | ||
|
|
6ff3f3c4c9 | ||
|
|
aef32d9417 | ||
|
|
2d55668af0 | ||
|
|
afd3af091a | ||
|
|
a8ebf8e9e9 | ||
|
|
cb0598cbe5 | ||
|
|
163837a7bc | ||
|
|
0c0ce30006 | ||
|
|
9e1ec67381 | ||
|
|
be90a06973 | ||
|
|
ed360ff3d1 | ||
|
|
80155f1fb7 | ||
|
|
a2eec82e28 | ||
|
|
348353cd43 | ||
|
|
1dd94e58cf | ||
|
|
5231850d13 | ||
|
|
c09ab3f209 | ||
|
|
ad779182d7 | ||
|
|
28acf6f4fc | ||
|
|
098daa08bf | ||
|
|
43f959e9c4 | ||
|
|
566c86d00e | ||
|
|
27d2c92acf | ||
|
|
bbad5476c8 | ||
|
|
cef1c24de5 | ||
|
|
99e4e523dd | ||
|
|
8bca8c7961 | ||
|
|
cfe9cecbe5 | ||
|
|
2cd39d05bf | ||
|
|
a41ca5a9c5 | ||
|
|
2cdb2f8de4 | ||
|
|
b5b3b9f1f3 |
84 changed files with 6621 additions and 26291 deletions
10
Dockerfile
10
Dockerfile
|
|
@ -18,9 +18,7 @@ RUN pip install --upgrade pip
|
|||
WORKDIR /app
|
||||
|
||||
# Copy the source code.
|
||||
COPY squeaknode ./squeaknode
|
||||
COPY proto ./proto
|
||||
COPY LICENSE MANIFEST.in README.md requirements.txt setup.cfg setup.py ./
|
||||
COPY . .
|
||||
|
||||
RUN pip install .[postgres]
|
||||
|
||||
|
|
@ -42,7 +40,7 @@ EXPOSE 12994
|
|||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Copy the entrypoint script.
|
||||
COPY "start-squeaknode.sh" .
|
||||
RUN chmod +x start-squeaknode.sh
|
||||
COPY "entrypoint.sh" .
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
CMD ["./start-squeaknode.sh"]
|
||||
CMD ["./entrypoint.sh"]
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -11,10 +11,10 @@ A peer-to-peer status feed with posts unlocked by Lightning.
|
|||
|
||||
Squeaknode allows you to create, view, buy, and sell squeaks.
|
||||
|
||||
A squeak is an immutable structure that:
|
||||
A squeak is an immutable, signed structure that:
|
||||
* contains up to 280 utf-8 characters of text, encrypted (can only be decrypted with access to the decryption key obtained by lightning payment)
|
||||
* contains the height and block hash of the latest bitcoin block
|
||||
* contains the public key of the author, and is signed by the author
|
||||
* contains the public key of the author
|
||||
* contains the public key of the recipient, if the text is end-to-end encrypted
|
||||
* contains the hash of another squeak, if the squeak is a reply
|
||||
* can be used to derive a unique hash
|
||||
|
|
@ -33,16 +33,16 @@ The protocol is defined [here](https://github.com/yzernik/squeak/blob/master/doc
|
|||
* a Tor SOCKS5 proxy (you can open Tor Browser and run it in the background)
|
||||
|
||||
### Step 1. Create the configuration
|
||||
> Update the **config.ini** file and fill in the relevant sections to connect to your Bitcoin node and LND node:
|
||||
> Create a **config.ini** file and fill in the relevant sections to connect to your Bitcoin node and LND node:
|
||||
|
||||
```
|
||||
[node]
|
||||
network=mainnet
|
||||
|
||||
[lnd]
|
||||
host=localhost
|
||||
tls_cert_path=~/.lnd/tls.cert
|
||||
macaroon_path=~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
|
||||
[lightning]
|
||||
backend=lnd
|
||||
lnd_tls_cert_path=~/.lnd/tls.cert
|
||||
lnd_macaroon_path=~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=localhost
|
||||
|
|
@ -60,6 +60,14 @@ username=devuser
|
|||
password=devpass
|
||||
```
|
||||
|
||||
If you want to use [c-lightning](https://github.com/ElementsProject/lightning) as the Lightning Network node backend instead of [LND](https://github.com/lightningnetwork/lnd), replace the `lightning` section with the following:
|
||||
|
||||
```
|
||||
[lightning]
|
||||
backend=clightning
|
||||
clightning_rpc_file=~/.lightning/lightning-rpc
|
||||
```
|
||||
|
||||
Add any other [configs](docs/configuration.md) that you need.
|
||||
|
||||
### Step 2. Install squeaknode:
|
||||
|
|
|
|||
20
clightning-config.ini
Normal file
20
clightning-config.ini
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[node]
|
||||
network=testnet
|
||||
|
||||
[lightning]
|
||||
backend=clightning
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=localhost
|
||||
rpc_port=18332
|
||||
rpc_user=devuser
|
||||
rpc_pass=devpass
|
||||
|
||||
[tor]
|
||||
proxy_ip=localhost
|
||||
proxy_port=9150
|
||||
|
||||
[webadmin]
|
||||
enabled=true
|
||||
username=devuser
|
||||
password=devpass
|
||||
29
config-other.ini
Normal file
29
config-other.ini
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
[node]
|
||||
network=testnet
|
||||
sqk_dir_path=/home/yzernik/.sqk-other
|
||||
|
||||
[lightning]
|
||||
external_host=localhost
|
||||
lnd_rpc_host=localhost
|
||||
lnd_rpc_port=10003
|
||||
lnd_tls_cert_path=/home/yzernik/.polar/networks/1/volumes/lnd/alice/tls.cert
|
||||
lnd_macaroon_path=/home/yzernik/.polar/networks/1/volumes/lnd/alice/data/chain/bitcoin/regtest/admin.macaroon
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=localhost
|
||||
rpc_port=18445
|
||||
rpc_user=polaruser
|
||||
rpc_pass=polarpass
|
||||
|
||||
[tor]
|
||||
proxy_ip=localhost
|
||||
proxy_port=9150
|
||||
|
||||
[webadmin]
|
||||
enabled=true
|
||||
username=devuser
|
||||
password=devpass
|
||||
port=12995
|
||||
|
||||
[server]
|
||||
port=18558
|
||||
17
config.ini
17
config.ini
|
|
@ -1,17 +1,18 @@
|
|||
[node]
|
||||
network=testnet
|
||||
|
||||
[lnd]
|
||||
host=localhost
|
||||
tls_cert_path=~/.lnd/tls.cert
|
||||
macaroon_path=~/.lnd/data/chain/bitcoin/testnet/admin.macaroon
|
||||
[lightning]
|
||||
external_host=localhost
|
||||
lnd_rpc_host=localhost
|
||||
lnd_rpc_port=10004
|
||||
lnd_tls_cert_path=/home/yzernik/.polar/networks/1/volumes/lnd/bob/tls.cert
|
||||
lnd_macaroon_path=/home/yzernik/.polar/networks/1/volumes/lnd/bob/data/chain/bitcoin/regtest/admin.macaroon
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=localhost
|
||||
rpc_port=18332
|
||||
rpc_user=devuser
|
||||
rpc_pass=devpass
|
||||
zeromq_hashblock_port=28334
|
||||
rpc_port=18445
|
||||
rpc_user=polaruser
|
||||
rpc_pass=polarpass
|
||||
|
||||
[tor]
|
||||
proxy_ip=localhost
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.12-alpine as builder
|
||||
FROM golang:1.18-alpine as builder
|
||||
|
||||
MAINTAINER Olaoluwa Osuntokun <laolu@lightning.network>
|
||||
|
||||
|
|
|
|||
25
docker/config-clightning.ini
Normal file
25
docker/config-clightning.ini
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
[node]
|
||||
network=testnet
|
||||
|
||||
[lightning]
|
||||
backend=clightning
|
||||
external_host=clightning_server
|
||||
external_port=19735
|
||||
|
||||
[clightning]
|
||||
enabled=true
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=bitcoin-core
|
||||
rpc_port=18332
|
||||
rpc_user=devuser
|
||||
rpc_pass=devpass
|
||||
|
||||
[tor]
|
||||
proxy_ip=tor-node
|
||||
proxy_port=9050
|
||||
|
||||
[webadmin]
|
||||
enabled=true
|
||||
username=devuser
|
||||
password=devpass
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
[node]
|
||||
network=testnet
|
||||
|
||||
[lnd]
|
||||
host=lnd
|
||||
tls_cert_path=~/.lnd/tls.cert
|
||||
macaroon_path=~/.lnd/data/chain/bitcoin/testnet/admin.macaroon
|
||||
[lightning]
|
||||
lnd_rpc_host=lnd
|
||||
external_host=lnd
|
||||
lnd_tls_cert_path=~/.lnd/tls.cert
|
||||
lnd_macaroon_path=~/.lnd/data/chain/bitcoin/testnet/admin.macaroon
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=bitcoin-core
|
||||
rpc_port=18332
|
||||
rpc_user=devuser
|
||||
rpc_pass=devpass
|
||||
zeromq_hashblock_port=28334
|
||||
|
||||
[tor]
|
||||
proxy_ip=tor-node
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@ services:
|
|||
environment:
|
||||
- NETWORK=mainnet
|
||||
|
||||
clightning_client:
|
||||
command:
|
||||
- --bitcoin-rpcconnect=bitcoin-core
|
||||
- --bitcoin-rpcuser=devuser
|
||||
- --bitcoin-rpcpassword=devpass
|
||||
- --network=mainnet
|
||||
- --plugin-dir=/usr/libexec/c-lightning/plugins
|
||||
- --alias=myitestnode
|
||||
- --log-level=debug
|
||||
|
||||
# squeaknode:
|
||||
# environment:
|
||||
# - NETWORK=mainnet
|
||||
|
|
|
|||
|
|
@ -15,6 +15,32 @@ services:
|
|||
-zmqpubhashblock=tcp://0.0.0.0:28334
|
||||
-zmqpubhashtx=tcp://0.0.0.0:28334
|
||||
|
||||
clightning_client:
|
||||
command:
|
||||
- --bitcoin-rpcconnect=bitcoin-core
|
||||
- --bitcoin-rpcuser=devuser
|
||||
- --bitcoin-rpcpassword=devpass
|
||||
- --network=testnet
|
||||
- --plugin-dir=/usr/libexec/c-lightning/plugins
|
||||
- --alias=myclientningclient
|
||||
- --log-level=debug
|
||||
- --rpc-file=/root/.lightning/lightning-rpc
|
||||
- --rpc-file-mode=0777
|
||||
- --proxy=tor-node:9050
|
||||
|
||||
clightning_server:
|
||||
command:
|
||||
- --bitcoin-rpcconnect=bitcoin-core
|
||||
- --bitcoin-rpcuser=devuser
|
||||
- --bitcoin-rpcpassword=devpass
|
||||
- --network=testnet
|
||||
- --plugin-dir=/usr/libexec/c-lightning/plugins
|
||||
- --alias=myclientningserver
|
||||
- --log-level=debug
|
||||
- --rpc-file=/root/.lightning/lightning-rpc
|
||||
- --rpc-file-mode=0777
|
||||
- --proxy=tor-node:9050
|
||||
|
||||
# squeaknode:
|
||||
# environment:
|
||||
# - NETWORK=testnet
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ services:
|
|||
image: ruimarinho/bitcoin-core:0.21.1
|
||||
container_name: bitcoin-core
|
||||
volumes:
|
||||
- ~/.bitcoin:/home/bitcoin/.bitcoin
|
||||
- bitcoin:/home/bitcoin/.bitcoin
|
||||
ports:
|
||||
- 8332:8332
|
||||
- 18332:18332
|
||||
|
|
@ -25,7 +25,7 @@ services:
|
|||
depends_on:
|
||||
- "tor-node"
|
||||
volumes:
|
||||
- ~/.lnd:/root/.lnd
|
||||
- lnd_client_dir:/root/.lnd
|
||||
- tor-dir:/var/lib/tor
|
||||
ports:
|
||||
- 10009:10009
|
||||
|
|
@ -57,6 +57,45 @@ services:
|
|||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./start-lnd.sh"]
|
||||
|
||||
clightning_client:
|
||||
image: elementsproject/lightningd:v0.11.0.1
|
||||
container_name: clightning_client
|
||||
expose:
|
||||
- "9735"
|
||||
- "19735"
|
||||
- "9835"
|
||||
- "10009"
|
||||
ports:
|
||||
- 10010:10009
|
||||
- 9835:9835
|
||||
volumes:
|
||||
- clightning_client_dir:/root/.lightning
|
||||
- tor-dir:/var/lib/tor
|
||||
links:
|
||||
- "bitcoin-core:bitcoin-core"
|
||||
depends_on:
|
||||
- "tor-node"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
|
||||
clightning_server:
|
||||
image: elementsproject/lightningd:v0.11.0.1
|
||||
container_name: clightning_server
|
||||
expose:
|
||||
- "9735"
|
||||
- "19735"
|
||||
- "9835"
|
||||
- "10009"
|
||||
volumes:
|
||||
- clightning_server_dir:/root/.lightning
|
||||
- tor-dir:/var/lib/tor
|
||||
links:
|
||||
- "bitcoin-core:bitcoin-core"
|
||||
depends_on:
|
||||
- "tor-node"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
|
||||
tor-node:
|
||||
image: osminogin/tor-simple
|
||||
container_name: tor-node
|
||||
|
|
@ -87,12 +126,44 @@ services:
|
|||
- "lnd_server:lnd"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./start-squeaknode.sh"]
|
||||
entrypoint: ["./entrypoint.sh"]
|
||||
|
||||
squeaknode_clightning:
|
||||
image: squeaknode
|
||||
container_name: squeaknode_clightning
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- "lnd_server"
|
||||
volumes:
|
||||
- clightning_server_dir:/root/.lightning
|
||||
- squeaknode_clightning_dir:/root/.sqk
|
||||
- ./config-clightning.ini:/config.ini
|
||||
ports:
|
||||
- 18558:18555
|
||||
- 12996:12994
|
||||
links:
|
||||
- "bitcoin-core:bitcoin-core"
|
||||
- "lnd_server:lnd"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./entrypoint.sh"]
|
||||
|
||||
volumes:
|
||||
bitcoin:
|
||||
driver: local
|
||||
lnd_client_dir:
|
||||
driver: local
|
||||
lnd_server_dir:
|
||||
driver: local
|
||||
clightning_client_dir:
|
||||
driver: local
|
||||
clightning_server_dir:
|
||||
driver: local
|
||||
squeaknode_dir:
|
||||
driver: local
|
||||
squeaknode_clightning_dir:
|
||||
driver: local
|
||||
tor-dir:
|
||||
driver: local
|
||||
|
|
|
|||
|
|
@ -26,13 +26,14 @@ bitcoin.rpc_user | string | | yes | "" | SQUEAKNODE_BITCOIN_RPC_USER | The usern
|
|||
bitcoin.rpc_pass | string | | yes | "" | SQUEAKNODE_BITCOIN_RPC_PASS | The password to use for authentication on the bitcoin node.
|
||||
bitcoin.rpc_use_ssl | boolean | [true, false] | yes | false | SQUEAKNODE_BITCOIN_USE_SSL | Use SSL for the connection to the bitcoin node.
|
||||
bitcoin.rpc_ssl_cert | str | | yes | "" | SQUEAKNODE_BITCOIN_SSL_CERT | The path to the SSL cert to use for connection to the bitcoin node, if one is used.
|
||||
bitcoin.zeromq_hashblock_port | int | | yes | 28334 | SQUEAKNODE_BITCOIN_ZEROMQ_HASHBLOCK_PORT | The port to use to subscribe with zeromq to new block hashes on the bitcoin node.
|
||||
lnd.host | string | | yes | "localhost" | SQUEAKNODE_LND_HOST | The host of the LND node to connect.
|
||||
lnd.external_host | string | | yes | "" | SQUEAKNODE_LND_EXTERNAL_HOST | The host of the LND node to share with other peers.
|
||||
lnd.port | int | | yes | | SQUEAKNODE_LND_PORT | The port of the LND node to use for LND peer connections.
|
||||
lnd.rpc_port | int | | yes | | SQUEAKNODE_LND_RPC_PORT | The port of the LND node to use for RPC connections.
|
||||
lnd.tls_cert_path | string | | yes | "" | SQUEAKNODE_LND_TLS_CERT_PATH | The path to the TLS certificate to use for LND connection.
|
||||
lnd.macaroon_path | string | | yes | "" | SQUEAKNODE_LND_MACAROON_PATH | The path to the macaroon to use for LND connection.
|
||||
lightning.backend | string | | yes | "lnd" | SQUEAKNODE_LIGHTNING_BACKEND | The Lightning Network node implementation to use.
|
||||
lightning.external_host | string | | yes | "" | SQUEAKNODE_LIGHTNING_EXTERNAL_HOST | The host of the lightning node to share with other peers.
|
||||
lightning.external_port | int | | yes | | SQUEAKNODE_LIGHTNING_EXTERNAL_PORT | The port of the lightning node to share with other peers.
|
||||
lightning.lnd_rpc_host | string | | yes | "localhost" | SQUEAKNODE_LIGHTNING_LND_RPC_HOST | The host of the LND node to connect.
|
||||
lightning.lnd_rpc_port | int | | yes | | SQUEAKNODE_LIGHTNING_LND_RPC_PORT | The port of the LND node to use for RPC connections.
|
||||
lightning.lnd_tls_cert_path | string | | yes | "" | SQUEAKNODE_LIGHTNING_LND_TLS_CERT_PATH | The path to the TLS certificate to use for LND connection.
|
||||
lightning.lnd_macaroon_path | string | | yes | "" | SQUEAKNODE_LIGHTNING_LND_MACAROON_PATH | The path to the macaroon to use for LND connection.
|
||||
lightning.clightning_rpc_file | string | | yes | "" | SQUEAKNODE_LIGHTNING_CLIGHTNING_RPC_FILE | The path to the file to use for c-lightning RPC.
|
||||
tor.proxy_ip | string | | yes | "" | SQUEAKNODE_TOR_PROXY_IP | The ip address or host of the SOCKS5 Tor proxy, if one is used.
|
||||
tor.proxy_port | int | | yes | 0 | SQUEAKNODE_TOR_PROXY_PORT | The port of the SOCKS5 Tor proxy, is one is used.
|
||||
db.connection_string | string | | yes | "" | SQUEAKNODE_DB_CONNECTION_STRING | The connection string to use to connect to a SQL database. If none is specified, a sqlite database will be used on the local file system.
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ You can also run your own squeaknode on your host machine.
|
|||
pip install .
|
||||
```
|
||||
|
||||
- Make sure that `~/.lnd` directory (created by docker container) has read permissions. You may need to change the permissions:
|
||||
- Copy the `~/.lnd` directory from the docker container to your host machine.
|
||||
```
|
||||
sudo chmod -R 755 ~/.lnd/
|
||||
docker cp lnd_client:/root/.lnd/ ~/.lnd
|
||||
```
|
||||
|
||||
- Run squeaknode with authentication disabled:
|
||||
|
|
|
|||
19393
frontend/package-lock.json
generated
19393
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,15 +12,17 @@
|
|||
"google-protobuf": "^3.12.4",
|
||||
"informed": "^4.6.1",
|
||||
"jwt-decode": "^2.2.0",
|
||||
"moment": "^2.29.2",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^16.13.1",
|
||||
"react-contenteditable": "^3.3.4",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-redux": "^7.2.1",
|
||||
"react-responsive": "^8.1.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "^5.0.0",
|
||||
"react-select": "^5.2.2",
|
||||
"react-tooltip": "^4.2.21",
|
||||
"sass": "^1.49.7",
|
||||
"serve": "^13.0.2",
|
||||
"socket.io-client": "^2.3.0"
|
||||
|
|
|
|||
|
|
@ -168,3 +168,10 @@ export const ICON_SEND = (props) => {
|
|||
export const ICON_QUESTION = (props) => {
|
||||
return <svg style={props.styles} viewBox="0 0 16 16"><g><path fill-rule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215 0 1.344-.665 2.288-1.79 2.973-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712 1.03-.632 1.397-1.135 1.397-2.028 0-.979-.758-1.698-1.926-1.698-1.009 0-1.71.529-1.938 1.402-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09 0-.618-.473-1.092-1.095-1.092-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z"/></g></svg>
|
||||
}
|
||||
|
||||
export const ICON_CLIPBOARD = (props) => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
|
||||
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
|
||||
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
|
||||
</svg>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ import {
|
|||
GetSentPaymentsForPeerReply,
|
||||
GetReceivedPaymentsForPeerRequest,
|
||||
GetReceivedPaymentsForPeerReply,
|
||||
DownloadSqueakSecretKeyRequest,
|
||||
DownloadSqueakSecretKeyReply,
|
||||
RenamePeerRequest,
|
||||
RenamePeerReply,
|
||||
} from '../proto/squeak_admin_pb';
|
||||
|
||||
import axios from 'axios'
|
||||
|
|
@ -716,8 +720,20 @@ export const getPrivateKey = (id) => {
|
|||
});
|
||||
}
|
||||
|
||||
export const getPeer = (network, host, port) => {
|
||||
export const getPeer = (peerId) => {
|
||||
console.log('Calling getPeer');
|
||||
const request = new GetPeerRequest();
|
||||
request.setPeerId(peerId);
|
||||
const deser = GetPeerReply.deserializeBinary;
|
||||
return baseRequest({
|
||||
url: '/getpeer',
|
||||
req: request,
|
||||
deser: deser,
|
||||
});
|
||||
}
|
||||
|
||||
export const getPeerByAddress = (network, host, port) => {
|
||||
console.log('Calling getPeerByAddress');
|
||||
const request = new GetPeerByAddressRequest();
|
||||
const peerAddress = new PeerAddress();
|
||||
peerAddress.setNetwork(network);
|
||||
|
|
@ -732,6 +748,19 @@ export const getPeer = (network, host, port) => {
|
|||
});
|
||||
}
|
||||
|
||||
export const renamePeer = (peerId, peerName) => {
|
||||
console.log('Calling renamePeer');
|
||||
const request = new RenamePeerRequest();
|
||||
request.setPeerId(peerId);
|
||||
request.setPeerName(peerName);
|
||||
const deser = RenamePeerReply.deserializeBinary;
|
||||
return baseRequest({
|
||||
url: '/renamepeer',
|
||||
req: request,
|
||||
deser: deser,
|
||||
});
|
||||
}
|
||||
|
||||
export const getConnectedPeers = () => {
|
||||
console.log('Calling getConnectedPeers');
|
||||
const request = new GetConnectedPeersRequest();
|
||||
|
|
@ -923,6 +952,18 @@ export const downloadSqueak = (squeakHash) => {
|
|||
});
|
||||
}
|
||||
|
||||
export const downloadSqueakSecretKey = (squeakHash) => {
|
||||
console.log('Calling downloadSqueakSecretKey');
|
||||
const request = new DownloadSqueakSecretKeyRequest();
|
||||
request.setSqueakHash(squeakHash);
|
||||
const deser = DownloadSqueakSecretKeyReply.deserializeBinary;
|
||||
return baseRequest({
|
||||
url: '/downloadsqueaksecretkey',
|
||||
req: request,
|
||||
deser: deser,
|
||||
});
|
||||
}
|
||||
|
||||
export const getTwitterAccounts = () => {
|
||||
console.log('Calling getTwitterAccounts');
|
||||
const request = new GetTwitterAccountsRequest();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
setDeletePeer,
|
||||
setPeerAutoconnectEnabled,
|
||||
setPeerAutoconnectDisabled,
|
||||
setRenamePeer,
|
||||
} from '../../features/peers/peersSlice'
|
||||
import {
|
||||
fetchPaymentSummaryForPeer,
|
||||
|
|
@ -121,13 +122,10 @@ const Peer = (props) => {
|
|||
|
||||
const editPeer = ({values}) => {
|
||||
console.log('Calling editPeer with name:', values.name);
|
||||
// TODO: call rename action.
|
||||
alert('Rename peer not yet implemented!');
|
||||
// dispatch(setRenameProfile({
|
||||
// peerId: user.getPeerId(),
|
||||
// profileName: editName,
|
||||
// }));
|
||||
// TODO: chain action to update profile squeaks with the new name.
|
||||
dispatch(setRenamePeer({
|
||||
peerId: peer.getPeerId(),
|
||||
peerName: values.name,
|
||||
}));
|
||||
setSaved(true)
|
||||
toggleEditModal()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import React, { useEffect, useState, useContext } from 'react'
|
||||
import './style.scss'
|
||||
import { withRouter, Link } from 'react-router-dom'
|
||||
import { ICON_SEARCH, ICON_ARROWBACK, ICON_CLOSE } from '../../Icons'
|
||||
import { ICON_SEARCH, ICON_ARROWBACK, ICON_CLOSE, ICON_CLIPBOARD } from '../../Icons'
|
||||
import { getProfileImageSrcString } from '../../squeakimages/images';
|
||||
import Loader from '../Loader'
|
||||
import SqueakCard from '../SqueakCard'
|
||||
import PeerCard from '../../features/peers/PeerCard'
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
import { Form, Input, Select, Checkbox, Relevant, Debug, TextArea, Option } from 'informed';
|
||||
|
||||
|
|
@ -59,17 +61,26 @@ const Peers = (props) => {
|
|||
return 'IPV4';
|
||||
};
|
||||
|
||||
const getExternalAddressStr = () => {
|
||||
if (externalAddress) {
|
||||
return `${externalAddress.getHost()}:${externalAddress.getPort()}`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
function removeHttp(url) {
|
||||
return url.replace(/^https?:\/\//, '');
|
||||
}
|
||||
|
||||
const savePeer = ({values}) => {
|
||||
const network = getNetwork(values.useTor);
|
||||
const strippedHost = removeHttp(values.host);
|
||||
const strippedAddress = removeHttp(values.address);
|
||||
const url = new URL(`http://${strippedAddress}`);
|
||||
dispatch(setSavePeer({
|
||||
name: values.name,
|
||||
host: strippedHost,
|
||||
port: values.port,
|
||||
host: url.hostname,
|
||||
port: url.port,
|
||||
network: network,
|
||||
}));
|
||||
toggleSavePeerModal();
|
||||
|
|
@ -90,8 +101,7 @@ const Peers = (props) => {
|
|||
</div>
|
||||
<div className="edit-input-wrap">
|
||||
<Input class="informed-input" name="name" label="Peer Name (not required)" />
|
||||
<Input class="informed-input" name="host" label="Host" />
|
||||
<Input class="informed-input" name="port" type="number" label="Port" />
|
||||
<Input class="informed-input" name="address" label="Address (host:port)" />
|
||||
<Checkbox class="informed-input" name="useTor" label="Connect With Tor: " />
|
||||
</div>
|
||||
|
||||
|
|
@ -112,12 +122,27 @@ const Peers = (props) => {
|
|||
const ShowExternalAddressForm = () => (
|
||||
<Form className="Squeak-input-side">
|
||||
<div className="edit-input-wrap">
|
||||
<p>Other squeaknodes can connect to your node using this address to exchange squeaks and offers.</p>
|
||||
<p>Other Squeaknode instances can connect to your node using this address to download squeaks and offers.</p>
|
||||
</div>
|
||||
<div className="edit-input-wrap">
|
||||
<Input class="informed-input" name="host" label="Host" initialValue={externalAddress && externalAddress.getHost()} readOnly />
|
||||
<Input class="informed-input" name="port" label="Port" initialValue={externalAddress && externalAddress.getPort()} readOnly/>
|
||||
<div class="float-container">
|
||||
<div class="float-child">
|
||||
<div className="edit-input-wrap">
|
||||
<Input class="informed-input" name="external-address" label="External Address" initialValue={externalAddress && `${getExternalAddressStr()}`} readOnly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-child">
|
||||
<CopyToClipboard
|
||||
text={externalAddress && `${getExternalAddressStr()}`}
|
||||
>
|
||||
<a data-tip="Copy address">
|
||||
<button fullWidth={false}>
|
||||
<ICON_CLIPBOARD />
|
||||
</button>
|
||||
</a>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
<ReactTooltip effect="solid" />
|
||||
</Form>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,3 +69,16 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.float-container {
|
||||
display: flex; /* ADDED */
|
||||
align-items: center; /* ADDED */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.float-child {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 730px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,16 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
|
||||
-webkit-transition: all 1s ease;
|
||||
-moz-transition: all 1s ease;
|
||||
-o-transition: all 1s ease;
|
||||
-ms-transition: all 1s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
img:hover {
|
||||
-webkit-filter: brightness(70%);
|
||||
filter: brightness(70%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,8 +111,28 @@
|
|||
.search-userPic-wrapper{
|
||||
flex-basis: 49px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.search-userPic{
|
||||
img{
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
min-width: 49px;
|
||||
|
||||
-webkit-transition: all 1s ease;
|
||||
-moz-transition: all 1s ease;
|
||||
-o-transition: all 1s ease;
|
||||
-ms-transition: all 1s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.search-userPic:hover{
|
||||
img{
|
||||
object-fit: cover;
|
||||
-webkit-filter: brightness(70%);
|
||||
filter: brightness(70%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,9 @@ import { ICON_REPLY, ICON_RETWEET,
|
|||
<Link onClick={(e)=>{
|
||||
e.stopPropagation();
|
||||
}} to={`/app/profile/${props.squeak.getAuthorPubkey()}`}>
|
||||
<img alt="" style={{borderRadius:'50%', minWidth:'49px'}} width="100%" height="49px" src={author ? `${getProfileImageSrcString(author)}` : null}/>
|
||||
<div className="card-userPic">
|
||||
<img alt="" width="100%" height="49px" src={author ? `${getProfileImageSrcString(author)}` : null}/>
|
||||
</div>
|
||||
</Link>
|
||||
{props.hasReply? <div className="squeak-reply-thread"></div> : null}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,27 @@
|
|||
margin-right: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
.card-userPic{
|
||||
img{
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
min-width: 49px;
|
||||
|
||||
-webkit-transition: all 1s ease;
|
||||
-moz-transition: all 1s ease;
|
||||
-o-transition: all 1s ease;
|
||||
-ms-transition: all 1s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.card-userPic:hover{
|
||||
img{
|
||||
-webkit-filter: brightness(70%);
|
||||
filter: brightness(70%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import {
|
|||
} from '@reduxjs/toolkit'
|
||||
import {
|
||||
getPeer,
|
||||
getPeerByAddress,
|
||||
getConnectedPeers,
|
||||
connectPeer,
|
||||
disconnectPeer,
|
||||
getSavedPeers,
|
||||
savePeer,
|
||||
deletePeer,
|
||||
renamePeer,
|
||||
enableAutoconnectPeer,
|
||||
disableAutoconnectPeer,
|
||||
} from '../../api/client'
|
||||
|
|
@ -37,7 +39,7 @@ export const fetchPeer = createAsyncThunk(
|
|||
let network = values.network;
|
||||
let host = values.host;
|
||||
let port = values.port;
|
||||
const response = await getPeer(
|
||||
const response = await getPeerByAddress(
|
||||
network,
|
||||
host,
|
||||
port,
|
||||
|
|
@ -153,6 +155,22 @@ export const setPeerAutoconnectDisabled = createAsyncThunk(
|
|||
}
|
||||
)
|
||||
|
||||
export const setRenamePeer = createAsyncThunk(
|
||||
'profile/setRenamePeer',
|
||||
async (values) => {
|
||||
console.log('Renaming peer');
|
||||
console.log(values.peerId);
|
||||
console.log(values.peerName);
|
||||
let peerId = values.peerId;
|
||||
let peerName = values.peerName;
|
||||
await renamePeer(peerId, peerName);
|
||||
console.log('Getting renamed peer');
|
||||
const response = await getPeer(peerId);
|
||||
console.log(response);
|
||||
return response.getSqueakPeer();
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
const peersSlice = createSlice({
|
||||
name: 'peers',
|
||||
|
|
@ -262,6 +280,14 @@ const peersSlice = createSlice({
|
|||
state.currentPeer = newSavedPeer;
|
||||
state.savedPeers = newSavedPeers;
|
||||
})
|
||||
.addCase(setRenamePeer.pending, (state, action) => {
|
||||
state.currentPeerStatus = 'loading'
|
||||
})
|
||||
.addCase(setRenamePeer.fulfilled, (state, action) => {
|
||||
const newPeer = action.payload;
|
||||
state.currentPeer = newPeer;
|
||||
state.currentPeerStatus = 'idle';
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@ const ProfileCard = (props) => {
|
|||
|
||||
return <Link onClick={(e)=>e.stopPropagation()} to={`/app/profile/${profile.getPubkey()}`} key={profile.getPubkey()} className="search-result-wapper">
|
||||
<div className="search-userPic-wrapper">
|
||||
<div className="search-userPic">
|
||||
<img style={{borderRadius:'50%', minWidth:'49px'}} width="100%" height="49px" src={`${getProfileImageSrcString(profile)}`}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="search-user-details">
|
||||
<div className="search-user-warp">
|
||||
<div className="search-user-info">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { Link } from 'react-router-dom'
|
|||
import { getProfileImageSrcString } from '../../squeakimages/images';
|
||||
import Loader from '../../components/Loader'
|
||||
|
||||
import { Form, Input, Checkbox, Relevant, Debug, TextArea, Option } from 'informed';
|
||||
import { Form, Input, Checkbox, Relevant, Debug, TextArea, Option, useFormApi } from 'informed';
|
||||
|
||||
import Select from 'react-select'
|
||||
|
||||
|
|
@ -21,6 +21,8 @@ import {
|
|||
selectBuySqueakStatus,
|
||||
selectSqueakOffers,
|
||||
fetchSqueakOffers,
|
||||
selectDownloadSqueakOffersStatus,
|
||||
setDownloadSqueakOffers,
|
||||
} from '../squeaks/squeaksSlice'
|
||||
import {
|
||||
fetchPaymentSummary,
|
||||
|
|
@ -43,6 +45,7 @@ const BuySqueak = (props) => {
|
|||
const signingProfiles = useSelector(selectSigningProfiles);
|
||||
const buySqueakStatus = useSelector(selectBuySqueakStatus);
|
||||
const squeakOffers = useSelector(selectSqueakOffers);
|
||||
const downloadSqueakOffersStatus = useSelector(selectDownloadSqueakOffersStatus);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [offer, setOffer] = useState(null);
|
||||
|
|
@ -86,12 +89,45 @@ const BuySqueak = (props) => {
|
|||
});
|
||||
}
|
||||
|
||||
const submitDownloadOffers = ({ values }) => {
|
||||
console.log(values);
|
||||
console.log('downloadOffers');
|
||||
dispatch(setDownloadSqueakOffers(squeakHash));
|
||||
}
|
||||
|
||||
const SubmitDownloadOffersButton = () => {
|
||||
const formApi = useFormApi();
|
||||
|
||||
return <button
|
||||
type="submit"
|
||||
className={'squeak-btn-side squeak-btn-active'}
|
||||
onClick={formApi.submitForm}>
|
||||
Re-download Offers
|
||||
</button>
|
||||
};
|
||||
|
||||
const DownloadOffersForm = () => (
|
||||
<Form onSubmit={submitDownloadOffers} className="Squeak-input-side">
|
||||
<div className="inner-input-links">
|
||||
<div className="input-links-side">
|
||||
</div>
|
||||
<div className="squeak-btn-holder">
|
||||
{downloadSqueakOffersStatus === 'idle' ?
|
||||
<SubmitDownloadOffersButton /> :
|
||||
<Loader />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
<div className="Squeak-input-side">
|
||||
<div className="edit-input-wrap">
|
||||
<DownloadOffersForm />
|
||||
{squeakOffers.length} offers
|
||||
<div className="inner-input-box">
|
||||
<Select options={optionsFromOffers(squeakOffers)} onChange={handleChangeOffer} />
|
||||
|
|
|
|||
|
|
@ -218,7 +218,9 @@ import { ICON_ARROWBACK, ICON_HEART, ICON_REPLY, ICON_RETWEET, ICON_HEARTFULL,
|
|||
<div className="squeak-header-content">
|
||||
<div className="squeak-user-pic">
|
||||
<Link to={`/app/profile/${squeak.getAuthorPubkey()}`}>
|
||||
<img alt="" style={{borderRadius:'50%', minWidth:'49px'}} width="100%" height="49px" src={author ? `${getProfileImageSrcString(author)}` : null}/>
|
||||
<div className="card-userPic">
|
||||
<img alt="" width="100%" height="49px" src={author ? `${getProfileImageSrcString(author)}` : null}/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="squeak-user-wrap">
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
getSqueakOffers,
|
||||
buySqueak,
|
||||
downloadSqueak,
|
||||
downloadSqueakSecretKey,
|
||||
} from '../../api/client'
|
||||
|
||||
const initialState = {
|
||||
|
|
@ -36,10 +37,10 @@ const initialState = {
|
|||
profileSqueaksStatus: 'idle',
|
||||
profileSqueaks: [],
|
||||
makeSqueakStatus: 'idle',
|
||||
squeakOffersStatus: 'idle',
|
||||
squeakOffers: [],
|
||||
buySqueakStatus: 'idle',
|
||||
downloadSqueakStatus: 'idle',
|
||||
downloadSqueakOffersStatus: 'idle',
|
||||
}
|
||||
|
||||
// Thunk functions
|
||||
|
|
@ -215,6 +216,16 @@ export const setDownloadSqueak = createAsyncThunk(
|
|||
}
|
||||
)
|
||||
|
||||
export const setDownloadSqueakOffers = createAsyncThunk(
|
||||
'squeaks/setDownloadSqueakOffers',
|
||||
async (squeakHash) => {
|
||||
console.log('Downloading squeak offers');
|
||||
await downloadSqueakSecretKey(squeakHash);
|
||||
const response = await getSqueakOffers(squeakHash);
|
||||
return response.getOffersList();
|
||||
}
|
||||
)
|
||||
|
||||
const updateCurrentSqueak = (state, newSqueak) => {
|
||||
const currentSqueak = state.currentSqueak;
|
||||
if (currentSqueak && currentSqueak.getSqueakHash() === newSqueak.getSqueakHash()) {
|
||||
|
|
@ -409,13 +420,11 @@ const squeaksSlice = createSlice({
|
|||
})
|
||||
.addCase(fetchSqueakOffers.pending, (state, action) => {
|
||||
state.squeakOffers = [];
|
||||
state.squeakOffersStatus = 'loading'
|
||||
})
|
||||
.addCase(fetchSqueakOffers.fulfilled, (state, action) => {
|
||||
console.log(action);
|
||||
const squeakOffers = action.payload;
|
||||
state.squeakOffers = squeakOffers;
|
||||
state.squeakOffersStatus = 'idle';
|
||||
})
|
||||
.addCase(setBuySqueak.pending, (state, action) => {
|
||||
state.buySqueakStatus = 'loading'
|
||||
|
|
@ -447,6 +456,18 @@ const squeaksSlice = createSlice({
|
|||
// TODO: check if current squeak is the one that got downloaded.
|
||||
state.currentSqueak = newSqueak;
|
||||
})
|
||||
.addCase(setDownloadSqueakOffers.pending, (state, action) => {
|
||||
state.downloadSqueakOffersStatus = 'loading'
|
||||
})
|
||||
.addCase(setDownloadSqueakOffers.rejected, (state, action) => {
|
||||
state.downloadSqueakOffersStatus = 'idle'
|
||||
})
|
||||
.addCase(setDownloadSqueakOffers.fulfilled, (state, action) => {
|
||||
console.log(action);
|
||||
state.downloadSqueakOffersStatus = 'idle';
|
||||
const squeakOffers = action.payload;
|
||||
state.squeakOffers = squeakOffers;
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -504,8 +525,8 @@ export const selectMakeSqueakStatus = state => state.squeaks.makeSqueakStatus
|
|||
|
||||
export const selectSqueakOffers = state => state.squeaks.squeakOffers
|
||||
|
||||
export const selectSqueakOffersStatus = state => state.squeaks.squeakOffersStatus
|
||||
|
||||
export const selectBuySqueakStatus = state => state.squeaks.buySqueakStatus
|
||||
|
||||
export const selectDownloadSqueakStatus = state => state.squeaks.downloadSqueakStatus
|
||||
|
||||
export const selectDownloadSqueakOffersStatus = state => state.squeaks.downloadSqueakOffersStatus
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ const TwitterAccountCard = (props) => {
|
|||
|
||||
return <div onClick={(e)=>e.stopPropagation()} key={profile.getPubkey()} className="search-result-wapper">
|
||||
<Link to={`/app/profile/${profile.getPubkey()}`} className="search-userPic-wrapper">
|
||||
<img style={{borderRadius:'50%', minWidth:'49px'}} width="100%" height="49px" src={`${getProfileImageSrcString(profile)}`}/>
|
||||
<div className="search-userPic">
|
||||
<img style={{borderRadius:'50%', minWidth:'49px'}} width="100%" height="49px" src={`${getProfileImageSrcString(profile)}`}/>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="search-user-details">
|
||||
<div className="search-user-warp">
|
||||
|
|
|
|||
12366
frontend/yarn.lock
12366
frontend/yarn.lock
File diff suppressed because it is too large
Load diff
|
|
@ -3,10 +3,10 @@ network=simnet
|
|||
price_msat=1000000
|
||||
max_squeaks_per_public_key_per_block=50
|
||||
|
||||
[lnd]
|
||||
host=lnd
|
||||
tls_cert_path=/root/.lnd/tls.cert
|
||||
macaroon_path=/root/.lnd/data/chain/bitcoin/simnet/admin.macaroon
|
||||
[lightning]
|
||||
lnd_rpc_host=lnd
|
||||
lnd_tls_cert_path=/root/.lnd/tls.cert
|
||||
lnd_macaroon_path=/root/.lnd/data/chain/bitcoin/simnet/admin.macaroon
|
||||
|
||||
[bitcoin]
|
||||
rpc_host=btcd
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ services:
|
|||
- "squeaknode_db:squeaknode_db"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./start-squeaknode.sh"]
|
||||
entrypoint: ["./entrypoint.sh"]
|
||||
|
||||
squeaknode_other:
|
||||
image: squeaknode
|
||||
|
|
@ -145,7 +145,7 @@ services:
|
|||
- "squeaknode_other_db:squeaknode_other_db"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./start-squeaknode.sh"]
|
||||
entrypoint: ["./entrypoint.sh"]
|
||||
|
||||
test:
|
||||
image: test
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
while true; do
|
||||
client_address=$(docker exec -it test_lnd_client lncli --network=simnet newaddress np2wkh | jq .address -r)
|
||||
MINING_ADDRESS=$client_address docker-compose up -d btcd
|
||||
MINING_ADDRESS=$client_address docker compose up -d btcd
|
||||
echo "Mining 1 blocks to address: $client_address ..."
|
||||
docker-compose run btcctl generate 400
|
||||
docker compose run btcctl generate 400
|
||||
sleep 10
|
||||
done
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ trap "kill 0" EXIT
|
|||
function mine_blocks {
|
||||
while true; do
|
||||
printf "Mining 1 block to address: $MINING_ADDRESS ..."
|
||||
docker-compose run btcctl generate 1
|
||||
docker compose run btcctl generate 1
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
cd itests
|
||||
docker-compose down --volumes --remove-orphans
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
docker compose down --volumes --remove-orphans
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
|
||||
# Initialize the blockchain with miner rewards going to the test client.
|
||||
|
||||
|
|
@ -29,9 +29,9 @@ do
|
|||
sleep 2
|
||||
done
|
||||
|
||||
MINING_ADDRESS=$client_address docker-compose up -d btcd
|
||||
export MINING_ADDRESS="$client_address docker compose up -d btcd"
|
||||
echo "Mining 400 blocks to address: $client_address ..."
|
||||
docker-compose run btcctl generate 400
|
||||
docker compose run btcctl generate 400
|
||||
echo "Finished mining blocks."
|
||||
sleep 30
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ mine_blocks &
|
|||
echo "Background mining task is in background..."
|
||||
|
||||
echo "Running test.sh...."
|
||||
docker-compose run test ./test.sh
|
||||
docker compose run test ./test.sh
|
||||
|
||||
echo "Shutting down itest..."
|
||||
# docker-compose down
|
||||
# docker compose down
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ from tests.util import create_signing_profile
|
|||
from tests.util import delete_profile
|
||||
from tests.util import delete_squeak
|
||||
from tests.util import download_squeak
|
||||
from tests.util import download_squeak_secret_key
|
||||
from tests.util import free_price
|
||||
from tests.util import get_balance
|
||||
from tests.util import get_external_address
|
||||
from tests.util import get_hash
|
||||
from tests.util import get_network
|
||||
|
|
@ -606,6 +608,18 @@ def test_delete_peer(admin_stub, peer_id):
|
|||
|
||||
def test_send_coins(admin_stub, other_admin_stub):
|
||||
new_address_response = admin_stub.LndNewAddress(ln.NewAddressRequest())
|
||||
get_balance_response = get_balance(
|
||||
other_admin_stub,
|
||||
)
|
||||
print("Get balance: {}".format(get_balance_response))
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------")
|
||||
print("--------------------", flush=True)
|
||||
send_coins_response = send_coins(
|
||||
other_admin_stub, new_address_response.address, 55555555
|
||||
)
|
||||
|
|
@ -631,6 +645,8 @@ def test_buy_squeak(
|
|||
):
|
||||
# Download squeak
|
||||
download_squeak(other_admin_stub, saved_squeak_hash)
|
||||
# Download secret key
|
||||
download_squeak_secret_key(other_admin_stub, saved_squeak_hash)
|
||||
|
||||
# Get the sent offers from the seller node
|
||||
get_sent_offers_response = admin_stub.GetSentOffers(
|
||||
|
|
@ -781,6 +797,8 @@ def test_download_free_squeak(
|
|||
with free_price(admin_stub):
|
||||
# Download squeak
|
||||
download_result = download_squeak(other_admin_stub, saved_squeak_hash)
|
||||
# Download secret key
|
||||
download_squeak_secret_key(other_admin_stub, saved_squeak_hash)
|
||||
print('download_result:')
|
||||
print(download_result)
|
||||
assert download_result.number_downloaded == 1
|
||||
|
|
@ -821,6 +839,8 @@ def test_download_single_squeak(
|
|||
|
||||
# Download squeak
|
||||
download_result = download_squeak(other_admin_stub, saved_squeak_hash)
|
||||
# Download secret key
|
||||
download_squeak_secret_key(other_admin_stub, saved_squeak_hash)
|
||||
assert download_result.number_downloaded == 1
|
||||
assert download_result.number_requested == 1
|
||||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,15 @@ def download_squeak(node_stub, squeak_hash):
|
|||
return download_squeak_response.download_result
|
||||
|
||||
|
||||
def download_squeak_secret_key(node_stub, squeak_hash):
|
||||
download_squeak_secret_key_response = node_stub.DownloadSqueakSecretKey(
|
||||
squeak_admin_pb2.DownloadSqueakSecretKeyRequest(
|
||||
squeak_hash=squeak_hash,
|
||||
),
|
||||
)
|
||||
return download_squeak_secret_key_response.download_result
|
||||
|
||||
|
||||
def download_squeaks(node_stub, pubkeys_in_hex, min_block, max_block, reply_to):
|
||||
download_squeaks_response = node_stub.DownloadSqueaks(
|
||||
squeak_admin_pb2.DownloadSqueaksRequest(
|
||||
|
|
@ -428,6 +437,13 @@ def disconnect_peer(node_stub, pubkey):
|
|||
)
|
||||
|
||||
|
||||
def get_balance(node_stub):
|
||||
get_balance_request = lnd_pb2.WalletBalanceRequest()
|
||||
return node_stub.LndWalletBalance(
|
||||
get_balance_request,
|
||||
)
|
||||
|
||||
|
||||
def send_coins(node_stub, addr, amount):
|
||||
send_coins_request = lnd_pb2.SendCoinsRequest(
|
||||
addr=addr,
|
||||
|
|
|
|||
|
|
@ -208,6 +208,10 @@ service SqueakAdmin {
|
|||
*/
|
||||
rpc DownloadSqueak (DownloadSqueakRequest) returns (DownloadSqueakReply) {}
|
||||
|
||||
/** sqkadmin: `downloadsqueaksecretkey`
|
||||
*/
|
||||
rpc DownloadSqueakSecretKey (DownloadSqueakSecretKeyRequest) returns (DownloadSqueakSecretKeyReply) {}
|
||||
|
||||
/** sqkadmin: `downloadoffers`
|
||||
*/
|
||||
rpc DownloadOffers (DownloadOffersRequest) returns (DownloadOffersReply) {}
|
||||
|
|
@ -1104,6 +1108,16 @@ message DownloadSqueakReply {
|
|||
DownloadResult download_result = 1;
|
||||
}
|
||||
|
||||
message DownloadSqueakSecretKeyRequest {
|
||||
/// The squeak hash.
|
||||
string squeak_hash = 1;
|
||||
}
|
||||
|
||||
message DownloadSqueakSecretKeyReply {
|
||||
// The download result
|
||||
DownloadResult download_result = 1;
|
||||
}
|
||||
|
||||
message DownloadOffersRequest {
|
||||
/// The squeak hash.
|
||||
string squeak_hash = 1;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
alembic==1.7.1
|
||||
docutils==0.17.1
|
||||
Flask==2.0.1
|
||||
flask-cors==3.0.10
|
||||
flask-login==0.5.0
|
||||
|
|
@ -7,6 +8,7 @@ googleapis-common-protos==1.53.0
|
|||
importlib_resources==1.4.0
|
||||
mypy-protobuf==2.9
|
||||
protobuf==3.17.3
|
||||
pyln-client==0.10.1
|
||||
PySocks==1.7.1
|
||||
python-bitcoinlib==0.11.0
|
||||
pyzmq==22.3.0
|
||||
|
|
|
|||
|
|
@ -20,4 +20,4 @@
|
|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
__version__ = '0.2.20'
|
||||
__version__ = '0.3.5'
|
||||
|
|
|
|||
|
|
@ -667,6 +667,19 @@ class SqueakAdminServerHandler(object):
|
|||
download_result=download_result_msg,
|
||||
)
|
||||
|
||||
def handle_download_squeak_secret_key(self, request):
|
||||
squeak_hash_str = request.squeak_hash
|
||||
squeak_hash = bytes.fromhex(squeak_hash_str)
|
||||
logger.info(
|
||||
"Handle download squeak secret key for hash: {}".format(squeak_hash_str))
|
||||
download_result = self.squeak_controller.download_single_squeak_secret_key(
|
||||
squeak_hash)
|
||||
logger.info("Download result: {}".format(download_result))
|
||||
download_result_msg = download_result_to_message(download_result)
|
||||
return squeak_admin_pb2.DownloadSqueakReply(
|
||||
download_result=download_result_msg,
|
||||
)
|
||||
|
||||
def handle_download_offers(self, request):
|
||||
squeak_hash_str = request.squeak_hash
|
||||
squeak_hash = bytes.fromhex(squeak_hash_str)
|
||||
|
|
|
|||
|
|
@ -200,6 +200,9 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
|
|||
def DownloadSqueak(self, request, context):
|
||||
return self.handler.handle_download_squeak(request)
|
||||
|
||||
def DownloadSqueakSecretKey(self, request, context):
|
||||
return self.handler.handle_download_squeak_secret_key(request)
|
||||
|
||||
def DownloadOffers(self, request, context):
|
||||
return self.handler.handle_download_offers(request)
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ def create_app(handler, username, password):
|
|||
|
||||
@login.unauthorized_handler
|
||||
def unauthorized_callback():
|
||||
return redirect("/login")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
def protobuf_serialized(request_message):
|
||||
def decorator(func):
|
||||
|
|
@ -403,6 +403,12 @@ def create_app(handler, username, password):
|
|||
def downloadsqueak(msg):
|
||||
return handler.handle_download_squeak(msg)
|
||||
|
||||
@app.route("/downloadsqueaksecretkey", methods=["POST"])
|
||||
@login_required
|
||||
@protobuf_serialized(squeak_admin_pb2.DownloadSqueakSecretKeyRequest())
|
||||
def downloadsqueaksecretkey(msg):
|
||||
return handler.handle_download_squeak_secret_key(msg)
|
||||
|
||||
@app.route("/downloadoffers", methods=["POST"])
|
||||
@login_required
|
||||
@protobuf_serialized(squeak_admin_pb2.DownloadOffersRequest())
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.206389cd.css",
|
||||
"main.js": "/static/js/main.de2a65b4.js",
|
||||
"main.css": "/static/css/main.fe6c5463.css",
|
||||
"main.js": "/static/js/main.f8361353.js",
|
||||
"static/css/306.7f7715a0.chunk.css": "/static/css/306.7f7715a0.chunk.css",
|
||||
"static/js/306.90b2a66c.chunk.js": "/static/js/306.90b2a66c.chunk.js",
|
||||
"static/css/900.b66ac5fb.chunk.css": "/static/css/900.b66ac5fb.chunk.css",
|
||||
"static/css/900.5027b0a1.chunk.css": "/static/css/900.5027b0a1.chunk.css",
|
||||
"static/js/900.1b104345.chunk.js": "/static/js/900.1b104345.chunk.js",
|
||||
"static/css/841.693c3c31.chunk.css": "/static/css/841.693c3c31.chunk.css",
|
||||
"static/js/841.9f23f674.chunk.js": "/static/js/841.9f23f674.chunk.js",
|
||||
"static/js/841.91f0aab2.chunk.js": "/static/js/841.91f0aab2.chunk.js",
|
||||
"static/media/icon.svg": "/static/media/icon.982210ec9275956e4d3cc77fa90167fd.svg",
|
||||
"index.html": "/index.html",
|
||||
"main.206389cd.css.map": "/static/css/main.206389cd.css.map",
|
||||
"main.de2a65b4.js.map": "/static/js/main.de2a65b4.js.map",
|
||||
"main.fe6c5463.css.map": "/static/css/main.fe6c5463.css.map",
|
||||
"main.f8361353.js.map": "/static/js/main.f8361353.js.map",
|
||||
"306.7f7715a0.chunk.css.map": "/static/css/306.7f7715a0.chunk.css.map",
|
||||
"306.90b2a66c.chunk.js.map": "/static/js/306.90b2a66c.chunk.js.map",
|
||||
"900.b66ac5fb.chunk.css.map": "/static/css/900.b66ac5fb.chunk.css.map",
|
||||
"900.5027b0a1.chunk.css.map": "/static/css/900.5027b0a1.chunk.css.map",
|
||||
"900.1b104345.chunk.js.map": "/static/js/900.1b104345.chunk.js.map",
|
||||
"841.693c3c31.chunk.css.map": "/static/css/841.693c3c31.chunk.css.map",
|
||||
"841.9f23f674.chunk.js.map": "/static/js/841.9f23f674.chunk.js.map"
|
||||
"841.91f0aab2.chunk.js.map": "/static/js/841.91f0aab2.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.206389cd.css",
|
||||
"static/js/main.de2a65b4.js"
|
||||
"static/css/main.fe6c5463.css",
|
||||
"static/js/main.f8361353.js"
|
||||
]
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#1da1f2"/><meta nam="description" content="Squeaknode!"/><meta name="og:title" content="Squeaknode"/><meta name="og:description" content="check out my the Squeaknode I made"/><link href="https://fonts.googleapis.com/css2?family=Assistant&display=swap" rel="stylesheet"><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Squeaknode</title><script defer="defer" src="/static/js/main.de2a65b4.js"></script><link href="/static/css/main.206389cd.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#1da1f2"/><meta nam="description" content="Squeaknode!"/><meta name="og:title" content="Squeaknode"/><meta name="og:description" content="check out my the Squeaknode I made"/><link href="https://fonts.googleapis.com/css2?family=Assistant&display=swap" rel="stylesheet"><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Squeaknode</title><script defer="defer" src="/static/js/main.f8361353.js"></script><link href="/static/css/main.fe6c5463.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.profile-wrapper{border-right:1px solid #e6ecf0;display:flex;flex-direction:column;max-width:600px;min-height:2000px;width:100%}.profile-header-wrapper{align-items:center;background-color:#fff;border-bottom:1px solid #e6ecf0;border-left:1px solid #e6ecf0;cursor:pointer;display:flex;height:53px;margin:0 auto;max-width:1000px;min-height:53px;padding-left:15px;padding-right:15px;position:-webkit-sticky;position:sticky;top:0;width:100%;z-index:8}.profile-header-back{align-items:flex-start;justify-content:center;min-height:30px;min-width:55px}.header-back-wrapper{align-items:center;border:1px solid transparent;border-radius:9999px;display:flex;height:39px;justify-content:center;margin-left:-5px;transition:.2s ease-in-out;width:39px;will-change:background-color}.header-back-wrapper svg{fill:#1da1f2;height:1.5em}.header-back-wrapper:hover{background-color:rgba(29,161,242,.1)}.profile-header-content{display:flex;flex-direction:column}.profile-header-name{font-size:19px;font-weight:800}.profile-header-squeaks{color:#657786;font-size:14px;line-height:19.6875px}.profile-banner-wrapper{height:200px;max-width:600px;position:relative}.profile-banner-wrapper img{height:100%;object-fit:cover;width:100%}.profile-details-wrapper{padding:10px 15px 15px}.profile-options{align-items:center;display:flex;justify-content:flex-end;position:relative}.profile-image-wrapper{border:4px solid #fff;border-radius:50%;bottom:0;height:134px;left:0;min-width:49px;position:absolute;width:134px;z-index:5}.profile-image-wrapper img{border-radius:50%;height:100%;object-fit:cover;transition:all 1s ease;width:100%}.profile-image-wrapper img:hover{-webkit-filter:brightness(70%);filter:brightness(70%)}.profile-edit-button{align-items:center;border:1px solid #1da1f2;border-radius:9999px;cursor:pointer;display:flex;justify-content:center;margin-left:7px;min-height:39px;min-width:98.8px;padding-left:1em;padding-right:1em;transition:.2s ease-in-out}.profile-edit-button span{color:#1da1f2;font-weight:800;text-align:center;width:100%}.profile-edit-button:hover{background-color:rgba(29,161,242,.1)}.unfollow-switch{background-color:#1da1f2}.unfollow-switch span{color:#fff!important}.unfollow-switch:hover{background-color:#ca2055!important;border:1px solid transparent}.unfollow-switch:hover span{color:#fff}.unfollow-switch:hover span span{display:none}.unfollow-switch:hover span:before{content:"Unfollow"}.profile-details-box{margin-top:40px}.profile-name{font-size:19px;font-weight:800}.profile-username{color:#657786;font-size:15px}.profile-bio{margin-bottom:10px;margin-top:10px}.profile-info-box{display:flex;margin-top:10px}.profile-info-box svg{fill:#657786;height:18.75px;margin-right:5px}.profile-date,.profile-location{color:#657786;margin-right:10px}.profile-social-box{display:flex;margin-top:7px}.profile-social-box div{cursor:pointer;display:flex}.profile-social-box div:hover{text-decoration:underline}.follow-num{cursor:pointer;font-weight:700;margin-right:3px}.follow-text{color:#657786;cursor:pointer;margin-right:20px}.profile-nav-menu{align-items:center;border-bottom:1px solid #e6ecf0;display:flex;justify-content:space-around}.profile-nav-item{border-bottom:2px solid transparent;color:#657786;cursor:pointer;font-weight:700;overflow:hidden;padding:15px;text-align:center;transition:.2s;white-space:nowrap;width:100%;will-change:background-color}.profile-nav-item:hover{background-color:rgba(29,161,242,.1);color:#1da1f2}.activeTab{border-bottom:2px solid #1da1f2;color:#1da1f2}.new-msg{align-items:center;border-radius:999px;cursor:pointer;display:flex;justify-content:center;padding:10px;transition:.2s ease-in-out}.new-msg:hover{background-color:rgba(29,161,242,.1)}.new-msg:hover svg{fill:#1da1f2}.new-msg svg{fill:#000;align-items:center;display:flex;height:25px;transition:.2s ease-in-out;width:25px}
|
||||
/*# sourceMappingURL=900.5027b0a1.chunk.css.map*/
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,2 +0,0 @@
|
|||
.profile-wrapper{border-right:1px solid #e6ecf0;display:flex;flex-direction:column;max-width:600px;min-height:2000px;width:100%}.profile-header-wrapper{align-items:center;background-color:#fff;border-bottom:1px solid #e6ecf0;border-left:1px solid #e6ecf0;cursor:pointer;display:flex;height:53px;margin:0 auto;max-width:1000px;min-height:53px;padding-left:15px;padding-right:15px;position:-webkit-sticky;position:sticky;top:0;width:100%;z-index:8}.profile-header-back{align-items:flex-start;justify-content:center;min-height:30px;min-width:55px}.header-back-wrapper{align-items:center;border:1px solid transparent;border-radius:9999px;display:flex;height:39px;justify-content:center;margin-left:-5px;transition:.2s ease-in-out;width:39px;will-change:background-color}.header-back-wrapper svg{fill:#1da1f2;height:1.5em}.header-back-wrapper:hover{background-color:rgba(29,161,242,.1)}.profile-header-content{display:flex;flex-direction:column}.profile-header-name{font-size:19px;font-weight:800}.profile-header-squeaks{color:#657786;font-size:14px;line-height:19.6875px}.profile-banner-wrapper{height:200px;max-width:600px;position:relative}.profile-banner-wrapper img{height:100%;object-fit:cover;width:100%}.profile-details-wrapper{padding:10px 15px 15px}.profile-options{align-items:center;display:flex;justify-content:flex-end;position:relative}.profile-image-wrapper{border:4px solid #fff;border-radius:50%;bottom:0;height:134px;left:0;min-width:49px;position:absolute;width:134px;z-index:5}.profile-image-wrapper img{border-radius:50%;height:100%;object-fit:cover;width:100%}.profile-edit-button{align-items:center;border:1px solid #1da1f2;border-radius:9999px;cursor:pointer;display:flex;justify-content:center;margin-left:7px;min-height:39px;min-width:98.8px;padding-left:1em;padding-right:1em;transition:.2s ease-in-out}.profile-edit-button span{color:#1da1f2;font-weight:800;text-align:center;width:100%}.profile-edit-button:hover{background-color:rgba(29,161,242,.1)}.unfollow-switch{background-color:#1da1f2}.unfollow-switch span{color:#fff!important}.unfollow-switch:hover{background-color:#ca2055!important;border:1px solid transparent}.unfollow-switch:hover span{color:#fff}.unfollow-switch:hover span span{display:none}.unfollow-switch:hover span:before{content:"Unfollow"}.profile-details-box{margin-top:40px}.profile-name{font-size:19px;font-weight:800}.profile-username{color:#657786;font-size:15px}.profile-bio{margin-bottom:10px;margin-top:10px}.profile-info-box{display:flex;margin-top:10px}.profile-info-box svg{fill:#657786;height:18.75px;margin-right:5px}.profile-date,.profile-location{color:#657786;margin-right:10px}.profile-social-box{display:flex;margin-top:7px}.profile-social-box div{cursor:pointer;display:flex}.profile-social-box div:hover{text-decoration:underline}.follow-num{cursor:pointer;font-weight:700;margin-right:3px}.follow-text{color:#657786;cursor:pointer;margin-right:20px}.profile-nav-menu{align-items:center;border-bottom:1px solid #e6ecf0;display:flex;justify-content:space-around}.profile-nav-item{border-bottom:2px solid transparent;color:#657786;cursor:pointer;font-weight:700;overflow:hidden;padding:15px;text-align:center;transition:.2s;white-space:nowrap;width:100%;will-change:background-color}.profile-nav-item:hover{background-color:rgba(29,161,242,.1);color:#1da1f2}.activeTab{border-bottom:2px solid #1da1f2;color:#1da1f2}.new-msg{align-items:center;border-radius:999px;cursor:pointer;display:flex;justify-content:center;padding:10px;transition:.2s ease-in-out}.new-msg:hover{background-color:rgba(29,161,242,.1)}.new-msg:hover svg{fill:#1da1f2}.new-msg svg{fill:#000;align-items:center;display:flex;height:25px;transition:.2s ease-in-out;width:25px}
|
||||
/*# sourceMappingURL=900.b66ac5fb.chunk.css.map*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -20,6 +20,7 @@
|
|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
import logging
|
||||
from concurrent.futures import as_completed
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from concurrent.futures import wait
|
||||
from typing import Optional
|
||||
|
|
@ -77,10 +78,30 @@ class NetworkController:
|
|||
)
|
||||
for downloader in downloaders
|
||||
]
|
||||
# wait for all tasks to complete
|
||||
# wait for at least one task to complete successfully.
|
||||
wait(futures)
|
||||
|
||||
def download_single_squeak(self, squeak_hash: bytes) -> None:
|
||||
peers = self.squeak_store.get_autoconnect_peers()
|
||||
downloaders = [
|
||||
self.get_downloader(peer)
|
||||
for peer in peers
|
||||
]
|
||||
executor = ThreadPoolExecutor(50)
|
||||
# submit tasks and collect futures
|
||||
futures = [
|
||||
executor.submit(
|
||||
downloader.download_single_squeak,
|
||||
squeak_hash
|
||||
)
|
||||
for downloader in downloaders]
|
||||
for future in as_completed(futures):
|
||||
err = future.exception()
|
||||
if err is None:
|
||||
break
|
||||
executor.shutdown(wait=False)
|
||||
|
||||
def download_single_squeak_secret_key(self, squeak_hash: bytes) -> None:
|
||||
peers = self.squeak_store.get_autoconnect_peers()
|
||||
downloaders = [
|
||||
self.get_downloader(peer)
|
||||
|
|
@ -90,7 +111,7 @@ class NetworkController:
|
|||
# submit tasks and collect futures
|
||||
futures = [
|
||||
executor.submit(
|
||||
downloader.download_single_squeak,
|
||||
downloader.download_single_squeak_secret_key,
|
||||
squeak_hash
|
||||
)
|
||||
for downloader in downloaders]
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from typing import List
|
|||
from typing import Optional
|
||||
|
||||
import requests
|
||||
from squeak.core import CBaseSqueak
|
||||
from squeak.core import CResqueak
|
||||
from squeak.core import CSqueak
|
||||
from squeak.core.keys import SqueakPublicKey
|
||||
|
|
@ -88,7 +89,7 @@ class PeerClient:
|
|||
for squeak_hash_str in squeak_hashes_str
|
||||
]
|
||||
|
||||
def get_squeak(self, squeak_hash: bytes) -> Optional[CSqueak]:
|
||||
def get_squeak(self, squeak_hash: bytes) -> Optional[CBaseSqueak]:
|
||||
squeak_hash_str = squeak_hash.hex()
|
||||
url = f"{self.base_url}/squeak/{squeak_hash_str}"
|
||||
r = requests.get(
|
||||
|
|
@ -98,19 +99,16 @@ class PeerClient:
|
|||
)
|
||||
if r.status_code != requests.codes.ok:
|
||||
return None
|
||||
logger.info(r)
|
||||
squeak_json = r.json()
|
||||
# squeak_bytes = r.content
|
||||
squeak_type = squeak_json['squeak_type']
|
||||
squeak_bytes_hex = squeak_json['squeak_bytes']
|
||||
squeak_bytes = bytes.fromhex(squeak_bytes_hex)
|
||||
logger.info('Client downloaded: {}'.format(squeak_json))
|
||||
if squeak_type == 'squeak':
|
||||
squeak_bytes = r.content
|
||||
try:
|
||||
return CSqueak.deserialize(squeak_bytes)
|
||||
elif squeak_type == 'resqueak':
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return CResqueak.deserialize(squeak_bytes)
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_secret_key(self, squeak_hash: bytes) -> Optional[bytes]:
|
||||
squeak_hash_str = squeak_hash.hex()
|
||||
|
|
|
|||
|
|
@ -64,20 +64,31 @@ class PeerDownloader(ABC):
|
|||
pubkeys,
|
||||
)
|
||||
for squeak_hash in squeak_hashes:
|
||||
self.download_squeak(
|
||||
squeak_hash,
|
||||
min_block,
|
||||
max_block,
|
||||
pubkeys,
|
||||
)
|
||||
try:
|
||||
self.download_squeak(
|
||||
squeak_hash,
|
||||
min_block,
|
||||
max_block,
|
||||
pubkeys,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
for squeak_hash in squeak_hashes:
|
||||
self.download_secret_key(squeak_hash)
|
||||
try:
|
||||
self.download_secret_key(squeak_hash)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def download_single_squeak(
|
||||
self,
|
||||
squeak_hash: bytes,
|
||||
) -> None:
|
||||
self.download_squeak(squeak_hash)
|
||||
|
||||
def download_single_squeak_secret_key(
|
||||
self,
|
||||
squeak_hash: bytes,
|
||||
) -> None:
|
||||
self.download_secret_key(squeak_hash)
|
||||
|
||||
def download_squeak(
|
||||
|
|
@ -89,22 +100,22 @@ class PeerDownloader(ABC):
|
|||
) -> None:
|
||||
# Download the squeak if not already owned.
|
||||
if self.squeak_store.get_squeak(squeak_hash):
|
||||
return
|
||||
raise Exception('Squeak already saved.')
|
||||
|
||||
# Download the squeak if not already owned.
|
||||
squeak = self.client.get_squeak(squeak_hash)
|
||||
|
||||
# Check if the squeak is valid.
|
||||
if not squeak:
|
||||
return
|
||||
raise Exception('Squeak not found.')
|
||||
if get_hash(squeak) != squeak_hash:
|
||||
return
|
||||
raise Exception('Squeak has wrong hash.')
|
||||
if min_block and squeak.nBlockHeight < min_block:
|
||||
return
|
||||
raise Exception('Squeak has block height below minimum.')
|
||||
if max_block and squeak.nBlockHeight > max_block:
|
||||
return
|
||||
raise Exception('Squeak has block height above minimum.')
|
||||
if pubkeys and squeak.GetPubKey() not in pubkeys:
|
||||
return
|
||||
raise Exception('Squeak has wronge pubkey.')
|
||||
|
||||
# Save the squeak.
|
||||
self.squeak_store.save_squeak(squeak)
|
||||
|
|
@ -114,10 +125,10 @@ class PeerDownloader(ABC):
|
|||
|
||||
# Check if squeak is already owned.
|
||||
if not squeak:
|
||||
return
|
||||
raise Exception('Squeak is not already saved.')
|
||||
|
||||
if self.squeak_store.get_squeak_secret_key(squeak_hash):
|
||||
return
|
||||
raise Exception('Squeak secret key is already saved.')
|
||||
|
||||
# Download the secret key if not already owned.
|
||||
secret_key = self.client.get_secret_key(squeak_hash)
|
||||
|
|
@ -127,7 +138,7 @@ class PeerDownloader(ABC):
|
|||
|
||||
for received_offer in self.squeak_store.get_received_offers(squeak_hash):
|
||||
if received_offer.peer_address == self.peer.address:
|
||||
return
|
||||
raise Exception('Received offer from this peer already saved.')
|
||||
|
||||
# Download the offer if secret key not already owned.
|
||||
offer = self.client.get_offer(squeak_hash)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ DEFAULT_LOG_LEVEL = "INFO"
|
|||
DEFAULT_MAX_SQUEAKS = 10000
|
||||
DEFAULT_MAX_SQUEAKS_PER_PUBLIC_KEY_PER_BLOCK = 100
|
||||
DEFAULT_SERVER_RPC_HOST = "0.0.0.0"
|
||||
DEFAULT_SERVER_RPC_PORT = None
|
||||
DEFAULT_SERVER_RPC_PORT = 8555
|
||||
DEFAULT_EXTERNAL_PORT = 8555
|
||||
DEFAULT_ADMIN_RPC_HOST = "0.0.0.0"
|
||||
DEFAULT_ADMIN_RPC_PORT = 8994
|
||||
|
|
@ -48,17 +48,20 @@ DEFAULT_WEBADMIN_HOST = "0.0.0.0"
|
|||
DEFAULT_WEBADMIN_PORT = 12994
|
||||
DEFAULT_BITCOIN_RPC_HOST = "localhost"
|
||||
DEFAULT_BITCOIN_RPC_PORT = 18334
|
||||
DEFAULT_BITCOIN_ZEROMQ_HASHBLOCK_PORT = 28334
|
||||
BITCOIN_RPC_PORT = {
|
||||
"mainnet": 8332,
|
||||
"testnet": 18332,
|
||||
"simnet": 18556,
|
||||
}
|
||||
DEFAULT_LIGHTNING_BACKEND = "lnd"
|
||||
DEFAULT_LND_PORT = 9735
|
||||
DEFAULT_LND_RPC_PORT = 10009
|
||||
DEFAULT_CLIGHTNING_DIR = ".lightning"
|
||||
DEFAULT_CLIGHTNING_RPC_FILE = str(
|
||||
Path.home() / DEFAULT_CLIGHTNING_DIR / 'lightning-rpc')
|
||||
DEFAULT_SQK_DIR = ".sqk"
|
||||
DEFAULT_SQK_DIR_PATH = str(Path.home() / DEFAULT_SQK_DIR)
|
||||
DEFAULT_LND_HOST = "localhost"
|
||||
DEFAULT_LND_RPC_HOST = "localhost"
|
||||
DEFAULT_INTEREST_BLOCK_INTERVAL = 2016
|
||||
DEFAULT_SENT_OFFER_RETENTION_S = 86400
|
||||
DEFAULT_RECEIVED_OFFER_RETENTION_S = 86400
|
||||
|
|
@ -78,18 +81,19 @@ class BitcoinConfig(Config):
|
|||
rpc_pass = key(cast=str, required=False, default="")
|
||||
rpc_use_ssl = key(cast=bool, required=False, default=False)
|
||||
rpc_ssl_cert = key(cast=str, required=False, default="")
|
||||
zeromq_hashblock_port = key(cast=int, required=False,
|
||||
default=DEFAULT_BITCOIN_ZEROMQ_HASHBLOCK_PORT)
|
||||
|
||||
|
||||
@section('lnd')
|
||||
class LndConfig(Config):
|
||||
host = key(cast=str, required=False, default=DEFAULT_LND_HOST)
|
||||
@section('lightning')
|
||||
class LightningConfig(Config):
|
||||
backend = key(cast=str, required=False, default=DEFAULT_LIGHTNING_BACKEND)
|
||||
external_host = key(cast=str, required=False, default="")
|
||||
port = key(cast=int, required=False, default=DEFAULT_LND_PORT)
|
||||
rpc_port = key(cast=int, required=False, default=DEFAULT_LND_RPC_PORT)
|
||||
tls_cert_path = key(cast=str, required=False, default="")
|
||||
macaroon_path = key(cast=str, required=False, default="")
|
||||
external_port = key(cast=int, required=False, default=DEFAULT_LND_PORT)
|
||||
lnd_rpc_host = key(cast=str, required=False, default=DEFAULT_LND_RPC_HOST)
|
||||
lnd_rpc_port = key(cast=int, required=False, default=DEFAULT_LND_RPC_PORT)
|
||||
lnd_tls_cert_path = key(cast=str, required=False, default="")
|
||||
lnd_macaroon_path = key(cast=str, required=False, default="")
|
||||
clightning_rpc_file = key(cast=str, required=False,
|
||||
default=DEFAULT_CLIGHTNING_RPC_FILE)
|
||||
|
||||
|
||||
@section('tor')
|
||||
|
|
@ -102,7 +106,7 @@ class TorConfig(Config):
|
|||
class ServerConfig(Config):
|
||||
enabled = key(cast=bool, required=False, default=True)
|
||||
host = key(cast=str, required=False, default=DEFAULT_SERVER_RPC_HOST)
|
||||
port = key(cast=int, required=False, default=DEFAULT_SERVER_RPC_PORT)
|
||||
port = key(cast=int, required=False, default=0)
|
||||
external_address = key(cast=str, required=False, default="")
|
||||
external_port = key(cast=int, required=False,
|
||||
default=DEFAULT_EXTERNAL_PORT)
|
||||
|
|
@ -172,7 +176,7 @@ class TwitterConfig(Config):
|
|||
|
||||
class SqueaknodeConfig(Config):
|
||||
bitcoin = group_key(BitcoinConfig)
|
||||
lnd = group_key(LndConfig)
|
||||
lightning = group_key(LightningConfig)
|
||||
tor = group_key(TorConfig)
|
||||
server = group_key(ServerConfig)
|
||||
rpc = group_key(RpcConfig)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,13 @@ from squeaknode.core.squeak_peer import SqueakPeer
|
|||
def create_saved_peer(
|
||||
peer_name: str,
|
||||
peer_address: PeerAddress,
|
||||
default_peer_port: int,
|
||||
) -> SqueakPeer:
|
||||
validate_saved_peer_name(peer_name)
|
||||
if peer_address.port == 0:
|
||||
peer_address = peer_address._replace(
|
||||
port=default_peer_port,
|
||||
)
|
||||
return SqueakPeer(
|
||||
peer_id=None,
|
||||
peer_name=peer_name,
|
||||
|
|
|
|||
|
|
@ -24,13 +24,11 @@ from typing import Callable
|
|||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
import grpc
|
||||
from bitcoin.core import CBlockHeader
|
||||
from squeak.core import CBaseSqueak
|
||||
from squeak.core import CSqueak
|
||||
|
||||
from squeaknode.bitcoin.bitcoin_client import BitcoinClient
|
||||
from squeaknode.core.exception import InvoiceSubscriptionError
|
||||
from squeaknode.core.lightning_address import LightningAddressHostPort
|
||||
from squeaknode.core.offer import Offer
|
||||
from squeaknode.core.peer_address import PeerAddress
|
||||
|
|
@ -415,25 +413,20 @@ class SqueakCore:
|
|||
|
||||
def get_payment_stream():
|
||||
# Yield the received payments.
|
||||
try:
|
||||
for invoice in invoice_stream.result_stream:
|
||||
if invoice.settled:
|
||||
payment_hash = invoice.r_hash
|
||||
settle_index = invoice.settle_index
|
||||
sent_offer = get_sent_offer_fn(payment_hash)
|
||||
if sent_offer is not None:
|
||||
yield ReceivedPayment(
|
||||
received_payment_id=None,
|
||||
created_time_ms=None,
|
||||
squeak_hash=sent_offer.squeak_hash,
|
||||
payment_hash=sent_offer.payment_hash,
|
||||
price_msat=sent_offer.price_msat,
|
||||
settle_index=settle_index,
|
||||
peer_address=sent_offer.peer_address,
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
if e.code() != grpc.StatusCode.CANCELLED:
|
||||
raise InvoiceSubscriptionError()
|
||||
for invoice in invoice_stream.result_stream:
|
||||
payment_hash = invoice.r_hash
|
||||
settle_index = invoice.settle_index
|
||||
sent_offer = get_sent_offer_fn(payment_hash)
|
||||
if sent_offer is not None:
|
||||
yield ReceivedPayment(
|
||||
received_payment_id=None,
|
||||
created_time_ms=None,
|
||||
squeak_hash=sent_offer.squeak_hash,
|
||||
payment_hash=sent_offer.payment_hash,
|
||||
price_msat=sent_offer.price_msat,
|
||||
settle_index=settle_index,
|
||||
peer_address=sent_offer.peer_address,
|
||||
)
|
||||
|
||||
return ReceivedPaymentsStream(
|
||||
cancel_fn=cancel_subscription,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from squeaknode.db.models import Models
|
|||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
config = context.config # type: ignore
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
|
|
|
|||
140
squeaknode/lightning/clightning_lightning_client.py
Normal file
140
squeaknode/lightning/clightning_lightning_client.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2020 Jonathan Zernik
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from pyln.client import LightningRpc
|
||||
|
||||
from squeaknode.core.exception import InvoiceSubscriptionError
|
||||
from squeaknode.lightning.info import Info
|
||||
from squeaknode.lightning.invoice import Invoice
|
||||
from squeaknode.lightning.invoice_stream import InvoiceStream
|
||||
from squeaknode.lightning.lightning_client import LightningClient
|
||||
from squeaknode.lightning.pay_req import PayReq
|
||||
from squeaknode.lightning.payment import Payment
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CLightningClient(LightningClient):
|
||||
"""Access a c-lightning instance using the Python API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rpc_path: str,
|
||||
) -> None:
|
||||
self.rpc_path = rpc_path
|
||||
|
||||
def init(self):
|
||||
# Create an instance of the LightningRpc object using the Core Lightning daemon on your computer.
|
||||
logger.info('initializing clightning client: {}'.format(self.rpc_path))
|
||||
self.lrpc = LightningRpc(self.rpc_path)
|
||||
|
||||
def pay_invoice(self, payment_request: str) -> Payment:
|
||||
payment = self.lrpc.pay(payment_request)
|
||||
if payment['status'] == 'complete':
|
||||
return Payment(
|
||||
payment_preimage=bytes.fromhex(payment['payment_preimage']),
|
||||
payment_error='',
|
||||
)
|
||||
else:
|
||||
return Payment(
|
||||
payment_preimage=b'',
|
||||
payment_error='Payment failed.',
|
||||
)
|
||||
|
||||
def get_info(self) -> Info:
|
||||
info = self.lrpc.getinfo()
|
||||
pubkey = info['id']
|
||||
binding = info.get('binding')
|
||||
uris = []
|
||||
if binding:
|
||||
for b in binding:
|
||||
address = b['address']
|
||||
if ':' not in address: # TODO: Change type of uri to LightningAddress.
|
||||
port = b['port']
|
||||
uri = f"{pubkey}@{address}:{port}"
|
||||
uris.append(uri)
|
||||
return Info(
|
||||
uris=uris,
|
||||
)
|
||||
|
||||
def decode_pay_req(self, payment_request: str) -> PayReq:
|
||||
pay_req = self.lrpc.decodepay(payment_request)
|
||||
return PayReq(
|
||||
payment_hash=bytes.fromhex(pay_req['payment_hash']),
|
||||
payment_point=b'', # TODO: Use real payment point.
|
||||
num_msat=pay_req['amount_msat'].millisatoshis,
|
||||
destination=pay_req['payee'],
|
||||
timestamp=int(pay_req['created_at']),
|
||||
expiry=int(pay_req['expiry']),
|
||||
)
|
||||
|
||||
def create_invoice(self, preimage: bytes, amount_msat: int) -> Invoice:
|
||||
created_invoice = self.lrpc.invoice(
|
||||
amount_msat,
|
||||
label=str(uuid.uuid4()),
|
||||
description="Squeaknode invoice",
|
||||
preimage=preimage.hex(),
|
||||
)
|
||||
creation_time = int(time.time())
|
||||
expiry = int(created_invoice['expires_at']) - creation_time
|
||||
return Invoice(
|
||||
r_hash=bytes.fromhex(created_invoice['payment_hash']),
|
||||
payment_request=created_invoice['bolt11'],
|
||||
value_msat=amount_msat,
|
||||
settled=False,
|
||||
settle_index=0,
|
||||
creation_date=creation_time,
|
||||
expiry=expiry,
|
||||
)
|
||||
|
||||
def subscribe_invoices(self, settle_index: int) -> InvoiceStream:
|
||||
|
||||
def cancel_fn():
|
||||
return None
|
||||
|
||||
def get_invoice_stream():
|
||||
try:
|
||||
pay_index = settle_index
|
||||
while True:
|
||||
payment = self.lrpc.waitanyinvoice(lastpay_index=pay_index)
|
||||
if payment['status'] == 'paid':
|
||||
pay_index = payment.get('pay_index') or 0
|
||||
yield Invoice(
|
||||
r_hash=bytes.fromhex(payment['payment_hash']),
|
||||
payment_request=payment.get('bolt11') or '',
|
||||
value_msat=payment.get('value_msat'),
|
||||
settled=True,
|
||||
settle_index=pay_index,
|
||||
creation_date=0, # This value is ignored.
|
||||
expiry=payment['expires_at'],
|
||||
)
|
||||
except Exception:
|
||||
raise InvoiceSubscriptionError()
|
||||
|
||||
return InvoiceStream(
|
||||
cancel=cancel_fn,
|
||||
result_stream=get_invoice_stream(),
|
||||
)
|
||||
|
|
@ -34,6 +34,13 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class LightningClient(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def init(self):
|
||||
"""Initialize the lightning client.
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_info(self) -> Info:
|
||||
"""Get info about the lightning node.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import grpc
|
|||
|
||||
from proto import lnd_pb2
|
||||
from proto import lnd_pb2_grpc
|
||||
from squeaknode.core.exception import InvoiceSubscriptionError
|
||||
from squeaknode.lightning.info import Info
|
||||
from squeaknode.lightning.invoice import Invoice
|
||||
from squeaknode.lightning.invoice_stream import InvoiceStream
|
||||
|
|
@ -165,7 +166,25 @@ class LNDLightningClient(LightningClient):
|
|||
subscribe_result = self.stub.SubscribeInvoices(
|
||||
subscribe_invoices_request,
|
||||
)
|
||||
|
||||
def get_invoice_stream():
|
||||
try:
|
||||
for invoice in subscribe_result:
|
||||
if invoice.settled:
|
||||
yield Invoice(
|
||||
r_hash=invoice.r_hash,
|
||||
payment_request=invoice.payment_request,
|
||||
value_msat=invoice.value_msat,
|
||||
settled=invoice.settled,
|
||||
settle_index=invoice.settle_index,
|
||||
creation_date=invoice.creation_date,
|
||||
expiry=invoice.expiry,
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
if e.code() != grpc.StatusCode.CANCELLED:
|
||||
raise InvoiceSubscriptionError()
|
||||
|
||||
return InvoiceStream(
|
||||
cancel=subscribe_result.cancel,
|
||||
result_stream=iter(subscribe_result),
|
||||
result_stream=get_invoice_stream(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from typing import NamedTuple
|
|||
|
||||
|
||||
class PayReq(NamedTuple):
|
||||
"""Represents info about the lightning node."""
|
||||
"""Represents a payment request."""
|
||||
payment_hash: bytes
|
||||
payment_point: bytes
|
||||
num_msat: int
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ class PaymentProcessorTask:
|
|||
def process_subscribed_invoices(self):
|
||||
while not self.stopped.is_set():
|
||||
try:
|
||||
latest_settle_index = self.get_latest_settle_index()
|
||||
# Use zero as starting settle index, so that no invoices are skipped.
|
||||
# latest_settle_index = self.get_latest_settle_index()
|
||||
latest_settle_index = 0
|
||||
logger.info("Starting payment subscription with settle index: {}".format(
|
||||
latest_settle_index,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -105,10 +105,10 @@ class SqueakController:
|
|||
peer_address: PeerAddress,
|
||||
) -> Optional[Offer]:
|
||||
lnd_external_address: Optional[LightningAddressHostPort] = None
|
||||
if self.config.lnd.external_host:
|
||||
if self.config.lightning.external_host:
|
||||
lnd_external_address = LightningAddressHostPort(
|
||||
host=self.config.lnd.external_host,
|
||||
port=self.config.lnd.port,
|
||||
host=self.config.lightning.external_host,
|
||||
port=self.config.lightning.external_port,
|
||||
)
|
||||
price_msat = self.get_sell_price_msat()
|
||||
if price_msat == 0:
|
||||
|
|
@ -187,7 +187,11 @@ class SqueakController:
|
|||
return self.squeak_store.get_squeak_profile_private_key(profile_id)
|
||||
|
||||
def create_peer(self, peer_name: str, peer_address: PeerAddress):
|
||||
return self.squeak_store.create_peer(peer_name, peer_address)
|
||||
return self.squeak_store.create_peer(
|
||||
peer_name,
|
||||
peer_address,
|
||||
self.config.server.port or 0,
|
||||
)
|
||||
|
||||
def get_peer(self, peer_id: int) -> Optional[SqueakPeer]:
|
||||
return self.squeak_store.get_peer(peer_id)
|
||||
|
|
@ -338,6 +342,10 @@ class SqueakController:
|
|||
self.network_controller.download_single_squeak(squeak_hash)
|
||||
return DownloadResult(1, 1, 0, 9999)
|
||||
|
||||
def download_single_squeak_secret_key(self, squeak_hash: bytes) -> DownloadResult:
|
||||
self.network_controller.download_single_squeak_secret_key(squeak_hash)
|
||||
return DownloadResult(1, 1, 0, 9999)
|
||||
|
||||
def get_timeline_squeak_entries(
|
||||
self,
|
||||
limit: int,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ from squeaknode.core.squeak_core import SqueakCore
|
|||
from squeaknode.db.db_engine import get_connection_string
|
||||
from squeaknode.db.db_engine import get_engine
|
||||
from squeaknode.db.squeak_db import SqueakDb
|
||||
from squeaknode.lightning.clightning_lightning_client import CLightningClient
|
||||
from squeaknode.lightning.lnd_lightning_client import LNDLightningClient
|
||||
from squeaknode.node.node_settings import NodeSettings
|
||||
from squeaknode.node.payment_processor import PaymentProcessor
|
||||
|
|
@ -48,6 +49,7 @@ from squeaknode.server.app import SqueakPeerWebServer
|
|||
from squeaknode.server.squeak_peer_server_handler import SqueakPeerServerHandler
|
||||
from squeaknode.twitter.twitter_forwarder import TwitterForwarder
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -118,12 +120,21 @@ class SqueakNode:
|
|||
self.node_settings = NodeSettings(self.squeak_db)
|
||||
|
||||
def create_lightning_client(self):
|
||||
self.lightning_client = LNDLightningClient(
|
||||
self.config.lnd.host,
|
||||
self.config.lnd.rpc_port,
|
||||
self.config.lnd.tls_cert_path,
|
||||
self.config.lnd.macaroon_path,
|
||||
)
|
||||
if self.config.lightning.backend == 'lnd':
|
||||
self.lightning_client = LNDLightningClient(
|
||||
self.config.lightning.lnd_rpc_host,
|
||||
self.config.lightning.lnd_rpc_port,
|
||||
self.config.lightning.lnd_tls_cert_path,
|
||||
self.config.lightning.lnd_macaroon_path,
|
||||
)
|
||||
elif self.config.lightning.backend == 'clightning':
|
||||
self.lightning_client = CLightningClient(
|
||||
self.config.lightning.clightning_rpc_file,
|
||||
)
|
||||
else:
|
||||
raise Exception('Invalid lightning backend: {}'.format(
|
||||
self.config.lightning.backend,
|
||||
))
|
||||
|
||||
def create_bitcoin_client(self):
|
||||
self.bitcoin_client = BitcoinCoreClient(
|
||||
|
|
@ -217,7 +228,7 @@ class SqueakNode:
|
|||
def create_peer_web_server(self):
|
||||
self.peer_web_server = SqueakPeerWebServer(
|
||||
self.config.server.host,
|
||||
squeak.params.params.DEFAULT_PORT,
|
||||
self.config.server.port or squeak.params.params.DEFAULT_PORT,
|
||||
self.peer_handler,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -389,10 +389,11 @@ class SqueakStore:
|
|||
))
|
||||
return get_profile_private_key(profile)
|
||||
|
||||
def create_peer(self, peer_name: str, peer_address: PeerAddress):
|
||||
def create_peer(self, peer_name: str, peer_address: PeerAddress, default_peer_port):
|
||||
squeak_peer = create_saved_peer(
|
||||
peer_name,
|
||||
peer_address,
|
||||
default_peer_port,
|
||||
)
|
||||
return self.squeak_db.insert_peer(squeak_peer)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,13 +59,10 @@ def create_app(handler):
|
|||
@app.route('/squeak/<hash>')
|
||||
def squeak(hash):
|
||||
try:
|
||||
squeak_type, squeak_bytes = handler.handle_get_squeak_bytes(hash)
|
||||
squeak_bytes = handler.handle_get_squeak_bytes(hash)
|
||||
except NotFoundError:
|
||||
return "Not found", 404
|
||||
return jsonify({
|
||||
'squeak_type': squeak_type,
|
||||
'squeak_bytes': squeak_bytes.hex(),
|
||||
})
|
||||
return squeak_bytes
|
||||
|
||||
@app.route('/secretkey/<hash>')
|
||||
def secret_key(hash):
|
||||
|
|
@ -80,8 +77,9 @@ def create_app(handler):
|
|||
@app.route('/offer/<hash>')
|
||||
def offer(hash):
|
||||
client_host = request.remote_addr
|
||||
client_port = request.environ.get('REMOTE_PORT') or 0
|
||||
try:
|
||||
offer = handler.handle_get_offer(hash, client_host)
|
||||
offer = handler.handle_get_offer(hash, client_host, client_port)
|
||||
except NotFoundError:
|
||||
return "Not found", 404
|
||||
return jsonify({
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
import logging
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from squeak.core.keys import SqueakPublicKey
|
||||
|
||||
|
|
@ -55,17 +54,16 @@ class SqueakPeerServerHandler(object):
|
|||
self.node_settings = node_settings
|
||||
self.config = config
|
||||
|
||||
def handle_get_squeak_bytes(self, squeak_hash_str) -> Tuple[str, bytes]:
|
||||
def handle_get_squeak_bytes(self, squeak_hash_str) -> bytes:
|
||||
"""Return a tuple with the squeak type and the squeak bytes.
|
||||
|
||||
Returns: (str, bytes)
|
||||
Returns: bytes
|
||||
"""
|
||||
squeak_hash = bytes.fromhex(squeak_hash_str)
|
||||
squeak = self.squeak_controller.get_squeak(squeak_hash)
|
||||
if not squeak:
|
||||
raise NotFoundError()
|
||||
squeak_type = 'resqueak' if squeak.is_resqueak else 'squeak'
|
||||
return squeak_type, squeak.serialize()
|
||||
return squeak.serialize()
|
||||
|
||||
def handle_get_secret_key(self, squeak_hash_str) -> bytes:
|
||||
squeak_hash = bytes.fromhex(squeak_hash_str)
|
||||
|
|
@ -77,12 +75,12 @@ class SqueakPeerServerHandler(object):
|
|||
raise NotFoundError()
|
||||
return secret_key
|
||||
|
||||
def handle_get_offer(self, squeak_hash_str, client_host) -> Offer:
|
||||
def handle_get_offer(self, squeak_hash_str, client_host, client_port) -> Offer:
|
||||
squeak_hash = bytes.fromhex(squeak_hash_str)
|
||||
client_addr = PeerAddress(
|
||||
network=Network.IPV4,
|
||||
host=client_host,
|
||||
port=0,
|
||||
port=client_port,
|
||||
)
|
||||
offer = self.squeak_controller.get_packaged_offer(
|
||||
squeak_hash,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ def peer_name():
|
|||
yield "fake_peer_name"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def default_peer_port():
|
||||
yield 9999
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def peer_address():
|
||||
yield PeerAddress(
|
||||
|
|
@ -58,24 +63,37 @@ def peer_address_with_no_port():
|
|||
)
|
||||
|
||||
|
||||
def test_create_saved_peer(peer_name, peer_address):
|
||||
def test_create_saved_peer(peer_name, peer_address, default_peer_port):
|
||||
peer = create_saved_peer(
|
||||
peer_name,
|
||||
peer_address,
|
||||
default_peer_port,
|
||||
)
|
||||
|
||||
assert peer.peer_name == peer_name
|
||||
assert peer.address == peer_address
|
||||
|
||||
|
||||
def test_create_saved_peer_empty_name(peer_address):
|
||||
create_saved_peer("", peer_address)
|
||||
def test_create_saved_peer_empty_name(peer_address, default_peer_port):
|
||||
create_saved_peer("", peer_address, default_peer_port)
|
||||
|
||||
|
||||
def test_create_saved_peer_use_tor(peer_name, peer_address_with_tor):
|
||||
def test_create_saved_peer_no_port(peer_name, peer_address_with_no_port, default_peer_port):
|
||||
peer = create_saved_peer(
|
||||
peer_name,
|
||||
peer_address_with_no_port,
|
||||
default_peer_port,
|
||||
)
|
||||
|
||||
assert peer.peer_name == peer_name
|
||||
assert peer.address.port == default_peer_port
|
||||
|
||||
|
||||
def test_create_saved_peer_use_tor(peer_name, peer_address_with_tor, default_peer_port):
|
||||
peer = create_saved_peer(
|
||||
peer_name,
|
||||
peer_address_with_tor,
|
||||
default_peer_port,
|
||||
)
|
||||
|
||||
assert peer.peer_name == peer_name
|
||||
|
|
|
|||
|
|
@ -131,9 +131,10 @@ def failed_payment(payment_request):
|
|||
|
||||
@pytest.fixture
|
||||
def invoice_stream(settled_invoice, unsettled_invoice):
|
||||
# Length of invoices list is 1.
|
||||
invoices = [
|
||||
settled_invoice,
|
||||
unsettled_invoice,
|
||||
# unsettled_invoice,
|
||||
]
|
||||
yield InvoiceStream(
|
||||
cancel=lambda: None,
|
||||
|
|
@ -188,6 +189,9 @@ class MockLightningClient(LightningClient):
|
|||
self.payment = payment
|
||||
self.invoice_stream = invoice_stream
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
def get_info(self):
|
||||
return self.info
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ def rpc_invoice(preimage, payment_hash, payment_request, price_msat, creation_da
|
|||
r_hash=payment_hash,
|
||||
payment_request=payment_request,
|
||||
value_msat=price_msat,
|
||||
settled=False,
|
||||
settled=True,
|
||||
settle_index=0,
|
||||
creation_date=creation_date,
|
||||
expiry=expiry,
|
||||
|
|
@ -122,7 +122,7 @@ def invoice(payment_hash, payment_request, price_msat, creation_date, expiry):
|
|||
r_hash=payment_hash,
|
||||
payment_request=payment_request,
|
||||
value_msat=price_msat,
|
||||
settled=False,
|
||||
settled=True,
|
||||
settle_index=0,
|
||||
creation_date=creation_date,
|
||||
expiry=expiry,
|
||||
|
|
|
|||
|
|
@ -167,4 +167,5 @@ def test_create_peer(squeak_store, squeak_controller, peer_address):
|
|||
squeak_store.create_peer.assert_called_with(
|
||||
"fake_peer_name",
|
||||
peer_address,
|
||||
0,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ def gen_squeak_peer(peer_name):
|
|||
return create_saved_peer(
|
||||
peer_name,
|
||||
peer_address,
|
||||
0,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue