主页 > imtoken手机钱包 > 比特币核心技术开发交易(上)

比特币核心技术开发交易(上)

imtoken手机钱包 2023-03-28 07:30:17

前言

交易是比特币的核心,区块链的唯一目的就是安全可靠地存储交易。 在区块链中,交易一旦创建,任何人都无法修改或删除。 今天,我们将开始实现事务。 但是,由于交易是一个很大的话题,我将其分为两部分: 在今天的部分中,我们将实现交易的基本框架。 在第二部分,我们将继续讨论它的一些细节。

由于比特币采用的是UTXO模型,而非账户模型,因此“余额”的概念并不直接存在,需要遍历整个交易历史来获取余额。

比特币交易

请在 blockchain.info 上查看下图中的交易信息。

1

交易由一些输入和输出组成:

类型交易结构{

ID []字节

Vin[]TX输入

Vout []TXOutput }

对于每一笔新的交易,它的输入都会参考上一笔交易的输出(这里有个例外,coinbase交易)bcc发送到btc地址,参考就是花费的意思。 所谓引用之前的输出,就是将之前的输出包含在另一笔交易的输入中,也就是花费之前的交易输出。 交易的输出是硬币实际存储的地方。 下图说明了事务之间的相互关系:

注意:

有些输出与输入无关。 之前一笔交易的输入可以引用之前多笔交易的输出。 输入必须引用输出。

在整篇文章中,我们将使用“金钱”、“硬币”、“花费”、“发送”、“帐户”等词。 但是在比特币中,没有这样的概念。 一个交易只是一个脚本(script)来锁定(lock)一些值(value),而这些值只能被锁定它们的人解锁(unlock)。

每笔比特币交易都会产生一个输出,并记录在区块链上。 将比特币发送给某人实际上意味着创建一个新的 UTXO 并将其注册到该人的地址,该地址可供他使用。

交易输出

让我们从输出开始:

bcc发送到btc地址_btc钱包地址怎么弄_btc充错地址找回

类型 TXOutput 结构 {

值整数

ScriptPubKey 字符串 }

输出主要包括两部分:

一定数量的比特币(Value)是一个锁定的脚本(ScriptPubKey)。 要花这笔钱,必须解锁剧本。

其实就是存储“币”的输出(注意,就是上面的Value字段)。 这里的存储指的是用一个数学谜题锁定输出,这个谜题保存在ScriptPubKey中。 在内部,比特币使用一种称为 Script 的脚本语言来定义锁定和解锁输出的逻辑。 虽然该语言相当原始(故意避免潜在的黑客攻击和误用)且不复杂,但我们不会在这里讨论它的细节。 您可以在此处找到详细说明。

在比特币中,值字段存储的是聪的数量,而不是 BTC 的数量。 一聪等于一比特币的百万分之一(0.00000001 BTC),这也是比特币中最小的货币单位(如一美分硬币)。

由于地址尚未实现,我们将暂时避免涉及逻辑的完整脚本。 ScriptPubKey 将存储一个任意字符串(用户定义的钱包地址)。

顺便说一句,有了这样的脚本语言,也意味着比特币实际上可以作为一个智能合约平台。

关于产出的一个非常重要的一点:它们是不可分割的。 也就是说,你不能只引用它的一部分。 要么不用,要用就得一下子用完。 当在新交易中引用输出时bcc发送到btc地址,输出必须完全花费。 如果它的值大于要求,则生成一个更改返回给发送者。 这与现实世界的场景非常相似,当你想支付时,如果某件东西价值 1 美元,你给了 5 美元的钞票,那么你会得到 4 美元的找零。

发送硬币

现在,我们想发送一些硬币给其他人。 为此,我们需要创建一个新交易,将其放入一个区块中,然后挖掘该区块。 以前我们只实现了 coinbase 交易(这是一种特殊的交易),现在我们需要一个通用的普通交易:

func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction { var inputs []TXInput var outputs []TXOutput

acc, validOutputs := bc. FindSpendableOutputs(来自,数量)

如果账户 < 金额 {

log.Panic("错误:资金不足")

}

// 构建输入列表

btc钱包地址怎么弄_btc充错地址找回_bcc发送到btc地址

对于 txid,outs := range validOutputs {

txID,错误:=十六进制。 解码字符串(txid)

对于 _, out := range outs {

输入 := TXInput{txID, out, from}

输入=追加(输入,输入)

} }

// 构建输出列表

outputs = append(outputs, TXOutput{amount, to})

如果账户 > 金额 {

outputs = append(outputs, TXOutput{acc - amount, from})

// 改变 }

tx := 交易{无,输入,输出}

TX。 设置名称()

返回 &tx}

在创建新的输出之前,我们必须首先找到所有未花费的输出并确保它们具有足够的价值(value),这就是 FindSpendableOutputs 方法所做的。 然后,对于找到的每个输出,都会创建一个引用该输出的输入。 接下来,我们创建两个输出:

一个被收件人地址锁定。 这是将硬币实际转移到其他地址。 一个被发件人地址锁定。 这是一个变化。 只有当未花费的输出超过新交易所需时才会产生。 记住:输出是不可分割的。

FindSpendableOutputs 方法基于之前定义的 FindUnspentTransactions 方法:

func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (

btc充错地址找回_bcc发送到btc地址_btc钱包地址怎么弄

整数,映射[字符串][]整数){

unspentOutputs := make(map[string][]int)

未花费的 TXs := bc. FindUnspentTransactions(地址)

累积 := 0 工作:

对于 _, tx := range unspentTXs {

txID := hex.EncodeToString(tx.ID)

对于 outIdx,out := range tx.Vout {

如果 out.CanBeUnlockedWith(address) && 累积 < 金额 {

累积 += out.Value

unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)

如果累计 >= 金额 {

休息工作}

}

} }

返回累积的、未花费的输出

}

此方法遍历所有未花费的交易并累积其价值。 当累计值大于或等于我们要发送的值时,它停止并返回累计值,以及按交易 ID 分组的输出索引。 我们只需要提取足够的钱来支付。

现在,我们可以修改 Blockchain.MineBlock 方法:

btc钱包地址怎么弄_bcc发送到btc地址_btc充错地址找回

func (bc *Blockchain) MineBlock(transactions []*Transaction) {

... newBlock := NewBlock(transactions, lastHash)

...}

最后,让我们实现发送方法:

func (cli *CLI) send(from, to string, amount int) {

bc := NewBlockchain(来自)

推迟 bc.db.Close()

tx := NewUTXOTransaction(from, to, amount, bc)

公元前。 MineBlock([]*交易{tx})

fmt.Println("成功!")}

发送硬币意味着创建新的交易并通过挖掘新区块将其打包到区块链中。 然而,比特币不会一次性完成所有这些事情(尽管我们当前的实现是这样做的)。 相反,它将所有新交易放入一个内存池中,然后当矿工准备好挖掘一个新区块时,它会从内存池中提取所有交易,创建一个候选区块。 只有当包含这些交易的区块被挖掘并添加到区块链时,其中的交易才开始被确认。

让我们检查发送硬币是否有效:

$ blockchain_go send -from Ivan -to Pedro -amount 6 00000001b56d60f86f72ab2a59fadb197d767b97d4873732be505e0a65cc1e37

成功! $ blockchain_go getbalance -address Ivan 'Ivan' 的余额:4

$ blockchain_go getbalance -address Pedro 'Pedro' 的余额:6

非常好! 现在,让我们创建更多交易以确保从多个输出发送硬币也能正常工作:

$ blockchain_go send -from Pedro -to Helen -amount 2 00000099938725eb2c7730844b3cd40209d46bce2c2af9d87c2b7611fe9d5bdf

成功!$

bcc发送到btc地址_btc充错地址找回_btc钱包地址怎么弄

blockchain_go 发送 - 从伊万 - 到海伦 - 数量 2

000000a2edf94334b1d94f98d22d7e4c973261660397dc7340464f7959a7a9aa

成功!

现在,Helen 的硬币被锁定在两个输出中:一个来自 Pedro,一个来自 Ivan。 让我们将它们发送给其他人:

$ blockchain_go send -from Helen -to Rachel -amount 3

000000c58136cffa669e767b8f881d16e2ede3974d71df43058baaf8c069f1a0

成功! $ blockchain_go getbalance -address Ivan 'Ivan' 的余额:2

$ blockchain_go getbalance -address Pedro 'Pedro' 的余额:4

$ blockchain_go getbalance -address Helen 'Helen' 的余额:1

$ blockchain_go getbalance -address Rachel 'Rachel' 的余额:3

看起来不错! 现在,测试一些失败案例:

$ blockchain_go send -from Pedro -to Ivan -amount 5

恐慌:错误:资金不足

$ blockchain_go getbalance -address Pedro 'Pedro' 的余额:4

$ blockchain_go getbalance -address Ivan 'Ivan' 的余额:2

总结

这并不容易,但现在终于达成协议! 但是,我们仍然缺少一些关键功能,例如比特币:

地址。 我们还没有基于私钥的真实地址。 奖赏(奖赏)。 现在挖矿肯定是赚不到钱的! UTXO 集合。 获取余额需要扫描整个区块链,当块很多时,这可能需要很长时间。 另外,如果我们要验证后续交易,将需要很长时间。 UTXO 集合就是为了解决这些问题,加速交易相关的操作。 内存池(mempool)。 在交易被打包成区块之前,这些交易存储在内存池中。 在我们目前的实现中,一个区块只包含一笔交易,效率很低。来源:Blockchain Research Lab

本文使用有道云笔记编辑点击体验