flow_study开源项目为 Flow 开发者搭建一个开发学习平台。
该项目包括3个模块,代码下载模块,搜索模块,展示模块。flow_study开源项目主要作为flow学习的有效工具和开发平台。本章介绍的是flow_study_spider,flow合约代码的ETL。
详细代码访问
服务 flow_study_server github.com/flowstudy/f…
爬虫入库 flow_study_spider github.com/flowstudy/f…
数据表结构 flow_study_sql github.com/flowstudy/f…
前端 flow_study_web github.com/flowstudy/f…
下图是flow区块的基本结构
get_block.py
为了获取区块的全部信息,首先程序 get_block.py通过 flow_py_sdk提供的接口获取最新的区块信息,并将获取的相关信息存入数据库的flow_block表,
async with flow_client(
host="access.mainnet.nodes.onflow.org", port=9000
) as client:
latest_block = await client.get_latest_block()
height = latest_block.height
parent_id_hex = latest_block.parent_id.hex()
data = {
"block_id" : latest_block.id.hex(),
"signatures" : latest_block.signatures[0].hex(),
"parent_id" : parent_id_hex,
"height" : latest_block.height,
"fetch_time" : time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()),
"timestamp" : latest_block.timestamp,
}
访问flowscan.org/ 可查看区块的相关信息
update_trans_multi.py
上面的函数我们已经把区块的信息存入flow_block表中了,现在可以调用get_trans.py的get_trans函数,通过flow_block表中的区块高度,获取区块的交易信息,如交易名称contract_name,交易地址contract_address,并将获取到的信息存储到flow_trans_data表中。
下面是get_trans.py中通过flow height获取交易信息的部分代码,由flow区块的基本结构可知,get_trans函数获取交易信息需要先获取collection, 通过collection信息,使用以下函数可以获取到交易中的用户地址,交易的脚本代码,合约地址等信息。最后把收集到的信息存在flow_trans_data表中。
async with flow_client(
host="access.mainnet.nodes.onflow.org", port=9000
) as client:
# 通过height获取区块信息。
block = await client.get_block_by_height(height=height)
#collection是transaction的集合
trans_list = []
# 获得区块中的collection列表
for i in range(len(block.collection_guarantees)):
collection_id = block.collection_guarantees[i].collection_id # 获得第i个 collection id
collection = await client.get_collection_by_i_d(id=collection_id) # 获得 collection 详情
for trans_id in collection.transaction_ids: # 获得 collection 中所有transaction 列表,并遍历
trans_id_hex = trans_id.hex() # 获得交易id,16进制形式
transaction = await client.get_transaction(id=trans_id) # 根据交易id 获得交易详情
user_address= transaction.proposal_key.address.hex() # 获得交易中的用户地址
trans_script = transaction.script.decode("utf-8") # 获得交易的脚本代码
import_data_list = get_contract_address(trans_script) # 从交易代码中解析合约地址
# 遍历合约地址,构造需要插入数据库的数据
for import_data in import_data_list:
trans_data = {
"trans_id":trans_id_hex,
"user_address":"0x" + user_address, "contract_name":import_data["contract_name"],
"contract_address":import_data["contract_address"],
"fetch_time":time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),
"height":height,
} #注意使用的时候,基础的nft等类,可以不用
#print(trans_data)
trans_list.append(trans_data)
其中trans_script需要经get_contract_address函数的简单解析才能获取到contract_name,contract_address,具体代码如下。
def get_contract_address(trans_script):
#step 1,获得所有引用
p = re.compile('import.{3,30}from 0x\w{10,25}') #引用的正则
import_list = p.findall(trans_script)
#step 2,解析所有引用
import_data_list = []
for item in import_list:
item_list = item.split()
contract_name = item_list[1]
contract_address = item_list[3]
#print(contract_name, contract_address)
import_data = {
"contract_name":contract_name,
"contract_address":contract_address,
}
import_data_list.append(import_data)
return import_data_list
update_contract.py 每小时运行一次,把flow_trans_data表中的contract_address去重后,插入flow_contract_address表,相当于接下来要处理的任务表,接下来在get_contract.py中处理这些合约。
get_contract.py
通过表中的contract_address获取合约代码,具体实现如下列代码,通过合约地址可以获取service_account_address,从而获取到账户信息,接下来解析account就可以得到contract_address,contract_code,contract_name。其中,contract_name需要从contract_code中进行简单的正则匹配筛选出来。
async def get_contract(address):
async with flow_client(
host="access.mainnet.nodes.onflow.org", port=9000
) as client:
service_account_address = bytes.fromhex(address)
#到最新高度的信息
account = await client.get_account(
address=service_account_address
)
contract_data_list = []
for key in account.contracts:
contract = account.contracts[key]
#print(contract.decode())
contract_code = contract.decode()
contract_name = get_contract_name(contract_code)
contract_data = {
"contract_address": "0x"+address,
"contract_name": contract_name,
"contract_code": contract_code
}
contract_data_list.append(contract_data)
return contract_data_list
contract_name正则匹配代码。
def get_contract_name(code):
#step 1,获得定义
#step 1,获得所有引用
p = re.compile('pub contract.*|access(all) contract.*') #引用的正则
import_list = p.findall(code) #包含回车
contract_name_line = import_list[0]
contract_name_line = contract_name_line.replace(" interface","")
contract_name_line = contract_name_line.replace(":", " ")
contract_name_line = contract_name_line.replace("{", " ")
contract_name_line_item = contract_name_line.split()
contract_name = contract_name_line_item[2].strip()
return contract_name
parse_flow_code.py
为flow_code表中的contract_type,contract_category添加属性,获取表中每一行的contract_code,通过正则解析contract_code的contract_type属于interface,contract还是transaction。get_code_category函数解析合约名称得到code_category并把code_category存入flow_code表。
contract_struct_parser2.py
为了获取合约代码中各个代码块的位置,实现快速的跳转,contract_struct_parser2.py调用一个由go语言编写的解析服务,服务具体代码可以在GitHub地址github.com/HoppingChar… 中查看,解析后的code_text包含合约代码的每一部分从第几行开始到第几行结束等位置信息,经过parse_code函数的解析,struct_type代码块的类型,struct_name代码块的名字,start_pos此代码块的开始位置,end_pos此代码块的结束位置。
update_relate_code.py
主要功能分析flow_code中合约的相关代码,存入contract_relation表,update_relate_code函数获取flow_code表中待处理的合约名称,合约地址,合约代码,调用code_relation.py的get_code_related函数解析相关合约的contract_code,contract_name。
code_relation.py
有两个函数,get_code_related 获取代码相关代码,指的是本段合约代码所引用的合约,输出相关合约的合约名称,合约地址,list 格式如下 [{"contract_name":"name1","contract_address":"address1"}, {"contract_name":"name2","contract_address":"address2"}]。
relate_contract_list = []
#step 1 ,获得所有的import行
#step 1,获得所有引用
p = re.compile('import.{3,30}from 0x\w{10,25}') #引用的正则
import_list = p.findall(contract_code)
# step 2 ,解析每一行,获得相关的合约地址和合约名称
for item in import_list:
item_list = item.split()
contract_name = item_list[1]
contract_address = item_list[3]
#print(contract_name, contract_address)
import_data = {
"contract_name":contract_name,
"contract_address":contract_address,
}
relate_contract_list.append(import_data)
return relate_contract_list
get_code_relate_transaction,获取代码相关交易transaction,存入数据库的flow_code_relate_transaction表。 具体关系解释如下:
来源数据库表:contract_relation
存储数据库表:flow_code_relate_transaction
从相关合约表contract_relation 获取关系, 41行的相关交易是指33行的CharityNFT,因为从表中可以看出41被33 引用,并且33的 flow_code contract_type 是transaction。
| contract_relation表示例 | |||||
|---|---|---|---|---|---|
| 33 | Admin | 0x097bafa4e0b48eef | CharityNFT | 0x097bafa4e0b48eef | 2022-11-26 19:45:22 |
| 41 | CharityNFT | 0x097bafa4e0b48eef | NonFungibleToken | 0x1d7e57aa55817448 | 2022-11-27 00:32:59 |
其他代码解释
update_fcode_es.py 全量向es中导入flow_code表数据。
update_trans.py 从 flow_block读取未处理的区块,获得区块内的交易信息,主要是合约代码和名称,插入flow_trans_data。
get_flow_trans_link.py 获得flow转账的events记录,存入flow_token表。
get_flow_trans_link_to.py 获得flow转账的events记录,存入flow_token_to表。