主页 > 苹果手机如何下载imtoken > 一篇了解如何使用 Python 创建比特币交易的文章

一篇了解如何使用 Python 创建比特币交易的文章

苹果手机如何下载imtoken 2023-01-17 02:13:38

比特币价格的涨跌,一直牵动着大家非常关心的小心脏。从去年初的 800 美元左右,到去年底飙升至 1978 年的最高点 21 美元。不到一年的时间,升值率接近25倍。虽然已经回落到8000多美元的价格,但价格几乎比去年同期高出一个数量级。币圈人士“过去一年赚的比过去10年多”,这是不争的事实。

对于区块链开发者来说,据说已经有每年500万元的天价了。因此,“跑进区块链”成为了很多程序员的共识。但是看到了很远的距离,如何才能快速上手呢?国外网友Ken Shirriff在博客中分享了他在手工茶古剑比特币交易中的代码以及他对比特币协议的体验。区块链大本营编译如下。

最近,媒体行业对比特币表现出极大的热情,这激发了我认真学习比特币的工作原理,从网络底层的数据流开始。通常人们使用钱包软件进行比特币交易。钱包软件对用户隐藏比特币交易过程,同时方便用户使用。我想亲自体验比特币交易。我的目标是在 Python 中手动创建比特币交易。一笔比特币交易,将交易以十六进制数据的形式广播到比特币网络,然后观察交易是如何加入区块链的。事实证明,这个过程很有趣,我希望你也会对此感兴趣。

在本文中,我将首先对比特币进行简单的概述,然后从以下几个方面引导大家学习比特币:创建比特币地址(比特币中的账户),进行比特币交易,签署交易,将交易广播到比特币网络,最后等待交易确认。

比特币简介:

首先,我将解释比特币系统的工作原理,然后我将深入了解全部细节。比特币是一种基于点对点网络的电子货币。您可以用现金在线购买比特币并将比特币转移给其他人。在某些商家中,您可以像支付宝一样使用比特币付款。当然,你也可以卖掉它。持有的比特币可以兑换成现金。

简而言之,在比特币网络中,分布式账本(区块链)随时记录并更新每个比特币的所有权。与银行不同,比特币不与个人或个人账户绑定。相反,比特币只属于一个比特币地址,如:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。说到这里,你可能已经糊涂了,这个角色中是否隐藏着比特币?当然不是,比特币地址是比特币网络中的一个身份,或者可以说是你在比特币中开的一个“银行账户”,我们用这个“账户”进行交易。在网站:blockchain.info,你可以找到所有的交易信息:

比特币账户信息

但是我如何证明这个帐户是我的呢?别着急,先往下看,我会一一解答你的问题。

比特币交易

如何像现金一样使用比特币?答案是创建一个事务。在一次交易中,比特币的所有者(上面提到的是比特币地址)将所有权转移到一个新的比特币地址。比特币的颠覆性创新之一是通过鼓励节点记账(也称为矿工挖矿)将交易记录保存在分布式数据库中。交易按区块分组,大约每十分钟在比特币网络中生成一个新区块,并成为交易记录的一部分,称为区块链。添加到区块链的交易可以被认为是成功的交易。现在的问题是,谁来为你保管账目?这是一个矿工。矿工的挖矿过程是将账户记录在区块链中。矿工需要验证每笔交易是否正确。验证后,矿工开始计算一个困难的数学问题(密码学中的哈希函数)。 ),最先想出答案的人可以生成一个区块,也就是挖出一个新区块,这个区块就会成为区块链的新部分。

也许你会问,明明是做会计,做会计工作,为什么还要叫挖矿呢?与传统的地下石头开采一样,比特币开采也将获得回报。挖矿是发行新比特币的过程。目前,每挖出一个矿场,矿工就会从系统中获得 12.5 个比特币作为奖励。这是 1 美元2. 50,000 美元的巨款。此外,矿工还可以获得该区块中的所有交易费用。例如,在区块高度 512587 中,幸运矿工总共收获了 12.829 个比特币。正因为如此,矿工之间的竞争非常激烈。挖矿的难度和矿工之间的激烈竞争是比特币安全的重要保障,因为它可以确保没有坏人可以操纵系统。

点对点网络

比特币没有中央服务器,而是在点对点网络上运行。如果你运行一个比特币节点,你就会成为网络的一部分。比特币网络中的节点相互交换其存储的交易、区块和 IP 地址信息(用于在节点之间建立连接以相互通信)。当您第一次连接到比特币网络时,您的节点会从随机选择的节点下载有关区块链的信息。反过来,您的节点向迟到的加入者提供信息。当你想创建一个比特币交易时比特币的交易数据怎么看,你将交易发送到一些节点,这些节点在比特币网络中广播交易,直到整个网络收到交易。矿工将收集您的交易信息,生成包含您交易的区块,并将其广播到整个网络。这时候你的节点也会收到区块信息。验证后,将交易添加到区域。在区块链中,你的交易是成功的。

加密技术

现在回到证明比特币账户是谁的问题。比特币使用数字签名技术确保只有比特币账户的所有者才能使用账户中的比特币。比特币地址的所有者拥有与该地址匹配的私钥,当你花费比特币时,你用这个私钥签署交易,证明自己是账户的所有者。这有点像现实生活中的盖章,盖章意味着授权。如何验证,公钥与比特币账户相关联,可用公钥验证签名是否正确。这就解决了比特币账户是谁的问题。

如何区分不同的交易?交易和区块使用加密哈希值进行索引。是不是有点眼熟?是的,在比特币协议中,很多地方都用到了哈希函数。 .

其实比特币不是这样的。

探索比特币协议

在下一篇文章中,我将向您介绍如何手动进行比特币交易。首先,我生成了一个比特币账户和对应的公钥和私钥。接下来我发起一个比特币交易,我将少量比特币转移到这个新生成的账户。在此期间手动签署此交易很困难,并且花费了我很多时间。最后,我将此交易发送到比特币网络并等待它被添加到区块链中。本文的其余部分将详细介绍这些步骤。

事实证明,手动进行比特币交易比我想象的要困难。正如你所看到的,比特币的协议有点混乱:它使用大端数(寻址高位字节,将高位字节存储在起始地址),小端数(寻址低位,存储低位) order bytes 序数字节存储在起始位置),定长数字,可变长度数字,自定义编码格式,DER编码格式,以及各种加密算法。因此,仅仅将数据转换为正确的格式就浪费了很多时间。

我遇到的第二个困难是加密,试试手动加密,你会发现密码学对人是多么的不友好,甚至是无情的。即使你只输入了一个字节,交易也会因为错误而被拒绝,并且它不会告诉你出了什么问题,你只需要重新开始。

最后,手动签署交易的过程也比想象的要困难得多,交易的每一步都必须零错误地签署,否则必须退回并重新开始。

比特币地址和密钥

在第一步中,我创建了一个比特币地址。通常,人们使用比特币客户端软件来创建比特币地址和与之相关的密钥。抱着学习的态度,写了一些Python代码生成比特币地址,揭示了地址创建的机制。

比特币使用一系列密钥和地址,下图解释了它们的关系。首先,您创建一个随机的 256 位私钥,用于在花费比特币时签署交易。因此,私钥必须保密,否则您的比特币可能会被盗。

椭圆曲线数字签名算法(ECDSA,美国政府标准,我们将在后面讨论)从私钥生成 512 位公钥,用于验证交易的签名。但是不方便的是,比特币协议需要给这个公钥加上前缀04,这个公钥在交易签署之前是不会泄露的,不像其他系统中公钥是用来公开的。

比特币地址与公钥的关系

下一步是生成与他人交易时使用的比特币地址。 512 位的公钥太长而无法使用,因此使用 SHA-256 和 RIPEMD 散列算法将其减少到 160 位。然后使用比特币定义的 Base58Check 编码将密钥编码为 ASCII(美国信息交换标准代码)格式。获得的地址(例如:上面的1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa)就是你在收到别人的比特币时要发布的地址。请注意,您无法从比特币地址恢复公钥或私钥。如果您丢失了您的私钥(假设您将其存储在硬盘上,但硬盘丢失了),您的比特币将永远丢失。

最后,Wallet Exchange Format Key (WIF) 用于将私钥添加到你的钱包软件中,这只是将私钥通过 Base58Check 编码转换为 ASCII 格式,这一步是可逆的,易于逆向转换恢复256 位私钥。 (我的私钥在图片中,我很好奇是否有人会使用我的私钥窃取(用私钥签署交易,从而转移)我价值 80 美分的比特币,但有人这样做了,可在

看,也算是对教学的贡献吧。 )

简而言之,密钥分为三种:私钥、公钥和公钥的哈希值。使用 Base58Check 编码后,它们都以 ASCII 格式向外界表达。私钥是其中最重要的,因为在花费比特币时需要它来签署交易,而所有其他密钥都可以从私钥生成。公钥的哈希值就是你刚才看到的比特币地址。

我使用下面的代码片段来生成 WIF 格式的私钥和地址。私钥只是一个随机的 256 位数字。公钥是使用椭圆曲线数字签名算法从私钥生成的。公钥使用 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)

复制

keyUtils.py

从内部分析交易

交易是比特币系统的基本操作。你可能认为交易只是将比特币从一个地址转移到另一个地址,但交易并不是那么简单。一笔交易由一个或多个输入和输出组成,交易中的每个输入地址都提供比特币,每个输出地址都接受比特币。

比特币的交易数据怎么看

一个简单的比特币交易,交易C花费了从交易A和交易B获得的0.008个比特币,其中0.001个比特币作为交易费用支付给矿工

上图显示了一个简单的比特币交易“C”,其中在交易A中获得了0.005个比特币,在交易B中获得了0.003个比特币。(图中箭头指向从新交易的输入到获得这些比特币的交易的输出,所以比特币的流动方向与箭头的相反。)对于输出,有 0.003 个比特币给了第一个第二个比特币地址有0.004个比特币,剩余的0.001个比特币作为交易费支付给矿工。请注意,此交易不会影响交易 A 中的其他比特币,输出为 0.015。

在一次交易中,输入的比特币地址必须花掉所有比特币,如果你在上一笔交易中收到了100个比特币,但你只想花1个比特币,要创建这个交易你必须花掉所有100个比特币,那怎么办剩下的 99 个比特币?解决方案是在交易中再增加一个输出,将剩余的 99 个比特币转移给自己。这样你就可以花费任意数量的比特币。

交易通常会支付交易费用。如果交易中输入比特币的总和大于输出比特币的总和,则剩余费用为矿工的交易费用。这笔费用没有明确要求,但对于矿工来说,没有交易费用的交易被归类为低优先级交易,可能需要几天时间才能处理,甚至被矿工直接丢弃。交易费用通常不高,但会影响您的交易。

手动创建交易

如下图所示,在我的实验中,我发起了一个一入一出的交易。我在 Coinbase 上买了一些比特币,并把 0.00101234 个比特币放入地址:

在1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5中,交易哈希为:

81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48,我的目标是创建一个交易将这些比特币转移到我的其他地址:

1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,扣除0.0001比特币的交易费用后,目的地址将收到0.00091234比特币。

比特币交易结构示例

Blockchain.info 上的交易历史

根据协议标准,创建此事务很简单。如下表所示,这个交易只有一个输入,源自81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48中的输出0(第一个输出)。输出为 0.00091234 个比特币(91234 是十六进制的 0x016462),它以 little-endian 格式存储在 value 字段中。加密过程中的scriptSig和scriptPubKey就比较复杂了,我们后面再讨论。

版本

01 00 00 00

输入计数

01

输入

先前的输出哈希(反转)

48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81

以前的输出索引

00 00 00 00

脚本长度

脚本签名

包含签名的脚本

顺序

ff ff ff ff

输出计数

01

输出

价值

62 64 01 00 00 00 00 00

脚本长度

scriptPubKey

包含目标地址的脚本

区块锁定时间

00 00 00 00

这是我用来生成交易的代码,这段代码只是将数据打包成一个二进制文件。签约比较困难,我们稍后再谈。

# 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("

比特币的交易数据怎么看

复制

txnUtils.py

如何签署比特币交易

下图简要描述了交易是如何签署和相互连接的。对于从比特币地址 B 到比特币地址 C 的中间转移。交易的内容(包括上一笔交易的哈希(索引))经过哈希处理并用 B 的私钥签名。另外,B的公钥也包含在交易中。

任何人都可以通过执行一些简单的操作来验证 B 是否签署了交易。首先,B 的公钥与之前收到比特币交易的地址进行验证,证明 B 的公钥是有效的。 (如前所述,地址很容易从公钥计算出来)。接下来可以通过B的公钥来验证B的交易签名的真实性。这些步骤确保了交易是有效的,并且交易是由 B 授权的。比特币与其他人的不同之处在于 B 的公钥在 B 发起交易之前是不公开的。

在比特币系统中,比特币通过区块链上的交易在不同地址之间转移。区块链上的每一笔交易都可以被验证,保证比特币交易的有效性。

比特币交易的互联

比特币脚本语言

您可能认为只需在交易上附加签名即可签署比特币交易,但事实并非如此,过程非常复杂。事实上,每一笔交易都包含一个确认交易有效的“小程序”。这个“小程序”是用脚本语言编写的,通过这种基于堆栈的比特币脚本语言,我们可以处理很多复杂的比特币支付场景。例如,托管系统可以设置规则,只要三分之二的用户授权就可以执行交易,或者设置其他合约。

脚本语言非常复杂,大约有 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 操作首先将签名压入堆栈,然后将公钥压入堆栈。 OPHASH-160 操作计算公钥的 160 位哈希值,PUSHDATA 操作将交易中的输入地址(输入帐号)推入堆栈,然后 OP-EQUALVERIFY 操作验证中的值前两个堆栈相等(验证)。你在本次交易中使用的比特币是否属于你)——如果公钥的哈希值等于上一次交易中的输出地址,则证明公钥有效(证明这个比特币是你的)。最后,OP_CHECKSIG操作会检查交易的签名是否与栈中的公钥和签名匹配,匹配则证明签名有效(证明交易是你授权的)

签署交易

我发现签署这笔交易是手动使用比特币最难的部分,这个过程非常困难且容易出错。签名的基本思路很简单,使用椭圆曲线签名算法和私钥生成交易的数字签名,但细节比较棘手。签署交易的过程可以用这 19 个步骤来描述。

签署交易的 19 个步骤

签署交易对我来说是一个巨大的挑战,这涉及到在交易内容中没有添加签名的情况下如何签署交易。为了避免这个问题,在计算生成的签名之前,我将之前交易中的scriptPubKey复制到当前交易中(当前交易正在被签名),然后将签名转换成脚本语言的代码,创建一个嵌入到当前交易。交易中的 scriptSig 脚本。对于有多个输入的交易,对交易进行签名比较复杂,因为每个输入都需要一个单独的签名,这里不再详细讨论。

哈希步骤难倒了我。在签名之前,交易中有一个临时附加的哈希常量。对于常规交易,该值为 SIGHASH_ALL (0x00000001)。签名后,该哈希值将从交易内容的末尾移除,并附加到 scriptSig 脚本中。

比特币中另一个令人讨厌的事情是,虽然签名和公钥都是 512 位椭圆曲线值,但它们的表示方式完全不同:签名以 DER 编码进行编码,而公钥以纯字节表示。还有,这两个值都有一个额外的字节,但是位置并不一致:额外的哈希常量SIGHASH_ALL放在签名之后,值04放在公钥之前。

调试签名很困难,因为 ECDSA 算法需要随机数。计算的签名每次都会不同,因此无法与已知的正确签名进行比较。

由于上述复杂性,我花了很长时间才获得签名。但是,最后我找出了签名代码中的所有错误,并成功地用它签署了一笔交易。这是我使用的签名代码:

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 signed2_txn

复制

txnUtils.py

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

推送数据 47

47

签名(DER)

顺序

30

长度

44

比特币的交易数据怎么看

整数

02

长度

20

X

2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 a6 76 c5 4f f7 13

整数

02

长度

20

是的

6c 66 24 d7 62 a1 fc ef 46 18 28 4e ad 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82

SIGHASH_ALL

01

推送数据 41

41

公钥

类型

04

X

14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bf 3d 8a 40 4c fb d0 70 4f 13 5b 6a d4 b2 d3 ee 75 13

是的

10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cd

最终的 scriptPubKey 脚本包含成功使用比特币时必须执行的脚本。需要注意的是,当这些比特币在未来被使用时,这个脚本将会被执行。它包含目标地址1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,用十六进制表示,而不是Base58Check,脚本的作用是只有这个目标地址的私钥拥有者才能消费比特币,所以目标地址实际上就是这些比特币的拥有者

OP_DUP

76

OP_HASH160

a9

推送数据 14

14

公钥哈希

c8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5c

OP_EQUALVERIFY

88

OP_CHECKSIG

交流

最终交易

经过以上一系列操作,我们就完成了最终的交易。不过别忘了此时的交易还没有加入区块链,收款人还没有收到你的比特币。

比特币的交易数据怎么看

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

复制

makeTransaction.py

最终交易信息如下:

版本

01 00 00 00

输入计数

01

输入

先前的输出哈希(反转)

48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81

以前的输出索引

00 00 00 00

脚本长度

8a

脚本签名

47 30 44 02 20 2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 f6 76 c5 4f f7 13 02 20 6c 66 64 d7 62 a1 28 4e 广告 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82 01 41 04 14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bfb 3d5b 74 8a 4 bd3 6 ee 75 13 10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cd

顺序

ff ff ff ff

输出计数

01

输出

价值

62 64 01 00 00 00 00 00

脚本长度

19

scriptPubKey

76 a9 14 c8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5c 88 ac

区块锁定时间

00 00 00 00

插曲:椭圆曲线签名

比特币的签名算法采用椭圆曲线签名算法,这么实用的功能,你可能想知道它是怎么做到的?当英国数学家安德鲁·怀尔斯攻克费马大定理时,我第一次接触到了椭圆曲线的算法。椭圆曲线的数学思想很有意思,这里我就给大家简单介绍一下。

椭圆曲线这个名字很容易混淆,因为椭圆曲线不是椭圆,看起来也不像椭圆,甚至椭圆曲线也和椭圆几乎没有相关性。通俗地说,椭圆曲线是满足简单方程 y^2 = x^3 + ax + b 的曲线。比特币中使用的椭圆曲线称为secp256k1,它满足方程y^2 = x^3 + 7。

secp256k1椭圆曲线

椭圆曲线的一个重要特性是,您可以使用一个简单的规则来定义椭圆曲线上的点相加:如果您在曲线上画一条线,则该线与曲线在点 A、B 和 C 处相交。 , 那么这个加法定义为 A+B+C=0。根据这个加法的定义,我们可以定义整数乘法:例如 4A = A + A + A + A。

为什么椭圆曲线在密码学中很有用?因为椭圆曲线对于整数乘法速度很快,所以它们需要蛮力进行除法。例如,您可以快速计算乘法 12345678*A = Q,但是如果您只知道 A 和 Q,则在 n*A=Q 中求解 n 是非常困难的。所以在椭圆曲线算法中,这里 12345678 将是私钥并且曲线上的点 Q 将是公钥。

在密码学中,一个点的坐标不是它在曲线上的实值点,而是一个整数的模数。椭圆曲线的一个很好的特性是对实数或模数进行运算的数学运算几乎相同。正因为如此,比特币的椭圆曲线不像上图,而是一堆乱七八糟的 256 位点(想想一个充满了很多乱点的空间)。

椭圆曲线数字签名算法(ECDSA)接收交易的哈希值,使用交易数据、私钥和随机数在椭圆曲线上生成一个新点,从而对交易进行签名。任何拥有公钥、交易数据和签名的人都可以通过简单的椭圆曲线运算来验证签名的有效性。通过阅读本文,您应该明白为什么只有拥有私钥的人才能签署消息,而拥有公钥的人才能验证消息。

比特币的交易数据怎么看

将交易发送到比特币网络

回到交易,别忘了此时我们的交易还没有加入区块链,它不是有效交易。我刚刚创建并签署了一笔交易。下一步是将这笔交易发送到比特币网络,网络中的矿工收集交易并将其打包成块。

如何在比特币网络中寻找节点

首先我想在比特币的点对点网络中找到一个节点。随着节点的进出,节点列表会动态更新。当一个比特币节点连接到另一个节点时,它们会不断地相互交换新发现的比特币节点信息,因此新节点加入的消息会迅速传遍全网。互联网。

但是,新的比特币节点如何首先找到比特币节点?这是一个先有鸡还是先有蛋的问题。比特币节点以多种方式解决了这个问题。几个受信任的比特币节点将注册在 DNS 系统(域名系统,一个在万维网上将域名和 IP 地址相互映射的分布式数据库),域名为 bitseed.xf2.org) ,通过执行nslookup命令,可以得到这些节点的IP地址,只要其中一个还在工作。如果不幸的是它们都不起作用,您可以尝试连接到那些在您的客户端中硬编码的地址。

Nslookup 命令可用于查找比特币节点

当用户启动或停止比特币客户端时,节点会加入或离开比特币网络。因此,连接节点存在很多不确定性。在我的实验过程中,我遇到了连接的节点已经离开比特币网络的情况。如果要重复我的实验,最好多找几个节点,可能需要多次。 Try to find a running node.

Communicate with bitcoin nodes

Once I obtained the IP address of a working bitcoin node, the immediate priority was to send my transaction through this node to bitcoin's peer-to-peer network. Using a peer-to-peer network protocol is simple enough, I open a TCP connection on port 8333 to any peer, send a message, and then receive a feedback message. Bitcoin's peer-to-peer protocol is user friendly and continues to communicate with me even when my request data is wrong.

IMPORTANT: As some have pointed out, if you want to repeat my experiment, remember to use Bitcoin's testnet, where you can use "virtual" bitcoins for transactions. Because on the real network, if you're not careful, you can lose all your bitcoins. Remember the 100 bitcoin transfer 1 transaction mentioned above, if you forget to transfer the remaining bitcoins to yourself, the remaining 99 bitcoins will be paid to the miners as transaction fees. But for the sake of science, I don't care about losing my $1 worth of bitcoins in the real bitcoin network.

The protocol contains 24 different kinds of information. Each message is a simple binary large object (BLOB, a container that can store binary files) that contains an ASCII command and a binary valid argument for the command. The protocol can be found on the Bitcoin wiki.

The first step in connecting to the Bitcoin network is to establish a connection by exchanging client version information. First, I sent a client version message with my protocol version number, IP address, and other stuff. The Bitcoin node also replied to me with its version information. After this, I should reply with a verack message (version acknowledgement, version confirmation) to confirm its version information. As I said, the bitcoin peer-to-peer network protocol is very user friendly, even if I skip the verack message, everything works fine after that.

This step of exchanging version information is not easy because the information has a standard format, but don't be afraid, it can be created with a few lines of code. The makeMessage function in the code snippet below can generate a message from a random number, command name, and command arguments. The getVersionMessage function creates parameters for the version message by packing the fields together.

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('

复制

msgUtils.py

Send transaction tx

I use the following simplified Python code to send my transaction to the Bitcoin network. This code sends a client version message and accepts (or ignores) the version information and verack information of the Bitcoin node. Finally send my transaction as tx message. The hex string in the code is the transaction I created earlier.

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')))

复制

minimalSendTxn.py

The following screenshot of Wireshark (a software that captures and analyzes network packets) shows how I send transactions into the Bitcoin network. I wrote a script in Python to analyze network data, here I use Wireshark for simplicity. You can see my tx transaction from the picture.

Wireshark captures this transaction tx that is being uploaded to the Bitcoin network

In order to monitor the progress of my transaction in real time, I ran a new node in the Bitcoin network. After 5 seconds of sending my transaction to the Bitcoin network, another node sent me this tx message, It contains the hash of the transaction I just sent, so in just a few seconds, my transaction has spread all over the Bitcoin network, at least part of it.

Transaction succeeded: my transaction was added to the blockchain

After sending my transaction to the Bitcoin network, I need to wait for it to be mined and added to the blockchain before I can declare my experiment a success. 10分钟后,我的比特币节点收到一条含有新区块信息的inv消息(参见下图Wireshark抓到的网络封包),检查这个区块后发现我的交易被包含在了区块中,证明我的交易是有效的,我的实验成功了。通过我的比特币钱包软件和在线查询,再一次确认了我已经交易成功。可以说,经过不断的努力,我成功手动创建了一笔交易,并让比特币系统接受了它。 (当然了,我也经过了几次失败的尝试,这些错误的交易都消失在了网络之中,永远都不会被检索到。

Wireshark中抓取的新区块产生的封包信息

我的交易是被当时哈希算力(挖矿速度)最大的矿池(多个矿工一起挖矿)GHash.IO挖出,区块高度为279068,区块哈希为0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee,在上图Wireshark数据包中inv消息的哈希值是经前后反转得到的ee192……。你应该会发现区块的哈希值以大量的0开头,在一个16进制的哈希值中发现一个以这么多0开头的数,这就是为什么挖矿如此困难的原因。这个区块中由462笔交易,我的交易是其中之一。

高度为279068的区块以及我发起的这笔交易

()

挖到这个区块的矿工们收到了25个比特币的奖励,交易费总共是0.104个比特币,按当时的市价分别为19000美元和80美元。我支付了0.0001个比特币的交易费,大约是我交易额的10%,按当时的市价为8美分。

结论

手动进行比特币交易比我想象中困难得多,但是在这个过程中我学到了很多,希望你也是。我的Python代码仅仅是为了介绍,如果你想跟我一样用Python手动进行比特币交易,也可以试试这几个项目。

写在最后

2017年是区块链的井喷之年,经过一年的积攒,2018年将迎来区块链的落地之年,区块链会逐渐颠覆各行各业。对于个人,区块链的机会会越来越多,也许你错过了比特币的投资,不妨现在抓住区块链这个风口,投资自己,多学习相关知识,区块链大有可为,投身区块链的你将大有作为!

顺便打个广告,链接如下,感兴趣的同学不妨点进去看看。

孟岩领衔研发PDJ区块链全阶实战课程二期班正式面市

大家可以比较市面上的不同类型区块链课程,货比三家,选适合自己的,赶紧上车是王道。