from block import Block
from flask import Flask, render_template, request, jsonify
from transaction import *
from gene_addr import Addr
import threading
import time
class BlockChain:
def __init__(self):
self.chain = []
self.tr_wating = []
genesis_block = Block()
genesis_block.bits = 3 #초기 난이도
genesis_block.coinbase()
genesis_block.gen_mrkl_root()
genesis_block.gen_hash()
self.chain.append(genesis_block)
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
def make_block(self): #prev hash랑 코인베이스 거래 실행해서 리턴
result = Block()
result.coinbase()
result.prev_hash = self.chain[-1].hash
return result
def add_block(self): #웹에서 버튼만들어서 테스트용 함수, 클릭하면 블락 만들어서 투입하는 용도
new_block = self.make_block()
for tr in self.tr_wating:
new_block.add_transaction(tr)
new_block.gen_mrkl_root()
new_block.gen_hash()
self.tr_wating.clear()
self.chain.append(new_block)
def mining(self): #적당한 난이도 설정, 루프 돌때마다 대기열의 tr넣어주고 대기열 비워주고
#얘는 threading import해서 subthread에서 계속 돌도록 할거야
#얘를 Block클래스로 빼는게 나을 수도 있겠다는 생각
while True:
new_block = self.make_block()
new_block.bits = 4
time.sleep(20)
occured_tr = []
for tr in self.tr_wating:
new_block.add_transaction(tr)
occured_tr.append(tr)
new_block.gen_mrkl_root()
new_block.gen_hash()
for i in occured_tr:
self.tr_wating.remove(i)
print('block is appended')
self.chain.append(new_block)
def check_tr_wating(self,v,h,addr): #대기열에 있는 tr중 해당 utxo썼는지 check
if len(self.tr_wating) == 0:
return True
for item in self.tr_wating:
for i in item.inputs:
if i.hash == h and i.address == addr and i.value == v:
return False
return True
def make_transaction(self, mykey, youraddr, money): #tr 만들어서 대기열에 넣어줌
myutxos = self.find_utxo(mykey.addr)
money = int(money)
for item in myutxos: #대기열 tr에서 utxo썼는지 check
if self.check_tr_wating(*item):
pass
else:
myutxos.remove(item)
tr = Transaction()
using_utxo = []
using_value = 0
for item in myutxos:
if using_value >= money:
break
else:
using_utxo.append(item)
using_value+=item[0]
if using_value < money:
return None
for item in using_utxo:
txin = TxIn()
txin.value = item[0]
txin.hash = item[1]
txin.address = item[2]
txin.pubk = mykey.pubk
tr.addinput(txin)
txout = TxOut()
txout.to = youraddr
txout.value = money
tr.addoutput(txout)
if using_value > money:
txout2 = TxOut()
txout2.to = mykey.addr
txout2.value = using_value-money
tr.addoutput(txout2)
tr.gen_hash()
tr.sign(mykey.priv)
if tr.can_spent()==True:
return tr
else:
return None #None이면 트랜잭션 에러라고 확인이 필요하다고 알려줘야됨
def ask_transaction(self,tr):
self.tr_wating.append(tr)
def find_transaction(self,tr):
for b in self.chain:
for t in b.transactions:
if t.hash == tr:
return t
return None
def find_utxo(self,addr):
all_hash_of_trout = []
all_hash_of_trin = []
for b in self.chain: #체인의 모든블락
for t in b.transactions: #블락의 모든 트랜잭션
for tout in t.outputs: #트랜잭션의 모든 아웃풋
if tout.to == addr: #아웃풋에 해당주소 있는지 검사
all_hash_of_trout.append((t.hash, tout.value)) #있다면 해당 tr 아이디 가져옴
for tin in t.inputs: #트랜잭션의 모든 인풋
if tin.address == addr: #인풋에 해당주소 있는지 검사
all_hash_of_trin.append(tin.hash) #있으면 해당 txIn 안의 hash 가져옴
for inhash in all_hash_of_trin:
flag = False
for outhash,outvalue in all_hash_of_trout:
if inhash == outhash: #해쉬주소가 같으면 이미 그 output은 쓰인 것
all_hash_of_trout.remove((outhash, outvalue))
flag = True
continue
if flag==True:
continue
result = []
for h,v in all_hash_of_trout:
result.append((v, h, addr)) #input에서 쓸수있는 형태로
result.sort(reverse=True)
return result
def find_my_transaction(self, addr):
mytr = []
for b in self.chain:
for t in b.transactions:
flag = False
for t_in in t.inputs:
if t_in.address == addr:
mytr.append(t)
flag = True
continue
for t_out in t.outputs:
if t_out.to == addr:
mytr.append(t)
flag = True
continue
if flag==True:
continue
return mytr
if __name__=='__main__':
app = Flask(__name__)
mykey = None
chain = BlockChain()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['POST', 'GET'])
def login():
global mykey
if request.method == 'POST':
mystring = request.form['mystring']
mykey = Addr(mystring)
myaddr = mykey.addr
myutxos = chain.find_utxo(myaddr)
mymoney = 0
for item in myutxos:
mymoney += item[0]
return render_template('index.html', addr=myaddr, money=mymoney)
@app.route('/send', methods=['POST'])
def send():
youraddr = request.form['address']
money = request.form['money']
tr = chain.make_transaction(mykey, youraddr, money)
if tr == None:
return render_template('index.html', tr_result='fail')
chain.ask_transaction(tr)
print(chain.tr_wating)
return render_template('index.html', tr_result='success')
@app.route('/makeblock')
def make_block():
print('this is occured!')
print('this is before the block is made', chain.tr_wating)
chain.add_block()
print('this is after the block is made', chain.tr_wating)
print(chain.toJSON())
return render_template('index.html', result='success')
@app.route('/find_tr')
def find_tr():
mytr = chain.find_my_transaction(mykey.addr)
mytrjson = []
for t in mytr:
mytrdict = {'hash': '', 'sender': '', 'receiver': [], 'value': [], 'date': ''}
mytrdict['hash'] = t.hash
if len(t.inputs) != 0:
mytrdict['sender'] = t.inputs[0].address
for i in t.outputs:
mytrdict['receiver'].append(i.to)
mytrdict['value'].append(i.value)
mytrdict['date'] = t.time
mytrjson.append(mytrdict)
return json.dumps(mytrjson)
t_server = threading.Thread(target=app.run, kwargs={'host': '0.0.0.0', 'port': 8080})
t_server.start()
t_mining = threading.Thread(target=chain.mining)
t_mining.start()
t_server.join()
t_mining.join()