《区块链编程》第七章

《区块链编程》第七章

jiaoyi的创建与验证

审核不通过,提示违法违规, 尝试一下是否是jy两个字的原因

练习1

p129

代码实现

# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date:   2022-01-09 18:28:43
# @Last Modified by:   从化北
# @Last Modified time: 2022-01-09 18:53:24


class Tx:
...
def sig_hash(self, input_index):
    '''Returns the integer representation of the hash that needs to get
    signed for index input_index'''
    # start the serialization with version
    # use int_to_little_endian in 4 bytes
    s = int_to_little_endian(self.version, 4)
    # add how many inputs there are using encode_varint
    s += encode_varint(len(self.tx_ins))
    # loop through each input using enumerate, so we have the input index
    for i, tx_in in enumerate(self.tx_ins):
        # if the input index is the one we're signing
        if i == input_index:
            # Only the signature script of the specified input is replaced,
            # and the signature scripts of other inputs are empty.
            # the previous tx's ScriptPubkey is the ScriptSig
            # Otherwise, the ScriptSig is empty
            # add the serialization of the input with the ScriptSig we want
            s += TxIn(
                prev_tx=tx_in.prev_tx,
                prev_index=tx_in.prev_index,
                script_sig=tx_in.script_pubkey(self.testnet),
                sequence=tx_in.sequence,
            ).serialize()
        else:
            s += TxIn(prev_tx=tx_in.prev_tx,
                      prev_index=tx_in.prev_index,
                      sequence=tx_in.sequence,
                      ).serialize()
    # add how many outputs there are using encode_varint
    s += encode_varint(len(self.tx_outs))
    # add the serialization of each output
    for tx_out in self.tx_outs:
        s += tx_out.serialize()
    # add the locktime using int_to_little_endian in 4 bytes
    s += int_to_little_endian(self.locktime, 4)
    # add SIGHASH_ALL using int_to_little_endian in 4 bytes
    s += int_to_little_endian(SIGHASH_ALL, 4)
    # hash256 the serialization
    h256 = hash256(s)
    # convert the result to an integer using int.from_bytes(x, 'big')
    return int.from_bytes(h256, 'big')


对上述函数代码的解释

对sig_hash函数的解释:
    输入:交易中input的索引
    输出:z(签名哈希)
    内部执行逻辑:
        首先说明一个前提: 每一个input对应一个z,(换句话说,每一个签名脚本对应一个z)
        求当前input的z,将该input的签名脚本更换为其父交易output中的公钥脚本。(p126)
        其他各部分均不变。
        而后对更改后的交易,进行hash256,转换为大端序的int值,即为当前input的z。

练习2

p129

代码实现

# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date:   2022-01-09 18:53:45
# @Last Modified by:   从化北
# @Last Modified time: 2022-01-09 18:58:41


class Tx:
...
def verify_input(self, input_index):
        '''Returns whether the input has a valid signature'''
        # get the relevant input
        tx_in = self.tx_ins[input_index]
        # grab the previous ScriptPubKey
        script_pubkey = tx_in.script_pubkey(testnet=self.testnet)
        # get the signature hash (z)
        z = self.sig_hash(input_index)
        # combine the current ScriptSig and the previous ScriptPubKey
        combined = tx_in.script_sig + script_pubkey
        # evaluate the combined script
        return combined.evaluate(z)

对上述函数代码的解释

对verify_input函数的解释:
    输入:交易中input的索引
    输出:True/False
    函数要做的事情:验证input是否有效
    验证逻辑:
        验证input有效,就是验证Script对象运行结果非0。
        一个Script对象的组成:
            当前input的签名脚本
            当前input父交易的公钥脚本
        而Script对象需要z来验证。具体深入请看Script章节以及椭圆曲线密码学数字签名算法部分

练习3

p134

代码实现

# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date:   2022-01-09 19:35:33
# @Last Modified by:   从化北
# @Last Modified time: 2022-01-09 19:35:33



class Tx:
...
    def sign_input(self, input_index, private_key):
        # get the signature hash (z)
        z = self.sig_hash(input_index)
        # get der signature of z from private key
        der = private_key.sign(z).der()
        # append the SIGHASH_ALL to der (use SIGHASH_ALL.to_bytes(1, 'big'))
        sig = der + SIGHASH_ALL.to_bytes(1, 'big')
        # calculate the sec
        sec = private_key.point.sec()
        # initialize a new script with [sig, sec] as the cmds
        # change input's script_sig to new script
        self.tx_ins[input_index].script_sig = Script([sig, sec])
        # return whether sig is valid using self.verify_input
        return self.verify_input(input_index)

对上述函数代码的解释

对sign_input函数的解释:
    输入:
        交易中input的索引
        私钥
    输出:输出input签名脚本的验证结果( True / False)
    函数要做的事情:
        填充某一input的签名脚本
        验证input签名是否有效
     p2pk标准脚本的脚本签名包括两个元素:
        sig = der + 签名授权哈希
        sec

练习4

p135

代码实现

# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date:   2022-01-09 21:58:03
# @Last Modified by:   从化北
# @Last Modified time: 2022-01-10 16:21:53
from ecc import PrivateKey
from helper import decode_base58, SIGHASH_ALL, little_endian_to_int, hash256
from script import p2pkh_script, Script
from tx import TxIn, TxOut, Tx

# 父交易ID,现在申请的,里面有0.001=十万聪
prev_tx = bytes.fromhex('941999e918522d1d9c4691ab67dcd0effe22ed3a5664ab93f397fe865c600553')
# 父交易的序号
prev_index = 0
# 目标地址,(donate address)
target_address = '37NFX8KWAQbaodUG6pE1hNUH1dXgkpzbyZ'
# 金额
target_mount = 0.00006
# 剩于部分发送的地址,另一个新建的地址,第三个
change_address = 'mosFzEQBzLyMgEPGu9LXoaB4iDjo4okffL'
# 向“剩余地址”发送的金额,其他作为手续费
change_mount = 0.0008
# 秘钥
secret = b"***********"
secret = little_endian_to_int(hash256(secret))
# 由秘钥创建的私钥
priv = PrivateKey(secret=secret)
# 构建input
tx_ins = []
# 添加input
tx_ins.append(TxIn(prev_tx, prev_index))
# 构建output
tx_outs = []
# 将目的地址的base58形式解析出哈希地址
h160 = decode_base58(target_address)
# 构建交易的公钥脚本
script_pubkey = p2pkh_script(h160)
# 交易以聪为单位,1 比特币 = 1亿 聪
target_satoshis = int(target_mount * 100000000)
# 构建并添加output
tx_outs.append(TxOut(target_satoshis, script_pubkey))
# 将“剩余地址”的base58形式解析出哈希地址
h160 = decode_base58(change_address)
# 构建交易的公钥脚本
script_pubkey = p2pkh_script(h160)
# 交易以聪为单位,1 比特币 = 1亿 聪
change_satoshis = int(change_mount * 100000000)
# 构建并添加output
tx_outs.append(TxOut(change_satoshis, script_pubkey))
# 构建交易
tx_obj = Tx(1, tx_ins, tx_outs, 0, testnet=True)
# 校验签名可用
print(tx_obj.sign_input(0, priv))
# 打印交易的完整hex
print(tx_obj.serialize().hex())

# 广播交易的网站
# https://live.blockcypher.com/btc/pushtx

运行结果

补充说明, 这个交易已经被我发布了.
广播交易的一个网站

True
01000000015305605c86fe97f393ab64563aed22feefd0dc67ab91469c1d2d5218e9991994000000006a473044022100f34a7dbbaa83de7e064c1ce5664ac80eb6c201538eafd4fe94a453432ac2b8c8021f5037b3d9fc4f60bc9879bcd8fcd5a711885892989b475033f48c2c6112273701210284bfcdd7f8d2cc4732cabc508c7f309b5ac5705698a8753a183ddff550e2d153ffffffff0270170000000000001976a9143e443375e10eef0236cdb243bdec473918c9a1dd88ac80380100000000001976a9145b98183ab5ffaeee7d03f3a58da039db9b88cd9a88ac00000000
[Finished in 1.5s]

看一下发布后的样子

  1. 一个input 两个output
  2. 广播交易的网站之一(这个突然挂掉了,下面test05,使用了另一个广播交易的网站)
  3. tx_id : 941999e918522d1d9c4691ab67dcd0effe22ed3a5664ab93f397fe865c600553
  4. 感兴趣大家可以自己查一下这个交易链接,这里给出另一个区块链浏览器
    发布后的样子

练习5

p135

代码实现

# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date:   2022-01-10 16:28:04
# @Last Modified by:   从化北
# @Last Modified time: 2022-01-10 18:03:42
from ecc import PrivateKey
from helper import decode_base58, SIGHASH_ALL, little_endian_to_int, hash256
from script import p2pkh_script, Script
from tx import TxIn, TxOut, Tx

# 1 btc = 1亿 聪
RATE = 100000000
# 父交易1的ID;上一个找零交易, 0.0008 btc
prev_tx_1 = bytes.fromhex('50bf400cfbd2669434be467e9e277e1db88610f0ef2f8ec5a1631f0bf9f5ae78')
# 父交易1的序号
prev_index_1 = 1
# 父交易2的ID,水龙头交易;0.0001 btc
prev_tx_2 = bytes.fromhex('019ea8b778aac75b6b344e69912a5a98927bcefccaaf366f84e31ddc74668407')
# 父交易1的序号
prev_index_2 = 0
# 一共0.0009 btc
# 目标地址1,自己的一个地址
target_address = 'mvgTF5toE4Y7d59ibvq5E7fMka7uiAYprs'
target_mount = 0.00006
# 剩于部分发送的地址,自己的另一个地址
# https://bitcoinfaucet.uo1.net/send.php
denote_address = 'mh7ymnwwfG22BcdLmYf5cnHYLjjXpEFbcj'
# 向“剩余地址”发送的金额,留出0.001作为手续费
denote_mount = 0.00002
# 秘钥数字, 花钱
secret = b"******************"
secret = little_endian_to_int(hash256(secret))
# 由秘钥创建的私钥
priv = PrivateKey(secret=secret)
# 构建input
tx_ins = []
# 添加两个input
tx_ins.append(TxIn(prev_tx_1, prev_index_1))
tx_ins.append(TxIn(prev_tx_2, prev_index_2))
# 构建output
tx_outs = []
# 将目的地址的base58形式解析出哈希地址
h160_1 = decode_base58(target_address)
h160_2 = decode_base58(denote_address)

# 构建交易的公钥脚本
script_pubkey_1 = p2pkh_script(h160_1)
script_pubkey_2 = p2pkh_script(h160_2)
# print(script_pubkey)
# 交易以聪为单位,1 比特币 = 1亿 聪
target_satoshis = int(target_mount * RATE)
decote_satoshis = int(denote_mount * RATE)
# 构建并添加output
tx_outs.append(TxOut(target_satoshis, script_pubkey_1))
tx_outs.append(TxOut(target_satoshis, script_pubkey_2))
# 构建交易
tx_obj = Tx(1, tx_ins, tx_outs, 0, testnet=True)
print(tx_obj.fee())
# 校验签名可用
print(tx_obj.sign_input(0, priv))
print(tx_obj.sign_input(1, priv))
# 打印交易的完整hex
print(tx_obj.serialize().hex())

运行结果

补充说明, 这个交易已经被我发布了.
广播交易的一个另一个网站,之前那个不知怎么,暂时挂掉了

True
True
010000000278aef5f90b1f63a1c58e2feff01086b81d7e279e7e46be349466d2fb0c40bf50010000006b48304502210089c548abd0e736c66dcb3f80a91457316b8b38c35e28dab6e80c689d90795d9102206ce6a37dbc709eed10552ba21504ac31dc5672277204cc6ee90eae9c64234fcb012102fbb36a53ef74d0ed81e114fc5e47624d8f9e4d329983db90020b3dbbcd25a21cffffffff07846674dc1de3846f36afcafcce7b92985a2a91694e346b5bc7aa78b7a89e01000000006b483045022100a094452d4475dd0dc74f212ab259761f32c04b6ef9ef276461d4d6926603cf7a02201638858c428f79f0bb0ec48bd101353c238528b8c7ddb997f64f1d5f4e71be21012102fbb36a53ef74d0ed81e114fc5e47624d8f9e4d329983db90020b3dbbcd25a21cffffffff0270170000000000001976a914a655cbb0b31bcd08157a6aeecc5d76a2e5be2bbe88ac70170000000000001976a9141197eb1e848ae52638df6bf205ffd9dd2d93c26788ac00000000
[Finished in 2.2s]

看一下发布后的样子

两个input 两个output
tx_id : 5e33aadfa079c3dac8176203ab5644a75d2129cf98f4085499b52887e73c2543
感兴趣大家可以自己查一下这个交易链接,这里给出另一个区块链浏览器
两个输入两个输出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值