Developing components.
* Created database classes * Wrote initial queries * Added validations
This commit is contained in:
parent
14def28b68
commit
c83f5feb4b
@ -1,7 +1,7 @@
|
|||||||
CREATE DATABASE IF NOT EXISTS chain;
|
CREATE DATABASE IF NOT EXISTS chain;
|
||||||
USE chain;
|
USE chain;
|
||||||
CREATE TABLE IF NOT EXISTS blocks(
|
CREATE TABLE IF NOT EXISTS blocks(
|
||||||
rowId 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,23 +10,26 @@ 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(
|
||||||
rowId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
||||||
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(
|
||||||
rowId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
||||||
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,
|
||||||
receiver VARCHAR(128) NOT NULL,
|
receiver VARCHAR(128) NOT NULL,
|
||||||
|
signature VARCHAR(128) 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 (blockId) REFERENCES blocks(blockId),
|
FOREIGN KEY (blockId) REFERENCES blocks(blockId),
|
||||||
FOREIGN KEY (cardId) REFERENCES cards(cardId)
|
FOREIGN KEY (cardId) REFERENCES cards(cardId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS peers(
|
CREATE TABLE IF NOT EXISTS peers(
|
||||||
peerId INT PRIMARY KEY AUTO INCREMENT,
|
sqlId INT PRIMARY KEY AUTO INCREMENT,
|
||||||
|
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,
|
||||||
downCount INT DEFAULT 0,
|
downCount INT DEFAULT 0,
|
||||||
|
@ -1,40 +1,45 @@
|
|||||||
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(
|
||||||
rowId 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,
|
||||||
email VARCHAR(64) UNIQUE NOT NULL,
|
email VARCHAR(64) UNIQUE NOT NULL,
|
||||||
balance FLOAT NOT NULL DEFAULT 0,
|
balance FLOAT NOT NULL DEFAULT 0,
|
||||||
public_key VARCHAR(128) NOT NULL
|
publicKey VARCHAR(128) NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS buyOrders(
|
CREATE TABLE IF NOT EXISTS buy_orders(
|
||||||
rowId 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(),
|
||||||
|
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
||||||
pageId INT NOT NULL,
|
pageId INT NOT NULL,
|
||||||
price FLOAT NOT NULL,
|
price FLOAT NOT NULL,
|
||||||
volume INT NOT NULL,
|
volume INT NOT NULL,
|
||||||
fee FLOAT NOT NULL DEFAULT 0,
|
fee FLOAT NOT NULL DEFAULT 0,
|
||||||
|
sold INT NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY (userId) REFERENCES users(userId)
|
FOREIGN KEY (userId) REFERENCES users(userId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS sellOrders(
|
CREATE TABLE IF NOT EXISTS sell_orders(
|
||||||
rowId 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(),
|
||||||
|
expires DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP() + INTERVAL 3 DAY,
|
||||||
price FLOAT NOT NULL,
|
price FLOAT NOT NULL,
|
||||||
fee FLOAT NOT NULL,
|
fee FLOAT NOT NULL,
|
||||||
FOREIGN KEY (userId) REFERENCES users(userId)
|
FOREIGN KEY (userId) REFERENCES users(userId)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS sellOrdersItems(
|
CREATE TABLE IF NOT EXISTS sell_orders_items(
|
||||||
rowId 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 sellOrders(sellOrderId) NOT NULL,
|
FOREIGN KEY (sellOrderId) REFERENCES sell_orders(sellOrderId) NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS transactions(
|
CREATE TABLE IF NOT EXISTS transactions(
|
||||||
rowId 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 buyOrders(buyOrderId) NOT NULL,
|
FOREIGN KEY (buyOrderId) REFERENCES buy_orders(buyOrderId) NOT NULL,
|
||||||
FOREIGN KEY (sellOrderId) REFERENCES sellOrders(sellOrderId) NOT NULL
|
FOREIGN KEY (sellOrderItemSqlId) REFERENCES sell_order_items(sqlId) NOT NULL
|
||||||
);
|
);
|
||||||
|
10
db/getDeck.sql
Normal file
10
db/getDeck.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
SELECT cardId, pageId, transactionId
|
||||||
|
FROM (
|
||||||
|
SELECT cardId, pageId, transactionId, ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY cardId ORDER BY timestamp DESC
|
||||||
|
) AS rowNumber
|
||||||
|
FROM transactions
|
||||||
|
JOIN cards
|
||||||
|
WHERE receiver == {userId}
|
||||||
|
) AS subquery
|
||||||
|
WHERE rowNumber == 0;
|
0
db/getLastTransactionForCard.sql
Normal file
0
db/getLastTransactionForCard.sql
Normal file
@ -15,19 +15,5 @@ class BuyOrder(Order):
|
|||||||
def add_transaction(self, transaction):
|
def add_transaction(self, transaction):
|
||||||
if len(self.transactions) < self.volume:
|
if len(self.transactions) < self.volume:
|
||||||
# TODO: update database
|
# TODO: update database
|
||||||
self.transactions.append(transaction)
|
|
||||||
else:
|
else:
|
||||||
raise self.Fulfilled("Order volume has already been fulfilled.")
|
raise self.Fulfilled("Order volume has already been fulfilled.")
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return json.dumps({
|
|
||||||
"orderId": str(self.orderId),
|
|
||||||
"timestamp": str(self.timestamp)
|
|
||||||
"userId": str(self.userId)
|
|
||||||
"orderType": str(self.orderType),
|
|
||||||
"cardId": self.cardId,
|
|
||||||
"price": self.price,
|
|
||||||
"volume": self.volume,
|
|
||||||
"fee": self.fee,
|
|
||||||
"transactions": [str(each) for each in self.transactions]
|
|
||||||
})
|
|
||||||
|
12
wikideck/Market/Database.py
Normal file
12
wikideck/Market/Database.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class Database():
|
||||||
|
def __init__(self, host, port, user, password):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.conn = mariadb.connect(
|
||||||
|
host = self.host,
|
||||||
|
port = self.port,
|
||||||
|
user = self.user,
|
||||||
|
password = self.password
|
||||||
|
)
|
@ -1,11 +1,17 @@
|
|||||||
from flask import Flask
|
import flask
|
||||||
|
|
||||||
market = Flask(__name__)
|
from Market.Database import Database
|
||||||
|
|
||||||
# TODO: figure out db
|
market = flask.Blueprint("market", __name__)
|
||||||
|
db = Database()
|
||||||
|
|
||||||
# TODO: users and logins
|
# TODO: users and logins
|
||||||
|
|
||||||
|
@app.get('/')
|
||||||
|
def market():
|
||||||
|
# TODO: return a page to view market stats
|
||||||
|
return 200
|
||||||
|
|
||||||
###
|
###
|
||||||
# Submit an order to the market.
|
# Submit an order to the market.
|
||||||
# This method calls the Order().validate() method to validate the order schema.
|
# This method calls the Order().validate() method to validate the order schema.
|
||||||
@ -34,6 +40,3 @@ def order_post():
|
|||||||
def orders_get():
|
def orders_get():
|
||||||
# TODO: order queries
|
# TODO: order queries
|
||||||
return 200
|
return 200
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run()
|
|
||||||
|
@ -26,7 +26,3 @@ class Order(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def load_from_data(self):
|
def load_from_data(self):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def __str__(self):
|
|
||||||
continue
|
|
||||||
|
@ -16,15 +16,6 @@ class SellOrder():
|
|||||||
self.items.pop(transaction.cardId)
|
self.items.pop(transaction.cardId)
|
||||||
self.transactions.append(transaction)
|
self.transactions.append(transaction)
|
||||||
|
|
||||||
def __str__(self):
|
def validate(self):
|
||||||
return json.dumps({
|
# TODO: no duplicate items
|
||||||
"orderId": str(self.orderId),
|
return
|
||||||
"timestamp": str(self.timestamp),
|
|
||||||
"userId": str(self.userId),
|
|
||||||
"orderType": str(self.orderType),
|
|
||||||
"cardId": self.cardId,
|
|
||||||
"items": self.items,
|
|
||||||
"price": self.price,
|
|
||||||
"fee": self.fee,
|
|
||||||
"transactions": [str(each) for each in self.transactions]
|
|
||||||
})
|
|
||||||
|
@ -3,16 +3,24 @@ import wikipedia
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
class Card():
|
class Card():
|
||||||
def __init__(self, cardId=uuid.uuid4(), pageId=wikipedia.page(wikipedia.random()).pageid):
|
def __init__(self, cardId=uuid.uuid4(),
|
||||||
|
pageId=wikipedia.page(wikipedia.random()).pageid, data=None):
|
||||||
self.cardId = cardId
|
self.cardId = cardId
|
||||||
self.pageId = pageId
|
self.pageId = pageId
|
||||||
|
if data:
|
||||||
|
self.load_from_data(data)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
try:
|
try:
|
||||||
|
# TODO: cardId is UUID
|
||||||
wikipedia.page(pageid=self.pageId)
|
wikipedia.page(pageid=self.pageId)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise self.Invalid("Page ID does not match a Wikipedia page.")
|
raise self.Invalid("Page ID does not match a Wikipedia page.")
|
||||||
|
|
||||||
|
def load_from_data(self, data):
|
||||||
|
self.cardId = uuid.UUID(data['cardId'])
|
||||||
|
self.pageId = data['pageId']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"cardId": str(self.cardId),
|
"cardId": str(self.cardId),
|
||||||
|
111
wikideck/Mine/Database.py
Normal file
111
wikideck/Mine/Database.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
class Database():
|
||||||
|
SQL_GET_LAST_BLOCK = """
|
||||||
|
SELECT *
|
||||||
|
FROM blocks
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT 1;
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_CARD = """
|
||||||
|
SELECT *
|
||||||
|
FROM cards
|
||||||
|
WHERE cardId == {};
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_CARD_OWNER = """
|
||||||
|
SELECT receiver
|
||||||
|
FROM transactions
|
||||||
|
WHERE cardId == {}
|
||||||
|
AND isPending == false
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT 1;
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_PENDING_TRANSACTIONS = """
|
||||||
|
SELECT *
|
||||||
|
FROM transactions
|
||||||
|
WHERE isPending == true;
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_TRANSACTION = """
|
||||||
|
SELECT *
|
||||||
|
FROM transactions
|
||||||
|
WHERE transactionId == {}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_INSERT_BLOCK = """
|
||||||
|
INSERT INTO blocks (blockId, blockHash, previousHash, timestamp, height, nonce)
|
||||||
|
VALUES ({}, {}, {}, {}, {}, {});
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_INSERT_TRANSACTION = """
|
||||||
|
INSERT INTO transactions (
|
||||||
|
transactionId,
|
||||||
|
timestamp,
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
|
signature,
|
||||||
|
blockId,
|
||||||
|
cardId
|
||||||
|
)
|
||||||
|
VALUES ({}, {}, {}, {}, {});
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host, port, user, password):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.conn = mariadb.connect(
|
||||||
|
host = self.host,
|
||||||
|
port = self.port,
|
||||||
|
user = self.user,
|
||||||
|
password = self.password
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_last_block(self):
|
||||||
|
return Block(
|
||||||
|
data=self.conn.cursor().execute(SQL_GET_LAST_BLOCK)[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_card(self, cardId):
|
||||||
|
return Card(
|
||||||
|
data=self.conn.cursor().execute(SQL_GET_CARD.format(cardId))[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_card_owner(self, cardId):
|
||||||
|
return self.conn.cursor().execute(
|
||||||
|
SQL_GET_CARD_OWNER.format(cardId)
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
def get_pending_transactions(self):
|
||||||
|
return [
|
||||||
|
Transaction(
|
||||||
|
data=each
|
||||||
|
) for each in self.conn.cursor().execute(SQL_GET_PENDING_TRANSACTIONS)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_transaction(self, transactionId):
|
||||||
|
return Transaction(
|
||||||
|
data=self.conn.cursor().execute(SQL_GET_TRANSACTION.format(transactionId))
|
||||||
|
)
|
||||||
|
|
||||||
|
def insert_block(self, block):
|
||||||
|
return self.conn.cursor().execute(SQL_INSERT_BLOCK.format(
|
||||||
|
str(block.blockId),
|
||||||
|
block.blockHash.hexdigest(),
|
||||||
|
block.previousHash.hexdigest(),
|
||||||
|
str(block.timestamp),
|
||||||
|
block.height,
|
||||||
|
block.nonce
|
||||||
|
))
|
||||||
|
|
||||||
|
def insert_transaction(self, transaction):
|
||||||
|
return self.conn.cursor().execute(SQL_INSERT_TRANSACTION.format(
|
||||||
|
str(transaction.transactionId),
|
||||||
|
str(transaction.timestamp),
|
||||||
|
transaction.sender,
|
||||||
|
transaction.receiver,
|
||||||
|
transaction.signature,
|
||||||
|
str(transaction.cardId)
|
||||||
|
))
|
@ -1,8 +1,10 @@
|
|||||||
from flask import Flask
|
import flask
|
||||||
import json
|
import json
|
||||||
|
|
||||||
mine = Flask(__name__)
|
from Mine.Database import Database
|
||||||
db = mariadb.connect(**config)
|
|
||||||
|
mine = flask.Blueprint("mine", __name__)
|
||||||
|
db = Database()
|
||||||
|
|
||||||
@app.get('/')
|
@app.get('/')
|
||||||
def mine():
|
def mine():
|
||||||
@ -71,9 +73,10 @@ def blocks_post():
|
|||||||
raise Transaction.Invalid(
|
raise Transaction.Invalid(
|
||||||
f"Incorrect signature on {transaction.transactionId}."
|
f"Incorrect signature on {transaction.transactionId}."
|
||||||
)
|
)
|
||||||
# TODO: write to database
|
db.insert_block(block)
|
||||||
with open(f"{DATA_PATH}/{newBlock.blockId}.json", 'w') as f:
|
with open(f"{DATA_PATH}/{newBlock.blockId}.json", 'w') as f:
|
||||||
f.write(str(newBlock))
|
f.write(str(newBlock))
|
||||||
|
# TODO: update transactions
|
||||||
# TODO: update peers
|
# TODO: update peers
|
||||||
return str(newBlock), 200
|
return str(newBlock), 200
|
||||||
except: Transaction.Invalid as e:
|
except: Transaction.Invalid as e:
|
||||||
@ -101,7 +104,7 @@ def blocks_get():
|
|||||||
height = lastBlock.height + 1,
|
height = lastBlock.height + 1,
|
||||||
difficulty = DIFFICULTY_REQUIREMENT,
|
difficulty = DIFFICULTY_REQUIREMENT,
|
||||||
transactions = [
|
transactions = [
|
||||||
Transaction(data=each) for each in get_pending_transactions()
|
Transaction(data=each) for each in db.get_pending_transactions()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -111,7 +114,9 @@ def blocks_get():
|
|||||||
###
|
###
|
||||||
@app.get('/cards')
|
@app.get('/cards')
|
||||||
def cards_get():
|
def cards_get():
|
||||||
|
# TODO: render cards in html
|
||||||
# TODO: query cards
|
# TODO: query cards
|
||||||
|
# TODO: get decks
|
||||||
return 200
|
return 200
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -124,10 +129,20 @@ def transactions_put():
|
|||||||
try:
|
try:
|
||||||
newTransaction = Transaction(data=request.get_json())
|
newTransaction = Transaction(data=request.get_json())
|
||||||
newTransaction.validate()
|
newTransaction.validate()
|
||||||
# TODO: validate transaction against blockchain
|
if not get_card(newTransaction.cardId):
|
||||||
# TODO: add transaction to database
|
raise Transaction.Invalid(
|
||||||
|
f"Card {newTransaction.cardId} does not exist.",
|
||||||
|
404
|
||||||
|
)
|
||||||
|
if newTransaction.sender != get_card_owner(newTransaction.cardId):
|
||||||
|
raise Transaction.Unauthorized(
|
||||||
|
f"{newTransaction.sender} does not own {newTransaction.cardId}."
|
||||||
|
)
|
||||||
|
insert_transaction(newTransaction)
|
||||||
# TODO: update peers?
|
# TODO: update peers?
|
||||||
return 200
|
return 200
|
||||||
|
except mariadb.Error as e:
|
||||||
|
return e, 500
|
||||||
except Transaction.Unauthorized as e:
|
except Transaction.Unauthorized as e:
|
||||||
return e, e.statusCode
|
return e, e.statusCode
|
||||||
except Transaction.Invalid as e:
|
except Transaction.Invalid as e:
|
||||||
@ -153,6 +168,3 @@ def peers_post():
|
|||||||
def peers_get():
|
def peers_get():
|
||||||
# TODO: query peers
|
# TODO: query peers
|
||||||
return 200
|
return 200
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run()
|
|
||||||
|
@ -23,9 +23,7 @@ class Transaction():
|
|||||||
# TODO: validate receiver
|
# TODO: validate receiver
|
||||||
# TODO: validate signature
|
# TODO: validate signature
|
||||||
if False:
|
if False:
|
||||||
raise self.Unauthorized()
|
raise self.Unauthorized("Failed signature check.")
|
||||||
elif False:
|
|
||||||
raise self.Invalid()
|
|
||||||
|
|
||||||
def sign(self, privateKey):
|
def sign(self, privateKey):
|
||||||
# TODO: use rsa private key to sign block
|
# TODO: use rsa private key to sign block
|
||||||
|
64
wikideck/OrderMatcher/Database.py
Normal file
64
wikideck/OrderMatcher/Database.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
class Database():
|
||||||
|
SQL_GET_BUY_ORDERS = """
|
||||||
|
SELECT *
|
||||||
|
FROM orders
|
||||||
|
WHERE orderType == 'buy';
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_UNFULFILLED_BUY_ORDERS = """
|
||||||
|
SELECT b.buyOrderId, b.price, b.volume - COUNT(t.sqlId) AS remainingVolume
|
||||||
|
FROM buy_orders b
|
||||||
|
JOIN transactions t
|
||||||
|
ON b.buyOrderId = t.buyOrderId
|
||||||
|
WHERE b.expires > CURRENT_TIMESTAMP()
|
||||||
|
GROUP BY b.buyOrderId
|
||||||
|
HAVING COUNT(t.sqlId) < b.volume
|
||||||
|
ORDER BY b.timestamp ASC
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_GET_AVAILABLE_SELL_ORDER_ITEMS_FOR_BUY_ORDER = """
|
||||||
|
SELECT o.sellOrderId, o.price, o.fee, o.userId, i.sqlId, i.cardId
|
||||||
|
FROM sell_orders o
|
||||||
|
JOIN sell_order_items i
|
||||||
|
ON o.sellOrderId = i.sellOrderId
|
||||||
|
WHERE i.cardId == {}
|
||||||
|
AND o.price <= {}
|
||||||
|
AND o.expires > CURRENT_TIMESTAMP()
|
||||||
|
AND i.sqlId NOT IN (
|
||||||
|
SELECT sellOrderItemSqlId
|
||||||
|
FROM transactions
|
||||||
|
WHERE isAbandoned == false;
|
||||||
|
)
|
||||||
|
ORDER BY price DESC;
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_INSERT_TRANSACTION = """
|
||||||
|
INSERT INTO transactions (transactionId, buyOrderId, sellOrderItemSqlId)
|
||||||
|
VALUES ({}, {}, {});
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.conn = mariadb.connect(
|
||||||
|
# TODO: default values
|
||||||
|
host = os.getenv("ORDER_MATCHER_DB_HOST", None),
|
||||||
|
port = os.getenv("ORDER_MATCHER_DB_PORT", 3342),
|
||||||
|
user = os.getenv("ORDER_MATCHER_DB_USER", None),
|
||||||
|
password = os.getenv("ORDER_MATCHER_DB_PASSWORD", None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_available_sell_order_items_for_buy_order(self, buyOrderId, price):
|
||||||
|
return self.conn.cursor().execute(
|
||||||
|
SQL_GET_AVAILABLE_SELL_ORDER_ITEMS_FOR_BUY_ORDER.format(
|
||||||
|
buyOrderId,
|
||||||
|
price
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def insert_transaction(self, transactionId, buyOrderId, sellOrderItemSqlId):
|
||||||
|
self.conn.cursor().execute(
|
||||||
|
SQL_INSERT_TRANSACTION.format(
|
||||||
|
transactionId,
|
||||||
|
buyOrderId,
|
||||||
|
sellOrderItemSqlId
|
||||||
|
)
|
||||||
|
)
|
@ -5,17 +5,45 @@
|
|||||||
# If the order matcher finds a match for the order, it creates a transaction and
|
# If the order matcher finds a match for the order, it creates a transaction and
|
||||||
# sends it to the mine.
|
# sends it to the mine.
|
||||||
###
|
###
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from Mine.Transaction import Transaction
|
||||||
|
from OrderMatcher.Database import Database
|
||||||
|
|
||||||
class OrderMatcher():
|
class OrderMatcher():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
self.db = Database()
|
||||||
|
|
||||||
def match_orders(self):
|
def match_orders(self):
|
||||||
# TODO: find matching orders
|
try:
|
||||||
# TODO: generate transactions
|
for buyOrder in self.db.get_unfulfilled_buy_orders():
|
||||||
# TODO: send transactions to mine
|
for item in self.db.get_available_sell_order_items_for_buy_order(
|
||||||
|
buyOrder[0],
|
||||||
|
buyOrder[1]
|
||||||
|
)[:buyOrder[2]]:
|
||||||
|
newTransaction = Transaction(
|
||||||
|
cardId = item[5],
|
||||||
|
sender = item[3],
|
||||||
|
receiver = currentUser, # TODO: get current currentUser
|
||||||
|
)
|
||||||
|
newTransaction.sign(userPrivateKey) # TODO: get userPrivateKey
|
||||||
|
r = requests.post(
|
||||||
|
f"{MINE_URL}/transactions",
|
||||||
|
data=str(newTransaction)
|
||||||
|
)
|
||||||
|
# TODO: only insert on success
|
||||||
|
self.db.insert_transaction(
|
||||||
|
newTransaction.transactionId,
|
||||||
|
buyOrder[0],
|
||||||
|
item[5]
|
||||||
|
)
|
||||||
|
# TODO: error handling
|
||||||
|
except Exception as e:
|
||||||
|
print("crap!")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
orderMatcher = OrderMatcher()
|
orderMatcher = OrderMatcher()
|
||||||
while True:
|
while True:
|
||||||
orderMatcher.match_orders()
|
orderMatcher.match_orders()
|
||||||
time.sleep(10)
|
time.sleep(INTERVAL)
|
||||||
|
12
wikideck/StatusChecker/Database.py
Normal file
12
wikideck/StatusChecker/Database.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class Database():
|
||||||
|
def __init__(self, host, port, user, password):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.conn = mariadb.connect(
|
||||||
|
host = self.host,
|
||||||
|
port = self.port,
|
||||||
|
user = self.user,
|
||||||
|
password = self.password
|
||||||
|
)
|
@ -1,30 +1,29 @@
|
|||||||
import os
|
|
||||||
import dotenv
|
import dotenv
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
ROLE = os.getenv("ROLE", None)
|
ROLE = os.getenv("ROLE", None)
|
||||||
INTERVAL = os.getenv("INTERVAL", 15)
|
INTERVAL = os.getenv("INTERVAL", 15)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
dotenv.load_dotenv()
|
if ROLE == "api":
|
||||||
if ROLE == "market":
|
|
||||||
from Market.Market import market
|
from Market.Market import market
|
||||||
market.run()
|
|
||||||
elif ROLE == "mine":
|
|
||||||
from Mine.Mine import mine
|
from Mine.Mine import mine
|
||||||
mine.run()
|
app = Flask(__name__)
|
||||||
|
app.register_blueprint(market, url_prefix="/market")
|
||||||
|
app.register_blueprint(mine, url_prefix="/mine")
|
||||||
|
app.run()
|
||||||
elif ROLE == "order_matcher":
|
elif ROLE == "order_matcher":
|
||||||
from OrderMatcher.OrderMatcher import OrderMatcher
|
from OrderMatcher.OrderMatcher import OrderMatcher
|
||||||
import time
|
|
||||||
orderMatcher = OrderMatcher()
|
orderMatcher = OrderMatcher()
|
||||||
while True:
|
while True:
|
||||||
orderMatcher.match_orders()
|
orderMatcher.match_orders()
|
||||||
time.sleep(INTERVAL)
|
time.sleep(INTERVAL)
|
||||||
elif ROLE == "status_checker":
|
elif ROLE == "status_checker":
|
||||||
from StatusChecker.StatusChecker import StatusChecker
|
from StatusChecker.StatusChecker import StatusChecker
|
||||||
import time
|
|
||||||
statusChecker = StatusChecker()
|
statusChecker = StatusChecker()
|
||||||
while True:
|
while True:
|
||||||
statusChecker.update_pending_transactions()
|
statusChecker.check_pending_transactions()
|
||||||
time.sleep(INTERVAL)
|
time.sleep(INTERVAL)
|
||||||
else:
|
|
||||||
raise Exception("Role must be one of: market, mine, order_matcher, status_checker")
|
|
||||||
|
Loading…
Reference in New Issue
Block a user