├── .gitignore ├── README.md ├── README_zh.md ├── account.py ├── block.py ├── console ├── data ├── account ├── blockchain ├── tx └── untx ├── database.py ├── lib ├── common.py └── ripemd.py ├── miner.py ├── model.py ├── node.py ├── rpc.py └── transaction.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | test.py 6 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [中文说明](https://github.com/Carlos-Zen/blockchain_python/blob/master/README_zh.md) 2 | 3 | # Blockchain-python 4 | 5 | A blockchain implementation in Python only for study. 6 | 7 | The Blockchain-python implements simple blockchain and transactions. Currently, the implementation already has mining, transaction, communication between nodes, and file persistence of blocks and transactions. 8 | The communication between nodes is via rpc based on http, rather than p2p network. Because the implementation of P2p is more complicated, it is too complicated to understand the framework of blockchain. 9 | The verification based on cryptography has not yet been realized, and the verification of blocks between nodes and the verification of transactions have not yet been realized. 10 | 11 | ## Installation 12 | 13 | 1. Make sure [Python 3.6+](https://www.python.org/downloads/) is installed. 14 | 2. Git Clone 15 | ``` 16 | $ git clone https://github.com/Carlos-Zen/blockchain.git 17 | $ cd blockchain 18 | ``` 19 | 20 | ## Usage 21 | 22 | - Create Account 23 | ``` 24 | $ python console account create 25 | ``` 26 | - Run the miner 27 | ``` 28 | $ python console miner start 3008 29 | ``` 30 | - Transaction transfer. 31 | ``` 32 | $ python console tx transfer from_address to_address amount 33 | ``` 34 | - Transaction list. 35 | ``` 36 | $ python console tx list 37 | ``` 38 | - Blockchain shows. 39 | ``` 40 | $ python console blockchain list 41 | ``` 42 | ### Node Network 43 | Copy the code resource to a new directory.While the miner before was running then: 44 | ``` 45 | $ cd {another_blockchain_directory} 46 | $ python console node add 3008 47 | $ python console node run 3009 48 | ``` 49 | When a new block mined , block and transactions will broadcast to other nodes. 50 | 51 | ## All command 52 | Use like this: 53 | 54 | ``` 55 | $ python console [module] [action] params... 56 | ``` 57 | Such as: 58 | ``` 59 | $ python console tx list 60 | ``` 61 | 62 | | Module | Action | Params | Desc | 63 | |----------|------------|------------------------------------|--------------------------------------------------| 64 | | account | create | NONEED | Create new account | 65 | | account | get | NONEED | Show all account | 66 | | account | current | NONEED | The miner reward account | 67 | | miner | start | ip:port/port | Such as 3008 or 127.0.0.1:3008 | 68 | | node | run | ip:port/port | Such as 3008 or 127.0.0.1:3008 | 69 | | node | list | NONEED | Show all node that will broadcast to | 70 | | node | add | ip:port | Add a node that will broadcast to | 71 | | tx | transfer | from_address to_address amount | Transfer coin from from_address to to_address | 72 | | tx | list | NONEED | Show all transactions | 73 | 74 | # Introduce 75 | ## About block 76 | The blockchain is a data structure that is linked sequentially from back to forward by blocks containing transaction information. SHA256 cryptographic hashing is performed on each block header to generate a hash value. A bitcoin block is as follows: 77 | 78 | ``` 79 | { 80 | "size":43560, 81 | "version":2, 82 | 83 | "previousblockhash":"00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249", 84 | "merkleroot":"5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d", 85 | "time":1388185038, 86 | "difficulty":1180923195.25802612, 87 | "nonce":4215469401, 88 | "tx":["257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77", 89 | #[...many more transactions omitted...] 90 | "05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634" 91 | ] 92 | } 93 | ``` 94 | A blockchain is a linked list structure of blocks. The essence of mining is a new block, based on existing information such as parent block hash, timestamp, transaction merkle hash, plus a nonce (number from 0) 95 | A sha256 representation string is generated after the connection. If the preceding digits start with several zeroes, the number of zeros is the difficulty of mining, and half is dynamically adjusted based on the remaining number and the generation speed of the previous block, such as: 96 | ``` 97 | 00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249 98 | ``` 99 | Successful mining, block generation 100 | 101 | ### About Blockchain-python block 102 | 103 | Blockchain-python simplified block structure, a blockchain-python block data is as follows: 104 | ``` 105 | { 106 | "index": 7, 107 | "timestamp": 1528972070, 108 | "tx": [ 109 | "b959b3d2099ca304c67087edbf05b79d1f2501b1f407df5e51a1a8c22bb3334d", 110 | "613e4af7266e01ea338d30681ef606bad26e4cdfa4ec7a6f431e22420c8291fd", 111 | "be7095a764cb241606a67c9064bc8dbc2da2370d49459bd492473ea5ce304cb3" 112 | ], 113 | "previous_block": "00003e17e04d9c9d2c2f5629de20bda58f59af36417a7e50eb77a74a028b026a", 114 | "nouce": 11063, 115 | "hash": "00006805c75d0db1685616d9ea5730f6203eda744a16fcc78ef1f3c244083ea4" 116 | } 117 | ``` 118 | The calculation of block hash is roughly the same as that of Bitcoin. Our difficulty setting is relatively low, so the hash in front of this block has only 4 zeros. 119 | This is for easier mining to understand the principle and generally can be produced in a few seconds. One block. In addition, Bitcoin's tx field represents the root node hash of the merkle tree that consists of the transaction hash. 120 | For simplicity, we put it directly into the array of transaction hash. 121 | 122 | ## About miner 123 | 124 | The sha256 used by the mining algorithm, Bitcoin's algorithm is based on the block header +Nouce (a number) as a string. Simple blockchain simplifies the header information, but the mechanism and Bitcoin are constant. 125 | The blockchain is stored locally in the file in json format. The generation of a block is related to the transaction information, so the block information is also stored when the block is stored. 126 | There will be rewards for mining, and the reward will be recorded as the first transaction in the blockchain. 127 | - Rewards for mining are rewarded by the generated block itself 128 | - The miner also gets the amount entered for all transactions in the block - the amount of money that was exported 129 | - There will be some sorting rules for the transactions to be certified, sorting according to the block age, transaction fee, transaction amount, etc. 130 | 131 | We simplified the implementation and only implemented rewards. The reward will be awarded to the current account. If the current account does not exist, please generate an account through the following command line: 132 | ``` 133 | $ python console account create 134 | ``` 135 | 136 | ## About network 137 | 138 | The blockchain network is a P2P (Peer-to-Peer, end-to-end) network. We use Python's own RPC mechanism for simplification. 139 | - Different nodes can be connected by adding node operations 140 | - Unicom's nodes will automatically spread new transaction information 141 | - The new node will synchronize all the data of other node's blockchain while ensuring the maximum chain 142 | - Digging out new blocks will notify other nodes to synchronize 143 | 144 | ## About transaction 145 | 146 | Bitcoin uses the UTXO model and does not directly exist in the concept of “balance”. The balance needs to be obtained by traversing the entire transaction history. We also implement this mechanism. 147 | A transaction is a combination of some input and output. In our transaction, we accept multiple inputs and generate multiple outputs. 148 | - The calculation of the balance is made through the unconsumed verified transaction output - the output of the consumer transaction, which is commonly known as UTXO 149 | - Transactions not placed in new block will be broadcast to all nodes waiting to be verified 150 | - After waiting for the miner to dig into a new block, the trade will be saved as transaction information in the transaction database. 151 | 152 | The correctness check of the transaction is under development. 153 | 154 | ## Contributing 155 | 156 | Contributions are welcome! Please feel free to submit a Pull Request. 157 | 158 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # Blockchain-python 2 | 3 | Python实现的简单区块链,主要用于学习使用 4 | 5 | 实现了简单的区块链和交易,已经具备了挖矿、交易、节点间通讯、以及区块和交易的文件持久化。 6 | 节点间通讯通过建立在http基础之上的rpc,而非p2p网络,因为p2p的实现比较复杂,对于了解区块链的框架来说过于复杂。 7 | 建立在密码学基础上的校验暂未实现,节点间对区块的校验,以及交易的校验目前还未能实现。 8 | 9 | ## 安装 10 | 11 | 为了简单,blockchain-python没有任何软件包的依赖,直接执行源码就可以了。 12 | 13 | 1. 安装[Python 3.6+](https://www.python.org/downloads/) . 14 | 2. 下载源码,Git Clone 15 | ``` 16 | $ git clone https://github.com/Carlos-Zen/blockchain.git 17 | $ cd blockchain 18 | ``` 19 | 20 | ## 使用指导 21 | 22 | - 创建账户 23 | ``` 24 | $ python console account create 25 | ``` 26 | - 开始挖矿 27 | ``` 28 | $ python console miner start 3008 29 | ``` 30 | - 转账交易 31 | ``` 32 | $ python console tx transfer from_address to_address amount 33 | ``` 34 | - 交易记录 35 | ``` 36 | $ python console tx list 37 | ``` 38 | - 查看所有区块 39 | ``` 40 | $ python console blockchain list 41 | ``` 42 | 43 | ### 节点网络 44 | 45 | 复制源码到一个新的目录,作为新的节点.或者复制到另一台机器上。下面代码演示本机两个节点: 46 | - 启动新节点 47 | ``` 48 | $ cd {another_blockchain_directory} 49 | $ python console node add 3008 50 | $ python console node run 3009 51 | ``` 52 | - 回到初始的源码目录下,要保证挖矿正在进行当中,然后添加新的节点: 53 | ``` 54 | $ python console node add 127.0.0.1:3009 55 | ``` 56 | 当一个新的区块块被挖掘时,新的区块和交易将广播给其他节点。 57 | 多个节点情况下,只要一个节点被添加,所有节点网络会同步。 58 | 59 | ## 命令行大全 60 | 使用如下: 61 | ``` 62 | $ python console [module] [action] params... 63 | ``` 64 | 比如: 65 | ``` 66 | $ python console tx list 67 | ``` 68 | 69 | | Module | Action | Params | Desc | 70 | |----------|------------|------------------------------------|--------------------------------------------------| 71 | | account | create | NONEED | 建立新帐户 | 72 | | account | get | NONEED | 显示所有帐户 | 73 | | account | current | NONEED | 矿工奖励账户 | 74 | | miner | start | ip:port/port | 如3008或127.0.0.1:3008 | 75 | | node | run | ip:port/port | 如3008或127.0.0.1:3008 | 76 | | node | list | NONEED | 显示将广播到的所有节点 | 77 | | node | add | ip:port | 添加一个将广播到的节点 | 78 | | tx | transfer | from_address to_address amount | coin从from_address转移到to_address | 79 | | tx | list | NONEED | 显示所有交易 | 80 | 81 | # 原理简介 82 | 83 | ## 关于区块 84 | 85 | ### 比特币区块原理简述 86 | 87 | 区块链是由包含交易信息的区块从后向前有序链接起来的数据结构,对每个区块头进行SHA256加密哈希,可生成一个哈希值。一个比特币区块如下: 88 | ``` 89 | { 90 | "size":43560, 91 | "version":2, 92 | 93 | "previousblockhash":"00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249", 94 | "merkleroot":"5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d", 95 | "time":1388185038, 96 | "difficulty":1180923195.25802612, 97 | "nonce":4215469401, 98 | "tx":["257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77", 99 | #[...many more transactions omitted...] 100 | "05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634" 101 | ] 102 | } 103 | ``` 104 | 区块链就是区块组成的链表结构。而挖矿的本质就是一个新区块,根据现有的一些信息比如父区块hash、时间戳、交易的merkle数根hash再加上一个nonce(从0开始增长的数字) 105 | 连接后生成一个sha256的表现字符串。如果前面数位是几个0开头,0的个数就是挖矿难度,一半根据剩余数量和上一个区块的生成速度动态调整,比如: 106 | ``` 107 | 00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249 108 | ``` 109 | 挖矿成功,区块生成。 110 | 111 | ### Blockchain-python区块简述 112 | 113 | Blockchain-python简化的区块结构,一个Blockchain-python的区块数据如下: 114 | ``` 115 | { 116 | "index": 7, 117 | "timestamp": 1528972070, 118 | "tx": [ 119 | "b959b3d2099ca304c67087edbf05b79d1f2501b1f407df5e51a1a8c22bb3334d", 120 | "613e4af7266e01ea338d30681ef606bad26e4cdfa4ec7a6f431e22420c8291fd", 121 | "be7095a764cb241606a67c9064bc8dbc2da2370d49459bd492473ea5ce304cb3" 122 | ], 123 | "previous_block": "00003e17e04d9c9d2c2f5629de20bda58f59af36417a7e50eb77a74a028b026a", 124 | "nouce": 11063, 125 | "hash": "00006805c75d0db1685616d9ea5730f6203eda744a16fcc78ef1f3c244083ea4" 126 | } 127 | ``` 128 | 区块hash的计算与比特币大致相同,我们的难度设置的比较低,所以这个区块的hash前面只有4个0,这是为了更方便的挖矿以了解原理,一般几秒钟可以产出一个区块。另,比特币的tx字段,代表的是交易hash组成的merkle树的根节点hash,我们为了简单,就直接放入了交易hash组成的数组。 129 | 130 | ## 关于挖矿 131 | 132 | 挖矿算法使用的sha256,比特币的算法是根据区块头信息+Nouce(一个数字)作为字符串。简单区块链简化的头部信息,但是机制和比特币是一直的。 133 | 区块链在本地以json格式化存储在文件中。一个区块的生成与交易信息是有关联的,所以区块存储的同时,交易信息也会存储下来。 134 | 挖矿会有奖励,奖励会作为区块链的第一笔交易记录下来. 135 | - 挖矿的奖励一个是来源于生成区块本身的奖励 136 | - 矿工还会获取纳入区块中的所有交易 输入的金额-输出的金额 的金额 137 | - 待认证的交易会有一些排序规则,根据区块链龄,交易费,交易金额等来做排序 138 | 139 | 我们简化了实现,只实现奖励的机制。奖励会奖励给当前账户,如果当前账户不存在,请通过下面的命令行生成一个账户: 140 | ``` 141 | $ python console account create 142 | ``` 143 | 144 | ## 关于节点网络 145 | 146 | 区块链网络是一个 P2P(Peer-to-Peer,端到端)的网络。我们为了简单化,使用了python自带的RPC机制。 147 | - 通过添加节点操作,可以联通不同节点 148 | - 联通的节点会自动传播新的交易信息 149 | - 新节点会同步其他节点的区块链的所有数据,同时保证最大链条 150 | - 挖出新的区块会通知其他节点进行同步 151 | 152 | ## 关于交易 153 | 154 | 比特币采用的是 UTXO 模型,并不直接存在“余额”这个概念,余额需要通过遍历整个交易历史得来。我们也实现这个机制。 155 | 一笔交易由一些输入(input)和输出(output)组合而来,在我们的交易中,也会接受多个输入然后产生多个输出。 156 | - 余额的计算通过未消费的已验证交易输出-已消费交易输出得到,也就是通常所说的UTXO 157 | - 未被放入新区块的交易,将会被广播到所有节点,等待被验证 158 | - 交易会等待矿工挖到新的区块之后,被当作区块的附属信息存入交易数据库中 159 | 160 | 交易的正确性校验,正在开发中。 161 | 162 | # 贡献 163 | 164 | 非常欢迎提交代码。 165 | 166 | -------------------------------------------------------------------------------- /account.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import hashlib 3 | import lib.common 4 | from model import Model 5 | from lib.common import pubkey_to_address 6 | from database import AccountDB 7 | 8 | def new_account(): 9 | private_key = lib.common.random_key() 10 | public_key = lib.common.hash160(private_key.encode()) 11 | address = pubkey_to_address(public_key.encode()) 12 | adb = AccountDB() 13 | adb.insert({'pubkey': public_key, 'address':address}) 14 | return private_key, public_key, address 15 | 16 | def get_account(): 17 | adb = AccountDB() 18 | return adb.find_one() -------------------------------------------------------------------------------- /block.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import hashlib 3 | import time 4 | from model import Model 5 | from rpc import BroadCast 6 | 7 | class Block(Model): 8 | 9 | def __init__(self, index, timestamp, tx, previous_hash): 10 | self.index = index 11 | self.timestamp = timestamp 12 | self.tx = tx 13 | self.previous_block = previous_hash 14 | 15 | def header_hash(self): 16 | """ 17 | Refer to bitcoin block header hash 18 | """ 19 | return hashlib.sha256((str(self.index) + str(self.timestamp) + str(self.tx) + str(self.previous_block)).encode('utf-8')).hexdigest() 20 | 21 | def pow(self): 22 | """ 23 | Proof of work. Add nouce to block. 24 | """ 25 | nouce = 0 26 | while self.valid(nouce) is False: 27 | nouce += 1 28 | self.nouce = nouce 29 | return nouce 30 | 31 | def make(self, nouce): 32 | """ 33 | Block hash generate. Add hash to block. 34 | """ 35 | self.hash = self.ghash(nouce) 36 | 37 | def ghash(self, nouce): 38 | """ 39 | Block hash generate. 40 | """ 41 | header_hash = self.header_hash() 42 | token = ''.join((header_hash, str(nouce))).encode('utf-8') 43 | return hashlib.sha256(token).hexdigest() 44 | 45 | def valid(self, nouce): 46 | """ 47 | Validates the Proof 48 | """ 49 | return self.ghash(nouce)[:4] == "0000" 50 | 51 | def to_dict(self): 52 | return self.__dict__ 53 | 54 | @classmethod 55 | def from_dict(cls, bdict): 56 | b = cls(bdict['index'], bdict['timestamp'], bdict['tx'], bdict['previous_block']) 57 | b.hash = bdict['hash'] 58 | b.nouce = bdict['nouce'] 59 | return b 60 | 61 | @staticmethod 62 | def spread(block): 63 | BroadCast().new_block(block) -------------------------------------------------------------------------------- /console: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | from account import * 3 | from rpc import get_clients, BroadCast, start_server 4 | from transaction import * 5 | from database import * 6 | from block import * 7 | import sys 8 | from miner import mine 9 | import multiprocessing 10 | import rpc 11 | from node import * 12 | from lib.common import cprint 13 | import inspect 14 | 15 | MODULES = ['account','tx','blockchain','miner','node'] 16 | 17 | def upper_first(string): 18 | return string[0].upper()+string[1:] 19 | 20 | class Node(): 21 | 22 | def add(self, args): 23 | add_node(args[0]) 24 | rpc.BroadCast().add_node(args[0]) 25 | cprint('Allnode',get_nodes()) 26 | 27 | def run(self, args): 28 | start_node(args[0]) 29 | 30 | def list(self, args): 31 | for t in NodeDB().find_all(): 32 | cprint('Node',t) 33 | 34 | class Miner(): 35 | def start(self, args): 36 | if get_account() == None: 37 | cprint('ERROR','Please create account before start miner.') 38 | exit() 39 | start_node(args[0]) 40 | while True : 41 | cprint('Miner new block',mine().to_dict()) 42 | 43 | class Account(): 44 | def create(self, args): 45 | ac = new_account() 46 | cprint('Private Key',ac[0]) 47 | cprint('Public Key',ac[1]) 48 | cprint('Address',ac[2]) 49 | 50 | def get(self, args): 51 | cprint('All Account',AccountDB().read()) 52 | 53 | def current(self, args): 54 | cprint('Current Account', get_account()) 55 | 56 | class Blockchain(): 57 | 58 | def list(self, args): 59 | for t in BlockChainDB().find_all(): 60 | cprint('Blockchain',str(t)) 61 | 62 | class Tx(): 63 | 64 | def list(self, args): 65 | for t in TransactionDB().find_all(): 66 | cprint('Transaction',t) 67 | 68 | def transfer(self, args): 69 | tx = Transaction.transfer(args[0], args[1], args[2]) 70 | print(Transaction.unblock_spread(tx)) 71 | cprint('Transaction tranfer',tx) 72 | 73 | def usage(class_name): 74 | module = globals()[upper_first(class_name)] 75 | print(' ' + class_name + '\r') 76 | print(' [action]\r') 77 | for k,v in module.__dict__.items(): 78 | if callable(v): 79 | print(' %s' % (k,)) 80 | print('\r') 81 | 82 | def help(): 83 | print("Usage: python console.py [module] [action]\r") 84 | print('[module]\n') 85 | for m in MODULES: 86 | usage(m) 87 | 88 | if __name__ == '__main__': 89 | if len(sys.argv) == 1: 90 | help() 91 | exit() 92 | module = sys.argv[1] 93 | if module == 'help': 94 | help() 95 | exit() 96 | if module not in MODULES: 97 | cprint('Error', 'First arg shoud in %s' % (str(MODULES,))) 98 | exit() 99 | mob = globals()[upper_first(module)]() 100 | method = sys.argv[2] 101 | # try: 102 | getattr(mob, method)(sys.argv[3:]) 103 | # except Exception as e: 104 | # cprint('ERROR','/(ㄒoㄒ)/~~, Maybe command params get wrong, please check and try again.') -------------------------------------------------------------------------------- /data/account: -------------------------------------------------------------------------------- 1 | [{"pubkey": "9c18ba33bf94cc8822fa97c3d2c9425da043ca56", "address": "1L8Q3xJyk5MnWoV1Qz6sfT57yGB6bA7DgR"}, {"pubkey": "1a799b5914acd0f287ce12a85b8bdee95516397c", "address": "19E6vuk3dk53uzonxRfFzqAvoKYMmGku66"}, {"pubkey": "523f6936c9541672fbaf43b17cee477abd10a618", "address": "1QF3G4FBG45f2zPzAdAWu3ddZGiQvZPeKq"}, {"pubkey": "2beb7ff7f0a87082a54645df38a4d482dccafb44", "address": "1AdoyhS4Wbq26PzzevxaYnkaQU5NgyJWFr"}, {"pubkey": "53ded2457c3f45a114f0169775553334aa8ba032", "address": "1N16GdiuJ6u2DHeKx9DeXuCKFXXRJpxbbT"}, {"pubkey": "b98dcedbbaf5732288cc1e5f83f1048a91c76019", "address": "1JcxiHznRbV6JRsEPMGUbPFPH9bGWHoYuW"}, {"pubkey": "695aa3dc9e878804a6c28ac465758d13b48f4e30", "address": "12EWTRDMidsr3DPsReKiuX2p514RE9z9uP"}] -------------------------------------------------------------------------------- /data/untx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carlos-Zen/blockchain-python/e28a4886cce03a790fe0a57fcd9896fa7b4b239f/data/untx -------------------------------------------------------------------------------- /database.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import json 3 | import os 4 | 5 | BASEDBPATH = 'data' 6 | BLOCKFILE = 'blockchain' 7 | TXFILE = 'tx' 8 | UNTXFILE = 'untx' 9 | ACCOUNTFILE = 'account' 10 | NODEFILE = 'node' 11 | 12 | class BaseDB(): 13 | 14 | filepath = '' 15 | 16 | def __init__(self): 17 | self.set_path() 18 | self.filepath = '/'.join((BASEDBPATH, self.filepath)) 19 | 20 | def set_path(self): 21 | pass 22 | 23 | def find_all(self): 24 | return self.read() 25 | 26 | def insert(self, item): 27 | self.write(item) 28 | 29 | def read(self): 30 | raw = '' 31 | if not os.path.exists(self.filepath): 32 | return [] 33 | with open(self.filepath,'r+') as f: 34 | raw = f.readline() 35 | if len(raw) > 0: 36 | data = json.loads(raw) 37 | else: 38 | data = [] 39 | return data 40 | 41 | def write(self, item): 42 | data = self.read() 43 | if isinstance(item,list): 44 | data = data + item 45 | else: 46 | data.append(item) 47 | with open(self.filepath,'w+') as f: 48 | f.write(json.dumps(data)) 49 | return True 50 | 51 | def clear(self): 52 | with open(self.filepath,'w+') as f: 53 | f.write('') 54 | 55 | def hash_insert(self, item): 56 | exists = False 57 | for i in self.find_all(): 58 | if item['hash'] == i['hash']: 59 | exists = True 60 | break 61 | if not exists: 62 | self.write(item) 63 | 64 | class NodeDB(BaseDB): 65 | 66 | def set_path(self): 67 | self.filepath = NODEFILE 68 | 69 | 70 | class AccountDB(BaseDB): 71 | def set_path(self): 72 | self.filepath = ACCOUNTFILE 73 | 74 | def find_one(self): 75 | ac = self.read() 76 | return ac[0] 77 | 78 | 79 | class BlockChainDB(BaseDB): 80 | 81 | def set_path(self): 82 | self.filepath = BLOCKFILE 83 | 84 | def last(self): 85 | bc = self.read() 86 | if len(bc) > 0: 87 | return bc[-1] 88 | else: 89 | return [] 90 | 91 | def find(self, hash): 92 | one = {} 93 | for item in self.find_all(): 94 | if item['hash'] == hash: 95 | one = item 96 | break 97 | return one 98 | 99 | def insert(self, item): 100 | self.hash_insert(item) 101 | 102 | class TransactionDB(BaseDB): 103 | """ 104 | Transactions that save with blockchain. 105 | """ 106 | def set_path(self): 107 | self.filepath = TXFILE 108 | 109 | def find(self, hash): 110 | one = {} 111 | for item in self.find_all(): 112 | if item['hash'] == hash: 113 | one = item 114 | break 115 | return one 116 | 117 | def insert(self, txs): 118 | if not isinstance(txs,list): 119 | txs = [txs] 120 | for tx in txs: 121 | self.hash_insert(tx) 122 | 123 | class UnTransactionDB(TransactionDB): 124 | """ 125 | Transactions that doesn't store in blockchain. 126 | """ 127 | def set_path(self): 128 | self.filepath = UNTXFILE 129 | 130 | def all_hashes(self): 131 | hashes = [] 132 | for item in self.find_all(): 133 | hashes.append(item['hash']) 134 | return hashes -------------------------------------------------------------------------------- /lib/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import hashlib 3 | import random 4 | import time 5 | from lib.ripemd import RIPEMD160 6 | import binascii 7 | import json 8 | 9 | P = 2**256 - 2**32 - 977 10 | N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 11 | A = 0 12 | B = 7 13 | Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 14 | Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 15 | G = (Gx, Gy) 16 | 17 | code_strings = { 18 | 2: '01', 19 | 10: '0123456789', 20 | 16: '0123456789abcdef', 21 | 32: 'abcdefghijklmnopqrstuvwxyz234567', 22 | 58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz', 23 | 256: ''.join([chr(x) for x in range(256)]) 24 | } 25 | 26 | def cprint(tag,content): 27 | print("[ %s ]" % (tag,)) 28 | if not isinstance(content,str): 29 | content = json.dumps(content, indent=4) 30 | print(' ' * 2 + content) 31 | 32 | def random_string(x): 33 | return str(os.urandom(x)) 34 | 35 | def random_key(): 36 | # Gotta be secure after that java.SecureRandom fiasco... 37 | entropy = random_string(32) \ 38 | + str(random.randrange(2**256)) \ 39 | + str(int(time.time() * 1000000)) 40 | return hashlib.sha256(entropy.encode()).hexdigest() 41 | 42 | def unlock_sig(prikey, timestamp): 43 | return hashlib.sha256((prikey+timestamp).encode()).hexdigest() 44 | 45 | def lock_sig(unlocksig, pubkey): 46 | return hashlib.sha256((unlocksig+pubkey).encode()).hexdigest() 47 | 48 | def bin_hash160(string): 49 | intermed = hashlib.sha256(string).digest() 50 | digest = '' 51 | try: 52 | digest = hashlib.new('ripemd160', intermed).digest() 53 | except: 54 | digest = RIPEMD160(intermed).digest() 55 | return digest 56 | 57 | def safe_hexlify(a): 58 | return str(binascii.hexlify(a), 'utf-8') 59 | 60 | def hash160(string): 61 | return safe_hexlify(bin_hash160(string)) 62 | 63 | def pubkey_to_address(pubkey, magicbyte=0): 64 | if isinstance(pubkey, (list, tuple)): 65 | pubkey = encode_pubkey(pubkey, 'bin') 66 | if len(pubkey) in [66, 130]: 67 | return bin_to_b58check( 68 | bin_hash160(binascii.unhexlify(pubkey)), magicbyte) 69 | return bin_to_b58check(bin_hash160(pubkey), magicbyte) 70 | 71 | def encode_pubkey(pub, formt): 72 | if not isinstance(pub, (tuple, list)): 73 | pub = decode_pubkey(pub) 74 | if formt == 'decimal': return pub 75 | elif formt == 'bin': return b'\x04' + encode(pub[0], 256, 32) + encode(pub[1], 256, 32) 76 | elif formt == 'bin_compressed': 77 | return from_int_to_byte(2+(pub[1] % 2)) + encode(pub[0], 256, 32) 78 | elif formt == 'hex': return '04' + encode(pub[0], 16, 64) + encode(pub[1], 16, 64) 79 | elif formt == 'hex_compressed': 80 | return '0'+str(2+(pub[1] % 2)) + encode(pub[0], 16, 64) 81 | elif formt == 'bin_electrum': return encode(pub[0], 256, 32) + encode(pub[1], 256, 32) 82 | elif formt == 'hex_electrum': return encode(pub[0], 16, 64) + encode(pub[1], 16, 64) 83 | else: raise Exception("Invalid format!") 84 | 85 | 86 | def decode_pubkey(pub, formt=None): 87 | if not formt: formt = get_pubkey_format(pub) 88 | if formt == 'decimal': return pub 89 | elif formt == 'bin': return (decode(pub[1:33], 256), decode(pub[33:65], 256)) 90 | elif formt == 'bin_compressed': 91 | x = decode(pub[1:33], 256) 92 | beta = pow(int(x*x*x+A*x+B), int((P+1)//4), int(P)) 93 | y = (P-beta) if ((beta + from_byte_to_int(pub[0])) % 2) else beta 94 | return (x, y) 95 | elif formt == 'hex': return (decode(pub[2:66], 16), decode(pub[66:130], 16)) 96 | elif formt == 'hex_compressed': 97 | return decode_pubkey(safe_from_hex(pub), 'bin_compressed') 98 | elif formt == 'bin_electrum': 99 | return (decode(pub[:32], 256), decode(pub[32:64], 256)) 100 | elif formt == 'hex_electrum': 101 | return (decode(pub[:64], 16), decode(pub[64:128], 16)) 102 | else: raise Exception("Invalid format!") 103 | 104 | def get_pubkey_format(pub): 105 | two = 2 106 | three = 3 107 | four = 4 108 | 109 | if isinstance(pub, (tuple, list)): return 'decimal' 110 | elif len(pub) == 65 and pub[0] == four: return 'bin' 111 | elif len(pub) == 130 and pub[0:2] == '04': return 'hex' 112 | elif len(pub) == 33 and pub[0] in [two, three]: return 'bin_compressed' 113 | elif len(pub) == 66 and pub[0:2] in ['02', '03']: return 'hex_compressed' 114 | elif len(pub) == 64: return 'bin_electrum' 115 | elif len(pub) == 128: return 'hex_electrum' 116 | else: raise Exception("Pubkey not in recognized format") 117 | 118 | 119 | def get_code_string(base): 120 | if base in code_strings: 121 | return code_strings[base] 122 | else: 123 | raise ValueError("Invalid base!") 124 | 125 | def changebase(string, frm, to, minlen=0): 126 | if frm == to: 127 | return lpad(string, get_code_string(frm)[0], minlen) 128 | return encode(decode(string, frm), to, minlen) 129 | 130 | 131 | def bin_dbl_sha256(s): 132 | bytes_to_hash = from_string_to_bytes(s) 133 | return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest() 134 | 135 | def lpad(msg, symbol, length): 136 | if len(msg) >= length: 137 | return msg 138 | return symbol * (length - len(msg)) + msg 139 | 140 | def bin_to_b58check(inp, magicbyte=0): 141 | inp_fmtd = from_int_to_byte(int(magicbyte))+inp 142 | 143 | leadingzbytes = 0 144 | for x in inp_fmtd: 145 | if x != 0: 146 | break 147 | leadingzbytes += 1 148 | 149 | checksum = bin_dbl_sha256(inp_fmtd)[:4] 150 | return '1' * leadingzbytes + changebase(inp_fmtd+checksum, 256, 58) 151 | 152 | def bytes_to_hex_string(b): 153 | if isinstance(b, str): 154 | return b 155 | 156 | return ''.join('{:02x}'.format(y) for y in b) 157 | 158 | def safe_from_hex(s): 159 | return bytes.fromhex(s) 160 | 161 | def from_int_representation_to_bytes(a): 162 | return bytes(str(a), 'utf-8') 163 | 164 | def from_int_to_byte(a): 165 | return bytes([a]) 166 | 167 | def from_byte_to_int(a): 168 | return a 169 | 170 | def from_string_to_bytes(a): 171 | return a if isinstance(a, bytes) else bytes(a, 'utf-8') 172 | 173 | def encode(val, base, minlen=0): 174 | base, minlen = int(base), int(minlen) 175 | code_string = get_code_string(base) 176 | result_bytes = bytes() 177 | while val > 0: 178 | curcode = code_string[val % base] 179 | result_bytes = bytes([ord(curcode)]) + result_bytes 180 | val //= base 181 | 182 | pad_size = minlen - len(result_bytes) 183 | 184 | padding_element = b'\x00' if base == 256 else b'1' \ 185 | if base == 58 else b'0' 186 | if (pad_size > 0): 187 | result_bytes = padding_element*pad_size + result_bytes 188 | 189 | result_string = ''.join([chr(y) for y in result_bytes]) 190 | result = result_bytes if base == 256 else result_string 191 | 192 | return result 193 | 194 | def decode(string, base): 195 | if base == 256 and isinstance(string, str): 196 | string = bytes(bytearray.fromhex(string)) 197 | base = int(base) 198 | code_string = get_code_string(base) 199 | result = 0 200 | if base == 256: 201 | def extract(d, cs): 202 | return d 203 | else: 204 | def extract(d, cs): 205 | return cs.find(d if isinstance(d, str) else chr(d)) 206 | 207 | if base == 16: 208 | string = string.lower() 209 | while len(string) > 0: 210 | result *= base 211 | result += extract(string[0], code_string) 212 | string = string[1:] 213 | return result -------------------------------------------------------------------------------- /lib/ripemd.py: -------------------------------------------------------------------------------- 1 | ## ripemd.py - pure Python implementation of the RIPEMD-160 algorithm. 2 | ## Bjorn Edstrom 16 december 2007. 3 | ## 4 | ## Copyrights 5 | ## ========== 6 | ## 7 | ## This code is a derived from an implementation by Markus Friedl which is 8 | ## subject to the following license. This Python implementation is not 9 | ## subject to any other license. 10 | ## 11 | ##/* 12 | ## * Copyright (c) 2001 Markus Friedl. All rights reserved. 13 | ## * 14 | ## * Redistribution and use in source and binary forms, with or without 15 | ## * modification, are permitted provided that the following conditions 16 | ## * are met: 17 | ## * 1. Redistributions of source code must retain the above copyright 18 | ## * notice, this list of conditions and the following disclaimer. 19 | ## * 2. Redistributions in binary form must reproduce the above copyright 20 | ## * notice, this list of conditions and the following disclaimer in the 21 | ## * documentation and/or other materials provided with the distribution. 22 | ## * 23 | ## * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 | ## * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 | ## * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | ## * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 | ## * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 | ## * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | ## * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | ## * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | ## * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 | ## * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | ## */ 34 | ##/* 35 | ## * Preneel, Bosselaers, Dobbertin, "The Cryptographic Hash Function RIPEMD-160", 36 | ## * RSA Laboratories, CryptoBytes, Volume 3, Number 2, Autumn 1997, 37 | ## * ftp://ftp.rsasecurity.com/pub/cryptobytes/crypto3n2.pdf 38 | ## */ 39 | 40 | try: 41 | import psyco 42 | psyco.full() 43 | except ImportError: 44 | pass 45 | 46 | import sys 47 | 48 | is_python2 = sys.version_info.major == 2 49 | #block_size = 1 50 | digest_size = 20 51 | digestsize = 20 52 | 53 | try: 54 | range = xrange 55 | except NameError: 56 | pass 57 | 58 | class RIPEMD160: 59 | """Return a new RIPEMD160 object. An optional string argument 60 | may be provided; if present, this string will be automatically 61 | hashed.""" 62 | 63 | def __init__(self, arg=None): 64 | self.ctx = RMDContext() 65 | if arg: 66 | self.update(arg) 67 | self.dig = None 68 | 69 | def update(self, arg): 70 | """update(arg)""" 71 | RMD160Update(self.ctx, arg, len(arg)) 72 | self.dig = None 73 | 74 | def digest(self): 75 | """digest()""" 76 | if self.dig: 77 | return self.dig 78 | ctx = self.ctx.copy() 79 | self.dig = RMD160Final(self.ctx) 80 | self.ctx = ctx 81 | return self.dig 82 | 83 | def hexdigest(self): 84 | """hexdigest()""" 85 | dig = self.digest() 86 | hex_digest = '' 87 | for d in dig: 88 | if (is_python2): 89 | hex_digest += '%02x' % ord(d) 90 | else: 91 | hex_digest += '%02x' % d 92 | return hex_digest 93 | 94 | def copy(self): 95 | """copy()""" 96 | import copy 97 | return copy.deepcopy(self) 98 | 99 | 100 | 101 | def new(arg=None): 102 | """Return a new RIPEMD160 object. An optional string argument 103 | may be provided; if present, this string will be automatically 104 | hashed.""" 105 | return RIPEMD160(arg) 106 | 107 | 108 | 109 | # 110 | # Private. 111 | # 112 | 113 | class RMDContext: 114 | def __init__(self): 115 | self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 116 | 0x10325476, 0xC3D2E1F0] # uint32 117 | self.count = 0 # uint64 118 | self.buffer = [0]*64 # uchar 119 | def copy(self): 120 | ctx = RMDContext() 121 | ctx.state = self.state[:] 122 | ctx.count = self.count 123 | ctx.buffer = self.buffer[:] 124 | return ctx 125 | 126 | K0 = 0x00000000 127 | K1 = 0x5A827999 128 | K2 = 0x6ED9EBA1 129 | K3 = 0x8F1BBCDC 130 | K4 = 0xA953FD4E 131 | 132 | KK0 = 0x50A28BE6 133 | KK1 = 0x5C4DD124 134 | KK2 = 0x6D703EF3 135 | KK3 = 0x7A6D76E9 136 | KK4 = 0x00000000 137 | 138 | def ROL(n, x): 139 | return ((x << n) & 0xffffffff) | (x >> (32 - n)) 140 | 141 | def F0(x, y, z): 142 | return x ^ y ^ z 143 | 144 | def F1(x, y, z): 145 | return (x & y) | (((~x) % 0x100000000) & z) 146 | 147 | def F2(x, y, z): 148 | return (x | ((~y) % 0x100000000)) ^ z 149 | 150 | def F3(x, y, z): 151 | return (x & z) | (((~z) % 0x100000000) & y) 152 | 153 | def F4(x, y, z): 154 | return x ^ (y | ((~z) % 0x100000000)) 155 | 156 | def R(a, b, c, d, e, Fj, Kj, sj, rj, X): 157 | a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e 158 | c = ROL(10, c) 159 | return a % 0x100000000, c 160 | 161 | PADDING = [0x80] + [0]*63 162 | 163 | import sys 164 | import struct 165 | 166 | def RMD160Transform(state, block): #uint32 state[5], uchar block[64] 167 | x = [0]*16 168 | if sys.byteorder == 'little': 169 | if is_python2: 170 | x = struct.unpack('<16L', ''.join([chr(x) for x in block[0:64]])) 171 | else: 172 | x = struct.unpack('<16L', bytes(block[0:64])) 173 | else: 174 | raise "Error!!" 175 | a = state[0] 176 | b = state[1] 177 | c = state[2] 178 | d = state[3] 179 | e = state[4] 180 | 181 | #/* Round 1 */ 182 | a, c = R(a, b, c, d, e, F0, K0, 11, 0, x); 183 | e, b = R(e, a, b, c, d, F0, K0, 14, 1, x); 184 | d, a = R(d, e, a, b, c, F0, K0, 15, 2, x); 185 | c, e = R(c, d, e, a, b, F0, K0, 12, 3, x); 186 | b, d = R(b, c, d, e, a, F0, K0, 5, 4, x); 187 | a, c = R(a, b, c, d, e, F0, K0, 8, 5, x); 188 | e, b = R(e, a, b, c, d, F0, K0, 7, 6, x); 189 | d, a = R(d, e, a, b, c, F0, K0, 9, 7, x); 190 | c, e = R(c, d, e, a, b, F0, K0, 11, 8, x); 191 | b, d = R(b, c, d, e, a, F0, K0, 13, 9, x); 192 | a, c = R(a, b, c, d, e, F0, K0, 14, 10, x); 193 | e, b = R(e, a, b, c, d, F0, K0, 15, 11, x); 194 | d, a = R(d, e, a, b, c, F0, K0, 6, 12, x); 195 | c, e = R(c, d, e, a, b, F0, K0, 7, 13, x); 196 | b, d = R(b, c, d, e, a, F0, K0, 9, 14, x); 197 | a, c = R(a, b, c, d, e, F0, K0, 8, 15, x); #/* #15 */ 198 | #/* Round 2 */ 199 | e, b = R(e, a, b, c, d, F1, K1, 7, 7, x); 200 | d, a = R(d, e, a, b, c, F1, K1, 6, 4, x); 201 | c, e = R(c, d, e, a, b, F1, K1, 8, 13, x); 202 | b, d = R(b, c, d, e, a, F1, K1, 13, 1, x); 203 | a, c = R(a, b, c, d, e, F1, K1, 11, 10, x); 204 | e, b = R(e, a, b, c, d, F1, K1, 9, 6, x); 205 | d, a = R(d, e, a, b, c, F1, K1, 7, 15, x); 206 | c, e = R(c, d, e, a, b, F1, K1, 15, 3, x); 207 | b, d = R(b, c, d, e, a, F1, K1, 7, 12, x); 208 | a, c = R(a, b, c, d, e, F1, K1, 12, 0, x); 209 | e, b = R(e, a, b, c, d, F1, K1, 15, 9, x); 210 | d, a = R(d, e, a, b, c, F1, K1, 9, 5, x); 211 | c, e = R(c, d, e, a, b, F1, K1, 11, 2, x); 212 | b, d = R(b, c, d, e, a, F1, K1, 7, 14, x); 213 | a, c = R(a, b, c, d, e, F1, K1, 13, 11, x); 214 | e, b = R(e, a, b, c, d, F1, K1, 12, 8, x); #/* #31 */ 215 | #/* Round 3 */ 216 | d, a = R(d, e, a, b, c, F2, K2, 11, 3, x); 217 | c, e = R(c, d, e, a, b, F2, K2, 13, 10, x); 218 | b, d = R(b, c, d, e, a, F2, K2, 6, 14, x); 219 | a, c = R(a, b, c, d, e, F2, K2, 7, 4, x); 220 | e, b = R(e, a, b, c, d, F2, K2, 14, 9, x); 221 | d, a = R(d, e, a, b, c, F2, K2, 9, 15, x); 222 | c, e = R(c, d, e, a, b, F2, K2, 13, 8, x); 223 | b, d = R(b, c, d, e, a, F2, K2, 15, 1, x); 224 | a, c = R(a, b, c, d, e, F2, K2, 14, 2, x); 225 | e, b = R(e, a, b, c, d, F2, K2, 8, 7, x); 226 | d, a = R(d, e, a, b, c, F2, K2, 13, 0, x); 227 | c, e = R(c, d, e, a, b, F2, K2, 6, 6, x); 228 | b, d = R(b, c, d, e, a, F2, K2, 5, 13, x); 229 | a, c = R(a, b, c, d, e, F2, K2, 12, 11, x); 230 | e, b = R(e, a, b, c, d, F2, K2, 7, 5, x); 231 | d, a = R(d, e, a, b, c, F2, K2, 5, 12, x); #/* #47 */ 232 | #/* Round 4 */ 233 | c, e = R(c, d, e, a, b, F3, K3, 11, 1, x); 234 | b, d = R(b, c, d, e, a, F3, K3, 12, 9, x); 235 | a, c = R(a, b, c, d, e, F3, K3, 14, 11, x); 236 | e, b = R(e, a, b, c, d, F3, K3, 15, 10, x); 237 | d, a = R(d, e, a, b, c, F3, K3, 14, 0, x); 238 | c, e = R(c, d, e, a, b, F3, K3, 15, 8, x); 239 | b, d = R(b, c, d, e, a, F3, K3, 9, 12, x); 240 | a, c = R(a, b, c, d, e, F3, K3, 8, 4, x); 241 | e, b = R(e, a, b, c, d, F3, K3, 9, 13, x); 242 | d, a = R(d, e, a, b, c, F3, K3, 14, 3, x); 243 | c, e = R(c, d, e, a, b, F3, K3, 5, 7, x); 244 | b, d = R(b, c, d, e, a, F3, K3, 6, 15, x); 245 | a, c = R(a, b, c, d, e, F3, K3, 8, 14, x); 246 | e, b = R(e, a, b, c, d, F3, K3, 6, 5, x); 247 | d, a = R(d, e, a, b, c, F3, K3, 5, 6, x); 248 | c, e = R(c, d, e, a, b, F3, K3, 12, 2, x); #/* #63 */ 249 | #/* Round 5 */ 250 | b, d = R(b, c, d, e, a, F4, K4, 9, 4, x); 251 | a, c = R(a, b, c, d, e, F4, K4, 15, 0, x); 252 | e, b = R(e, a, b, c, d, F4, K4, 5, 5, x); 253 | d, a = R(d, e, a, b, c, F4, K4, 11, 9, x); 254 | c, e = R(c, d, e, a, b, F4, K4, 6, 7, x); 255 | b, d = R(b, c, d, e, a, F4, K4, 8, 12, x); 256 | a, c = R(a, b, c, d, e, F4, K4, 13, 2, x); 257 | e, b = R(e, a, b, c, d, F4, K4, 12, 10, x); 258 | d, a = R(d, e, a, b, c, F4, K4, 5, 14, x); 259 | c, e = R(c, d, e, a, b, F4, K4, 12, 1, x); 260 | b, d = R(b, c, d, e, a, F4, K4, 13, 3, x); 261 | a, c = R(a, b, c, d, e, F4, K4, 14, 8, x); 262 | e, b = R(e, a, b, c, d, F4, K4, 11, 11, x); 263 | d, a = R(d, e, a, b, c, F4, K4, 8, 6, x); 264 | c, e = R(c, d, e, a, b, F4, K4, 5, 15, x); 265 | b, d = R(b, c, d, e, a, F4, K4, 6, 13, x); #/* #79 */ 266 | 267 | aa = a; 268 | bb = b; 269 | cc = c; 270 | dd = d; 271 | ee = e; 272 | 273 | a = state[0] 274 | b = state[1] 275 | c = state[2] 276 | d = state[3] 277 | e = state[4] 278 | 279 | #/* Parallel round 1 */ 280 | a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x) 281 | e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x) 282 | d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x) 283 | c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x) 284 | b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x) 285 | a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x) 286 | e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x) 287 | d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x) 288 | c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x) 289 | b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x) 290 | a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x) 291 | e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x) 292 | d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x) 293 | c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x) 294 | b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x) 295 | a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) #/* #15 */ 296 | #/* Parallel round 2 */ 297 | e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x) 298 | d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x) 299 | c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x) 300 | b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x) 301 | a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x) 302 | e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x) 303 | d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x) 304 | c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x) 305 | b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x) 306 | a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x) 307 | e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x) 308 | d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x) 309 | c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x) 310 | b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x) 311 | a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x) 312 | e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) #/* #31 */ 313 | #/* Parallel round 3 */ 314 | d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x) 315 | c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x) 316 | b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x) 317 | a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x) 318 | e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x) 319 | d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x) 320 | c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x) 321 | b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x) 322 | a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x) 323 | e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x) 324 | d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x) 325 | c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x) 326 | b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x) 327 | a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x) 328 | e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x) 329 | d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) #/* #47 */ 330 | #/* Parallel round 4 */ 331 | c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x) 332 | b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x) 333 | a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x) 334 | e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x) 335 | d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x) 336 | c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x) 337 | b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x) 338 | a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x) 339 | e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x) 340 | d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x) 341 | c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x) 342 | b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x) 343 | a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x) 344 | e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x) 345 | d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x) 346 | c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) #/* #63 */ 347 | #/* Parallel round 5 */ 348 | b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x) 349 | a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x) 350 | e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x) 351 | d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x) 352 | c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x) 353 | b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x) 354 | a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x) 355 | e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x) 356 | d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x) 357 | c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x) 358 | b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x) 359 | a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x) 360 | e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x) 361 | d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x) 362 | c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x) 363 | b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) #/* #79 */ 364 | 365 | t = (state[1] + cc + d) % 0x100000000; 366 | state[1] = (state[2] + dd + e) % 0x100000000; 367 | state[2] = (state[3] + ee + a) % 0x100000000; 368 | state[3] = (state[4] + aa + b) % 0x100000000; 369 | state[4] = (state[0] + bb + c) % 0x100000000; 370 | state[0] = t % 0x100000000; 371 | 372 | pass 373 | 374 | 375 | def RMD160Update(ctx, inp, inplen): 376 | if type(inp) == str: 377 | inp = [ord(i)&0xff for i in inp] 378 | 379 | have = int((ctx.count // 8) % 64) 380 | inplen = int(inplen) 381 | need = 64 - have 382 | ctx.count += 8 * inplen 383 | off = 0 384 | if inplen >= need: 385 | if have: 386 | for i in range(need): 387 | ctx.buffer[have+i] = inp[i] 388 | RMD160Transform(ctx.state, ctx.buffer) 389 | off = need 390 | have = 0 391 | while off + 64 <= inplen: 392 | RMD160Transform(ctx.state, inp[off:]) #<--- 393 | off += 64 394 | if off < inplen: 395 | # memcpy(ctx->buffer + have, input+off, len-off); 396 | for i in range(inplen - off): 397 | ctx.buffer[have+i] = inp[off+i] 398 | 399 | def RMD160Final(ctx): 400 | size = struct.pack(" len(blockchain): 35 | bcdb.clear() 36 | bcdb.write(bc) 37 | for txs in all_node_txs: 38 | if len(txs) > len(transactions): 39 | txdb.clear() 40 | txdb.write(txs) 41 | 42 | def get_nodes(): 43 | return NodeDB().find_all() 44 | 45 | def add_node(address): 46 | ndb = NodeDB() 47 | all_nodes = ndb.find_all() 48 | if address.find('http') != 0: 49 | address = 'http://' + address 50 | all_nodes.append(address) 51 | ndb.clear() 52 | ndb.write(rm_dup(all_nodes)) 53 | return address 54 | 55 | def check_node(address): 56 | pass 57 | 58 | def rm_dup(nodes): 59 | return sorted(set(nodes)) 60 | 61 | if __name__=='__main__': 62 | start_node(3009) -------------------------------------------------------------------------------- /rpc.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | from xmlrpc.server import SimpleXMLRPCServer 3 | from xmlrpc.client import ServerProxy 4 | from node import get_nodes, add_node 5 | from database import BlockChainDB, UnTransactionDB, TransactionDB 6 | from lib.common import cprint 7 | server = None 8 | 9 | PORT = 8301 10 | 11 | class RpcServer(): 12 | 13 | def __init__(self,server): 14 | self.server = server 15 | 16 | def ping(self): 17 | return True 18 | 19 | def get_blockchain(self): 20 | bcdb = BlockChainDB() 21 | return bcdb.find_all() 22 | 23 | def new_block(self,block): 24 | cprint('RPC', block) 25 | BlockChainDB().insert(block) 26 | UnTransactionDB().clear() 27 | cprint('INFO',"Receive new block.") 28 | return True 29 | 30 | def get_transactions(self): 31 | tdb = TransactionDB() 32 | return tdb.find_all() 33 | 34 | def new_untransaction(self,untx): 35 | cprint(__name__,untx) 36 | UnTransactionDB().insert(untx) 37 | cprint('INFO',"Receive new unchecked transaction.") 38 | return True 39 | 40 | def blocked_transactions(self,txs): 41 | TransactionDB().write(txs) 42 | cprint('INFO',"Receive new blocked transactions.") 43 | return True 44 | 45 | def add_node(self, address): 46 | add_node(address) 47 | return True 48 | 49 | class RpcClient(): 50 | 51 | ALLOW_METHOD = ['get_transactions', 'get_blockchain', 'new_block', 'new_untransaction', 'blocked_transactions', 'ping', 'add_node'] 52 | 53 | def __init__(self, node): 54 | self.node = node 55 | self.client = ServerProxy(node) 56 | 57 | def __getattr__(self, name): 58 | def noname(*args, **kw): 59 | if name in self.ALLOW_METHOD: 60 | return getattr(self.client, name)(*args, **kw) 61 | return noname 62 | 63 | class BroadCast(): 64 | 65 | def __getattr__(self, name): 66 | def noname(*args, **kw): 67 | cs = get_clients() 68 | rs = [] 69 | for c in cs: 70 | try: 71 | rs.append(getattr(c,name)(*args, **kw)) 72 | except ConnectionRefusedError: 73 | cprint('WARN', 'Contact with node %s failed when calling method %s , please check the node.' % (c.node,name)) 74 | else: 75 | cprint('INFO', 'Contact with node %s successful calling method %s .' % (c.node,name)) 76 | return rs 77 | return noname 78 | 79 | def start_server(ip, port=8301): 80 | server = SimpleXMLRPCServer((ip, port)) 81 | rpc = RpcServer(server) 82 | server.register_instance(rpc) 83 | server.serve_forever() 84 | 85 | def get_clients(): 86 | clients = [] 87 | nodes = get_nodes() 88 | 89 | for node in nodes: 90 | clients.append(RpcClient(node)) 91 | return clients -------------------------------------------------------------------------------- /transaction.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import time 3 | import json 4 | import hashlib 5 | from model import Model 6 | from database import TransactionDB, UnTransactionDB 7 | from rpc import BroadCast 8 | 9 | class Vin(Model): 10 | def __init__(self, utxo_hash, amount): 11 | self.hash = utxo_hash 12 | self.amount = amount 13 | # self.unLockSig = unLockSig 14 | 15 | class Vout(Model): 16 | def __init__(self, receiver, amount): 17 | self.receiver = receiver 18 | self.amount = amount 19 | self.hash = hashlib.sha256((str(time.time()) + str(self.receiver) + str(self.amount)).encode('utf-8')).hexdigest() 20 | # self.lockSig = lockSig 21 | 22 | @classmethod 23 | def get_unspent(cls, addr): 24 | """ 25 | Exclude all consumed VOUT, get unconsumed VOUT 26 | 27 | """ 28 | unspent = [] 29 | all_tx = TransactionDB().find_all() 30 | spend_vin = [] 31 | [spend_vin.extend(item['vin']) for item in all_tx] 32 | has_spend_hash = [vin['hash'] for vin in spend_vin] 33 | for item in all_tx: 34 | # Vout receiver is addr and the vout hasn't spent yet. 35 | # 地址匹配且未花费 36 | for vout in item['vout']: 37 | if vout['receiver'] == addr and vout['hash'] not in has_spend_hash: 38 | unspent.append(vout) 39 | return [Vin(tx['hash'], tx['amount']) for tx in unspent] 40 | 41 | class Transaction(): 42 | def __init__(self, vin, vout,): 43 | self.timestamp = int(time.time()) 44 | self.vin = vin 45 | self.vout = vout 46 | self.hash = self.gen_hash() 47 | 48 | def gen_hash(self): 49 | return hashlib.sha256((str(self.timestamp) + str(self.vin) + str(self.vout)).encode('utf-8')).hexdigest() 50 | 51 | @classmethod 52 | def transfer(cls, from_addr, to_addr, amount): 53 | if not isinstance(amount,int): 54 | amount = int(amount) 55 | unspents = Vout.get_unspent(from_addr) 56 | ready_utxo, change = select_outputs_greedy(unspents, amount) 57 | print('ready_utxo', ready_utxo[0].to_dict()) 58 | vin = ready_utxo 59 | vout = [] 60 | vout.append(Vout(to_addr, amount)) 61 | vout.append(Vout(from_addr, change)) 62 | tx = cls(vin, vout) 63 | tx_dict = tx.to_dict() 64 | UnTransactionDB().insert(tx_dict) 65 | return tx_dict 66 | 67 | @staticmethod 68 | def unblock_spread(untx): 69 | BroadCast().new_untransaction(untx) 70 | 71 | @staticmethod 72 | def blocked_spread(txs): 73 | BroadCast().blocked_transactions(txs) 74 | 75 | def to_dict(self): 76 | dt = self.__dict__ 77 | if not isinstance(self.vin, list): 78 | self.vin = [self.vin] 79 | if not isinstance(self.vout, list): 80 | self.vout = [self.vout] 81 | dt['vin'] = [i.__dict__ for i in self.vin] 82 | dt['vout'] = [i.__dict__ for i in self.vout] 83 | return dt 84 | 85 | def select_outputs_greedy(unspent, min_value): 86 | if not unspent: return None 87 | # 分割成两个列表。 88 | lessers = [utxo for utxo in unspent if utxo.amount < min_value] 89 | greaters = [utxo for utxo in unspent if utxo.amount >= min_value] 90 | key_func = lambda utxo: utxo.amount 91 | greaters.sort(key=key_func) 92 | if greaters: 93 | # 非空。寻找最小的greater。 94 | min_greater = greaters[0] 95 | change = min_greater.amount - min_value 96 | return [min_greater], change 97 | # 没有找到greaters。重新尝试若干更小的。 98 | # 从大到小排序。我们需要尽可能地使用最小的输入量。 99 | lessers.sort(key=key_func, reverse=True) 100 | result = [] 101 | accum = 0 102 | for utxo in lessers: 103 | result.append(utxo) 104 | accum += utxo.amount 105 | if accum >= min_value: 106 | change = accum - min_value 107 | return result, change 108 | # 没有找到。 109 | return None, 0 --------------------------------------------------------------------------------