g-coin 분석(2)
안녕하세요.
저는 공주대학교 컴퓨터공학부 컴퓨터소프트웨어전공 17학번 문승현입니다.
현재 블록체인 기술에 대해 공부를 하여 공부한 내용을 정리하고자 게시글을 작성하였습니다.
미리 읽어주시는 분들께 감사하며 잘못된 지식이 있을 경우 마음 편하게 피드백주시면 감사하겠습니다.
g-coin 분석(0) : http://blog.hubeen.kr/337
g-coin 분석(1) : http://blog.hubeen.kr/339
벌써 분석(2)라니 뿌듯함을 감출 수 없습니다!!!
오늘 게시글에서는 실제로 서버에서 소스코드를 작동을 시켜 사용을 해볼 예정입니다.
저는 골빈님께서 올려주신 Insomnia라는 툴을 사용하지 않고 포스트맨이라는 툴을 사용하였습니다.
서버 구동
위와 같이 실행을 하게 되면 아래와 같이 메세지가 띄어졌다면 서버가 실행된것입니다!
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
아, 저는 app.py의 코드를 외부 접속도 허용하게 실행하여 0.0.0.0 으로 메세지가 떴지만 다른 분들은 127.0.0.1로 뜨실 겁니다 !
서버를 실행을 성공적으로 하셨다면 이제 이것저것 쇼핑하듯이 구경해볼까요?!
채굴(mine)
http://서버주소:5000/mine 으로 post를 쏴보니 채굴이 되었음을 볼 수 있습니다.
{
"block": {
"header": {
"difficulty": 1,
"previous_hash": "64fb44605fe924d910fc285a72f2b8d9220fd4cff755510b6e0e6c476f4cae4e",
"proof": 11,
"timestamp": 1520200361.470756
},
"transactions": [
{
"amount": 1,
"recipient": "0e5d1ef7e4a944b2be47c1913dea0c8f",
"sender": "0"
}
]
},
"message": "New block is mined!"
}
보낸이는 "0"으로 신 혹은 관리자가 0e5d1ef7e4a944b2be47c1913dea0c8f 주소로 1을 보내준 것을 볼 수 잇네요!
그리고 메세지에 새로운 블럭이 채굴되었다라는 것도 보이네요!
블럭에 대한 헤더는 어려움을 1, 그리고 채굴 전의 초기 블럭 생성이 되었으니 그 초기 블럭의 해쉬를 이전 해쉬 값에 들어간것도 보이네요!
자 그러면 채굴이 되었다면 현재 블록체인이 어떻게 되어있는지 구경하러가볼까요?
블록체인 (chain)
초기 블럭과 현재 아까 채굴한 블럭이 이어져있는 것을 볼 수 있으며 전체 갯수는 2개라는 것을 볼 수 있습니다.
거래 (transaction)
신 혹은 관리자가 거래 전송
네, 이제 보내졌는지 거래 기록에 제대로 기록이 되었는지 확인해보죠!
거래 확인
[
{
"amount": 1024,
"recipient": "hubeen",
"sender": "0"
}
]
현재 거래 기록에 보낸이는 "0"으로 신 혹은 관리자가 받는이 hubeen에게 1024개를 보낸 것이 거래 기록에 잘 저장되있음을 볼 수 있습니다.
이제 hubeen이 postman에게 500개를 전송하는 것을 다시 해보도록 하죠!
hubeen->postman 500개 전송
거래 확인
네, 이렇게 코드 리뷰하고 서버에서 실제로 실행하여 확인을 하는 것까지 하였습니다.
이걸로 g-coin은 마무리하며 더 재밌고 괜찮은 코드를 발견하게 되면 그것도 작성하도록 하겠습니다.
읽어주셔서 감사합니다.
'0x30 Study > 0x32 Blockchain' 카테고리의 다른 글
g-coin 분석(2) (2) | 2018.03.05 |
---|---|
g-coin 분석(1) (1) | 2018.03.03 |
g-coin 분석(0) (0) | 2018.03.01 |
BlockChain을 공부하자! (0) (0) | 2018.02.28 |
Comment 2
g-coin 분석(1)
g-coin 분석(0) : g-coin 분석(0)
이전 게시글의 분석글을 위의 링크로 보실 수 있습니다.
이제 이전 분석을 이어가보도록 하겠습니다.
분석
남은 blockchain.py, miner.py, node.py, proof.py 는 현재 게시글에서 정리를 이어가도록 하겠습니다.
proof.py
import hashlib import gcoin.config as cfg def valid_proof(block, proof=None): """ Validates proof last digits of hash(previous_block.header, proof) == config.PROOF_DIGITS Args: block (obj): proof (int): proof to validate Returns: bool: """ proof = proof if proof else block.proof proof_seed = '{0}{1}'.format(block.header.hash(), proof).encode() proof_hash = hashlib.sha256(proof_seed).hexdigest() return proof_hash[:block.difficulty] == cfg.PROOF_DIGITS * block.difficulty def find_proof(block): """proof of work Args: block (obj): Returns: int: proof """ proof = 0 while valid_proof(block, proof) is False: proof += 1 return proof
proof.py는 config.py 파일을 불러옵니다.
그리고 두개의 함수가 있네요.
def valid_proof(block, proof=None): """ Validates proof last digits of hash(previous_block.header, proof) == config.PROOF_DIGITS Args: block (obj): proof (int): proof to validate Returns: bool: """ proof = proof if proof else block.proof proof_seed = '{0}{1}'.format(block.header.hash(), proof).encode() proof_hash = hashlib.sha256(proof_seed).hexdigest() return proof_hash[:block.difficulty] == cfg.PROOF_DIGITS * block.difficulty
def find_proof(block): """proof of work Args: block (obj): Returns: int: proof """ proof = 0 while valid_proof(block, proof) is False: proof += 1 return proof
인자로 넘겨받는 블록을 유요할때까지 계속 while문으로 돌리는 것을 볼 수 있었습니다.
유효한 해쉬값을 찾을때까지 +=1 을 하여 리턴하네요 !
miner.py
import gcoin.config as cfg import gcoin.proof as proof from gcoin.transaction import Transaction class Miner: def __init__(self, account_id): self.account_id = account_id def __call__(self, blockchain): # Adding mining rewards transaction = Transaction(cfg.GENESIS_ACCOUNT_ID, self.account_id, cfg.AMOUNT_OF_REWARD) blockchain.add_transaction(transaction) # Make new block with transactions and hash of last block new_block = blockchain.new_block() # Proof of Work new_proof = proof.find_proof(new_block) new_block.proof = new_proof blockchain.add_block(new_block) return new_block
채굴하는 코드입니다!!!
채굴!!! 가즈아!!!!!!
먼저 3개의 코드를 불러오는 것을 볼 수 있습니다.
config.py, proof.py, transaction.py
그리고 초기화에서 account_id 를 클래스 자신에게 대입을 하네요.
아마도 이 계정은 채굴자의 계정이 아닐까 싶습니다 :D
그리고 채굴의 기여한 내용을 거래 기록에 남기네요.
그리고 추가를 하구요.
그리고 채굴되었다면 새로운 블럭을 생성하여 채굴이 완료되는 것을 볼 수 있습니다.
그 뒤 새로운 블럭에 유효한 증명을 하여 새로운 블럭에 대입하고 블록체인에 추가를 하고 새로운 블럭을 리턴을 해주는 코드네요.
blockchain.py
import gcoin.proof import gcoin.config as cfg from gcoin.book import Book from gcoin.block import Block class BlockChain: def __init__(self, chain=None): """init chain with existing chain or make this new blockchain Args: chain: list of dictionary of Block, see load_chain """ self.chain = [] self.book = Book() self.transactions = [] if chain: self.load_chain(chain) else: self.init_chain() def init_chain(self): """Make genesis block""" genesis_block = Block([], previous_hash=cfg.GENESIS_HASH) genesis_proof = gcoin.proof.find_proof(genesis_block) genesis_block.proof = genesis_proof self.add_block(genesis_block) def add_transaction(self, transaction): """Add new transaction It will only add amount Args: transaction (obj): Transaction object Returns: int: index of next block of chain return -1 if it's not correct transaction """ if self.book.check_balance(transaction): self.transactions.append(transaction) return len(self.chain) + 1 # Add this transaction to next block else: raise Exception('Transaction is wrong.') def new_block(self): last_block = self.chain[-1] block = Block(self.transactions, previous_hash=last_block.hash()) self.transactions = [] return block def add_block(self, block): self.chain.append(block) self.book.apply(block.transactions) def valid(self): """Valid chain""" index = 1 while index < len(self): prev_block = self.chain[index-1] curr_block = self.chain[index] # Check hash with previous hash if curr_block.previous_hash != prev_block.hash(): return False # Check proof of current block if not gcoin.proof.valid_proof(curr_block): return False index += 1 return True def load_chain(self, chain): """load chain from list of dictionary from existing blockchain Args: chain (list): [{ transactions: [{ sender: 'dsf9s9f0ad' recipient: 'dfsad90fasf' amount: 12 }] proof: 318832940000 previous_hash: 'fj9afje9ajf9sef0s0f' timestamp: 1506057125.900785 }] """ for block in chain: block = Block.init_from_json(block) self.add_block(block) def last_block(self): return self.chain[-1] def dump(self): return [block.dump() for block in self.chain] def __len__(self): return len(self.chain)
이게 바로 제일 중요한 블록체인에 대한 코드가 아닐까 싶습니다.
찬찬히 코드를 살펴보도록 합시다!!!
def __init__(self, chain=None): """init chain with existing chain or make this new blockchain Args: chain: list of dictionary of Block, see load_chain """ self.chain = [] self.book = Book() self.transactions = [] if chain: self.load_chain(chain) else: self.init_chain()
def init_chain(self): """Make genesis block""" genesis_block = Block([], previous_hash=cfg.GENESIS_HASH) genesis_proof = gcoin.proof.find_proof(genesis_block) genesis_block.proof = genesis_proof self.add_block(genesis_block)
먼저 블록체인을 위해 리스트와 통장(?)과 거래 기록을 초기화하는 것을 볼 수 있습니다.
그 후 체인이 있을 경우 load_chain함수로 생성한 체인에 로드하네요.
아닐 경우엔 init_chain으로 체인을 초기화하는 것을 볼 수 있습니다.
블록체인에 첫 블록을 추가하는 부분이네요 ㅎㅎ
def add_transaction(self, transaction): """Add new transaction It will only add amount Args: transaction (obj): Transaction object Returns: int: index of next block of chain return -1 if it's not correct transaction """ if self.book.check_balance(transaction): self.transactions.append(transaction) return len(self.chain) + 1 # Add this transaction to next block else: raise Exception('Transaction is wrong.')
거래 기록 object를 받네요!
check_balance로 거래기록을 받은 것을 인자로 넘겨주어 제대로된 거래인지 확인을 합니다.
제대로 된 거래일 경우 거래 기록에 추가하고 체인에 대해 길이 +1을 하네요.
def new_block(self): last_block = self.chain[-1] block = Block(self.transactions, previous_hash=last_block.hash()) self.transactions = [] return block def add_block(self, block): self.chain.append(block) self.book.apply(block.transactions)
마지막 블록이라는 변수에 체인의 마지막 블록을 대입하며 블록을 생성하네요!
생성한 블럭은 반환해주네요!
그리고 말그대로 블럭체인에 블럭을 추가하는 코드입니다.
def load_chain(self, chain): """load chain from list of dictionary from existing blockchain Args: chain (list): [{ transactions: [{ sender: 'dsf9s9f0ad' recipient: 'dfsad90fasf' amount: 12 }] proof: 318832940000 previous_hash: 'fj9afje9ajf9sef0s0f' timestamp: 1506057125.900785 }] """ for block in chain: block = Block.init_from_json(block) self.add_block(block)
네 이건 json 데이터를 받아 block 을 초기화하고 블록체인에 추가하네요.
그 외엔 딱히 중요한 내용은 없는 것 같습니다.
node.py
import requests from uuid import uuid4 from gcoin.blockchain import BlockChain class Node: def __init__(self, id=None): self.id = id if id else self._generate_id() self.neighbor = set() @staticmethod def _generate_id(): return str(uuid4()).replace('-', '') def add(self, address): self.neighbor.add(address) def __len__(self): return len(self.neighbor) @staticmethod def fetch_neighbor_chain(address): res = requests.get('{0}/chain'.format(address)) return res.json() def consensus_with_neighbor(self, blockchain): """Consensus conflicts with neighbor Args: blockchain (obj): BlockChain object of mine for consensus Returns: obj or None: None if my blockchain is King new BlockChain object if my blockchain is looser """ new_blockchain = None max_length = len(blockchain) for node in self.neighbor: data = self.fetch_neighbor_chain(node) if data['length'] > max_length: new_blockchain = BlockChain(chain=data['chain']) if not new_blockchain.valid(): new_blockchain = None else: max_length = len(new_blockchain) return new_blockchain
네 드디어 마지막 파일입니다!!!!
def fetch_neighbor_chain(address): res = requests.get('{0}/chain'.format(address)) return res.json()
chain에 get 리퀘스트를 쏴서 현재 블록체인들의 json 파일을 리턴해주네요.
def consensus_with_neighbor(self, blockchain): """Consensus conflicts with neighbor Args: blockchain (obj): BlockChain object of mine for consensus Returns: obj or None: None if my blockchain is King new BlockChain object if my blockchain is looser """ new_blockchain = None max_length = len(blockchain) for node in self.neighbor: data = self.fetch_neighbor_chain(node) if data['length'] > max_length: new_blockchain = BlockChain(chain=data['chain']) if not new_blockchain.valid(): new_blockchain = None else: max_length = len(new_blockchain) return new_blockchain
블록체인을 인자로 받아 현재 체인에 대해 추가하는 코드 부분입니다.
이로써 모든 파일에 대한 코드를 분석해보았습니다.
다음 게시글에서는 서버에서 실행환경을 구축하여 실제로 동작을 하는지를 확인하도록 하겠습니다.
읽어주셔서 감사합니다.
'0x30 Study > 0x32 Blockchain' 카테고리의 다른 글
g-coin 분석(2) (2) | 2018.03.05 |
---|---|
g-coin 분석(1) (1) | 2018.03.03 |
g-coin 분석(0) (0) | 2018.03.01 |
BlockChain을 공부하자! (0) (0) | 2018.02.28 |
Comment 1
g-coin 분석(0)
안녕하세요.
저는 공주대학교 컴퓨터공학부 컴퓨터소프트웨어전공 17학번 문승현입니다.
현재 블록체인 기술에 대해 공부를 하여 공부한 내용을 정리하고자 게시글을 작성하였습니다.
미리 읽어주시는 분들께 감사하며 잘못된 지식이 있을 경우 마음 편하게 피드백주시면 감사하겠습니다.
먼저 제가 깃허브를 돌아다니다 괜찮은 자료를 발견하여 분석을 시작한 코인의 깃허브 주소입니다.
https://github.com/golbin/g-coin
먼저 다운받게 되면 위와 같이 파일들이 있습니다.
golbin
- app.py
- gcoin
- blockchain.py
- block.py
- book.py
- config.py
- miner.py
- node.py
- proof.py
- __pycache__
- requirements.txt
- transaction.py
패키지
패키지 설치
분석
app.py
from argparse import ArgumentParser from flask import Flask, jsonify, request from gcoin.node import Node from gcoin.miner import Miner from gcoin.blockchain import BlockChain from gcoin.transaction import Transaction app = Flask('g-coin') app.node = Node() app.miner = Miner(app.node.id) app.blockchain = BlockChain() @app.route('/transaction', methods=['POST']) def add_transaction(): # data.sender(str) # data.recipient(str) # data.amount(int) data = request.get_json() transaction = Transaction.init_from_json(data) try: next_index = app.blockchain.add_transaction(transaction) except Exception as e: return jsonify({'message': str(e)}), 403 response = {'message': 'Transaction will be added to {next_index}th block.'} return jsonify(response), 201 @app.route('/transaction', methods=['GET']) def get_pending_transactions(): transactions = [t.dump() for t in app.blockchain.transactions] return jsonify(transactions), 201 @app.route('/mine', methods=['POST']) def mine(): """Mining Have to make a standalone process But it's just a prototype """ block = app.miner(app.blockchain) response = { 'message': "New block is mined!", 'block': block.dump() } return jsonify(response), 200 @app.route('/chain', methods=['GET']) def get_full_chain(): response = { 'chain': app.blockchain.dump(), 'length': len(app.blockchain) } return jsonify(response), 200 @app.route('/node', methods=['GET']) def get_all_nodes(): response = { 'nodes': list(app.node.neighbor), 'total': len(app.node) } return jsonify(response), 201 @app.route('/node', methods=['POST']) def add_node(): # data.address(str) data = request.get_json() app.node.add(data['address']) response = { 'message': 'New app.node is added.', 'total': len(app.node) } return jsonify(response), 201 @app.route('/chain/valid', methods=['GET']) def valid_chain(): valid = app.blockchain.valid() response = {'result': valid} return jsonify(response), 200 @app.route('/consensus', methods=['POST']) def consensus(): new_blockchain = app.node.consensus_with_neighbor(app.blockchain) if new_blockchain: app.blockchain = new_blockchain response = {'message': 'Our chain was replaced.'} else: response = {'message': 'I\'m King of the World.'} return jsonify(response), 200 if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('--port', default=5000, type=int) args = parser.parse_args() app.run(host='0.0.0.0',port=args.port)
보게 되면 --port 로 받은 인자를 int 형으로 플라스크의 포트로 설정하는 것을 볼 수 있습니다.
네, app.py는 그냥 플라스크를 실행하는 것을 알 수 있습니다.
아, 참 깃허브에서 받은 코드가 다른 부분이 있습니다.
app.run(host='0.0.0.0',port=args.port)
바로 이 부분인데요.
아마 host='0.0.0.0'이 없으실겁니다.
저는 외부 접속을 허용하기 위해 이 부분에 코드를 추가하였습니다.
config.py
# for the first chain GENESIS_HASH = 'g' # for mining rewards GENESIS_ACCOUNT_ID = '0' AMOUNT_OF_REWARD = 1 # for proof of work DIFFICULTY = 1 # number of digits is difficulty PROOF_DIGITS = '0' # Finding PROOF_DIGITS * DIFFICULTY ('00', '000', ..) is proof of work
config.py부터 보도록 하겠습니다.
GENESIS_HASH = 'g'
첫번째의 체인의 해쉬를 설정하는 부분입니다.
GENESIS_ACCOUNT_ID = '0' AMOUNT_OF_REWARD = 1
채굴을 할 때에 나누어줄 갯수와 초기의 계정의 아이디를 설정하는 부분입니다.
쉽게 말하면 '신', '관리자'의 계정을 설정하는 부분입니다.
DIFFICULTY = 1 # number of digits is difficulty PROOF_DIGITS = '0' # Finding PROOF_DIGITS * DIFFICULTY ('00', '000', ..) is proof of work
채굴에 대해 난이도를 설정하는 부분입니다.
현재의 코드는 난이도가 1로 설정이 되어있어 PROOF_DIGITS * 1로 '0' 되어있습니다.
이 난이도가 2로 설정이 되면 PROOF_DIGITS * 2가 되어 '00' 으로 동작한다는 것을 주석을 통해 알 수 있었습니다.
block.py
import json import hashlib from time import time import gcoin.config as cfg from gcoin.transaction import Transaction class BlockHeader: def __init__(self, previous_hash=None, timestamp=0, difficulty=0, proof=0): """Block Args: previous_hash (str): timestamp (float): difficulty (int): proof (int): """ self.previous_hash = previous_hash self.timestamp = timestamp if timestamp else time() self.difficulty = difficulty if difficulty else cfg.DIFFICULTY self.proof = proof def hash(self): """Make hash of a header of current block for finding proof Don't use 'proof' because there is no 'proof' in new block at the first time. """ header_dump = self.dump() header_dump.pop('proof', None) header_dump = json.dumps(header_dump, sort_keys=True).encode() header_hash = hashlib.sha256(header_dump).hexdigest() return header_hash def dump(self, proof=True): data = { 'previous_hash': self.previous_hash, 'timestamp': self.timestamp, 'difficulty': self.difficulty } if proof: data['proof'] = self.proof return data @classmethod def init_from_json(cls, data): return cls(data['previous_hash'], data['timestamp'], data['difficulty'], data['proof']) class Block: def __init__(self, transactions, previous_hash=None): """Block Args: transactions (list): list of Transaction object previous_hash (str): """ self.transactions = transactions self.header = BlockHeader(previous_hash) @property def previous_hash(self): return self.header.previous_hash @property def difficulty(self): return self.header.difficulty @property def proof(self): return self.header.proof @proof.setter def proof(self, value): self.header.proof = value def hash(self): """Make hash of current block""" block_dump = json.dumps(self.dump(), sort_keys=True).encode() block_hash = hashlib.sha256(block_dump).hexdigest() return block_hash def dump(self): return { 'header': self.header.dump(), 'transactions': [t.dump() for t in self.transactions] } @classmethod def init_from_json(cls, data): transactions = [Transaction.init_from_json(t) for t in data['transactions']] header = BlockHeader.init_from_json(data['header']) self = cls(transactions) self.header = header return self
블록의 구조를 볼 수 있는 코드입니다.
transactions : 거래 정보들
previous_hash : 이전 해쉬
proof : 보상?
timestamp : 블록이 생성된 시간
hash : 자기 블록의 해쉬
difficulty : 어려움?
import gcoin.config as cfg
일단 아까 저희가 처음에 본 config.py를 임포트하는 것을 볼 수 있습니다.
self.difficulty = difficulty if difficulty else cfg.DIFFICULTY
난이도를 설정한 것을 불러와 대입을 하는 것을 볼 수 있습니다.
class BlockHeader: def __init__(self, previous_hash=None, timestamp=0, difficulty=0, proof=0): """Block Args: previous_hash (str): timestamp (float): difficulty (int): proof (int): """ self.previous_hash = previous_hash self.timestamp = timestamp if timestamp else time() self.difficulty = difficulty if difficulty else cfg.DIFFICULTY self.proof = proof def hash(self): """Make hash of a header of current block for finding proof Don't use 'proof' because there is no 'proof' in new block at the first time. """ header_dump = self.dump() header_dump.pop('proof', None) header_dump = json.dumps(header_dump, sort_keys=True).encode() header_hash = hashlib.sha256(header_dump).hexdigest() return header_hash def dump(self, proof=True): data = { 'previous_hash': self.previous_hash, 'timestamp': self.timestamp, 'difficulty': self.difficulty } if proof: data['proof'] = self.proof return data @classmethod def init_from_json(cls, data): return cls(data['previous_hash'], data['timestamp'], data['difficulty'], data['proof'])
인자는 4개로 previous_hash, timestamp, difficulty, proof가 있습니다.
previous_hash : string 타입으로 이전 블록의 해쉬 값입니다.
timestamp : float 타입으로 블록의 생성 시간입니다.
difficulty : int 타입으로 난이도 결정의 값입니다.
proof : int 타입으로 아직까지 무슨 역할을 하는 아이인진 모르겠네요.
인자를 받으며 에러를 위해 초기 값들은 None, 0, 0, 0으로 설정하는 것을 볼 수 있습니다.
현재 자기 자신의 값에 받은 값을 대입하는 것을 볼 수 있습니다.
hash()
header_dump = self.dump() header_dump.pop('proof', None) header_dump = json.dumps(header_dump, sort_keys=True).encode() header_hash = hashlib.sha256(header_dump).hexdigest()
init_from_json()
def init_from_json(cls, data): return cls(data['previous_hash'], data['timestamp'], data['difficulty'], data['proof'])
JSON 데이터를 받아 블록정보를 초기화합니다.
dump()
def dump(self, proof=True): data = { 'previous_hash': self.previous_hash, 'timestamp': self.timestamp, 'difficulty': self.difficulty } if proof: data['proof'] = self.proof return data
블록의 정보를 반환해주는 함수입니다.
class Block: def __init__(self, transactions, previous_hash=None): """Block Args: transactions (list): list of Transaction object previous_hash (str): """ self.transactions = transactions self.header = BlockHeader(previous_hash) @property def previous_hash(self): return self.header.previous_hash @property def difficulty(self): return self.header.difficulty @property def proof(self): return self.header.proof @proof.setter def proof(self, value): self.header.proof = value def hash(self): """Make hash of current block""" block_dump = json.dumps(self.dump(), sort_keys=True).encode() block_hash = hashlib.sha256(block_dump).hexdigest() return block_hash def dump(self): return { 'header': self.header.dump(), 'transactions': [t.dump() for t in self.transactions] } @classmethod def init_from_json(cls, data): transactions = [Transaction.init_from_json(t) for t in data['transactions']] header = BlockHeader.init_from_json(data['header']) self = cls(transactions) self.header = header return self
transactions : list 타입으로 거래 정보들이 들어있는 곳입니다.
previous_hash : string 타입으로 이전 해쉬 값이 들어있는 곳입니다.
그리고 함수들의 역할은 위에서 설명한 함수들과 같은 역할을 하는 아이들이라 딱히 큰 설명은 없이 넘어가도 될 것 같습니다.
transaction.py
class Transaction: def __init__(self, sender, recipient, amount): """Transaction Args: sender (str): recipient (str): amount (int): positive number """ self.sender = sender self.recipient = recipient self.amount = amount if amount < 1: raise Exception('Amount have to be positive number.') def dump(self): return { 'sender': self.sender, 'recipient': self.recipient, 'amount': self.amount } @classmethod def init_from_json(cls, data): return cls(data['sender'], data['recipient'], data['amount'])
파일 명을 보고 딱 알아차릴 수 있겠네요!
거래정보를 관련하는 아이일 것이라 예상할 수 있었습니다.
인자는 3개로 sender, recipient, amount 입니다.
sender : string 타입으로 보내는 이의 정보가 담기는 변수입니다.
recipient : string 타입으로 받는 이의 정보가 담기는 변수입니다.
amount : int 타입으로 보낼 갯수가 담기는 변수입니다.
if amount < 1: raise Exception('Amount have to be positive number.')
dump()
def dump(self): return { 'sender': self.sender, 'recipient': self.recipient, 'amount': self.amount }
init_from_json()
def init_from_json(cls, data): return cls(data['sender'], data['recipient'], data['amount'])
json 값을 초기화해주는 함수입니다.
book.py
"""account book""" import gcoin.config as cfg class Account: def __init__(self): self.target = [] # sender or recipient self.amount = [] # - / + amount def sum(self): return sum(self.amount) def add(self, target, amount): self.target.append(target) self.amount.append(amount) class Book: def __init__(self): self.account = {} def check_balance(self, transaction): """Check sender's balance TODO: check balance in transactions in next blocks Args: transaction (obj): Transaction object Returns: bool: """ if transaction.sender == cfg.GENESIS_ACCOUNT_ID: # for mining rewards return True if transaction.sender in self.account: account = self.account[transaction.sender] return account.sum() - transaction.amount >= 0 else: return False def get_account(self, account_id): if account_id not in self.account: self.account[account_id] = Account() return self.account[account_id] def apply(self, transactions): """Add new transactions to book in new block Args: transactions (obj): Transaction object """ for t in transactions: sender = self.get_account(t.sender) recipient = self.get_account(t.recipient) sender.add(recipient, -t.amount) recipient.add(sender, t.amount)
book이라니? 이게 무슨 녀석인지 이해가 안됬지만 코드를 보고 대략 파악이 가능했습니다.
계정에 대해 갯수를 반환해주는 아이엿습니다.
쉽게 말하면 통장정도가 되겠네요.
여기에도 두개의 클래스가 있습니다.
class Account: def __init__(self): self.target = [] # sender or recipient self.amount = [] # - / + amount def sum(self): return sum(self.amount) def add(self, target, amount): self.target.append(target) self.amount.append(amount)
sum()
add()
class Book: def __init__(self): self.account = {} def check_balance(self, transaction): """Check sender's balance TODO: check balance in transactions in next blocks Args: transaction (obj): Transaction object Returns: bool: """ if transaction.sender == cfg.GENESIS_ACCOUNT_ID: # for mining rewards return True if transaction.sender in self.account: account = self.account[transaction.sender] return account.sum() - transaction.amount >= 0 else: return False def get_account(self, account_id): if account_id not in self.account: self.account[account_id] = Account() return self.account[account_id] def apply(self, transactions): """Add new transactions to book in new block Args: transactions (obj): Transaction object """ for t in transactions: sender = self.get_account(t.sender) recipient = self.get_account(t.recipient) sender.add(recipient, -t.amount) recipient.add(sender, t.amount)
현재 계정을 초기화하네요.
dicinary 타입으로 계정마다 갯수를 정하게 하는 것 같습니다.
check_balance()
if transaction.sender == cfg.GENESIS_ACCOUNT_ID: # for mining rewards return True
만약 보내는 이가 '신', '관리자' 계정이면 무조건 True가 되네요.
이에 대한 코드는 보안에 대한 취약점이 되지 않을까 싶습니다.
if transaction.sender in self.account: account = self.account[transaction.sender] return account.sum() - transaction.amount >= 0 else: return False
거래 내용에 보내는 이가 계정 안에 있으면 계정에 대한 갯수를 보내거나 지불하는 것을 볼 수 있으며 계정 안에 없을 경우에는 False를 리턴하는 것을 볼 수 있습니다.
get_account()
apply()
남은 blockchain.py, miner.py, node.py, proof.py 는 다음 게시글에서 정리하도록 하겠습니다.
읽어주셔서 감사합니다.
'0x30 Study > 0x32 Blockchain' 카테고리의 다른 글
g-coin 분석(2) (2) | 2018.03.05 |
---|---|
g-coin 분석(1) (1) | 2018.03.03 |
g-coin 분석(0) (0) | 2018.03.01 |
BlockChain을 공부하자! (0) (0) | 2018.02.28 |
Comment 0
BlockChain을 공부하자! (0)
안녕하세요.
저는 공주대학교 컴퓨터공학부 컴퓨터소프트웨어전공 17학번 문승현입니다.
현재 블록체인 기술에 대해 공부를 하여 공부한 내용을 정리하고자 게시글을 작성하였습니다.
미리 읽어주시는 분들께 감사하며 잘못된 지식이 있을 경우 마음 편하게 피드백주시면 감사하겠습니다.
현재 게시글에 대한 깃허브는 아래에 주소를 기입하도록 하겠습니다.
https://github.com/schedutron/SnakeCoin
블록 내부 구조
class Block: def __init__(self, index, timestamp, data, previous_hash): self.index = index self.timestamp = timestamp self.data = data self.previous_hash = previous_hash self.hash = self.hash_block() def __str__(self): return 'Block #{}'.format(self.index) def hash_block(self): sha = hasher.sha256() seq = (str(x) for x in ( self.index, self.timestamp, self.data, self.previous_hash)) sha.update(''.join(seq).encode('utf-8')) return sha.hexdigest()
index : 현재 블록의 위치 (첫 블록 0)
timestamp : 블록이 생성된 시간
data : 블록체인에 포함하고자 하는 데이터
previous_hash = 이전 블록의 해쉬
hash : sha256으로 index + timestamp + data + previous_hash들을 암호화함
함수 분석
def make_genesis_block(): """Make the first block in a block-chain.""" block = Block(index=0, timestamp=datetime.now(), data="Genesis Block", previous_hash="0") return block
make_genesis_block()
위의 함수는 코드를 보시면 아시겠지만 index를 0으로 설정하여 현재 시간과 데이터는 Genesis Block 이라는 문장과 이전 해쉬가 없기때문에 0으로 설정되어 블록이 생성되는 것을 볼 수 있습니다.
첫번째 블록을 만드는 함수라는 것을 알 수 있습니다.
def next_block(last_block, data=''): """Return next block in a block chain.""" idx = last_block.index + 1 block = Block(index=idx,
timestamp=datetime.now(),
data='{}{}'.format(data, idx),
previous_hash=last_block.hash)
return block
next_block(last_block, data='')
인자는 2개로 하나는 이전 블록을 넘겨받으며, 새로 추가할 데이터를 받는 것을 볼 수 있습니다.
idx 는 이전 블록의 인덱스 +1 을 한 값으로 설정된 뒤에 인덱스를 넣으며 새로운 블럭이 생성되는 것을 볼 수 있습니다.
그렇습니다.
이전 블럭을 받아 새로운 블럭을 만드는 함수라는 것을 알 수 있었습니다.
채굴 알고리즘
처음에 블록체인을 공부하다 채굴이라는 개념을 잡기가 많이 어려웠습니다.
멘토님에게 질문을 드렸는데
새로운 블럭을 생성하는 것이 채굴이라는 것을 알게 되었다.
그렇다 이 코드에서 채굴 알고리즘은 있지 않다.
이쯤에서 채굴이 무엇인지 Araboza.
채굴
* 채굴 필요성
- 공급자 역할
- 수요공급법칙
* 작업 증명 : (Proof of Work)
- 작업 증명은 새로운 블록을 블록체인에 추가하는 ‘작업’을 증명한다는 것이라고 한다.
그렇다면 현재보다 채굴의 난이도를 올리는 방법은 무엇이 있을까?
뭐 딜레이를 줘서 간단하게 난이도를 올리는 방법과 암호화 알고리즘들을 사용하여 브루트포싱 기법으로 때려맞춰서 난이도를 올리는 방법들이 있다고 합니다!
필자는 후자의 방법을 사용하였습니다.
코드는 아래와 같습니다.
def next_block(last_block, data, i): """Return next block in a block chain.""" sw = False while(sw == False): print("mining...") if(base64.b64encode(str(i)) == base64.b64encode(str(random.randrange(0, i+1)))): sw = True; else: sw = False; if(sw): idx = last_block.index + 1 block = Block(index=idx, timestamp=datetime.now(), data='{}{}'.format(data, idx),previous_hash=last_block.hash) return block
간단한 코드로써 브루트 포싱으로 채굴을 하게 해보았다.
random 함수로 n번째로 생성되는 블럭의 n을 암호화한 값을 랜덤으로 초이스하여 때려맞추는 브루트 포싱으로 채굴되게 해보았다.
난이도는 채굴이 된 블럭 수 만큼 증가하는 것을 볼 수 있었다.
이것에 대해 더 채굴 난이도를 올리고자하면 많은 코인들이 사용하는 암호화 알고리즘을 이용하여 코드를 작성한다면 난이도는 많이 올를 것이라는 것을 알게 되었다.
'0x30 Study > 0x32 Blockchain' 카테고리의 다른 글
g-coin 분석(2) (2) | 2018.03.05 |
---|---|
g-coin 분석(1) (1) | 2018.03.03 |
g-coin 분석(0) (0) | 2018.03.01 |
BlockChain을 공부하자! (0) (0) | 2018.02.28 |
Comment 0
[Reverse Lab 스터디] Ada Cracked(0)
[ 출처 : 닌자보이 란타로 ]
이 Ada Cracked 시리즈 글을 올릴 때는 이 사진을 이용할 예정이다.
이번 주 고른 프로그램이다.
이 프로그램은 베타 버전때의 파일인데.
사용 기한이 만료되었다며 메세지 창을 띄우고 종료를 해버린다.
메인을 따라가다보면 날짜를 비교하는 부분을 발견할 수 있다.
로컬 시간이랑 임의로 정해둔 0x07DE비교하는 것을 알 수 있다.
여기서 임의로 ECX의 값을 높은 값으로 바꿔주면 된다.
10초 제한도 비교하는 변수(?)명을 알아내서 시간 체크하는 부분을 찾아내면 되겠다.
문자열 발견
0x2710(10000) (1000ms = 1초)
10초와 비교하는 부분을 발견할 수 있었다.
이번 주 과제 끝,
'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글
[Reverse Lab 스터디] Ada Cracked(0) (1) | 2016.08.10 |
---|---|
[Reverse Lab 스터디] DLL Injection (0) | 2016.08.10 |
[ReverseLab 스터디] UPX Packing (0) | 2016.07.26 |
Comment 1
[Reverse Lab 스터디] DLL Injection
DLL Injection
이번 글 내용 한 줄 요약할 수 있는 사진을 만들어버렸다...
DLL 인젝션은 말 그대로 다른 프로세서에 DLL을 강제로 박아버리는 것이다.
LoadLibrary() API를 호출하여 DLL을 로딩(Loading) 시키는 것인데.
DLL Injection은 다른 프로세서에 DLL을 삽입시키느냐, 자신의 프로세스에 삽입시키느냐에 따라 로딩시키는 대상에 따라 달라집니다.
What is DLL?
Purpose of DLL file use
1. 크기
2. 악성 코드
3. 패치
Make DLL File in VS2015
이제 코드를 작성하시고 빌드를 하고 DLL을 사용하면 됩니다.
Injector
DWORD FindProcessID(LPCTSTR szProcessName)
LPCTSTR szProcessName : 프로세스 이름 [ Ex) notepad.exe, kakaotalk.exe ]
Return : 프로세스 PID 값을 반환합니다.
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
DWORD dwPID : 프로세스의 PID 값
LPCTSTR szDllName : DLL 경로 [ Ex) C:\dsadasdas.dll, d:\asdasdas.dll ]
Return : 무조건 True를 반환하나, Handle을 구하지 못할 경우에는 False를 반환합니다.
InjectDll() 함수 코드 설명
먼저 OpenProcess에서 PID의 핸들을 구합니다.
핸들을 구하여 이 프로세스를 제어할 수 있습니다.
VirtualAllocEx() 함수로 그 프로세스의 메모리 공간에 버퍼를 할당합니다.
dll을 인젝션할 경로의 길이 만큼 할당합니다.
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);
할당 받은 주소가 담긴 pRemoteBuf에 WriteProcessMemory 함수로 DLL 경로 문자열 #define DEF_DLL_PATH ("c:\\Malware.dll") 를 써줍니다.
이로서 원하는 프로세서 메모리 공간에 DLL 파일의 경로가 적재되었습니다.
'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글
[Reverse Lab 스터디] Ada Cracked(0) (1) | 2016.08.10 |
---|---|
[Reverse Lab 스터디] DLL Injection (0) | 2016.08.10 |
[ReverseLab 스터디] UPX Packing (0) | 2016.07.26 |
Comment 0
[ReverseLab 스터디] UPX Packing
실행압축 (Packing)
실행되는 순간 메모리에서 압축을 해제하고 실행을 시키는 기술이기에,
어딘가에 압축 해제 루틴이 담겨있어서, 이런 루틴을 지난 후에 실행시킨다는 게 특징이다.
이런 패킹을 사용하는 목적은 아래라고 생각합니다.
1. 파일 자체의 크기를 줄이기 위함
2. 내부 코드와 리소스등을 사용자가 보지 못하게 방해하는 것.
3. 분석을 하지 않게 귀차니즘을 넘겨주기 위해
이라고 볼 수 있습니다.
UPX Packing
먼저 보기 전에 UPX 패킹이 이루어지고 난 뒤에 PE가 어떻게 변경되는지 보도록 하겠습니다.
앗, 딱 봐도 IMAGE_SECTION_HEADER, SECTION 들이 UPX0, UPX1 들로 압축된 것을 볼 수 있습니다.
또 UPX0 SECTION의 RawDataSize를 보면 0으로 되있는 것을 볼 수 있으므로,
파일 내에 존재하지 않는 것을 볼 수 있습니다.
하지만 UPX0 SECTION의 Virtual Size를 보면 알 수 있습니다.
실제 파일에서의 사이즈는 0으로 되어있는데 메모리에서의 사이즈는 큰 것을 볼 수 있습니다.
그렇다면 이유는 압축 해제 코드와 압축된 원본 코드들은 UPX1 SECTION에 존재하다는 것을 예상할 수 있습니다.
그렇다면 파일이 실행이 되면 원본코드는 UPX0 섹션에 해제시킨다는 것을 예상할 수 있겠네요 (웃음)
Analyze
PUSHAD
POPAD
PUSHAD 명령어의 반대로 스택에 넣은 CPU 레지스터 값들을 CPU 레지스터로 복귀시키는 명령어입니다.
처음 실행될 때에 PUSHAD로 CPU 레지스터 들의 값들을 스택에 집어넣는 것을 볼 수 있습니다.
스택에 들어간 것을 볼 수 있습니다.
EAX~EDI의 값들이 순서대로 스택에 들어간 것을 볼 수 있습니다.
그 후에 ESI에 UPX1 시작 주소를 넘겨주어 세팅을 합니다.
EDI에는 0x001CE000(UPX1) + 0xFFFFFFFFFFFE3000 = 0x1B1000(UPX0)
(이 뒤에서 주소가 갑자기 바뀌는데... 디버거가 한번 팅겨서 주소가 달라진 것이니 착각하지마시길 바랍니다!)
아무튼 UPX0의 주소를 스택에 넣고 이제 점프합니다.
EBX에 [UPX1] 주소에 담긴 값을 저장합니다.
그리고 ESI, EDI 레지스터에는 위의 사진과 같이 세팅이 되어있는데.
0x41e000과 0x401000은 UPX1(.data) 주소와 0x401000(.text)주소입니다.
맞는 것을 볼 수 있습니다.
[EDX]에서 값을 가져와서 AL에 쳐박고
EDI(UPX0)에 복사함을 알 수 있습니다.
FFF2만큼 UPX0에 0으로 초기화하는 부분임을 알 수 있습니다.
이 뒤의 0x40370F주소 이후에 IMAGE_SECTION_HEADER를 채우게 됩니다.
ESI가 가르키는 곳에 있는 값을 EDI에 값들을 복사하는 것을 볼 수 있습니다.
그리고 EDX의 값을 가져와서 EDI에 쑤컹쑤컹하는 것을 볼 수 있습니다.
그리고 이 부분이 압축 해제 루프입니다.
그 뒤로는 IAT 세팅입니다.
퍄퍄 이 곳에서 하나하나 이동시키는 것을 볼 수 있습니다!
그 뒤에 스택에 EAX~EBP까지 PUSH한 후 POPAD를 하는 모습입니다.
POPAD를 한 후의 CPU 레지스터의 상태입니다.
모든 압축 해제 과정이 끝나게 되면 OEP로 점프를 하게 됩니다.
넵!
실제로 패킹 안한 프로그램을 열어보면 같음을 확인할 수 있습니다.
그렇다면 하나의 꼼수를 알아내자면 UPX 패킹일 경우에는 그저 POPAD를 찾아서 그 뒤에 있는 JMP 주소가 oep라는 것을 알 수 있습니다. (웃음)
'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글
[Reverse Lab 스터디] Ada Cracked(0) (1) | 2016.08.10 |
---|---|
[Reverse Lab 스터디] DLL Injection (0) | 2016.08.10 |
[ReverseLab 스터디] UPX Packing (0) | 2016.07.26 |
복잡해보이지만 차근차근 설명해주셔서 이해가 잘되네요!^^
읽어주셔서 감사합니다 ^^