使用python與以太坊網(wǎng)絡進行交互

web3.py是基于python的以太坊庫川蒙,內(nèi)部封裝了對于etereum的rpc請求,這篇文章介紹了使用web3.py v6與以太坊或以太坊系列的區(qū)塊鏈網(wǎng)絡進行交互的常用方式。

安裝web3.py

pip install web3

加載以太坊節(jié)點:

from web3 import Web3
w3 = Web3(Web3.HTTPProvider('https://<your-provider-url>'))

如果是本地節(jié)點盗扇,例如ganache,地址為http://localhost:8545如果是線上節(jié)點,一般使用一些節(jié)點服務商提供的節(jié)點地址,例如Infura夺脾、quicknodealchemy等茉继。小狐貍metamast只是個錢包入口咧叭,它不提供節(jié)點api,它本身使用的也是infur提供的節(jié)點烁竭。智能合約開發(fā)框架truffle菲茬、truffle都提供了本地用于測試的私鏈,同樣可以通過節(jié)點連接派撕。

獲取區(qū)塊詳情

w3.eth.get_block('latest')

這個方法可以使用'latset'獲取最新塊婉弹,也可以指定高度、區(qū)塊哈希獲取指定高度终吼,返回一堆區(qū)塊頭數(shù)據(jù)

{'difficulty': 1,
 'gasLimit': 6283185,
 'gasUsed': 0,
 'hash': HexBytes('0x0'),
 'logsBloom': HexBytes('0x000'),
 'miner': '0x0',
 'mixHash': HexBytes('0x0'),
 'nonce': HexBytes('0x0'),
 'number': 0,
 'parentHash': HexBytes('0x000'),
 'proofOfAuthorityData': HexBytes('0x000'),
 'receiptsRoot': HexBytes('0x0'),
 'sha3Uncles': HexBytes('0x0'),
 'size': 622,
 'stateRoot': HexBytes('0x0'),
 'timestamp': 0,
 'totalDifficulty': 1,
 'transactions': [],
 'transactionsRoot': HexBytes('0x0'),
 'uncles': []}

常用函數(shù)和工具方法

  1. 獲取當前gasprice w3.eth.gase_price镀赌,經(jīng)常需要使用
  2. 獲取當前區(qū)塊高度 w3.eth.block_number
  3. 獲取當前鏈ID w3.eth.chain_id
  4. 獲取某賬戶的余額 w3.eth.get_balance(account='0x0',block_identifier=111111) 注意是eth余額,不是token的.注意第二個參數(shù)block_identifier可以指定區(qū)塊高度际跪,或者區(qū)塊哈希
  5. Web3.toChecksumAddress(address):將地址(字符串類型)轉(zhuǎn)換為校驗和形式商佛,以提高地址的安全性蛙粘。
  6. Web3.keccak(text):計算一個字符串的 Keccak-256 哈希值。在 Solidity 中威彰,也可以使用 keccak256 函數(shù)計算一個字符串的哈希值出牧。
  7. Web3.is_address(address):判斷一個地址是否為合法的以太坊地址。
  8. Web3.to_wei(1,'ether') 數(shù)值轉(zhuǎn)化,將ether轉(zhuǎn)為wei
  9. Web3.from_wei(1000000000000000000, 'ether'):將單位wei轉(zhuǎn)化為其他單位

以太坊原生交易

受益于以太坊的常規(guī)賬戶模型歇盼,轉(zhuǎn)賬的邏輯要比BTC的UTXO模型要簡潔清晰得多舔痕,在以太坊系統(tǒng)中從A向B轉(zhuǎn)賬,就是把A賬戶中的余額扣除豹缀,把B賬戶中的余額增加伯复。以下是轉(zhuǎn)賬的基本流程代碼:

from eth_account.account import Account, LocalAccount, SignedTransaction  
from web3 import Web3, HTTPProvider  
  
# 以太坊原生交易s  
sender_addr = "0x1111111111111111111111111111..."  
sender_private_key = "0xffffffffffffffffffffffff..."  
to_address = "0xB1476dFdAD625D787A006Ed362EBa5872a88Ae1A"  
  
w3 = Web3(HTTPProvider("http://127.0.0.1:8545"))  
## 1、構(gòu)建交易數(shù)據(jù)  
tx = {  
    'from': sender_addr,  # 如果傳入的地址不是發(fā)送者的地址邢笙,那么會出現(xiàn)typeerror的錯誤  
    'to': to_address,  
    'value': w3.to_wei(1, 'ether'),  # 這里的單位是wei,10**18 wei = 1 ether
    'gas': 21000,  
    'gasPrice': w3.eth.gas_price,  
    'nonce': w3.eth.get_transaction_count(sender_addr)  
}  
## 2啸如、簽名交易  
account: LocalAccount = Account.from_key(sender_private_key)  
sign_tx: SignedTransaction = account.sign_transaction(tx)  
## 3、發(fā)送交易  
tx_hash = w3.eth.send_raw_transaction(sign_tx.rawTransaction)  
## 4氮惯、阻塞等待交易結(jié)果  
result = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60)  
print(result)

注意:

  1. 交易數(shù)據(jù)中from這個字段可以不傳的叮雳,但如果使用,必須保證地址與使用的私鑰匹配妇汗,否則無法進行簽名
  2. 交易能否執(zhí)行成功與gas有關(guān)帘不,gas過低會造成以太坊虛擬機無法執(zhí)行事務,高了會返回多余gas杨箭,原生交易固定為21000寞焙,其他交易可以預估web3.eth.estimate_gas(tx),但一般直接上限寫死即可互婿,比如100000
  3. 能否上鏈被礦工打包與gas無關(guān)捣郊,與gasPrice相關(guān),gas價格太低了礦工嫌棄你慈参,太高了你嫌棄礦工呛牲,web3.py提供了預估當前最佳gasprice的方法w3.eth.gase_price,你和礦工都滿意
  4. value這個字段是交易的值懂牧,單位是Wei侈净,對于原生交易,這個值表示從A轉(zhuǎn)給B的錢僧凤,對于合約交易,這個值一般為0元扔。
  5. nonce這個值是記錄交易發(fā)起者交易序列的躯保,每次轉(zhuǎn)賬都比上一次的nonce加1,使用wb3.eth.get_transaction_count(sender)獲取sender的nonce值(注意澎语,這個值已經(jīng)進行了加1)
  6. web3.py的簽名過程不同版本還不一樣途事,這里使用的是v6.1.0
  7. 這里使用的是eth_account提供的Account進行簽名验懊,對于原生交易和合約交易效果一樣,但可能對于一些新EIP的交易類型不支持
  8. 簽名的兩個參數(shù)尸变,一個是需要簽名的交易數(shù)據(jù)义图,一個是私鑰,注意私鑰的保密
  9. 簽名完成后會生成Raw交易數(shù)據(jù)召烂,結(jié)構(gòu)如下碱工,實際上最后用到的只有rawTransaction這個字段
{
 'hash': HexBytes('0x126431f2a7fda003aada7c2ce52b0ce3cbdbb1896230d3333b9eea24f42d15b0'), 
 'r': 110093478023675319011132687961420618950720745285952062287904334878381994888509,
 'rawTransaction':HexBytes('0x........'), 
 's': 33674551144139401179914073499472892825822542092106065756005379322302694600392,  
 'v': 0}

注意,發(fā)送過程是異步的奏夫,以太坊從交易發(fā)送到節(jié)點確認需要一定的時間怕篷,主網(wǎng)一般10~15秒,goerli這類測試網(wǎng)要快一點酗昼,如arbitrum這類L2的網(wǎng)絡最快基本上1~3秒廊谓。
發(fā)送交易后可以等待交易確認再執(zhí)行后續(xù)操作,使用方法:

resp = w3.eth.wait_for_transaction_receipt(tx_hash)

這個方法其實是一個死循環(huán)在請求節(jié)點獲取交易哈希狀態(tài)結(jié)果麻削,默認超時120秒

可以將上述流程封裝成函數(shù)蒸痹,方便調(diào)用

from eth_account.account import Account, LocalAccount, SignedTransaction  
from hexbytes import HexBytes  
from web3 import Web3, HTTPProvider  
  
  
def transfer(w3: Web3, account: LocalAccount, to: str, value: int, gas=21000) -> HexBytes:  
     """
    交易、普通轉(zhuǎn)賬
    :param w3: 以太坊客戶端Web3實例
    :param account: 以太坊賬戶呛哟,由eth-account提供
    :param to: 接收地址
    :param value: 轉(zhuǎn)賬金額,單位時wei,1 ether=10**18 wei
    :param gas: gas消耗电抚,默認21000,不能比這個低
    :return: 交易哈希 字節(jié)數(shù)組
    """
    ## 1竖共、構(gòu)建交易數(shù)據(jù)  
    tx = {  
        'from': account.address,  
        'to': to,  
        'value': value,  
        'gas': gas,  
        'gasPrice': w3.eth.gas_price,  
        'nonce': w3.eth.get_transaction_count(account.address)  
    }  
    ## 2蝙叛、簽名  
    sign_tx: SignedTransaction = account.sign_transaction(tx)  
    ## 3、發(fā)送  
    return w3.eth.send_raw_transaction(sign_tx.rawTransaction)
    
if __name__ == '__main__':  
    w3 = Web3(HTTPProvider(endpoint_uri='http://127.0.0.1:8545'))  
    account = Account.from_key("<你的私鑰>")  
    tx_hash = transfer(w3, account, '<接收地址>', 1*10**17)  
    print(f"Success Transfer Hash:{tx_hash.hex()}")  
    result = w3.eth.wait_for_transaction_receipt(tx_hash)  
    print(f"Transfer Receipt:{result}")

調(diào)用合約方法

智能合約一旦部署就不允許再修改公给,后續(xù)對于合約的使用絕大部分集中在合約方法調(diào)用過濾合約日志

  1. 智能合約的方法從外部調(diào)用的角度可以分為兩種借帘,一種是view方法,一種是非view方法淌铐。
  • view方法只是從區(qū)塊鏈上查詢數(shù)據(jù)肺然,不會進行數(shù)據(jù)更改,不需要調(diào)用者進行簽名操作
  • 非view方法需要修改區(qū)塊鏈上數(shù)據(jù)腿准,需要調(diào)用者進行簽名操作
  1. 調(diào)用合約需要生成一個合約實例际起,這取決于兩個東西,一是合約address吐葱,二是合約ABI

在本文中街望,需要部署一個簡單的合約到區(qū)塊鏈上:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.9;

contract Demo {
    mapping(address => uint) private _ages;

    function setAge(uint age) public {
        _ages[msg.sender] = age;
    }
    function getAge(address user) public view returns (uint) {
        return _ages[user];
    }
}

在上面的合約中,提供了一個狀態(tài)變量_ages來記錄地址和年齡的映射關(guān)系弟跑,并提供了兩個方法灾前,setAge(uint age)設置調(diào)用者的年齡,getAge(address user)獲取指定地址的年齡孟辑。

調(diào)用合約的View方法:

對于View方法哎甲,調(diào)用方法分為幾步:

  1. 加載Web3實例
  2. 加載合約實例
  3. 獲取合約方法對象
  4. 使用call()方法調(diào)用
from web3 import Web3, HTTPProvider

# 連接以太坊節(jié)點
w3 = Web3(HTTPProvider(endpoint_uri='http://127.0.0.1:8545'))

# 獲取合約實例
contract_address = '<合約地址>'
contract_abi = [] # 合約abi,這是一個list結(jié)構(gòu)
contract_instance = w3.eth.contract(address=contract_address, abi=contract_abi)

# 獲取View函數(shù)對象并調(diào)用
func = getattr(contract_instance.functions, 'getAge')(user='0xC3845C061D0e0929744B01CbB6c23c31402B3E3a')
result=func.call()
print(f"result={result})

調(diào)用合約非View方法

對于非View方法蔫敲,則分為下面幾步:

  1. 加載Web3實例
  2. 加載合約實例
  3. 獲取合約方法對象
  4. 將方法對象綁定到交易數(shù)據(jù)中的data字段中
  5. 對交易數(shù)據(jù)進行簽名
  6. 發(fā)送簽名后的tx
from eth_account.account import Account, SignedTransaction
from web3 import Web3, HTTPProvider

# 調(diào)用者地址
sender_address = '<調(diào)用者地址>'
sender_privatekey = '<調(diào)用者私鑰>'

# 連接以太坊節(jié)點
w3 = Web3(HTTPProvider(endpoint_uri='http://127.0.0.1:8545'))

# 獲取合約實例
contract_address = '0x6EC21D47FCF6F06eeE9b36Ee85Ae25417b44697a'
contract_abi = [] # 合約ABI,這是一個List結(jié)構(gòu)
contract_instance = w3.eth.contract(address=contract_address, abi=contract_abi)

# 獲取調(diào)用函數(shù)對象
func = getattr(contract_instance.functions, 'setAge')(age=18)

# 構(gòu)造交易數(shù)據(jù)
tx = func.build_transaction({
    'nonce': w3.eth.get_transaction_count(sender_address),
    'value': 0,
    'gas': 100000,
    'gasPrice': w3.eth.gas_price})

sign_tx: SignedTransaction = Account.sign_transaction(tx, sender_privatekey)
tx_hash = w3.eth.send_raw_transaction(sign_tx.rawTransaction)
print(tx_hash.hex())

同樣炭玫,可以將上述過程封裝成函數(shù)奈嘿,方便調(diào)用:


from eth_account.account import Account, LocalAccount, SignedTransaction
from web3 import Web3, HTTPProvider


def call_contract_sign_func(w3: Web3, account: LocalAccount, address: str, abi: list, func_name: str,
                            gas=200000, value=0, **kwargs):
    """
    調(diào)用合約非View方法
    :param w3: Web3 實例
    :param account: 賬戶實例
    :param address: 合約地址
    :param abi: 合約ABI
    :param func_name: 合約方法名
    :param gas: 燃氣,默認200000
    :param value: 附帶轉(zhuǎn)賬金額,默認0
    :param kwargs: 方法參數(shù)
    :return: 交易哈希字節(jié)數(shù)組
    """
    contract_instance = w3.eth.contract(address=address, abi=abi)
    func = getattr(contract_instance.functions, func_name)(**kwargs)
    # 構(gòu)造交易數(shù)據(jù)
    tx = func.build_transaction({
        'nonce': w3.eth.get_transaction_count(account.address),
        'value': value,
        'gas': gas,
        'gasPrice': w3.eth.gas_price})
    # 簽名
    sign_tx: SignedTransaction = account.sign_transaction(tx)
    # 發(fā)送
    return w3.eth.send_raw_transaction(sign_tx.rawTransaction)


def call_contract_view_func(w3, address, abi, func_name, **kwargs):
    """
    調(diào)用合約View方法
    :param w3:Web3實例
    :param address:合約地址
    :param abi:合約ABI
    :param func_name:合約方法名稱
    :param kwargs:合約方法參數(shù)
    :return:函數(shù)執(zhí)行結(jié)果
    """
    contract_instance = w3.eth.contract(address=address, abi=abi)
    func = getattr(contract_instance.functions, func_name)(**kwargs)
    return func.call()


if __name__ == '__main__':
    w = Web3(HTTPProvider(endpoint_uri='http://127.0.0.1:8545'))
    contract_address = '<合約地址>'
    contract_abi = [] # 合約abi
    caller: LocalAccount = Account.from_key('<賬戶私鑰>')
    ## 調(diào)用view方法
    result = call_contract_view_func(w3=w, address=contract_address, abi=contract_abi, func_name='getAge',
                                     user='0xC3845C061D0e0929744B01CbB6c23c31402B3E3a')
    print(result)
        ## 調(diào)用非View方法
    tx_hash = call_contract_sign_func(w3=w, account=caller, address=contract_address, abi=contract_abi,
                                      func_name='setAge',
                                      age=8877)
    print(tx_hash.hex())
    ## 等待并返回調(diào)用結(jié)果
    resp = w.eth.wait_for_transaction_receipt(tx_hash)
    print(resp)

上面的代碼中可以看出調(diào)用合約非view方法實際上就是一次轉(zhuǎn)賬交易吞加,在交易中把對合約方法的調(diào)用信息寫入交易數(shù)據(jù)的data字段中裙犹,剩下的就是evm需要考慮的事情了。

過濾合約日志

日志的過濾是合約使用的另一個重要方面榴鼎,為了便于日志演示伯诬,對上面使用的合約進行簡單的改造,加上event:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.9;

contract Demo {
    // 日志
    event SetAge(address user, uint age);
    mapping(address => uint) private _ages;
    function setAge(uint age) public {
        _ages[msg.sender] = age;
      // 發(fā)射日志
        emit SetAge(msg.sender, age);
    }

    function getAge(address user) public view returns (uint) {
        return _ages[user];
    }
}

過濾合約日志分如下幾步:

  1. 加載web3實例
  2. 加載合約實例
  3. 獲取合約事件對象
  4. 創(chuàng)建事件過濾器
  5. 檢索過濾后的全部事件
from web3 import Web3, HTTPProvider

contract_address = '<合約地址>'
contract_abi = [] # 合約ABI
# 加載Web3實例
w3 = Web3(HTTPProvider(endpoint_uri='http://127.0.0.1:8545'))
# 加載合約實例
contract_instance = w3.eth.contract(address=contract_address, abi=contract_abi)
# 獲取event對象
my_event = contract_instance.events.SetAge()
# 創(chuàng)建事件過濾器巫财,
my_filter = my_event.create_filter(fromBlock=1, toBlock=28)
# 檢索事件
for event in my_filter.get_all_entries():
    print(dict(event))
   

返回的event數(shù)據(jù)如下盗似,包含了日志的基本信息:

  • 區(qū)塊信息:區(qū)塊高度、哈希
  • 交易哈希平项、交易哈希索引
  • 日志索引
  • event名
  • event 數(shù)據(jù)信息
{
 'args':AttributeDict({'user': '0xC3845C061D0e0929744B01CbB6c23c31402B3E3a', 'age': 8877}), 
 'event': 'SetAge',
 'logIndex': 0,
 'transactionIndex': 0,
 'transactionHash': HexBytes('0x0975e324106b995512a331f0fa29e8af1ef607e41753c84cacf5a6009dda34e7'), 
 'address': '0x9d95b127281335A846E4f36e36D45963e452d3D4',
 'blockHash': HexBytes('0x1b67007d4f40c93c1e881a32299541cffe837b8acc64724ac093ae7d9bc4e832'),
 'blockNumber': 28
}

上面的代碼中并沒有體現(xiàn)log的topic信息赫舒,這里需要首先說明 Logevent闽瓢、topic的關(guān)系接癌,以太坊的Log數(shù)據(jù)結(jié)構(gòu)如下:

```go
type Log struct {  
   // 日志所屬的區(qū)塊高度  
   BlockNumber uint64 `json:"blockNumber"`  
   // 日志所屬的交易哈希  
   BlockHash common.Hash `json:"blockHash"`  
   // 日志所屬的區(qū)塊哈希  
   TxHash common.Hash `json:"transactionHash" gencodec:"required"`  
   // 日志所屬的交易序列號  
   TxIndex uint `json:"transactionIndex"`  
  
   // 日志在交易中的序列,一個交易可以包含多個日志扣讼,這個字段表示日志在交易中的索引  
   Index uint `json:"logIndex"`  
   // 是否被移除缺猛,注意如果鏈進行了分叉,那么本字段會變成true椭符,如果為true荔燎,那么不應該識別為正確的log  
   Removed bool `json:"removed"`  
  
   // 發(fā)送出日志的智能合約地址,注意销钝,只有合約賬戶才能發(fā)送event  
   Address common.Address `json:"address" gencodec:"required"`  
   // 日志主題列表有咨,這個字段是為了方便查詢時進行過濾  
   Topics []common.Hash `json:"topics" gencodec:"required"`  
   // event攜帶的數(shù)據(jù)字節(jié)數(shù)組  
   Data []byte `json:"data" gencodec:"required"`  
}
}
```

Log 是以太坊中的一個事件記錄,是一個 key-value 對的集合蒸健,其中 key 是字符串類型的 topics座享,value 是一個任意長度的字節(jié)數(shù)組 data。Log 可以被合約 emit似忧,也可以由以太坊系統(tǒng)自動生成渣叛,例如在轉(zhuǎn)賬時生成的 Transfer Log。每筆交易都有可能會產(chǎn)生 Log橡娄。

Event 是合約中的一類特殊函數(shù)诗箍,它允許合約在執(zhí)行過程中,向外部環(huán)境廣播某些內(nèi)容(包括 Log 和數(shù)據(jù))挽唉。Event 可以定義多個參數(shù)滤祖,這些參數(shù)可以是基本類型(如 uint256、string 等)瓶籽,也可以是自定義類型匠童。Event 可以被觸發(fā)多次,并且每次觸發(fā)都會生成一個獨立的 Log塑顺。

Topic 是 Log 中的一個結(jié)構(gòu)汤求。在 Emit Event 的時候,每個 Event 參數(shù)可能會被標記為 indexed 或不 indexed严拒,indexed 的參數(shù)會被記錄在 Event 的 Topic 中扬绪,不 indexed 的參數(shù)則會被記錄在 Log 的 Data 中。具體來說裤唠,Event 的 keccak 哈希會成為 Topic 的第一個元素挤牛,Events 中 indexed 的參數(shù)會按照順序在后面添加。

上面過濾日志的代碼中种蘸,只是通過合約地址和區(qū)塊范圍進行過濾墓赴,如果要過濾event和或者過濾方法參數(shù)那么需要在構(gòu)造過濾器時添加topics字段,

每個log都有一個topic集合航瞭,每個event至少有一個topic诫硕,即event方法的簽名

topic=w3.keccak(text="SetAge(address,uint256)").hex()

如果event的參數(shù)被indexed修飾,那么也可以生成topic

topic=w3.keccak(text="user(address)").hex()

在構(gòu)建過濾器時添加topics

my_filter = my_event.create_filter(fromBlock=1, toBlock=28, topics=[w3.keccak(text="SetAge(address,uint256)").hex()])

topic相當于給日志打標簽刊侯,通過標簽可以提高過濾效率章办,但每次要計算哈希還是挺麻煩的,如果logs數(shù)量本身不大滨彻,可以直接全量過濾后再通過event name藕届。

Web3.py 還提供了很多其他有用的工具,比如用于解析和生成合約 ABI 編碼的方法疮绷。但合約的部署翰舌、測試一般由hardhat、truffle這類框架完成冬骚,使用的是js或者ts,這方面python的存在感比較低椅贱。

參考:

  1. web3.py官網(wǎng)文檔
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市只冻,隨后出現(xiàn)的幾起案子庇麦,更是在濱河造成了極大的恐慌,老刑警劉巖喜德,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件山橄,死亡現(xiàn)場離奇詭異,居然都是意外死亡舍悯,警方通過查閱死者的電腦和手機航棱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門睡雇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饮醇,你說我怎么就攤上這事它抱。” “怎么了朴艰?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵观蓄,是天一觀的道長。 經(jīng)常有香客問我祠墅,道長侮穿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任毁嗦,我火速辦了婚禮亲茅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘金矛。我一直安慰自己芯急,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布驶俊。 她就那樣靜靜地躺著娶耍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饼酿。 梳的紋絲不亂的頭發(fā)上榕酒,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音故俐,去河邊找鬼想鹰。 笑死,一個胖子當著我的面吹牛药版,可吹牛的內(nèi)容都是我干的辑舷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼槽片,長吁一口氣:“原來是場噩夢啊……” “哼何缓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起还栓,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤碌廓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后剩盒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谷婆,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纪挎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片期贫。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖廷区,靈堂內(nèi)的尸體忽然破棺而出唯灵,到底是詐尸還是另有隱情贾铝,我是刑警寧澤隙轻,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站垢揩,受9級特大地震影響玖绿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叁巨,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一斑匪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锋勺,春花似錦蚀瘸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苏章,卻和暖如春寂嘉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枫绅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工泉孩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人并淋。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓寓搬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親县耽。 傳聞我的和親對象是個殘疾皇子句喷,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容