2020-09-21 06:03:22 +01:00
|
|
|
from docker import from_env, APIClient
|
2020-09-21 17:05:02 +01:00
|
|
|
from docker.errors import NotFound, NullResource, APIError
|
2020-09-21 06:03:22 +01:00
|
|
|
from socket import socket
|
2020-09-25 08:10:48 +01:00
|
|
|
from os.path import expanduser
|
|
|
|
from secrets import token_urlsafe
|
2020-09-28 04:52:58 +01:00
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from time import sleep
|
2020-09-21 06:03:22 +01:00
|
|
|
from wowstash import config
|
|
|
|
from wowstash.models import User
|
2020-09-25 08:10:48 +01:00
|
|
|
from wowstash.factory import db
|
2020-09-21 06:03:22 +01:00
|
|
|
from wowstash.library.jsonrpc import daemon
|
2020-09-24 22:18:51 +01:00
|
|
|
from wowstash.library.elasticsearch import send_es
|
|
|
|
|
2020-09-21 06:03:22 +01:00
|
|
|
|
|
|
|
class Docker(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.client = from_env()
|
|
|
|
self.wownero_image = getattr(config, 'WOWNERO_IMAGE', 'lalanza808/wownero')
|
2020-09-25 08:10:48 +01:00
|
|
|
self.wallet_dir = expanduser(getattr(config, 'WALLET_DIR', '~/data/wallets'))
|
|
|
|
self.listen_port = 8888
|
2020-09-21 06:03:22 +01:00
|
|
|
|
|
|
|
def create_wallet(self, user_id):
|
|
|
|
u = User.query.get(user_id)
|
2020-09-25 18:31:49 +01:00
|
|
|
volume_name = self.get_user_volume(u.id)
|
2020-09-25 08:10:48 +01:00
|
|
|
u.wallet_password = token_urlsafe(12)
|
|
|
|
db.session.commit()
|
2020-09-21 06:03:22 +01:00
|
|
|
command = f"""wownero-wallet-cli \
|
|
|
|
--generate-new-wallet /wallet/{u.id}.wallet \
|
|
|
|
--restore-height {daemon.info()['height']} \
|
|
|
|
--password {u.wallet_password} \
|
|
|
|
--mnemonic-language English \
|
2020-09-25 18:31:49 +01:00
|
|
|
--daemon-address {config.DAEMON_PROTO}://{config.DAEMON_HOST}:{config.DAEMON_PORT} \
|
|
|
|
--daemon-login {config.DAEMON_USER}:{config.DAEMON_PASS} \
|
2020-09-21 06:03:22 +01:00
|
|
|
--log-file /wallet/{u.id}-create.log
|
|
|
|
--command version
|
|
|
|
"""
|
2020-09-25 18:31:49 +01:00
|
|
|
if not self.volume_exists(volume_name):
|
|
|
|
self.client.volumes.create(
|
|
|
|
name=volume_name,
|
|
|
|
driver='local'
|
|
|
|
)
|
2020-09-21 06:03:22 +01:00
|
|
|
container = self.client.containers.run(
|
|
|
|
self.wownero_image,
|
|
|
|
command=command,
|
|
|
|
auto_remove=True,
|
|
|
|
name=f'create_wallet_{u.id}',
|
|
|
|
remove=True,
|
|
|
|
detach=True,
|
|
|
|
volumes={
|
2020-09-25 18:31:49 +01:00
|
|
|
volume_name: {
|
2020-09-21 06:03:22 +01:00
|
|
|
'bind': '/wallet',
|
|
|
|
'mode': 'rw'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2020-09-24 22:18:51 +01:00
|
|
|
send_es({'type': 'create_wallet', 'user': u.email})
|
2020-09-21 06:03:22 +01:00
|
|
|
return container.short_id
|
|
|
|
|
|
|
|
def start_wallet(self, user_id):
|
|
|
|
u = User.query.get(user_id)
|
2020-09-21 19:42:17 +01:00
|
|
|
container_name = f'rpc_wallet_{u.id}'
|
2020-09-25 18:31:49 +01:00
|
|
|
volume_name = self.get_user_volume(u.id)
|
2020-09-21 06:03:22 +01:00
|
|
|
command = f"""wownero-wallet-rpc \
|
|
|
|
--non-interactive \
|
|
|
|
--rpc-bind-port {self.listen_port} \
|
2020-09-21 08:03:04 +01:00
|
|
|
--rpc-bind-ip 0.0.0.0 \
|
|
|
|
--confirm-external-bind \
|
2020-09-21 06:03:22 +01:00
|
|
|
--wallet-file /wallet/{u.id}.wallet \
|
|
|
|
--rpc-login {u.id}:{u.wallet_password} \
|
|
|
|
--password {u.wallet_password} \
|
2020-09-25 18:31:49 +01:00
|
|
|
--daemon-address {config.DAEMON_PROTO}://{config.DAEMON_HOST}:{config.DAEMON_PORT} \
|
|
|
|
--daemon-login {config.DAEMON_USER}:{config.DAEMON_PASS} \
|
2020-09-21 06:03:22 +01:00
|
|
|
--log-file /wallet/{u.id}-rpc.log
|
|
|
|
"""
|
2020-09-21 17:05:02 +01:00
|
|
|
try:
|
|
|
|
container = self.client.containers.run(
|
|
|
|
self.wownero_image,
|
|
|
|
command=command,
|
|
|
|
auto_remove=True,
|
|
|
|
name=container_name,
|
|
|
|
remove=True,
|
|
|
|
detach=True,
|
|
|
|
ports={
|
|
|
|
f'{self.listen_port}/tcp': ('127.0.0.1', None)
|
|
|
|
},
|
|
|
|
volumes={
|
2020-09-25 18:31:49 +01:00
|
|
|
volume_name: {
|
2020-09-21 17:05:02 +01:00
|
|
|
'bind': '/wallet',
|
|
|
|
'mode': 'rw'
|
|
|
|
}
|
2020-09-21 06:03:22 +01:00
|
|
|
}
|
2020-09-21 17:05:02 +01:00
|
|
|
)
|
2020-09-24 22:18:51 +01:00
|
|
|
send_es({'type': 'start_wallet', 'user': u.email})
|
2020-09-21 17:05:02 +01:00
|
|
|
return container.short_id
|
|
|
|
except APIError as e:
|
|
|
|
if str(e).startswith('409'):
|
|
|
|
container = self.client.containers.get(container_name)
|
|
|
|
return container.short_id
|
2020-09-21 06:03:22 +01:00
|
|
|
|
|
|
|
def get_port(self, container_id):
|
|
|
|
client = APIClient()
|
|
|
|
port_data = client.port(container_id, self.listen_port)
|
|
|
|
host_port = port_data[0]['HostPort']
|
|
|
|
return int(host_port)
|
|
|
|
|
|
|
|
def container_exists(self, container_id):
|
|
|
|
try:
|
|
|
|
self.client.containers.get(container_id)
|
|
|
|
return True
|
|
|
|
except NotFound:
|
|
|
|
return False
|
2020-09-21 17:05:02 +01:00
|
|
|
except NullResource:
|
|
|
|
return False
|
2020-09-21 08:03:04 +01:00
|
|
|
|
2020-09-25 18:31:49 +01:00
|
|
|
def volume_exists(self, volume_id):
|
|
|
|
try:
|
|
|
|
self.client.volumes.get(volume_id)
|
|
|
|
return True
|
|
|
|
except NotFound:
|
|
|
|
return False
|
|
|
|
except NullResource:
|
|
|
|
return False
|
|
|
|
|
2020-09-21 08:03:04 +01:00
|
|
|
def stop_container(self, container_id):
|
|
|
|
if self.container_exists(container_id):
|
|
|
|
c = self.client.containers.get(container_id)
|
|
|
|
c.stop()
|
|
|
|
|
2020-09-25 08:10:48 +01:00
|
|
|
def delete_wallet_data(self, user_id):
|
2020-09-25 18:31:49 +01:00
|
|
|
volume_name = self.get_user_volume(user_id)
|
|
|
|
volume = self.client.volumes.get(volume_name)
|
|
|
|
try:
|
|
|
|
volume.remove()
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
|
|
raise
|
|
|
|
|
|
|
|
def get_user_volume(self, user_id):
|
|
|
|
volume_name = f'user_{user_id}_wallet'
|
|
|
|
return volume_name
|
2020-09-25 08:10:48 +01:00
|
|
|
|
2020-09-21 08:03:04 +01:00
|
|
|
def cleanup(self):
|
|
|
|
users = User.query.all()
|
|
|
|
for u in users:
|
2020-09-28 04:52:58 +01:00
|
|
|
# Delete inactive wallet sessions
|
|
|
|
if u.wallet_start:
|
|
|
|
session_lifetime = getattr(config, 'PERMANENT_SESSION_LIFETIME', 3600)
|
|
|
|
expiration_time = u.wallet_start + timedelta(seconds=session_lifetime)
|
|
|
|
now = datetime.utcnow()
|
|
|
|
time_diff = expiration_time - now
|
|
|
|
if time_diff.total_seconds() <= 0:
|
|
|
|
print(f'[+] Found expired container for {u}. killing it')
|
|
|
|
self.stop_container(u.wallet_container)
|
|
|
|
sleep(2)
|
|
|
|
# Remove wallet db data if not running but it's in db
|
|
|
|
if u.wallet_container and not self.container_exists(u.wallet_container):
|
|
|
|
print(f'[+] Found stale data for {u}')
|
|
|
|
u.clear_wallet_data()
|
2020-09-21 08:03:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
docker = Docker()
|