主页 > imtoken钱包华为安装不了 > 如何使用原始比特币协议

如何使用原始比特币协议

imtoken钱包华为安装不了 2023-01-18 17:57:56

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

媒体对比特币的关注让我了解了比特币的真正运作方式,一直到流经网络的字节数。 普通人使用隐藏真相的软件,但我想亲自了解比特币协议。 我的目标是直接使用比特币系统:手动创建比特币交易,将其作为十六进制数据提供给系统,然后查看它是如何处理的。 事实证明它比我预期的要困难得多,但我在此过程中学到了很多东西,希望你会发现它很有趣。

这篇博文从对比特币的简要介绍开始比特币合约交易教程,然后跳转到底层细节:创建比特币地址、进行交易、签署交易、使交易对点对点网络可用以及观察结果.

比特币快速概览

在深入细节之前,我将首先快速概述一下比特币的工作原理。 比特币是一种相对较新的数字货币,可以通过互联网进行转移。 你可以在 Coinbase 或 MtGox 等网站上用美元或其他传统资金购买比特币,将比特币发送给其他人,在某些地方用他们买东西,以及用比特币兑换美元。 为了稍微简化,比特币由分布式数据库中的条目组成,该数据库跟踪比特币的所有权。 与银行不同,比特币不与用户或账户绑定。 相反,比特币由比特币地址拥有,例如 1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。

比特币交易

交易是花费比特币的机制。 在交易中,一些比特币的所有者将所有权转移到一个新地址。 比特币的一个关键创新是如何通过挖矿将交易记录在分布式数据库中。 交易被分组到块中,大约每 10 分钟发送一个新的交易块并成为交易日志的一部分,称为区块链,这表示交易已经(或多或少)正式。 比特币挖矿是将交易放入区块以确保每个人对交易日志的看法一致的过程。 为了开采一个区块,矿工必须找到一个极其罕见的解决方案来解决(否则毫无意义的)密码问题。 找到这个解决方案会生成一个已开采的区块,该区块将成为官方区块链的一部分。

挖矿也是比特币进入系统的一种新机制。 当一个区块被成功开采时,新的比特币会在区块中产生并支付给矿工。 这个挖矿奖金很大——目前每个区块 25 个比特币(约 19,000 美元)。 此外,矿工赚取与区块交易相关的任何费用。 因此,挖矿竞争非常激烈,许多人试图挖出区块。 挖矿的难度和竞争力是比特币安全的关键部分,因为它确保没有人可以用坏块淹没系统。

点对点网络

没有集中的比特币服务器。 相反,比特币在点对点网络上运行。 如果你运行比特币客户端,你就会成为该网络的一部分。 网络上的节点相互交换交易、区块和其他节点的地址。 首次连接到网络时,客户端会从某个随机节点或对等点下载区块链。 反过来,您的客户端可能会向其他节点提供数据。 当你创建一个比特币交易时,你将它发送给某个节点,该节点将它发送给其他节点,依此类推,直到它到达整个网络。 矿工接受你的交易,生成一个包含你的交易的开采区块,并将这个开采区块发送给对等方。 最终,您的客户端将收到该块,并且您的客户端将显示交易已被处理。

加密

比特币使用数字签名来确保只有比特币的所有者才能使用它们。 比特币地址的所有者拥有与该地址关联的私钥。 为了花费比特币,他们用这个私钥签署交易,证明他们是所有者。 (这有点像签署实物支票以使其有效。)公钥与每个比特币地址相关联,任何人都可以使用它来验证数字签名。

块和事务由其内容的 256 位加密哈希值标识。 该哈希值在比特币协议中的多个地方使用。 此外,找到一个特殊的哈希值对于挖掘块来说是一个难题。

深入研究原始比特币协议

本文的其余部分将介绍我如何使用原始比特币协议。 首先,我生成了一个比特币地址和密钥。 接下来,我进行了一笔交易,将少量比特币转入该地址。 签署这笔交易花了我很多时间和困难。 最后,我将这笔交易发送到比特币点对点网络并等待它被开采。 本文的其余部分详细描述了这些步骤。 事实证明,实际使用比特币协议比我想象的要难。 正如您将看到的,该协议有点乱:它使用大端数字、小端数字、固定长度数字、可变长度数字、自定义编码、DER 编码和各种加密算法,似乎在随机的。 因此,将数据转换为正确的格式是一项非常烦人的工作。

直接使用该协议的第二个复杂问题是加密,这是不可原谅的。 如果出现字节错误,则交易会在不知道出了什么问题的情况下被拒绝。

我遇到的最后一个困难是签署交易的过程比必要的要困难得多,有许多细节需要更正。 特别是,交易的签名版本与实际使用的版本有很大不同。

比特币地址和密钥

我的第一步是创建一个比特币地址。 通常,您使用比特币客户端软件来创建地址和相关密钥。 但是,我编写了一些 Python 代码来创建地址,准确显示了幕后发生的事情。 比特币使用各种密钥和地址,因此下图可能有助于解释它们。 首先创建一个随机的 256 位私钥。 需要私钥来签署交易,从而转移(花费)比特币。 因此,私钥必须保密,否则您的比特币可能会被盗。

椭圆曲线 DSA 算法从私钥生成 512 位公钥。 (稍后讨论椭圆曲线密码学。)此公钥用于验证交易签名。 不方便的是,比特币协议为公钥添加了前缀 04。 与大多数公钥公开的系统不同,公钥在交易签署之前不会公开。

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

下一步是生成一个比特币地址与他人分享。 由于 512 位公钥并不方便大,因此使用 SHA-256 和 RIPEMD 哈希算法将其分解为 160 位。 然后使用比特币的自定义 Base58Check 编码将密钥编码为 ASCII。 生成的地址,例如 1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,是人们发布以接收比特币的地址。 请注意,您无法从该地址确定公钥或私钥。 如果您丢失了您的私钥(例如丢掉您的硬盘),您的比特币将永远丢失。

最后,钱包交换格式密钥(WIF)用于将私钥添加到客户端钱包软件中。 这只是将私钥的Base58Check编码转换为ASCII,可以很容易地逆向得到一个256位的私钥。 (我很好奇是否有人会使用上面的私钥来窃取我的 80 美分比特币,当然有人这样做了。)

总而言之,密钥分为三种类型:私钥、公钥和公钥的哈希值,它们使用 Base58Check 编码在 ASCII 之外表示。 私钥是一个重要的密钥,因为它需要访问可以生成其他密钥的比特币。 公钥散列就是您所看到的比特币地址。

我使用以下代码片段生成 WIF 格式的私钥和地址。 私钥只是一个随机的 256 位数字。 ECDSA 加密库从私钥生成公钥。 比特币地址是通过 SHA-256 哈希、RIPEMD-160 哈希,然后使用校验和进行 Base58 编码生成的。 最后将私钥在Base58Check中进行编码,生成WIF编码,用于将私钥输入比特币客户端软件。 注意:此 Python 随机函数的加密强度不高; 如果这样做,请使用更好的功能。

def privateKeyToWif(key_hex):    
    return utils.base58CheckEncode(0x80, key_hex.decode('hex'))
    
def privateKeyToPublicKey(s):
    sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)
    vk = sk.verifying_key
    return ('\04' + sk.verifying_key.to_string()).encode('hex')
    
def pubKeyToAddr(s):
    ripemd160 = hashlib.new('ripemd160')

比特币合约单位_比特币莱特币量子链等交易_比特币合约交易教程

   ripemd160.update(hashlib.sha256(s.decode('hex')).digest())    return utils.base58CheckEncode(0, ripemd160.digest()) def keyToAddr(s):    return pubKeyToAddr(privateKeyToPublicKey(s)) # Warning: this random function is not cryptographically strong and is just for example private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)]) print keyUtils.privateKeyToWif(private_key) print keyUtils.keyToAddr(private_key)

交易中

交易是比特币系统的基本操作。 您可能希望一笔交易只是将一些比特币从一个地址转移到另一个地址,但实际情况要比这复杂得多。 比特币交易在一个或多个输入和输出之间移动比特币。 每个输入都是一个交易和一个提供比特币的地址。 每个输出都是一个接收比特币的地址,以及发送到该地址的比特币数量。

比特币莱特币量子链等交易_比特币合约单位_比特币合约交易教程

上图显示了示例交易“C”。 在此交易中,0.005BTC 取自交易 A 的地址,0.003BTC 取自交易 B 的地址。请注意,箭头是对先前输出的引用,因此返回到比特币流。 ) 对于输出,0.003BTC 指向第一个地址,0.004BTC 指向第二个地址。 剩余的 0.001BTC 作为费用给区块的矿工。 请注意,交易 A 的其他输出中的 0.015 BTC 不会用于此交易。

使用的每个输入都必须在交易中完全花费。 如果一个地址在一笔交易中收到了 100 个比特币,而你只想花费 1 个比特币,那么这笔交易必须花光所有 100 个比特币。解决方案是使用第二个输出进行找零,它将剩余的 99 个比特币返还给你。

交易还可以包括费用。 如果在添加输入和减去输出后还剩下任何比特币,余额就是支付给矿工的费用。 费用不是严格要求的,但免费交易不是矿工的优先事项,可能几天不处理,或完全丢弃。 一笔交易的典型费用为 0.0002 比特币(约 20 美分),因此费用很低但并非微不足道。

手动创建交易

在我的实验中,我使用了一个简单的交易,具有一个输入和一个输出,如下所示。 我从 Coinbase 的比特币开始,将 0.00101234 比特币放入地址 1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5,这是交易 81b4c832...我的目标是创建一个交易,将这些比特币转移到我在上面创建的地址,1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,1 位货币费用减去 0.00 因此,目标地址将收到 0.00091234 个比特币。

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

按照规范,可以很容易地组装未签名的交易,如下所示。 有一个输入使用来自事务 81b4c832 的输出 0(第一个输出)81b4c832...请注意,此事务哈希在事务中被不方便地反转。 输出数量为0.00091234个比特币(91234十六进制为0x016462),以little endian格式存储在value字段中。 加密部分——criptSig 和 scriptPubKey 比较复杂,稍后再讨论。

比特币莱特币量子链等交易_比特币合约单位_比特币合约交易教程

这是我用来生成此未签名交易的代码。 这只是将数据打包成二进制的问题。 签署交易是最困难的部分,如下所示。

# Makes a transaction from the inputs
# outputs is a list of [redemptionSatoshis, outputScript]
def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs):
    def makeOutput(data):
        redemptionSatoshis, outputScript = data
        return (struct.pack("比特币莱特币量子链等交易_比特币合约交易教程_比特币合约单位

       outputTransactionHash.decode('hex')[::-1].encode('hex') + # reverse outputTransactionHash        struct.pack('

比特币交易是如何签署的

下图给出了交易如何签名和链接的简化视图。 考虑一个中间交易,将比特币从地址 B 转移到地址 C。交易的内容(包括之前交易的哈希)被哈希并用 B 的私钥签名。 此外,B 的公钥包含在交易中。

通过几个步骤,任何人都可以验证这笔交易是B授权的。首先,B的公钥必须与之前交易中B的地址相对应,证明公钥是有效的。 (如前所述,地址可以很容易地从公钥中推导出来。) 接下来,可以在交易中使用B的公钥来验证B的交易签名。 这些步骤确保交易有效并得到 B 的授权。比特币的一个意想不到的部分是 B 的公钥只有在交易中使用后才会公开。

使用这个系统,比特币在一系列交易中从一个地址传递到另一个地址。 链中的每一步都可以验证,以确保比特币的有效使用。 请注意,交易通常可以有多个输入和输出,因此链会分支成树。

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

比特币脚本语言

您可能只想通过在交易中包含签名来签署比特币交易,但这个过程要复杂得多。 事实上,在每笔交易中都有一个小程序可以执行,以确定交易是否有效。 该程序是用 Script 编写的,这是一种基于堆栈的比特币脚本语言。 复杂的赎回条件可以用这种语言表达。 例如,托管系统可能要求三个特定用户中的两个必须签署交易才能使用它。 或者可以设置各种类型的合同。

脚本语言非常复杂,大约有 80 种不同的操作码。 它包括算术运算、位运算、字符串运算、条件运算和堆栈运算。 该语言还包括必要的加密操作(SHA-256、RIPEMD 等)作为原语。 为确保脚本终止,该语言不包含任何循环操作。 (因此,它不是图灵完备的。)但在实践中,只支持几种类型的交易。

为了使比特币交易有效,交易脚本的两个部分都必须成功运行。 旧交易中的脚本称为scriptPubKey,新交易中的脚本称为scriptSig。 为验证交易,先执行 scriptSig,然后执行 scriptPubKey。 如果脚本成功完成,则交易有效并且可以使用比特币。 否则,交易无效。 关键是旧交易中的 scriptPubKey 定义了花费比特币的条件。 新交易中的 scriptSig 必须提供满足条件的数据。

在标准交易中,scriptSig 将签名(从私钥生成)推入堆栈,然后是公钥。 接下来,执行 scriptPubKey(来自源交易)来验证公钥,然后是签名。

如脚本中所述,scriptSig 是:

PUSHDATA
signature data and SIGHASH_ALL
PUSHDATA
public key data

scriptPubKey 是:

OP_DUP
OP_HASH160
PUSHDATA
Bitcoin address (public key hash)
OP_EQUALVERIFY
OP_CHECKSIG

执行此代码时,PUSHDATA 首先将签名压入堆栈。 下一个 PUSHDATA 将公钥压入堆栈。 接下来,OP_DUP 将公钥复制到堆栈中。 OP_HASH160 计算公钥的 160 位散列。 PUSHDATA 推送所需的比特币地址。 然后OP_EQUALVERIFY验证前两个栈值是否相等。 新交易的公钥哈希与旧地址中的地址相匹配。 这证明公钥是有效的。 接下来,OP_CHECKSIG 检查交易的签名是否与堆栈上的公钥和签名相匹配。 这证明签名是有效的。

签署协议

我发现签署交易是手动使用比特币最困难的部分,这个过程非常困难且容易出错。 基本思想是使用 ECDSA 椭圆曲线算法和私钥生成交易的数字签名,但细节很棘手。 签名过程已描述为 19 个步骤(更多信息)。 单击下面的缩略图可查看该过程的详细图表。

比特币合约交易教程_比特币合约单位_比特币莱特币量子链等交易

比特币莱特币量子链等交易_比特币合约交易教程_比特币合约单位

最大的复杂是签名出现在交易的中间,这就产生了一个问题,即交易在签署之前是如何签署的。 为避免此问题,在计算签名之前,将 scriptPubKey 脚本从源交易复制到支出交易(即正在签名的交易)中。 然后将签名转换为脚本语言的代码,创建嵌入交易中的 scriptSig 脚本。 似乎在签名期间使用先前交易的 scriptPubKey 是出于历史原因而不是任何逻辑原因。 对于具有多个输入的交易,签名更加复杂,因为每个输入都需要单独的签名,但我不会详细介绍。

让我绊倒的步骤是散列类型。 在签名之前,交易有一个临时附加的哈希类型常量。 对于常规事务,这是 SIGHASH_ALL (0x00000001)。 签名后,这个哈希类型在最后从交易中移除并附加到 scriptSig。

比特币协议的另一个恼人之处在于,签名和公钥都是 512 位的椭圆曲线值,但它们的表示方式完全不同:签名采用 DER 编码,而公钥则表示为普通字节. 还有,两个值都有一个额外的字节,但是位置不一致:SIGHASH_ALL放在签名后面,type 04放在公钥前面。

由于 ECDSA 算法使用随机数,调试签名变得更加困难。 所以每次计算出来的签名都不一样,不能和已知的好签名相比较。

更新(2014 年 2 月):每次签名更改的一个重要副作用是,如果重新签署交易,交易的哈希值将发生变化。 这称为事务可维护性。 第三方也可以通过一些简单的方式修改交易,从而改变哈希值,但不会改变交易的含义。 尽管多年来为人所知,可塑性最近已成为 MtGox 的一个大问题(新闻稿)(2014 年 2 月)。

由于这些复杂情况,我花了很长时间才开始签名。 但最后,我从签名代码中得到了所有的错误,并成功地签署了一笔交易。 这是我使用的代码片段。

def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs):
    myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs)
         + "01000000") # hash code
    s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest()
    sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1)
    sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype
    pubKey = keyUtils.privateKeyToPublicKey(privateKey)
    scriptSig = utils.varstr(sig).encode('hex') + utils.varstr(pubKey.decode('hex')).encode('hex')
    signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs)
    verifyTxnSignature(signed_txn)
    return signed_txn

最终的 scriptSig 包含源地址的签名和公钥 (1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5)。 这证明我可以花掉这些比特币,使交易有效。

比特币合约单位_比特币莱特币量子链等交易_比特币合约交易教程

最终的 scriptPubKey 包含成功花费比特币所必须使用的脚本。 请注意,此脚本将在将来使用比特币时在任意时间执行。 它包含十六进制的目标地址 (1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa) 而不是 Base58Check。 结果是只有地址私钥的所有者才能使用比特币,因此地址实际上就是所有者。

比特币莱特币量子链等交易_比特币合约交易教程_比特币合约单位

最终交易

一旦完成所有必要的方法,就可以组装最终的交易。

privateKey = keyUtils.wifToPrivateKey("5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD") #1MMMM
signed_txn = txnUtils.makeSignedTransaction(privateKey,
        "81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48", # output (prev) transaction hash
        0, # sourceIndex
        keyUtils.addrHashToScriptPubKey("1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5"),
        [[91234, #satoshis
        keyUtils.addrHashToScriptPubKey("1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa")]]

比特币合约交易教程_比特币莱特币量子链等交易_比特币合约单位

       )     txnUtils.verifyTxnSignature(signed_txn) print 'SIGNED TXN', signed_txn

最终交易看起来像这样。 这将上面的 scriptSig 和 scriptPubKey 与前面描述的未签名交易结合在一起。

比特币莱特币量子链等交易_比特币合约单位_比特币合约交易教程

切线:了解椭圆曲线

比特币使用椭圆曲线作为签名算法的一部分。 在解决费马大定理的背景下,我以前听说过椭圆曲线,所以我很好奇它们是什么。 椭圆曲线的数学很有趣比特币合约交易教程,所以我绕道而行,快速概述一下。 椭圆曲线这个名字很容易混淆:椭圆曲线不是椭圆,看起来也不像椭圆,而且跟椭圆关系不大。 椭圆曲线是满足相当简单的方程 y2=x3+ax+b 的曲线。 比特币使用称为 secp256k1 的特定椭圆曲线,其简单方程为 y2=x3+7。

比特币莱特币量子链等交易_比特币合约交易教程_比特币合约单位

椭圆曲线的一个重要特性是你可以使用一个简单的规则来定义曲线上的点的加法:如果你在曲线上画一条直线并且它与三个点 A、B 和 C 相交,则加法由 A+ 定义B+C=0。 由于椭圆曲线的特殊性质,以这种方式定义的加法“正常”工作并形成一个群。 如果定义了加法,则可以定义整数乘法:例如4A=A+A+A+A。

使椭圆曲线在密码学中有用的原因是它们允许快速整数乘法,但除法基本上需要蛮力。 例如,您可以非常快速地计算诸如 12345678A=Q 之类的产品(通过计算 2 的幂),但是如果您只知道 A 和 Q,则很难求解 nA=Q。在椭圆曲线密码学中,密码 12345678 将是私钥, 曲线上的 Q 点就是公钥。

在密码学中,坐标不是使用曲线上的实值点,而是对数字取模的整数。 椭圆曲线的一个令人惊讶的特性是,无论是使用实数还是模运算,数学运算都非常相似。 所以比特币的椭圆曲线看起来不像上图,而是一团乱七八糟的 256 个点(想象一个大的灰色方点)。

椭圆曲线数字签名算法 (ECDSA) 采用消息哈希,然后对消息、私钥和随机数进行一些简单的椭圆曲线算法,以在曲线上生成一个新点来提供签名。 任何拥有公钥、消息和签名的人都可以执行一些简单的椭圆曲线算法来验证签名是否有效。 因此,只有拥有私钥的人才能签署消息,但任何拥有公钥的人都可以验证消息。

有关椭圆曲线的更多信息,请参阅参考资料。

将我的交易发送到 p2p 网络

撇开椭圆曲线不谈,此时我创建了一个交易并对其进行了签名。 下一步是将它发送到点对点网络,在那里它会被矿工接收并合并成一个块。

如何找到同行

使用 p2p 网络的第一步是找到一个点。 每当有人运行客户端时,对等列表每隔几秒就会更改一次。 一旦节点连接到对等点,它们就会通过在发现时交换地址消息来共享新的对等点。 结果,新的同伴迅速在系统中传播开来。

但是,如何找到第一个同伴存在先有鸡还是先有蛋的问题。 比特币客户以多种方式解决这个问题。 几个可靠的对等点在 DNS 中以名称 bitseed.xf2.org 注册。 通过执行 nslookup,客户端获得这些对等点的 IP 地址,并希望其中一个能正常工作。 如果这不起作用,请将对等点的种子列表硬编码到客户端中。

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

随着普通用户启动和停止比特币客户端,peer 进入和离开网络,因此客户端的流动性很大。 我使用的客户端现在不太可能运行,所以如果你想试验,你需要找到新的同行。 您可能需要尝试很多才能找到有效的方法。

与同龄人交谈

一旦我有了工作节点的地址,下一步就是将我的交易发送到对等网络。 使用点对点协议非常简单。 我在端口 8333 上打开到任意对等点的 TCP 连接,开始发送消息,然后依次接收消息。 比特币点对点协议非常宽容; 即使我完全搞砸了请求,同行也会继续沟通。

重要说明:正如一些人指出的那样,如果您想进行实验,您应该使用比特币测试网,它可以让您用“假”比特币进行实验,因为如果您搞砸了真实的网络,很容易丢失您宝贵的比特币。 (例如,如果您忘记更改交易地址,多余的比特币将作为费用支付给矿工。)但我认为我会使用真正的比特币网络并冒价值 1.00 美元比特币的风险。

该协议由大约 24 种不同的消息类型组成。 每条消息都是一个相当简单的二进制 blob,其中包含一个 ASCII 命令名称和适用于该命令的二进制负载。 该协议在比特币 wiki 上有详细记录。

连接到对等点的第一步是通过交换版本消息来建立连接。 首先,我发送一条版本消息,其中包含我的协议版本号、地址和一些其他内容。 对等方发回其版本消息。 此后,节点应使用 verack 消息确认版本消息。 (正如我提到的,该协议是宽松的——即使我跳过那一行,一切正常。)

生成版本消息并不完全是微不足道的,因为它有很多字段,但它可以用几行 Python 创建。 下面的 makeMessage 基于幻数、命令名称和有效负载构建任意对等消息。 getVersionMessage 通过将各种字段打包在一起来创建版本消息的有效负载。

magic = 0xd9b4bef9
def makeMessage(magic, command, payload):
    checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]
    return struct.pack('L12sL4s', magic, command, len(payload), checksum) + payload

比特币合约交易教程_比特币莱特币量子链等交易_比特币合约单位

def getVersionMsg():    version = 60002    services = 1    timestamp = int(time.time())    addr_me = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333)    addr_you = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333)    nonce = random.getrandbits(64)    sub_version_num = utils.varstr('')    start_height = 0    payload = struct.pack('

发送交易:tx

我使用下面精简的 Python 脚本将交易发送到对等网络。 该脚本发送版本消息,接收(并忽略)对等方的版本和 vera 消息,然后将事务作为 tx 消息发送。 十六进制字符串是我之前创建的交易。

def getTxMsg(payload):
  return makeMessage(magic, 'tx', payload)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("97.88.151.164", 8333))
sock.send(msgUtils.getVersionMsg())
sock.recv(1000) # receive version
sock.recv(1000) # receive verack
sock.send(msgUtils.getTxMsg("0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b481000000008a47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000".decode('hex')))

下面的截图显示了我的交易是如何在 Wireshark 网络分析程序中发送的。 我编写了 Python 脚本来处理比特币网络流量,但为了简单起见,我将在这里使用 Wireshark。 “tx”消息类型在 ASCII 转储中可见,在我的事务开始的下一行 (01 00 ...) 之后。

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

为了监控我的交易进度,我有一个套接字向另一个随机对等点开放。 发送我的交易五秒后,另一个节点发送了一条 tx 消息,其中包含我刚刚发送的交易的哈希值。 所以我的交易只需要几秒钟就可以通过点对点网络传递,或者至少是其中的一部分。

胜利:我的交易被开采了

将我的交易发送到点对点网络后,我需要等待它获胜。 十分钟后,我的脚本收到一条带有新块的 inv 消息(请参阅下面的 Wireshark 描述)。 检查这个区块显示它包含我的交易,证明我的交易是有效的。 我还可以通过查看我的比特币钱包并在线查询来验证这笔交易是否成功。 所以,经过一番努力,我设法手动创建了一个交易并让它被系统接受。 (不用说,我的前几次交易尝试都不成功,我的错误交易消失在网络中,再也看不到了。)

比特币合约单位_比特币合约交易教程_比特币莱特币量子链等交易

我的交易是由区块 #279068 中的大型 GHash.IO 矿池开采的,哈希值为 0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee。 (散列在上面的 inv 消息中被反转:ee19...)请注意,散列以很多零开始 - 在 quintillion 值中找到这样的字面值是挖矿如此困难的原因。 这个特定的区块包含 462 笔交易,其中只有一笔是我的。

为了开采该区块,矿工获得了 25 BTC 的奖励,总费用为 0.104 BTC,分别约为 19,000 美元和 80 美元。 我支付了 0.0001 比特币的费用,这大约是 8 美分或我交易的 10%。 挖掘过程非常有趣,但我将把它留到以后的文章中。

综上所述

使用原始比特币协议比我预期的要难,但我一路上学到了很多关于比特币的知识,我希望你也这样做。 我的代码纯粹是为了演示目的——如果你真的想通过 Python 使用比特币,请使用真实的库而不是我的代码。

文章来自官方博客: