Attempting server startup.
* Created basic client * Many bug fixes
This commit is contained in:
parent
c83f5feb4b
commit
741b1f5b2a
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
venv/*
|
venv/*
|
||||||
|
mine_data/*
|
||||||
|
mariadb_data/*
|
||||||
|
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FROM ericomeehan/httpd-mod-wsgi
|
||||||
|
RUN apt update && apt upgrade -y
|
||||||
|
RUN apt install -y libmariadb3 libmariadb-dev python3-dev python3-pip
|
||||||
|
COPY . /usr/local/apache2/htdocs
|
||||||
|
RUN pip3 install --break-system-packages -r /usr/local/apache2/htdocs/requirements.txt
|
||||||
|
EXPOSE 80
|
@ -1,7 +1,7 @@
|
|||||||
CREATE DATABASE IF NOT EXISTS market;
|
CREATE DATABASE IF NOT EXISTS market;
|
||||||
USE market;
|
USE market;
|
||||||
CREATE TABLE IF NOT EXISTS users(
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
userId VARCHAR(36) UNIQUE NOT NULL,
|
userId VARCHAR(36) UNIQUE NOT NULL,
|
||||||
userName VARCHAR(64) UNIQUE NOT NULL,
|
userName VARCHAR(64) UNIQUE NOT NULL,
|
||||||
passwordHash VARCHAR(64) NOT NULL,
|
passwordHash VARCHAR(64) NOT NULL,
|
||||||
@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS users(
|
|||||||
publicKey VARCHAR(128) NOT NULL
|
publicKey VARCHAR(128) NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS buy_orders(
|
CREATE TABLE IF NOT EXISTS buy_orders(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
buyOrderId VARCHAR(36) UNIQUE NOT NULL,
|
buyOrderId VARCHAR(36) UNIQUE NOT NULL,
|
||||||
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
||||||
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
||||||
@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS buy_orders(
|
|||||||
FOREIGN KEY (userId) REFERENCES users(userId)
|
FOREIGN KEY (userId) REFERENCES users(userId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS sell_orders(
|
CREATE TABLE IF NOT EXISTS sell_orders(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
sellOrderId VARCHAR(36) UNIQUE NOT NULL,
|
sellOrderId VARCHAR(36) UNIQUE NOT NULL,
|
||||||
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
|
||||||
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
||||||
@ -31,15 +31,21 @@ CREATE TABLE IF NOT EXISTS sell_orders(
|
|||||||
FOREIGN KEY (userId) REFERENCES users(userId)
|
FOREIGN KEY (userId) REFERENCES users(userId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS sell_orders_items(
|
CREATE TABLE IF NOT EXISTS sell_orders_items(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
cardId VARCHAR(36) NOT NULL,
|
cardId VARCHAR(36) NOT NULL,
|
||||||
FOREIGN KEY (sellOrderId) REFERENCES sell_orders(sellOrderId) NOT NULL
|
FOREIGN KEY (sellOrderId) REFERENCES sell_orders(sellOrderId) NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS transactions(
|
CREATE TABLE IF NOT EXISTS transactions(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
transactionId VARCHAR(36) UNIQUE NOT NULL,
|
transactionId VARCHAR(36) UNIQUE NOT NULL,
|
||||||
isPending BOOLEAN NOT NULL DEFAULT true,
|
isPending BOOLEAN NOT NULL DEFAULT true,
|
||||||
isAbandoned BOOLEAN NOT NULL DEFAULT false,
|
isAbandoned BOOLEAN NOT NULL DEFAULT false,
|
||||||
FOREIGN KEY (buyOrderId) REFERENCES buy_orders(buyOrderId) NOT NULL,
|
FOREIGN KEY (buyOrderId) REFERENCES buy_orders(buyOrderId) NOT NULL,
|
||||||
FOREIGN KEY (sellOrderItemSqlId) REFERENCES sell_order_items(sqlId) NOT NULL
|
FOREIGN KEY (sellOrderItemSqlId) REFERENCES sell_order_items(sqlId) NOT NULL
|
||||||
);
|
);
|
||||||
|
CREATE USER IF NOT EXISTS '${MARKET_DB_USER}'@'%' IDENTIFIED BY '${MARKET_DB_PASSWORD}';
|
||||||
|
CREATE USER IF NOT EXISTS '${ORDER_MATCHER_DB_USER}'@'%' IDENTIFIED BY '${ORDER_MATCHER_DB_PASSWORD}';
|
||||||
|
CREATE USER IF NOT EXISTS '${STATUS_CHECKER_DB_USER}'@'%' IDENTIFIED BY '${STATUS_CHECKER_DB_PASSWORD}';
|
||||||
|
GRANT ALL PRIVILEGES ON market.* TO '${MARKET_DB_USER}'@'%';
|
||||||
|
GRANT ALL PRIVILEGES ON market.* TO '${ORDER_MATCHER_DB_USER}'@'%';
|
||||||
|
GRANT ALL PRIVILEGES ON market.* TO '${STATUS_CHECKER_DB_USER}'@'%';
|
@ -1,7 +1,5 @@
|
|||||||
CREATE DATABASE IF NOT EXISTS chain;
|
|
||||||
USE chain;
|
|
||||||
CREATE TABLE IF NOT EXISTS blocks(
|
CREATE TABLE IF NOT EXISTS blocks(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
blockId VARCHAR(32) UNIQUE NOT NULL,
|
blockId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
blockHash VARCHAR(64) UNIQUE NOT NULL,
|
blockHash VARCHAR(64) UNIQUE NOT NULL,
|
||||||
previousHash VARCHAR(64) UNIQUE NOT NULL,
|
previousHash VARCHAR(64) UNIQUE NOT NULL,
|
||||||
@ -10,13 +8,16 @@ CREATE TABLE IF NOT EXISTS blocks(
|
|||||||
nonce INT NOT NULL
|
nonce INT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS cards(
|
CREATE TABLE IF NOT EXISTS cards(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
blockId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
cardId VARCHAR(32) UNIQUE NOT NULL,
|
cardId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
pageId INT NOT NULL,
|
pageId INT NOT NULL,
|
||||||
FOREIGN KEY (blockId) REFERENCES blocks(blockId)
|
FOREIGN KEY (blockId) REFERENCES blocks(blockId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS transactions(
|
CREATE TABLE IF NOT EXISTS transactions(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
blockId VARCHAR(32) NOT NULL,
|
||||||
|
cardId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
transactionId VARCHAR(32) UNIQUE NOT NULL,
|
transactionId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
timestamp DATETIME NOT NULL,
|
timestamp DATETIME NOT NULL,
|
||||||
sender VARCHAR(128) NOT NULL,
|
sender VARCHAR(128) NOT NULL,
|
||||||
@ -28,7 +29,7 @@ CREATE TABLE IF NOT EXISTS transactions(
|
|||||||
FOREIGN KEY (cardId) REFERENCES cards(cardId)
|
FOREIGN KEY (cardId) REFERENCES cards(cardId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS peers(
|
CREATE TABLE IF NOT EXISTS peers(
|
||||||
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
peerId VARCHAR(32) UNIQUE NOT NULL,
|
peerId VARCHAR(32) UNIQUE NOT NULL,
|
||||||
baseUrl VARCHAR(128) UNIQUE NOT NULL,
|
baseUrl VARCHAR(128) UNIQUE NOT NULL,
|
||||||
isUp BOOLEAN NOT NULL,
|
isUp BOOLEAN NOT NULL,
|
@ -1,3 +0,0 @@
|
|||||||
CREATE DATABASE IF NOT EXISTS peers;
|
|
||||||
USE peers;
|
|
||||||
CREATE TABLE IF NOT EXISTS peers
|
|
34
docker-compose.yaml
Normal file
34
docker-compose.yaml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
wikideck:
|
||||||
|
image: ericomeehan/wikideck:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
environment:
|
||||||
|
ROLE: api
|
||||||
|
DATA_PATH: /data
|
||||||
|
DIFFICULTY_REQUIREMENT: 3
|
||||||
|
MINE_DB_HOST: mariadb-mine
|
||||||
|
MINE_DB_USER: mine
|
||||||
|
MINE_DB_PASSWORD: 123abc
|
||||||
|
depends_on:
|
||||||
|
- mariadb-mine
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./mine_data:/data
|
||||||
|
|
||||||
|
mariadb-mine:
|
||||||
|
image: mariadb:latest
|
||||||
|
environment:
|
||||||
|
MARIADB_ROOT_PASSWORD: abc123
|
||||||
|
MARIADB_DATABASE: mine
|
||||||
|
MARIADB_USER: mine
|
||||||
|
MARIADB_PASSWORD: 123abc
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mine_data:
|
@ -1 +1,5 @@
|
|||||||
|
cryptography
|
||||||
|
dotenv
|
||||||
|
flask
|
||||||
|
mariadb
|
||||||
wikipedia
|
wikipedia
|
||||||
|
@ -1,33 +1,40 @@
|
|||||||
from card import Card
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from Mine.Card import Card
|
||||||
|
from Mine.Transaction import Transaction
|
||||||
|
|
||||||
class Block():
|
class Block():
|
||||||
def __init__(self, blockId=uuid.uuid4(), previousHash=None,
|
def __init__(self, blockId=uuid.uuid4(), previousHash="0",
|
||||||
timestamp=datetime.datetime.now(datetime.timezone.utc), height=0, nonce=0,
|
timestamp=datetime.datetime.now(datetime.timezone.utc), height=0, nonce=0,
|
||||||
card=Card(), transactions=[], authorPublicKey=None, data=None):
|
difficulty=0, card=Card(), transactions=[], data=None):
|
||||||
self.blockId = blockId
|
|
||||||
self.previousHash = previousHash
|
|
||||||
self.timestamp = timestamp
|
|
||||||
self.height = height
|
|
||||||
self.difficulty = difficulty
|
|
||||||
self.nonce = nonce
|
|
||||||
self.card = card
|
|
||||||
self.transactions = transactions.append(
|
|
||||||
Transaction(
|
|
||||||
cardId = self.card.id,
|
|
||||||
receiver = authorPublicKey
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if data:
|
if data:
|
||||||
self.load_from_data(data)
|
self.load_from_data(data)
|
||||||
|
else:
|
||||||
|
self.blockId = blockId
|
||||||
|
self.previousHash = previousHash
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.height = height
|
||||||
|
self.difficulty = difficulty
|
||||||
|
self.nonce = nonce
|
||||||
|
self.card = card
|
||||||
|
self.transactions = transactions
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.blockHash = hashlib.sha256(str(self).encode('utf-8'))
|
self.blockHash = hashlib.sha256(str(self).encode('utf-8'))
|
||||||
|
|
||||||
def mine(self):
|
def mine(self, authorPrivateKey):
|
||||||
|
self.transactions.append(
|
||||||
|
Transaction(
|
||||||
|
cardId = self.card.cardId,
|
||||||
|
sender = authorPrivateKey.public_key(),
|
||||||
|
receiver = authorPrivateKey.public_key(),
|
||||||
|
authorPrivateKey = authorPrivateKey
|
||||||
|
)
|
||||||
|
)
|
||||||
while int(self.blockHash.hexdigest(), 16) > 2**(256-self.difficulty):
|
while int(self.blockHash.hexdigest(), 16) > 2**(256-self.difficulty):
|
||||||
self.nonce += 1
|
self.nonce += 1
|
||||||
self.update()
|
self.update()
|
||||||
@ -43,20 +50,21 @@ class Block():
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
# TODO: validate blockId is uuid
|
# TODO: validate blockId is uuid
|
||||||
# TODO: validate previousHash is sha256 hash
|
# TODO: validate previousHash is sha256 hash
|
||||||
if not int(self.blockHash.hexdigest(), 16) > 2**(256-self.difficulty):
|
if not int(self.blockHash.hexdigest(), 16) <= 2**(256-self.difficulty):
|
||||||
raise self.Invalid("Hash does not meet difficulty requirement.")
|
raise self.Invalid("Hash does not meet difficulty requirement.")
|
||||||
# TODO: validate timestamp is timestamp
|
# TODO: validate timestamp is UTC timestamp
|
||||||
if not self.timestamp < datetime.datetime.now():
|
if not self.timestamp < datetime.datetime.now(datetime.timezone.utc):
|
||||||
raise self.Invalid("Timestamp in the future.")
|
raise self.Invalid("Timestamp in the future.")
|
||||||
if not self.height > 0:
|
if not self.height >= 0:
|
||||||
raise self.Invalid("Height less than 0.")
|
raise self.Invalid("Height less than 0.")
|
||||||
if not self.difficulty > 0:
|
if not self.difficulty >= 0:
|
||||||
raise self.Invalid("Difficulty less than 0.")
|
raise self.Invalid("Difficulty less than 0.")
|
||||||
if not self.nonce > 0:
|
if not self.nonce >= 0:
|
||||||
raise self.Invalid("Nonce less than 0.")
|
raise self.Invalid("Nonce less than 0.")
|
||||||
self.card.validate()
|
self.card.validate()
|
||||||
for transaction in self.transactions:
|
for transaction in self.transactions:
|
||||||
transaction.validate()
|
transaction.validate()
|
||||||
|
# TODO validate that one transaction gives the card to the author
|
||||||
|
|
||||||
def load_from_data(self, data):
|
def load_from_data(self, data):
|
||||||
self.blockId = uuid.UUID(data['blockId'])
|
self.blockId = uuid.UUID(data['blockId'])
|
||||||
@ -87,21 +95,24 @@ class Block():
|
|||||||
]
|
]
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def __str__(self):
|
def as_dict(self):
|
||||||
# The hash of the block is the SHA256 hash of what this method returns.
|
return {
|
||||||
return data.dumps({
|
"blockId": str(self.blockId),
|
||||||
"blockId": str(self.blockId)
|
|
||||||
"previousHash": self.previousHash,
|
"previousHash": self.previousHash,
|
||||||
"timestamp": str(self.timestamp)
|
"timestamp": str(self.timestamp),
|
||||||
"height": self.height,
|
"height": self.height,
|
||||||
"difficulty": self.difficulty,
|
"difficulty": self.difficulty,
|
||||||
"nonce": self.nonce,
|
"nonce": self.nonce,
|
||||||
"author": self.author,
|
"card": self.card.as_dict(),
|
||||||
"card": str(self.card),
|
"transactions": [each.as_dict() for each in self.transactions]
|
||||||
"transactions": [str(each) for each in transactions]
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# The hash of the block is the SHA256 hash of what this method returns.
|
||||||
|
return json.dumps(self.as_dict())
|
||||||
|
|
||||||
class Invalid(Exception):
|
class Invalid(Exception):
|
||||||
def __init__(message, status_code=406):
|
def __init__(self, message, status_code=406):
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
import requests
|
import requests
|
||||||
|
import json
|
||||||
import wikipedia
|
import wikipedia
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
class Card():
|
class Card():
|
||||||
def __init__(self, cardId=uuid.uuid4(),
|
def __init__(self, cardId=uuid.uuid4(), pageId=None, data=None):
|
||||||
pageId=wikipedia.page(wikipedia.random()).pageid, data=None):
|
|
||||||
self.cardId = cardId
|
self.cardId = cardId
|
||||||
self.pageId = pageId
|
while True:
|
||||||
|
try:
|
||||||
|
self.pageId = pageId if pageId else int(
|
||||||
|
wikipedia.page(
|
||||||
|
wikipedia.random()
|
||||||
|
).pageid
|
||||||
|
)
|
||||||
|
except wikipedia.exceptions.PageError as e:
|
||||||
|
# TODO: why is the random function returning lower-case pages?
|
||||||
|
continue
|
||||||
|
except wikipedia.exceptions.DisambiguationError as e:
|
||||||
|
# TODO pick random disambiuation option
|
||||||
|
continue
|
||||||
|
break
|
||||||
if data:
|
if data:
|
||||||
self.load_from_data(data)
|
self.load_from_data(data)
|
||||||
|
|
||||||
@ -21,11 +34,14 @@ class Card():
|
|||||||
self.cardId = uuid.UUID(data['cardId'])
|
self.cardId = uuid.UUID(data['cardId'])
|
||||||
self.pageId = data['pageId']
|
self.pageId = data['pageId']
|
||||||
|
|
||||||
def __str__(self):
|
def as_dict(self):
|
||||||
return json.dumps({
|
return {
|
||||||
"cardId": str(self.cardId),
|
"cardId": str(self.cardId),
|
||||||
"pageId": self.pageId
|
"pageId": self.pageId
|
||||||
})
|
}
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return json.dumps(self.as_dict())
|
||||||
|
|
||||||
class Invalid(Exception):
|
class Invalid(Exception):
|
||||||
def __init__(self, message, status_code=406):
|
def __init__(self, message, status_code=406):
|
||||||
|
@ -1,107 +1,186 @@
|
|||||||
|
import mariadb
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from Mine.Block import Block
|
||||||
|
from Mine.Transaction import Transaction
|
||||||
|
|
||||||
class Database():
|
class Database():
|
||||||
|
SQL_CREATE_DATABASE = [
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS mine.blocks(
|
||||||
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
blockId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
blockHash VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
previousHash VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
timestamp DATETIME NOT NULL,
|
||||||
|
height INT UNIQUE NOT NULL,
|
||||||
|
nonce INT NOT NULL
|
||||||
|
);
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS mine.cards(
|
||||||
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
blockId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
cardId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
pageId INT NOT NULL,
|
||||||
|
FOREIGN KEY (blockId) REFERENCES blocks(blockId)
|
||||||
|
);
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS mine.transactions(
|
||||||
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
blockId VARCHAR(37) NOT NULL,
|
||||||
|
cardId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
transactionId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
timestamp DATETIME NOT NULL,
|
||||||
|
sender VARCHAR(128) NOT NULL,
|
||||||
|
receiver VARCHAR(128) NOT NULL,
|
||||||
|
signature VARCHAR(128) NOT NULL,
|
||||||
|
isPending BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
isAbandoned BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
FOREIGN KEY (blockId) REFERENCES blocks(blockId),
|
||||||
|
FOREIGN KEY (cardId) REFERENCES cards(cardId)
|
||||||
|
);
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS mine.peers(
|
||||||
|
sqlId INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
peerId VARCHAR(37) UNIQUE NOT NULL,
|
||||||
|
baseUrl VARCHAR(128) UNIQUE NOT NULL,
|
||||||
|
isUp BOOLEAN NOT NULL,
|
||||||
|
downCount INT DEFAULT 0,
|
||||||
|
lastTry TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
|
||||||
|
SQL_GET_BLOCK_BY_ID = """
|
||||||
|
SELECT *
|
||||||
|
FROM blocks
|
||||||
|
WHERE blockId = '{}';
|
||||||
|
"""
|
||||||
|
|
||||||
SQL_GET_LAST_BLOCK = """
|
SQL_GET_LAST_BLOCK = """
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM blocks
|
FROM blocks
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL_GET_CARD = """
|
SQL_GET_CARD = """
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM cards
|
FROM cards
|
||||||
WHERE cardId == {};
|
WHERE cardId = '{}';
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL_GET_CARD_OWNER = """
|
SQL_GET_CARD_OWNER = """
|
||||||
SELECT receiver
|
SELECT receiver
|
||||||
FROM transactions
|
FROM transactions
|
||||||
WHERE cardId == {}
|
WHERE cardId = '{}'
|
||||||
AND isPending == false
|
AND isPending = False
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL_GET_PENDING_TRANSACTIONS = """
|
SQL_GET_PENDING_TRANSACTIONS = """
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM transactions
|
FROM transactions
|
||||||
WHERE isPending == true;
|
WHERE isPending = True
|
||||||
"""
|
ORDER BY timestamp ASC
|
||||||
|
LIMIT {};
|
||||||
|
"""
|
||||||
|
|
||||||
SQL_GET_TRANSACTION = """
|
SQL_GET_TRANSACTION_BY_ID = """
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM transactions
|
FROM transactions
|
||||||
WHERE transactionId == {}
|
WHERE transactionId == '{}'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL_INSERT_BLOCK = """
|
SQL_INSERT_BLOCK = """
|
||||||
INSERT INTO blocks (blockId, blockHash, previousHash, timestamp, height, nonce)
|
INSERT INTO blocks (blockId, blockHash, previousHash, timestamp, height, nonce)
|
||||||
VALUES ({}, {}, {}, {}, {}, {});
|
VALUES ('{}', '{}', '{}', '{}', {}, {});
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL_INSERT_TRANSACTION = """
|
SQL_INSERT_TRANSACTION = """
|
||||||
INSERT INTO transactions (
|
INSERT INTO transactions (
|
||||||
transactionId,
|
transactionId,
|
||||||
timestamp,
|
timestamp,
|
||||||
sender,
|
sender,
|
||||||
receiver,
|
receiver,
|
||||||
signature,
|
signature,
|
||||||
blockId,
|
blockId,
|
||||||
cardId
|
cardId
|
||||||
)
|
)
|
||||||
VALUES ({}, {}, {}, {}, {});
|
VALUES ({}, {}, {}, {}, {});
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host, port, user, password):
|
def __init__(self):
|
||||||
self.host = host
|
delay = 2
|
||||||
self.port = port
|
while True:
|
||||||
self.user = user
|
try:
|
||||||
self.password = password
|
self.conn = mariadb.connect(
|
||||||
self.conn = mariadb.connect(
|
host = os.getenv('MINE_DB_HOST', "mariadb"),
|
||||||
host = self.host,
|
port = os.getenv('MINE_DB_PORT', 3306),
|
||||||
port = self.port,
|
user = os.getenv('MINE_DB_USER', None),
|
||||||
user = self.user,
|
password = os.getenv('MINE_DB_PASSWORD', None),
|
||||||
password = self.password
|
database = 'mine'
|
||||||
|
)
|
||||||
|
for each in self.SQL_CREATE_DATABASE:
|
||||||
|
cursor = self.conn.cursor().execute(each)
|
||||||
|
except mariadb.Error as e:
|
||||||
|
time.sleep(delay := delay**2)
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
|
||||||
|
def get_block_by_id(self, blockId):
|
||||||
|
return Block(
|
||||||
|
data=self.conn.cursor().execute(
|
||||||
|
self.SQL_GET_BLOCK_BY_ID.format(blockId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_last_block(self):
|
def get_last_block(self):
|
||||||
return Block(
|
data=self.conn.cursor().execute(self.SQL_GET_LAST_BLOCK)
|
||||||
data=self.conn.cursor().execute(SQL_GET_LAST_BLOCK)[0]
|
return Block(data=data) if data else None
|
||||||
)
|
|
||||||
|
|
||||||
def get_card(self, cardId):
|
def get_card(self, cardId):
|
||||||
return Card(
|
return Card(
|
||||||
data=self.conn.cursor().execute(SQL_GET_CARD.format(cardId))[0]
|
data=self.conn.cursor().execute(self.SQL_GET_CARD.format(cardId))
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_card_owner(self, cardId):
|
def get_card_owner(self, cardId):
|
||||||
return self.conn.cursor().execute(
|
return self.conn.cursor().execute(
|
||||||
SQL_GET_CARD_OWNER.format(cardId)
|
self.SQL_GET_CARD_OWNER.format(cardId)
|
||||||
)[0]
|
)
|
||||||
|
|
||||||
def get_pending_transactions(self):
|
def get_pending_transactions(self, limit=32768):
|
||||||
|
pendingTransactions = self.conn.cursor().execute(
|
||||||
|
self.SQL_GET_PENDING_TRANSACTIONS.format(limit)
|
||||||
|
)
|
||||||
return [
|
return [
|
||||||
Transaction(
|
Transaction(
|
||||||
data=each
|
data=each
|
||||||
) for each in self.conn.cursor().execute(SQL_GET_PENDING_TRANSACTIONS)
|
) for each in pendingTransactions
|
||||||
]
|
] if pendingTransactions else []
|
||||||
|
|
||||||
def get_transaction(self, transactionId):
|
def get_transaction(self, transactionId):
|
||||||
return Transaction(
|
return Transaction(
|
||||||
data=self.conn.cursor().execute(SQL_GET_TRANSACTION.format(transactionId))
|
data=self.conn.cursor().execute(self.SQL_GET_TRANSACTION.format(transactionId))
|
||||||
)
|
)
|
||||||
|
|
||||||
def insert_block(self, block):
|
def insert_block(self, block):
|
||||||
return self.conn.cursor().execute(SQL_INSERT_BLOCK.format(
|
self.conn.cursor().execute(self.SQL_INSERT_BLOCK.format(
|
||||||
str(block.blockId),
|
str(block.blockId),
|
||||||
block.blockHash.hexdigest(),
|
block.blockHash.hexdigest(),
|
||||||
block.previousHash.hexdigest(),
|
block.previousHash,
|
||||||
str(block.timestamp),
|
str(block.timestamp),
|
||||||
block.height,
|
block.height,
|
||||||
block.nonce
|
block.nonce
|
||||||
))
|
))
|
||||||
|
|
||||||
def insert_transaction(self, transaction):
|
def insert_transaction(self, transaction):
|
||||||
return self.conn.cursor().execute(SQL_INSERT_TRANSACTION.format(
|
return self.conn.cursor().execute(self.SQL_INSERT_TRANSACTION.format(
|
||||||
str(transaction.transactionId),
|
str(transaction.transactionId),
|
||||||
str(transaction.timestamp),
|
str(transaction.timestamp),
|
||||||
transaction.sender,
|
transaction.sender,
|
||||||
|
@ -1,24 +1,75 @@
|
|||||||
import flask
|
import flask
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
from Mine.Block import Block
|
||||||
|
from Mine.Card import Card
|
||||||
from Mine.Database import Database
|
from Mine.Database import Database
|
||||||
|
from Mine.Transaction import Transaction
|
||||||
|
|
||||||
|
BLOCK_TRANSACTION_LIMIT = int(os.getenv('BLOCK_TRANSACTION_LIMIT', 32768))
|
||||||
|
DATA_PATH = os.getenv('DATA_PATH', '/var/lib/wikideck/blocks')
|
||||||
|
DIFFICULTY_REQUIREMENT = int(os.getenv('DIFFICULTY_REQUIREMENT', 0))
|
||||||
|
|
||||||
mine = flask.Blueprint("mine", __name__)
|
mine = flask.Blueprint("mine", __name__)
|
||||||
db = Database()
|
db = Database()
|
||||||
|
privateKey = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=4096
|
||||||
|
)
|
||||||
|
|
||||||
@app.get('/')
|
def save_block(block):
|
||||||
def mine():
|
with open(f"{DATA_PATH}/{block.blockId}.json", 'w') as f:
|
||||||
|
f.write(str(block)) # Blocks are json strings. This is the true block.
|
||||||
|
db.insert_block(block)
|
||||||
|
|
||||||
|
if not db.get_last_block():
|
||||||
|
# TODO: load blocks from files
|
||||||
|
# TODO: try to get blocks from peers
|
||||||
|
originBlock = Block(
|
||||||
|
difficulty = DIFFICULTY_REQUIREMENT,
|
||||||
|
)
|
||||||
|
originBlock.mine(privateKey)
|
||||||
|
originBlock.validate()
|
||||||
|
save_block(originBlock)
|
||||||
|
|
||||||
|
@mine.get('/')
|
||||||
|
def index_get():
|
||||||
# TODO: return a page to mine through the browser
|
# TODO: return a page to mine through the browser
|
||||||
return 200
|
return "Hello world!", 200
|
||||||
|
|
||||||
|
###
|
||||||
|
# Retrieve blocks and block data.
|
||||||
|
# Returns a skeleton block to be mined by default.
|
||||||
|
# Queries for a specific block when given parameters.
|
||||||
|
###
|
||||||
|
@mine.get('/blocks')
|
||||||
|
def blocks_get():
|
||||||
|
# TODO: block queries
|
||||||
|
blockId = flask.request.args.get('blockId', None)
|
||||||
|
lastBlock = db.get_last_block()
|
||||||
|
return flask.jsonify(
|
||||||
|
Block(
|
||||||
|
previousHash = lastBlock.blockHash.hexdigest(),
|
||||||
|
height = lastBlock.height + 1,
|
||||||
|
difficulty = DIFFICULTY_REQUIREMENT,
|
||||||
|
transactions = [
|
||||||
|
Transaction(data=each) for each in db.get_pending_transactions(
|
||||||
|
BLOCK_TRANSACTION_LIMIT
|
||||||
|
)
|
||||||
|
]
|
||||||
|
).as_dict()
|
||||||
|
)
|
||||||
|
|
||||||
###
|
###
|
||||||
# Submit a block to the chain.
|
# Submit a block to the chain.
|
||||||
# This method calls the Block().validate() method to validate the block schema.
|
# This method calls the Block().validate() method to validate the block schema.
|
||||||
# This method performs additional validations against the rest of the chain.
|
# This method performs additional validations against the rest of the chain.
|
||||||
###
|
###
|
||||||
@app.post('/blocks')
|
@mine.post('/blocks')
|
||||||
def blocks_post():
|
def blocks_post():
|
||||||
previousBlock = get_last_block()
|
|
||||||
try:
|
try:
|
||||||
newBlock = Block(data=request.get_json())
|
newBlock = Block(data=request.get_json())
|
||||||
newBlock.validate()
|
newBlock.validate()
|
||||||
@ -35,7 +86,7 @@ def blocks_post():
|
|||||||
raise Block.Invalid(
|
raise Block.Invalid(
|
||||||
f"Incorrect block height - should be {previousBlock.height + 1}."
|
f"Incorrect block height - should be {previousBlock.height + 1}."
|
||||||
)
|
)
|
||||||
if newBlock.difficulty != DIFFICULTY_REQUIREMENT:
|
if newBlock.difficulty < DIFFICULTY_REQUIREMENT:
|
||||||
raise Block.Invalid(
|
raise Block.Invalid(
|
||||||
f"Incorrect difficulty - should be {DIFFICULTY_REQUIREMENT}."
|
f"Incorrect difficulty - should be {DIFFICULTY_REQUIREMENT}."
|
||||||
)
|
)
|
||||||
@ -73,46 +124,24 @@ def blocks_post():
|
|||||||
raise Transaction.Invalid(
|
raise Transaction.Invalid(
|
||||||
f"Incorrect signature on {transaction.transactionId}."
|
f"Incorrect signature on {transaction.transactionId}."
|
||||||
)
|
)
|
||||||
db.insert_block(block)
|
save_block(block)
|
||||||
with open(f"{DATA_PATH}/{newBlock.blockId}.json", 'w') as f:
|
for transaction in newBlock.transactions:
|
||||||
f.write(str(newBlock))
|
db.update_transactions_is_pending_false(transaction.transactionId)
|
||||||
# TODO: update transactions
|
|
||||||
# TODO: update peers
|
# TODO: update peers
|
||||||
return str(newBlock), 200
|
return flask.jsonify(block.asDict())
|
||||||
except: Transaction.Invalid as e:
|
except Transaction.Invalid as e:
|
||||||
return e, e.statusCode
|
return e, e.statusCode
|
||||||
except: Transaction.AlreadyFulfilled as e:
|
except Transaction.AlreadyFulfilled as e:
|
||||||
return e, e.statusCode
|
return e, e.statusCode
|
||||||
except: Card.Invalid as e:
|
except Card.Invalid as e:
|
||||||
return e, e.statusCode
|
return e, e.statusCode
|
||||||
except: Block.Invalid as e:
|
except Block.Invalid as e:
|
||||||
return e, e.statusCode
|
return e, e.statusCode
|
||||||
|
|
||||||
###
|
|
||||||
# Retrieve blocks and block data.
|
|
||||||
# Returns a skeleton block to be mined by default.
|
|
||||||
# Queries for a specific block when given parameters.
|
|
||||||
###
|
|
||||||
@app.get('/blocks')
|
|
||||||
def blocks_get():
|
|
||||||
blockHash = request.args.get('blockHash', None)
|
|
||||||
height = request.args.get('height', None)
|
|
||||||
# TODO: block queries
|
|
||||||
return str(
|
|
||||||
Block(
|
|
||||||
previousHash = lastBlock.blockHash,
|
|
||||||
height = lastBlock.height + 1,
|
|
||||||
difficulty = DIFFICULTY_REQUIREMENT,
|
|
||||||
transactions = [
|
|
||||||
Transaction(data=each) for each in db.get_pending_transactions()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# Retrieve card data
|
# Retrieve card data
|
||||||
###
|
###
|
||||||
@app.get('/cards')
|
@mine.get('/cards')
|
||||||
def cards_get():
|
def cards_get():
|
||||||
# TODO: render cards in html
|
# TODO: render cards in html
|
||||||
# TODO: query cards
|
# TODO: query cards
|
||||||
@ -124,7 +153,7 @@ def cards_get():
|
|||||||
# This method performs a number of validations on the submitted transaction and returns
|
# This method performs a number of validations on the submitted transaction and returns
|
||||||
# a status code result.
|
# a status code result.
|
||||||
###
|
###
|
||||||
@app.put('/transactions')
|
@mine.put('/transactions')
|
||||||
def transactions_put():
|
def transactions_put():
|
||||||
try:
|
try:
|
||||||
newTransaction = Transaction(data=request.get_json())
|
newTransaction = Transaction(data=request.get_json())
|
||||||
@ -151,20 +180,25 @@ def transactions_put():
|
|||||||
###
|
###
|
||||||
# Retrieve a transaction.
|
# Retrieve a transaction.
|
||||||
###
|
###
|
||||||
@app.get('/transactions')
|
@mine.get('/transactions')
|
||||||
def transactions_get():
|
def transactions_get():
|
||||||
transactionId = request.args.get('transactionId', None)
|
transactionId = request.args.get('transactionId', None)
|
||||||
return get_transaction_by_id(transactionId) if transactionId else json.dumps(
|
return get_transaction_by_id(transactionId) if transactionId else json.dumps(
|
||||||
db.get_pending_transactions()
|
db.get_pending_transactions()
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.post('/peers')
|
@mine.post('/peers')
|
||||||
def peers_post():
|
def peers_post():
|
||||||
# TODO: validate peer
|
try:
|
||||||
# TODO: add peers to database
|
peer = Peer(data=request.get_json())
|
||||||
return 200
|
peer.validate()
|
||||||
|
# TODO: add peers to database
|
||||||
|
return 200
|
||||||
|
# TODO: error handling
|
||||||
|
except Exception as e:
|
||||||
|
print('crap!')
|
||||||
|
|
||||||
@app.get('/peers')
|
@mine.get('/peers')
|
||||||
def peers_get():
|
def peers_get():
|
||||||
# TODO: query peers
|
# TODO: query peers
|
||||||
return 200
|
return 200
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import data
|
import io
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
class Transaction():
|
class Transaction():
|
||||||
def __init__(self, transactionId=uuid.uuid4(),
|
def __init__(self, transactionId=uuid.uuid4(),
|
||||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||||
cardId=None, sender=None, receiver=None, signature=None, data=None):
|
cardId=None, sender=None, receiver=None, authorPrivateKey=None, signature=None,
|
||||||
|
data=None):
|
||||||
self.transactionId = transactionId
|
self.transactionId = transactionId
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.cardId = cardId
|
self.cardId = cardId
|
||||||
@ -14,6 +20,8 @@ class Transaction():
|
|||||||
self.signature = signature
|
self.signature = signature
|
||||||
if data:
|
if data:
|
||||||
self.load_from_data(data)
|
self.load_from_data(data)
|
||||||
|
if authorPrivateKey:
|
||||||
|
self.sign(authorPrivateKey)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
# TODO: validate transactionId
|
# TODO: validate transactionId
|
||||||
@ -22,12 +30,48 @@ class Transaction():
|
|||||||
# TODO: validate sender
|
# TODO: validate sender
|
||||||
# TODO: validate receiver
|
# TODO: validate receiver
|
||||||
# TODO: validate signature
|
# TODO: validate signature
|
||||||
if False:
|
try:
|
||||||
raise self.Unauthorized("Failed signature check.")
|
self.sender.verify(
|
||||||
|
self.signature,
|
||||||
|
bytearray(
|
||||||
|
json.dumps({
|
||||||
|
"transactionId": str(self.transactionId),
|
||||||
|
"timestamp": str(self.timestamp),
|
||||||
|
"cardId": str(self.cardId),
|
||||||
|
"receiver": self.receiver.public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
).decode('utf-8')
|
||||||
|
}).encode('utf-8')
|
||||||
|
),
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise self.InvalidSignature("Invalid signature.")
|
||||||
|
|
||||||
def sign(self, privateKey):
|
def sign(self, authorPrivateKey):
|
||||||
# TODO: use rsa private key to sign block
|
self.signature = authorPrivateKey.sign(
|
||||||
return None
|
bytearray(
|
||||||
|
json.dumps({
|
||||||
|
"transactionId": str(self.transactionId),
|
||||||
|
"timestamp": str(self.timestamp),
|
||||||
|
"cardId": str(self.cardId),
|
||||||
|
"receiver": self.receiver.public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
).decode('utf-8')
|
||||||
|
}).encode('utf-8')
|
||||||
|
),
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
|
||||||
def load_from_data(self, data):
|
def load_from_data(self, data):
|
||||||
self.transactionId = uuid.UUID(data['transactionId'])
|
self.transactionId = uuid.UUID(data['transactionId'])
|
||||||
@ -36,20 +80,29 @@ class Transaction():
|
|||||||
"%Y-%m-%d %H:%M:%S.%f%z"
|
"%Y-%m-%d %H:%M:%S.%f%z"
|
||||||
)
|
)
|
||||||
self.cardId = uuid.UUID(data['cardId'])
|
self.cardId = uuid.UUID(data['cardId'])
|
||||||
|
# TODO: load rsa keys
|
||||||
self.sender = data['sender']
|
self.sender = data['sender']
|
||||||
self.receiver = data['receiver']
|
self.receiver = data['receiver']
|
||||||
self.signature = data['signature']
|
self.signature = data['signature']
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
return {
|
||||||
|
"transactionId": str(self.transactionId),
|
||||||
|
"timestamp": str(self.timestamp),
|
||||||
|
"cardId": str(self.cardId),
|
||||||
|
"receiver": self.receiver.public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
).decode('utf-8'),
|
||||||
|
"sender": self.sender.public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
).decode('utf-8'),
|
||||||
|
"signature": self.signature.hex()
|
||||||
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return data.dumps({
|
return json.dumps(self.as_dict())
|
||||||
"transactionId": str(self.id),
|
|
||||||
"timestamp": str(self.timestamp),
|
|
||||||
"cardId": self.cardId,
|
|
||||||
"sender": self.sender,
|
|
||||||
"receiver": self.receiver,
|
|
||||||
"signature": self.signature
|
|
||||||
})
|
|
||||||
|
|
||||||
class Unauthorized(Exception):
|
class Unauthorized(Exception):
|
||||||
def __init__(self, message, statusCode=403):
|
def __init__(self, message, statusCode=403):
|
||||||
@ -70,3 +123,8 @@ class Transaction():
|
|||||||
def __init__(self, message, statusCode=500):
|
def __init__(self, message, statusCode=500):
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
self.statusCode = statusCode
|
self.statusCode = statusCode
|
||||||
|
|
||||||
|
class InvalidSignature(Exception):
|
||||||
|
def __init__(self, message, statusCode=400):
|
||||||
|
super().__init__(message)
|
||||||
|
self.statusCode = statusCode
|
||||||
|
BIN
wikideck/Mine/__pycache__/Block.cpython-311.pyc
Normal file
BIN
wikideck/Mine/__pycache__/Block.cpython-311.pyc
Normal file
Binary file not shown.
BIN
wikideck/Mine/__pycache__/Card.cpython-311.pyc
Normal file
BIN
wikideck/Mine/__pycache__/Card.cpython-311.pyc
Normal file
Binary file not shown.
BIN
wikideck/Mine/__pycache__/Database.cpython-311.pyc
Normal file
BIN
wikideck/Mine/__pycache__/Database.cpython-311.pyc
Normal file
Binary file not shown.
BIN
wikideck/Mine/__pycache__/Mine.cpython-311.pyc
Normal file
BIN
wikideck/Mine/__pycache__/Mine.cpython-311.pyc
Normal file
Binary file not shown.
BIN
wikideck/Mine/__pycache__/Transaction.cpython-311.pyc
Normal file
BIN
wikideck/Mine/__pycache__/Transaction.cpython-311.pyc
Normal file
Binary file not shown.
36
wikideck/PeerHandler/PeerHandler.py
Normal file
36
wikideck/PeerHandler/PeerHandler.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
###
|
||||||
|
# The PeerHandler sends new blocks and transactions to available peers.
|
||||||
|
###
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from Mine.Block import Block
|
||||||
|
from Mine.Transaction import Transaction
|
||||||
|
|
||||||
|
class PeerHandler():
|
||||||
|
def __init__(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def add_peer(self. peer):
|
||||||
|
try:
|
||||||
|
db.insert_peer(peer.peerId, peer.url)
|
||||||
|
# TODO: error handling
|
||||||
|
except Exception as e:
|
||||||
|
print('crap!')
|
||||||
|
|
||||||
|
def propogate_block(self, block):
|
||||||
|
try:
|
||||||
|
for peer in db.get_available_peers():
|
||||||
|
requests.post(peer.url, data=str(block))
|
||||||
|
db.update_last_try(peer.peerId)
|
||||||
|
# TODO: error handling
|
||||||
|
except Exception as e:
|
||||||
|
print('crap!')
|
||||||
|
|
||||||
|
def propogate_transaction(self, transaction):
|
||||||
|
try:
|
||||||
|
for peer in db.get_available_peers():
|
||||||
|
requests.post(peer.url, data=str(transaction))
|
||||||
|
# TODO: error handling
|
||||||
|
except Exception as e:
|
||||||
|
print('crap!')
|
0
wikideck/__init__.py
Normal file
0
wikideck/__init__.py
Normal file
@ -1,29 +1,14 @@
|
|||||||
import dotenv
|
import dotenv
|
||||||
|
import flask
|
||||||
import time
|
import time
|
||||||
import os
|
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
#from Market.Market import market
|
||||||
|
from Mine.Mine import mine
|
||||||
|
|
||||||
ROLE = os.getenv("ROLE", None)
|
app = flask.Flask(__name__)
|
||||||
INTERVAL = os.getenv("INTERVAL", 15)
|
#app.register_blueprint(market, url_prefix="/market")
|
||||||
|
app.register_blueprint(mine, url_prefix="/mine")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if ROLE == "api":
|
dotenv.load_dotenv()
|
||||||
from Market.Market import market
|
app.run()
|
||||||
from Mine.Mine import mine
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.register_blueprint(market, url_prefix="/market")
|
|
||||||
app.register_blueprint(mine, url_prefix="/mine")
|
|
||||||
app.run()
|
|
||||||
elif ROLE == "order_matcher":
|
|
||||||
from OrderMatcher.OrderMatcher import OrderMatcher
|
|
||||||
orderMatcher = OrderMatcher()
|
|
||||||
while True:
|
|
||||||
orderMatcher.match_orders()
|
|
||||||
time.sleep(INTERVAL)
|
|
||||||
elif ROLE == "status_checker":
|
|
||||||
from StatusChecker.StatusChecker import StatusChecker
|
|
||||||
statusChecker = StatusChecker()
|
|
||||||
while True:
|
|
||||||
statusChecker.check_pending_transactions()
|
|
||||||
time.sleep(INTERVAL)
|
|
||||||
|
23
wikideck/client.py
Normal file
23
wikideck/client.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
from Mine.Block import Block
|
||||||
|
|
||||||
|
WIKIDECK_URL = os.getenv('WIKIDECK_URL', 'http://localhost:8080/')
|
||||||
|
|
||||||
|
privateKey = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=4096
|
||||||
|
)
|
||||||
|
|
||||||
|
newBlock = Block(
|
||||||
|
data = requests.get(f"{WIKIDECK_URL}/mine/blocks").json()
|
||||||
|
)
|
||||||
|
newBlock.mine(privateKey)
|
||||||
|
print(newBlock)
|
||||||
|
|
||||||
|
r = requests.post(f"{WIKIDECK_URL}/mine/blocks", data=newBlock.as_dict())
|
||||||
|
print(r)
|
4
wsgi_app.py
Normal file
4
wsgi_app.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.insert(0, '/usr/local/apache2/htdocs/wikideck')
|
||||||
|
|
||||||
|
from app import app as application
|
Loading…
Reference in New Issue
Block a user