比特币入门-2-交易

对比特币来说,其他的所有模块都是为了保证交易的生成,在比特网络中得以传播和验证,并最终被添加在比特币总账簿

如果说钱包提供了身份证明和财产的证明和锁定,那么交易提供了财产的流动,如果失去了交易,那么钱包存在的意义并不大

一笔比特币交易的生命周期起始于它被创建的那刻(诞生 origination),任何人都可以发起交易,哪怕是和自己无关的交易。 随后,比特币交易会被以个或者多个签名加密,这些签名标志着对该交易指向的比特币资金的使用许可。接下来,比特币交易被广播到比特币网络中,每个节点都会做(比特币交易参与者)验证、如果交易信息通过验证,就将交易在网络中继续广播,同时,交易发起者会收到一条表示交易有效并被接受的返回信息。如果这笔交易被验证为有效,这个节点会拒绝接受这笔交易且同时返回给交易发起者一条表示交易被拒绝的信息,直到这笔交易被网络中绝多数节点接收。最终,比特币交易被一个挖矿节点验证,并被添加到区块链上一个记录着许多比特币交易的区块中

这又分为几个过程

  • 创建交易信息
  • 网络中广播并验证
  • 加入区块链

交易

交易信息

交易信息类似一张支票,可以理解为一个数据结构,一笔交易(这个数据结构)可以由任何人创建发起,关键是资金支付人的签名

交易信息结构

大小 字段 描述
4字节 版本 明确这笔交易的规则
1-9字节 输入数量 被包含的输入数量
不定 输入 一个或多个交易
1-9字节 输出数量 被包含的输出数量
不定 输出 一个或多个输出交易
4字节 时钟时间 一个UNIX时间戳或区块号

交易也可以指定锁定时间,即可以被加在区块中的最早时间,就像延期一个支票的生效时间

比特币交易的基本单位是不可分割的 UTXO,他可以是一聪的任意倍数,1比特币 = 一千万聪,一个用户接受比特币时,UTXO被存在区块中,实际上不存在交易的地址和签名,有的只是被锁住的分散在各个区块中的 UTXO,通过对区块链整个的推导计算,可以得到所有人拥有的 UTXO 即,余额

在比特币的世界中既没有账号也没有余额,只有分散到区块链中的UTXO

一个 UTXO 可以使 1聪 的任意倍,但是一个 UTXO 一旦被创造出来,使用时就无法再分割了,只能作为一个整体支付,所以一般支付时都会存在找零,即,多笔输出。例如,你有20比特币的UTXO并且想支付 1 比特币,那么你的交易必须消耗掉整个 20 比特币的UTXO并且产生两个输出:一个是支付了 1 比特币给接收人,另一个是支付 19 比特币的找零到你的钱包

    //简化版交易
    type Transaction struct {
        //交易的唯一标识,通过计算交易的hash值得到
        ID   []byte
        Vin  []TXInput
        Vout []TXOutput
    }
   

输出和输出

就像原始交易一样,你不能将一个 100 元平均撕开作为 50 支付,同时你也可以使用多个零钱组装为 50 元,所以我们的一次交易通常有多笔输入输出。被交易消耗的 UTXO 被称为交易输入,由交易创建的 UTXO 被称为交易输出。

输入和输出到底先有哪个,其实是现有输出,因为我们可以默认第一笔输入就是创世区块中系统的输入

每一笔比特币交易输出都会被记录下来,所有的输出都是作为另一个输入,然后被用于新的支付

输出结构

尺寸 字段 说明
8字节 总量 用聪表示的比特币值
1-9字节 锁定脚本尺寸 用字节表示的后面的锁定脚本的长度
边长 锁定脚本 一个定义了支付输出所需条件的脚本

简单的说,交易输入就是指向 UTXO 的指针,考虑一下,因为 UTXO 不可分割的特性,用户的余额就是所有没被指向输入的输出

一个交易的输入需要一个解锁脚本,用来满足 UTXO 的支付条件,解锁脚本通常是一个签名,用于证明对于锁定脚本中的比特币地址的所有权

输入结构

尺寸 字段 说明
32字节 交易 指向交易包含的被花费的 UTXO 的指针
4字节 索引 被话费的 UTXO 的索引
1-9字节 解锁脚本尺寸 用于解锁脚本的大小
边长 解锁脚本 一个达到 UTXO 锁定脚本中条件的脚本
4字节 序列号 目前未被使用的交易替换功能,设成0xFFFFFFFF处

比特币交易脚本语言,称为脚本,是一种类似Forth的逆波兰表达式的基于堆栈的执行语言。在UTXO上的锁定脚本和解锁脚本都以此脚本语言编写。 当一笔比特币交易被验证时,每一个输入值中的解锁脚本与其对应的锁定脚本同时 (互不干扰地)执行,以确定这笔交易是否满足支付条件。

目前,大多数经比特币网络处理的交易是以“Alice付给Bob”的形式存在,并基于一种称为“P2PKH”(Pay-toPublic-Key-Hash)脚本。但是,比特币交易不局限于“支付给Bob的比特币地址”的脚本。事实上,锁定脚本可以被编写成表达各种复杂的情况。

比特币脚本语言包含许多操作码,但都故意限定为一种重要的模式——除了有条件的流控制以外,没有循环或复杂流控制能力。这样就保证了脚本语言的图灵非完备性,这意味着脚本有限的复杂性和可预见的执行次数。脚本并不是一种通用语言,这些限制确保该语言不被用于创造无限循环或其它类型的逻辑炸弹,这样的炸弹可以植入在一笔交易中,引起针对比特币网络的“拒绝服务”攻击。记住,每一笔交易都会被网络中的全节点验证,受限制的语言能防止交易验证机制被作为一个漏洞而加以利用。

以太坊使用的solidity是一个图灵完备的语言,它为了防止大量的程序执行给网络带来巨大压力的做法是使用 Gas,合约的执行需要消耗Gas,Gas不够或者消耗完了,合约就不能执行了。

每一个比特币验证节点会通过同时执行锁定和解锁脚本来验证一笔交易。每个输入都包含一个解锁脚本,并引用了之前存在的UTXO。 验证软件将复制解锁脚本,检索输入所引用的UTXO,并从该UTXO复制锁定脚本。 然后依次执行解锁和锁定脚本。 如果解锁脚本满足锁定脚本条件,则输入有效(请参阅单独执行解锁和锁定脚本部分)。 所有输入都是独立验证的,作为交易总体验证的一部分。

     type TXOutput struct {
        //币值
        Value int
        //接受用户的公钥 hash 值
        //这里默认采用 P2PKH 并且定义了锁定和解锁程序
        PubKeyHash []byte
    }

    type TXInput struct {
        //该输入原来存在于哪个交易,id
        Txid []byte
        //交易的第几个输出,由交易 id 和 输出序号 共同锁定
        Vout int
        //用来解锁输出的签名
        Signature []byte
        //该输入来源的地址
        PubKey []byte
    }

    //签名
    r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign))
    util.ErrLogPanic(err)
    signature := append(r.Bytes(), s.Bytes()...)

    //验证签名
    r := big.Int{}
    s := big.Int{}
    siglen := len(val.Signature)
    r.SetBytes(input.Signature[:(siglen / 2)])
    s.SetBytes(input.Signature[(siglen / 2):])

    x := big.Int{}
    y := big.Int{}
    keylen := len(input.PubKey)
    x.SetBytes(input.PubKey[:(keylen / 2)])
    y.SetBytes(input.PubKey[(keylen / 2):])
    
    rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}
    if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false {
        return false
    }

由于一个输出只能被使用一次,所以每个输入应当都指向不同的交易输出(在验证输入时会验证它指向的输出是否已经被使用),这保证了每个输入都是不同的,他们的 hash 值也是唯一的,签名 = f(私钥, hash(输入)),每个签名都是唯一的,其他人就不能为输入伪造签名了

交易费

交易费可当做是为了包含一笔交易到一个区块中的奖励,交易费被矿工挖到,并记录在这个交易的区块中

交易费基于交易的尺寸,基于千字节来计算,而不是比特币的价值。总的来说,交易费基于市场所设置,生效于比特币网络中。矿工依据许多不同的标准,按重要性对交易进行排序,这包括费用,并且甚至可能在某种特定情况下免费处理交易。交易费影响处理优先级,这意味着有足够费用的交易会更可能地被包含在下一个挖出的区块中;与此同时,交易费不足或者没有交易费的交易可能会被推迟,基于尽力而为的原则在几个区块之后被处理,甚至可能根本不被处理。交易费不是强制的,并且没有交易费的交易也许最终会被处理,但是,包含交易费将提高处理优先级交易费在交易中并没有特殊的字段,而是通过输入 - 输出 = 差,得到交易费

在交易中并没有交易费的字段,而是由矿工决定,如果矿工觉得没有任何交易费则不处理该交易,则他可能不会被计入到区块中,则交易不成立

交易链条和孤立交易

随着交易的进行,他们组成了一个链条,每一笔交易都是基于之前的交易。

如果发生了一个父交易,在父交易没有被确定之前又产生了一个基于他的子交易,而子交易在父交易之前到达其他节点。这种情况下,节点会首先接受这个交易,而不能找到参考他的父交易,节点不会立即抛弃这个交易,而是将他放在临时交易池中,并将此交易广播给其他节点。

没有父交易的交易池被称为孤立交易池,一旦接受到父节点,所有与该父节点有关的交易都会被释放,递归地重新验证,然后整个交易链都会被交易池包括进去,并等待其他区块所挖走。内存中存储的孤立交易是有限的,这是为了防止客户端的 Dos 攻击

你可能感兴趣的:(区块链)