区块链知识系列 - 比特币的UTXO交易模型

UTXO

Bitcoin 采用了 UTXO 模型作为其底层存储的数据结构,其全称为 Unspent Transaction output,也就是未被使用的交易输出。
在这里插入图片描述

  1. 比特币客户端会在每次接收到新block时更新会所有未花掉的output,检查一个交易是否合法只需要看这个交易的所有input是否在这个未花费的output表中
  2. 输出存在已使用和未使用两个状态。BTC账本历史并不记录某个地址对应的BTC余额,地址的总余额是某一时刻与它关联的所有未使用输出的BTC数额之和。**所有未使用的交易输出(Unspent Transaction Outputs,UTXO)就构成了BTC这个区块链网络的一个状态。**网络中的(全功能)节点各自维护一个状态的副本,并在某一时刻就系统的状态达成最终一致。
  3. 一个输入同样也是一个列表,包含对之前一个未使用输出的引用,以及能够证明交易创建者满足该输出使用条件的有效签名。即证明交易创建者对他引用的未使用输出中BTC的使用权,被引用的输出会从UTXO中删除。一笔交易还要满足以下条件:输出所包含的总金额应小于等于输入所包含的总金额。这时输入与输出的差额作为交易费作为激励费用奖励给记账的节点。满足以上输入与输出的交易被看作是“合法的”。
  4. 当一个地址要发布一笔交易时,它所做的其实是向BTC整个网络中的节点广播该条交易,该条交易会被标记为“未确认的”(Unconfirmed)。BTC网络并不是收到一条广播就立刻更新系统的状态,而是有区块以及内存池的设计。
    在某一个时刻,所有BTC节点都维护着一个记录UTXO的账本,并有一个接收未确认交易的内存池(Mempool),当收到一条交易广播时,节点就会把该交易加入自己的内存池。

脚本

  1. 锁定脚本是放置在输出上的花费条件:它指定将来要花费输出必须满足的条件. scriptPubKey: 通常包含公钥或比特币地址(公钥的哈希)
  2. 解锁脚本是可以“解决”或满足锁定脚本放置到输出上的条件,从而花费输出的脚本.大多数情况下,它们包含用户钱包利用私钥生成的数字签名. scriptSig:
  3. 每个比特币验证节点通过一起执行锁定和解锁脚本来验证交易.只有正确满足输出条件的有效交易才会导致输出被视为“已花费”并从未使用的交易输出集和(UTXO集)中移除。
  4. 首先,使用堆栈执行引擎执行解锁脚本。如果解锁脚本没有错误地执行(例如,它没有遗留的“悬挂(dangling)”操作符),则复制主堆栈并执行锁定脚本。如果使用从解锁脚本复制的堆栈数据执行锁定脚本的结果为“TRUE”,则解锁脚本已成功解决由锁定脚本施加的条件,证明该输入是用于花费UTXO的有效授权。如果在执行组合脚本后仍然存在除“TRUE”之外的结果,则输入无效,因为它未能满足放置在UTXO上的消费条件。
  5. 请注意,交易的每个输入都是独立签署的。这是至关重要的,因为签名和输入都不必属于同一个“所有者”或被其使用。事实上,一个名为“CoinJoin”的特定交易方案利用这一事实来创建隐私的多方交易。
  6. 多方可以协作构建交易并各自签署一个输入。
创建数字签名

在比特币的ECDSA算法实现中,被签名的“消息”是交易,或者更准确地说是交易中特定数据子集的哈希(参见下文 签名哈希的类型 (SIGHASH)。签名密钥是用户的私钥。结果是如下签名:
((Sig = F_{sig}(F_{hash}(m), dA)))

其中:

  • dA 是签名私钥

  • m 是交易(或交易的一部分)

  • F__hash 是哈希函数

  • F__sig 是签名算法

  • Sig 是签名结果

生成签名 Sig ,由两部分组成: R 和 S: Sig = (R, S) , 再使用DER 的国际标准编码方案序列化为字节流。

签名的序列化 (DER)

R 和 S 的序列化字节流
序列化格式由以下九个元素组成:

  • 0x30 —— 标识 DER 序列的开始

  • 0x45 —— 序列长度 (69 bytes)

  • 0x02 —— 接下来是一个整数

  • 0x21 —— 整数的长度 (33 bytes)

  • R —— 00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb

  • 0x02 —— 接下来是另一个整数

  • 0x20 —— 另一个整数的长度 (32 bytes)

  • S —— 4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813

  • 一个后缀 (0x01) 标识使用的哈希类型 (SIGHASH_ALL)

验证签名

在这里插入图片描述

签名验证算法采用消息(交易或其部分数据的散列),签名者的公钥和签名( R 和 S 值),如果签名对此消息和公钥有效,则返回TRUE。

签名哈希的类型 (SIGHASH)

有三种 SIGHASH 标志: ALL, NONE, 和 SINGLE

SIGHASH flag Value Description
ALL 0x01 签名应用于所有输入和输出。
NONE 0x02 签名应用于所有输入,不包括任何输出
SINGLE 0x03 签名应用于所有输入,但仅应用于与签名输入具有相同索引编号的一个输出

BTC Transaction RAW

{
    
    
  "version": 1,
  "locktime": 0,
  "vin": [
    {
    
    
      //一个交易ID,引用包含正在使用的UTXO的交易
      "txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
      //一个输出索引(vout),用于标识来自该交易的哪个UTXO被引用(第一个为零)
      "vout": 0,
      //一个 scriptSig(解锁脚本),满足放置在UTXO上的条件,解锁它用于支出
      //大多数情况下,解锁脚本是一个证明比特币所有权的数字签名和公钥,但是并不是所有的解锁脚本都包含签名
      "scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
      //一个序列号
      "sequence": 4294967295
    }
 ],
  "vout": [
    {
    
    
	  //比特币数额,最小单位为 聪  satoshis
      "value": 0.01500000,
      //定义了花费这些输出所需条件的scriptPubKey
      "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
    
    
      "value": 0.08450000,
      "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
    }
  ]
}

数据结构定义

class  CTransaction
{
    
    
	const  std::vector<CTxIn> vin;
	const  std::vector<CTxOut> vout;
	//const  int32_t nVersion;
	//const  uint32_t nLockTime;
}

class  CTxIn
{
    
    
	COutPoint prevout; //来自哪一笔交易的输出
	CScript scriptSig; //签名
	uint32_t nSequence;//用于锁定时间(locktime)或禁用 (0xFFFFFFFF)
}

class  CTxOut
{
    
    
	CAmount nValue;			//余额
	CScript scriptPubKey; 	//持有当前输出的公钥
}
//--------------------------------------------------------------
class  COutPoint
{
    
    
	uint256 hash; 	//引用的交易id
	uint32_t n; 	//第几条输出
}

//Serialized script
typedef prevector<28, unsigned  char> CScriptBase;
class CScript : public  CScriptBase
{
    
    }

typedef  int64_t CAmount;

序列化

交易序列化 —— 输出
Size Field Description
8 字节 (小端序) 数量 Amount 以聪(satoshis = 10-8 bitcoin) 为单位的比特币价值
1——9 字节 (VarInt) 锁定脚本的大小 Locking-Script Size 后面的锁定脚本的字节数
变量 锁定脚本 Locking-Script 定义花费该输出的条件的脚本
交易序列化 —— 输入
Size Field Description
32 字节 交易的哈希值 Transaction Hash 指向包含要花费的UTXO的交易的指针
4 字节 输出的索引 Output Index 要花费的UTXO的索引,从0开始
1——9 字节 (VarInt) 解锁脚本的大小 Unlocking-Script Size 后面的解锁脚本的字节长度
变量 解锁脚本 Unlocking-Script 满足UTXO锁定脚本条件的脚本
4 字节 序列号 Sequence Number 用于锁定时间(locktime)或禁用 (0xFFFFFFFF)

手续费

在每一笔合法的交易中,所有的输入的 value 之和必须大于所有输出的 value 之和,这两者之间的差值就是矿工费:

sum(inputs.value) = sum(outputs.value) + fee
  • 交易费用是以交易数据的大小(KB)计算的,而不是比特币交易的价值。
  • 在Bitcoin Core中,收费中继策略由 minrelaytxfee 选项设置。当前的默认值是每KB数据0.00001比特币或0.01毫比特币。因此,默认情况下,低于0.00001比特币的交易将被视为免费,并且只在内存池有空间时才会被中转;否则,它们将被丢弃。比特币节点可以通过调整 minrelaytxfee 的值来覆盖默认的收费中继策略。
  • 费用估算的API接口
    curl https://bitcoinfees.earn.com/api/v1/fees/recommended

猜你喜欢

转载自blog.csdn.net/wcc19840827/article/details/112554046