简易区块链的搭建(3)——交易

本文介绍了区块链UTXO账户模型,其产生背景是解决双花问题,记录交易过程,结合共识机制可使交易溯源。还补充了Go标准库对字节序列操作方法及range遍历知识。同时给出区块链代码,按功能分成多个包,介绍了各包作用及关键概念如位置索引、TxID等。

背景知识

1.UTXO账户模型

产生背景:

为了解决第一类双花问题(一笔钱花两次)

原理介绍:

我们先来介绍传统的金融模式,你有10元存款,想转给我3元,银行会怎么操作?

很显然,他会将你的账户减3元,将我的账户加3元。

这种交易模式记录的是 交易结果

而UTXO账户模型记录的是 交易过程 下面是简单的例子:

还拿上述例子,你给我转账10元,那么这个机制会做出如下记录:

  1. 初始状态:你的账户有10元,由一个未花费交易输出(UTXO)组成,价值为10元。
  2. 转账3元给我:你想要向我转账3元。这个过程会创建一笔新的交易,包含一个输入和一个输出。
    • 输入:指向之前未花费的10元交易输出,消费掉这个UTXO。
    • 输出:一个新的UTXO,价值为7元,指向我的地址。
  3. 交易记录:区块链账本会记录这笔交易,包含以下信息:
    • 交易ID:由交易内容计算得出的唯一标识符。
    • 输入:指向之前未花费的10元交易输出的引用。
    • 输出:新的7元交易输出,指向我的地址。
    • 其他信息:例如交易的签名等。
  4. 更新你的账户余额:在这笔交易被确认并添加到区块链后,你的账户余额将更新为7元,因为之前的10元交易输出已经被消费掉了。
这样做的好处
  1. 结合共识机制,解决了第一类双花问题

  2. 使区块链的每一笔交易都可以溯源

如何解决双花问题?
  1. 交易机制会先检测账户是否存在这一笔资金
  2. 账户会记录下交易源头
  3. 通过区块链网络将这一交易广播到每一个账户

在这一机制下,第二笔交易会因为账户资金不足而被判定失败

本文只简单介绍一下,至于后续添加节点以及为什么不用时间戳来判断先后的问题以后再说。

2.补档bytes.Buffer

该类型在 Go 标准库中提供了一系列方法,用于对字节序列进行操作。以下是其中一些常用的方法:

  1. Write(p []byte) (n int, err error): 将字节序列 p 写入到缓冲区。
  2. WriteByte(c byte) error: 将单个字节 c 写入到缓冲区。
  3. WriteRune(r rune) (n int, err error): 将 Unicode 字符 r 及其 UTF-8 编码写入到缓冲区。
  4. WriteString(s string) (n int, err error): 将字符串 s 的字节写入到缓冲区。
  5. Read(p []byte) (n int, err error): 从缓冲区读取字节到 p 中。
  6. ReadByte() (byte, error): 从缓冲区读取单个字节。
  7. ReadRune() (r rune, size int, err error): 从缓冲区读取一个 UTF-8 编码的 Unicode 字符。
  8. ReadString(delim byte) (string, error): 从缓冲区读取直到遇到分隔符 delim 的字符串。
  9. Bytes() []byte: 返回缓冲区的字节切片。
  10. Reset(): 清空缓冲区,使其长度为 0,但不释放底层的缓冲区。
  11. Len() int: 返回缓冲区中的字节数。
  12. Cap() int: 返回缓冲区的容量。

3.补档——range遍历

在Go语言中,range关键字用于迭代数组、切片、字符串、映射等数据结构。

1. 迭代数组和切片
package main

import "fmt"

func main() {
   
   
    // 迭代数组
    nums := [3]int{
   
   1, 2, 3}
    for _, num := range nums {
   
   
        fmt.Println(num)
    }

    // 迭代切片
    fruits := []string{
   
   "apple", "banana", "orange"}
    for index, fruit := range fruits {
   
   
        fmt.Printf("Index: %d, Fruit: %s\n", index, fruit)
    }
}
//1
//2
//3
//Index: 0, Fruit: apple
//Index: 1, Fruit: banana
//Index: 2, Fruit: orange

在这个例子中,range被用于迭代数组和切片。对于数组,range会返回索引和值,而对于切片,则返回索引和对应元素的值。

2. 迭代字符串
package main

import "fmt"

func main() {
   
   
    // 迭代字符串
    str := "hello"
    for index, char := range str {
   
   
        fmt.Printf("Index: %d, Character: %c\n", index, char)
    }
}


//Index: 0, Character: h
//Index: 1, Character: e
//Index: 2, Character: l
//Index: 3, Character: l
//Index: 4, Character: o

在这个例子中,range被用于迭代字符串。在Go中,字符串被视为UTF-8编码的字节数组,因此range会返回每个字符的索引和对应的Unicode码点。

3. 迭代映射(map)
package main

import "fmt"

func main() {
   
   
    // 迭代映射
    ages := map[string]int{
   
   
        "Alice": 30,
        "Bob":   25,
        "Carol": 35,
    }
    for name, age := range ages {
   
   
        fmt.Printf("%s is %d years old\n", name, age)
    }
}


//Alice is 30 years old
//Bob is 25 years old
//Carol is 35 years old

在这个例子中,range被用于迭代映射。range会返回映射中的键值对,其中键用于标识映射中的条目,而值则是相应键的值。

源代码

package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/gob"
	"encoding/hex"
	"fmt"
	"log"
	"math"
	"math/big"
	"time"
)

// 常量
const (
	Difficulty = 12
	InitCoin   = 1000
)

// 输出值 货币价值和接收者
type TxOutput struct {
   
   
	Value     int
	ToAddress []byte
}

// 输入值 输入所引用的前一笔交易的唯一标识符(哈希) 位置索引 货币来源
type TxInput struct {
   
   
	TxID        []byte
	OutIdx      int
	FromAddress []byte
}

// 交易 交易本身的唯一标识符(哈希) 输入值 输出值
type Transaction struct {
   
   
	ID      []byte
	Inputs  []TxInput
	Outputs []TxOutput
}

type Block struct {
   
   
	Timestamp    int64
	Hash         []byte
	PrevHash     []byte
	Target       []byte
	Nonce        int64
	Transactions []*Transaction
}

type BlockChain struct {
   
   
	Blocks []*Block
}

// 下面两个函数实现  将整数转化为16进制 并且最后返回字节切片
func Handle(err error) {
   
   
	if err != nil {
   
   
		log.Panic(err)
	}
}

func ToHexInt(num int64) []byte {
   
   
	buff := new(bytes.Buffer)
	err := binary.Write(buff, binary.BigEndian, num)
	Handle(err)
	return buff.Bytes()
}

// 下面两个方法 实现了交易ID的创建
func (tx *Transaction) TxHash() []byte {
   
   
	var encoded bytes.Buffer
	var hash [32]byte

	encoder := gob.NewEncoder(&encoded)
	err := encoder.Encode(tx)
	Handle(err)

	hash = sha256.Sum256(encoded.Bytes())
	return hash[:]
}

func (tx *Transaction) SetID() {
   
   
	tx.ID = tx.TxHash()
}

// 创建每个区块链的第一个交易——基本交易
func BaseTx(toaddress []byte) *Transaction {
   
   
	txIn := TxInput{
   
   []byte{
   
   }, -1, []byte{
   
   }}
	txOut := TxOutput{
   
   InitCoin, toaddress}
	tx := Transaction{
   
   []byte("This is the Base Transaction!"), []TxInput{
   
   txIn}, []TxOutput{
   
   txOut}}
	return &tx
}

// 检查输入的地址是否与 TxInput 结构体中的 FromAddress 字段相匹配
func (in *TxInput) FromAddressRight(address []byte) bool {
   
   
	return bytes.Equal(in.FromAddress, address)
}
//检查输出的地址是否与 TxOutput 结构体中的 ToAddress 字段相匹配
func (out *TxOutput) ToAddressRight(address []byte) bool {
   
   
	return bytes.Equal(out.ToAddress, address)
}

func CreateBlock(prevhash []byte, txs []*Transaction) *Block {
   
   
	block := Block{
   
   time.Now().Unix(), []byte{
   
   }, prevhash, []byte{
   
   }, 0, txs}
	block.Target = block.GetTarget()
	block.Nonce = block.FindNonce()
	block.SetHash()
	return &block
}

func GenesisBlock() *Block {
   
   
	tx := BaseTx([]byte("Leo Cao"))
	return CreateBlock([]byte{
   
   }, []*Transaction{
   
   tx})
}

// 生成一个包含区块中所有交易的唯一标识符 ID 的摘要
func (b *Block) BackTrasactionSummary() []byte {
   
   
	txIDs := make([][]byte, 0)
	for _, tx := range b.Transactions {
   
   
		txIDs = append(txIDs, tx.ID)
	}
	summary := bytes.Join(txIDs, []byte{
   
   })
	return summary
}

func (b *Block) SetHash() {
   
   
	information := bytes.Join([][]byte{
   
   ToHexInt(b.Timestamp), b.PrevHash, b.Target, ToHexInt(b.Nonce), b.BackTrasactionSummary()}, []byte{
   
   })
	hash := sha256.Sum256(information)
	b.Hash = hash[:]
}

func (b *Block) GetTarget() []byte {
   
   
	target := big.NewInt(1)
	target.Lsh(target, uint(256-Difficulty))
	return target.Bytes()
}

func (b *Block) GetBase4Nonce(nonce int64) []byte {
   
   
	data := bytes.Join([][]byte{
   
   
		ToHexInt(b.Timestamp),
		b.PrevHash,
		ToHexInt(int64(nonce)),
		b.Target,
		b.BackTrasactionSummary(),
	}, []byte{
   
   })
	return data
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值