5.交易
5.1 设置Transaction数组
现在我们来扩展区块中的data内容,让它拥有更多的细节(交易信息是区块链的重要特征之一)
我们将上一节的文件复制并重命名,main函数中import也更新
/Users/xxx/go/src/publicChain/part28-transaction/BLC/Block.go
type Block struct {
--
//3.transaction data
Txs []*Transaction
--
}
/Users/xxx/go/src/publicChain/part28-transaction/BLC/Transaction.go
type Transaction struct {
//1 transaction hash
TxHash []byte
}
相应的如下函数的参数也需要修改
/Users/xxx/go/src/publicChain/part28-transaction/BLC/Block.go
func Newblock(height int64, preBlockHash []byte, txs []*Transaction) *Block {
//1.crate a new block
block := &Block{height, preBlockHash, txs, time.Now().Unix(), nil, 0}
--
}
func CrateGenesisBlock(txs []*Transaction) *Block {
return Newblock(1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, txs)
}
如下文件中我们之前使用的是字节数组,现在使用的是结构体引用的数组,所以我们来写个方法做个转换
/Users/xxx/go/src/publicChain/part28-transaction/BLC/ProofOfWork.go
func (pow *ProofOfWork) prepareData(nonce int64) []byte {
data := bytes.Join(
[][]byte{
--
pow.Block.HashTransactions(),
--
},
--
}
/Users/xxx/go/src/publicChain/part28-transaction/BLC/Block.go
func (block *Block) HashTransactions() []byte {
var txHashes [][]byte
var txHash [32]byte
for _,tx := range block.Txs {
txHashes = append(txHashes,tx.TxHash)
}
txHash = sha256.Sum256(bytes.Join(txHashes,[]byte{}))
return txHash[:]
}
如下函数的参数和调用也需修改
/Users/xxx/go/src/publicChain/part28-transaction/BLC/Blockchain.go
func CreatBlockchainWithGenesisBlock(txs []*Transaction) {
--
if b != nil {
genesisBlock := CrateGenesisBlock(txs)
--
}
func (blc *Blockchain) AddBlockToBlockchain(txs []*Transaction) {
--
newBlock := Newblock(block.Height+1, block.Hash, txs)
--
}
func (blc *Blockchain) PrintChain() {
blockChainIterator := blc.Iterator()
for {
--
fmt.Printf("Tx:%v\n", block.Txs)
--
}
/Users/xxx/go/src/publicChain/part28-transaction/BLC/CLI.go
func (cli CLI) addBlock(txs []*Transaction) {
--
blockchain.AddBlockToBlockchain(txs)
}
func (cli *CLI) creatGenesisBlockChain(txs []*Transaction) {
CreatBlockchainWithGenesisBlock(txs)
}
func (cli CLI) Run() {
--
cli.addBlock([]*Transaction{})
--
cli.creatGenesisBlockChain([]*Transaction{})
--
}
现在我们编译并运行,我们创建两个区块并打印,Tx里面无内容,下面我们逐步完善
./bc addBlock
./bc addBlock
./bc printChain
Height:2
PreBlockHash:00002c1fb2f5025412d3deb5012c7803cbdee684d4ffa370d3a603e8daf615a7
Tx:[]
Timestamp:2022-02-27 07:10:20 PM
Hash:0000ad63a2dc8626aef2d9cd0c3d4925c0533e93de23b7ce521268cfed4bfa88
Nonce:61738
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Tx:[]
Timestamp:2022-02-27 07:09:29 PM
Hash:00002c1fb2f5025412d3deb5012c7803cbdee684d4ffa370d3a603e8daf615a7
Nonce:89904
5.2 UTXO模型原理
我们将上面的文件夹复制,再创建和Transaction相关的文件
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/Transaction-Output.go
type TXOutput struct {
Money int64
ScriptPublicKey string //username
}
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/Transaction-TXInput.go
type TXInput struct {
//1 transaction hash
Txid []byte
//2 Store the index of TXOutput in Vout
Vout int
//username
ScriptSig string
}
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/Transaction.go
//UTXO
type Transaction struct {
//1 transaction hash
TxHash []byte
//2.77777
Vins []*TXInput
//3.output
Vouts []*TXOutput
}
通过上述的文件和数据结构,我们的目的在于转账时避免重复交易和数据准确,比如某笔交易不能重复执行两次,某个账户的余额是所有未花费的input金额之和
5.3 coinbase交易
我们来思考一下,产生交易的情况分为两种,一种是创建创世区块的时候产生的交易,另一种则是转账产生的交易
创建区块产生的交易,代码实现如下:
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/Transaction.go
func NewCoinbaseTransAction(address string) *Transaction {
txInput := &TXInput{[]byte{}, -1, "Genesis data"}
txOutput := &TXOutput{10, address}
txCoinbase := &Transaction{[]byte{}, []*TXInput{txInput}, []*TXOutput{txOutput}}
// set hash
txCoinbase.HashTransaction()
return txCoinbase
}
//SerializeBlock
func (transaction *Transaction) HashTransaction() {
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err := encoder.Encode(transaction)
if err != nil {
log.Panic(err)
}
hash := sha256.Sum256(result.Bytes())
transaction.TxHash = hash[:]
}
我们在上述代码中实现产生交易并且产生交易哈希
并且我们修改下命令行工具
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/CLI.go
func printUsage() {
--
fmt.Println("\tcreatBlockChain -address --transaction data")
--
}
func (cli *CLI) creatGenesisBlockChain(address string) {
// create coinbase transaction
CreatBlockchainWithGenesisBlock(address)
}
func (cli CLI) Run() {
--
if creatBlockChainCmd.Parsed() {
if *flagcreatBlockChainWithAddress == "" {
fmt.Println("the address shouldn't be null")
printUsage()
os.Exit(1)
}
cli.creatGenesisBlockChain(*flagcreatBlockChainWithAddress)
}
}
然后再用交易数据创建第一个区块
/Users/xxx/go/src/publicChain/part29-transaction-UTXO/BLC/Blockchain.go
func CreatBlockchainWithGenesisBlock(address string) {
--
//create a coinbase transaction
txCoinbase := NewCoinbaseTransAction(address)
genesisBlock := CrateGenesisBlock([]*Transaction{txCoinbase})
--
}
此时编译后即可运行创建第一笔交易以及使用第一笔交易创建创世区块
现在我们来尝试改变一下输出的信息
func (blc *Blockchain) PrintChain() {
--
for {
block := blockChainIterator.Next()
fmt.Printf("Height:%d\n", block.Height)
fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash)
fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))
fmt.Printf("Hash:%x\n", block.Hash)
fmt.Printf("Nonce:%d\n", block.Nonce)
fmt.Println("Txs:")
for _, tx := range block.Txs {
fmt.Printf("%x\n", tx.TxHash)
fmt.Println("Vins:")
for _, in := range tx.Vins {
fmt.Printf("%x\n", in.TxHash)
fmt.Printf("%d\n", in.Vouts)
fmt.Printf("%s\n", in.ScriptSig)
}
fmt.Println("Vouts")
for _, out := range tx.Vouts {
fmt.Println(out.Value)
fmt.Println(out.ScriptPublicKey)
}
}
fmt.Println()
--
}
编译并运行
go build -o bc main.go
./bc creatBlockChain -address "genesis block"
./bc creatBlockChain -address "genesis block"
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-01 05:51:22 PM
Hash:00000bb9bc46506d466e8a761bd05ca8d2e1dc0af4fbf7353a0845b1b57448e9
Nonce:2364241
Txs:
cae61714b995e10503ab48509cd88cd2527459e02fbfe65bd044b6458ee64bfa
Vins:
-1
Genesis data
Vouts
10
genesis block
5.4 转账命令行处理
转账可能遇到的问题,重复支付,怎么打包至区块,每一笔和每一笔怎么关联等。现在我们着手来解决这个问题,并且通过终端来实现转账
/Users/xxx/go/src/publicChain/part30-transaction-UTXO/BLC/CLI.go
func printUsage() {
--
fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --transaction details")
--
}
func (cli CLI) Run() {
isValidArgs()
//custom command
sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError)
--
flagFrom := sendBlockCmd.String("from", "", "origin address")
flagTo := sendBlockCmd.String("to", "", "destination address")
flagAmount := sendBlockCmd.String("amount", "", "transfer amount")
--
switch os.Args[1] {
case "send":
err := sendBlockCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
--
if sendBlockCmd.Parsed() {
if *flagFrom == "" || *flagTo == "" || *flagAmount == "" {
printUsage()
os.Exit(1)
}
//fmt.Println(*flagAddBlockData)
//cli.addBlock([]*Transaction{})
fmt.Println(*flagFrom)
fmt.Println(*flagTo)
fmt.Println(*flagAmount)
}
--
}
标准的JASON字符串转数组
/Users/xxx/go/src/publicChain/part31-transaction-TO-Array/BLC/utils.go
func JSONToArray(jsonString string)[]string {
//json to []string
var sArr []string
if err := json.Unmarshal([]byte(jsonString),&sArr);err != nil {
log.Panic(err)
}
return sArr
}
我们再来修改下如下内容
/Users/xxx/go/src/publicChain/part31-transaction-TO-Array/BLC/CLI.go
func (cli CLI) Run() {
--
//fmt.Println(*flagAddBlockData)
//cli.addBlock([]*Transaction{})
fmt.Println(*flagFrom)
fmt.Println(*flagTo)
fmt.Println(*flagAmount)
fmt.Println(JSONToArray(*flagFrom))
fmt.Println(JSONToArray(*flagTo))
fmt.Println(JSONToArray(*flagAmount))
}
--
}
在终端中编译并运行,成功打印
go build -o bc main.go
./bc send -from '["shiyiwei","jianquanqin"]' -to '["junC","xiaoyong"]' -amount '["4","6"]'
["shiyiwei","jianquanqin"]
["junC","xiaoyong"]
["4","6"]
[shiyiwei jianquanqin]
[junC xiaoyong]
[4 6]
? 上的转换函数能够正常运行,现在我们来重新写一个方法,再定义相关变量,以使得在终端输入命令就能够创建区块
/Users/xxx/go/src/publicChain/part32-transaction-UTXO-Send-Methods/BLC/CLI.go
func (cli CLI) send(from []string, to []string, amount []string) {
if DBExists() == false {
fmt.Println("data didn't exist")
os.Exit(1)
}
blockchain := BlockChainObject()
defer blockchain.DB.Close()
blockchain.MineNewBlock(from, to, amount)
}
func (cli CLI) Run() {
--
//fmt.Println(*flagAddBlockData)
//cli.addBlock([]*Transaction{})
//fmt.Println(*flagFrom)
//fmt.Println(*flagTo)
//fmt.Println(*flagAmount)
//
//fmt.Println(JSONToArray(*flagFrom))
//fmt.Println(JSONToArray(*flagTo))
//fmt.Println(JSONToArray(*flagAmount))
from := JSONToArray(*flagFrom)
to := JSONToArray(*flagTo)
amount := JSONToArray(*flagAmount)
cli.send(from, to, amount)
}
--
}
我们再来定义一个函数用于生产新的区块
/Users/xxx/go/src/publicChain/part32-transaction-UTXO-Send-Methods/BLC/Blockchain.go
//produce new block
func (blockchain *Blockchain) MineNewBlock(from, to, amount []string) {
fmt.Println(from)
fmt.Println(to)
fmt.Println(amount)
// Establish transaction slices through relevant algorithms
var txs []*Transaction
var block *Block
blockchain.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockTableName))
if b != nil {
hash := b.Get([]byte("l"))
blockBytes := b.Get(hash)
block = DeserializeBlock(blockBytes)
}
return nil
})
// Establish new block
block = Newblock(block.Height+1, block.Hash, txs)
//store new block
blockchain.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockTableName))
if b != nil {
b.Put(block.Hash, block.Serialize())
b.Put([]byte("l"), block.Hash)
blockchain.Tip = block.Hash
}
return nil
})
}
改一下代码风格
func CreatBlockchainWithGenesisBlock(address string)*Blockchain {
--
return BlockChainObject()
}
/Users/xxx/go/src/publicChain/part33-transaction-UTXO-Newblock/BLC/CLI.go
func CreatBlockchainWithGenesisBlock(address string) *Blockchain {
--
var genesisHash []byte
--
genesisHash = genesisBlock.Hash
}
return nil
})
return &Blockchain{genesisHash,db}
}
在终端中编译且能够正常运行,到此为止,交易框架整体已经搭建完成
./bc send -from '["shiyiwei","jianquanqin"]' -to '["junC","xiaoyong"]' -amount '["4","6"]'
[shiyiwei jianquanqin]
[junC xiaoyong]
[4 6]
00000057eb35441d4fe0715d98185302257004659fec98e6fd90150e3ee32a2e
5.5 构造区块交易测试数据
我们先将上面的文件复制一份,并且重命名后编译、创建第一个区块,发送一笔发送一笔交易,最后再打印区块链
./bc creatBlockChain -address "liyuechun"
./bc send -from '["liyuechun"]' -to '["juncheng"]' -amount '["6"]'
./bc printChain
Height:2
PreBlockHash:0000078085622c1c4a6b0597de3c54954363927ba53e9022fc7717121296fdea
Timestamp:2022-03-02 09:14:47 AM
Hash:00000d78ebc14fe73a6ffc6d4f08a34c1b98499aa0b93c8cec9056d01b2855e8
Nonce:1018025
Txs:
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-02 09:12:22 AM
Hash:0000078085622c1c4a6b0597de3c54954363927ba53e9022fc7717121296fdea
Nonce:41435
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
我们发现区块能够正常创建,但是Txs无内容,现在我们来着手解决这个问题
我们接下来建立两个区块之间真实的交易,如下函数会创建一个交易,包含三个参数,金额来源,金额目的地,金额数量,最后返回的仍然是一个交易,这个交易后续会用作创建区块
所有的入账和出账金额都会被记录进slice
/Users/xxx/go/src/publicChain/part34-transaction-new-transaction/BLC/Transaction.go
func NewSimpleTransaction(from, to string, amount int) *Transaction {
//consume
var txInputs []*TXInput
bytes, _ := hex.DecodeString("5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1")
txInput := &TXInput{[]byte(bytes), 0, from}
txInputs = append(txInputs, txInput)
//transfer
var txOutputs []*TXOutput
txOutput := &TXOutput{int64(amount), to}
txOutputs = append(txOutputs, txOutput)
//the rest amount
txOutput = &TXOutput{10 - int64(amount), from}
txOutputs = append(txOutputs, txOutput)
tx := &Transaction{[]byte{}, txInputs, txOutputs}
// set hash
tx.HashTransaction()
return tx
}
我们通过交易函数创建交易并且将其装入slice
/Users/xxx/go/src/publicChain/part34-transaction-new-transaction/BLC/Blockchain.go
func (blockchain *Blockchain) MineNewBlock(from, to, amount []string) {
--
//1.establish transaction
value ,_ := strconv.Atoi(amount[0])
tx := NewSimpleTransaction(from[0],to[0],value)
--
// Establish transaction slices through relevant algorithms
var txs []*Transaction
txs = append(txs,tx)
--
}
现在我们在终端中重新编译
./bc creatBlockChain -address "liyuechun"
./bc send -from '["liyuechun"]' -to '["juncheng"]' -amount '["6"]'
./bc printChain
Height:2
PreBlockHash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Timestamp:2022-03-02 11:11:06 AM
Hash:00000dff1f4d4714570e54b761ef20a05c78d9c4e7ee2f70a896971560e644ef
Nonce:1811875
Txs:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
Vins:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
0
liyuechun
Vouts
6
juncheng
4
liyuechun
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-02 11:10:10 AM
Hash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Nonce:1680697
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
我们看到交易信息都被打印出来了
现在我们来构造第三个测试数据区块
我们现在仍然使用的是“死”数据,让我们先更新一下代码部分
func NewSimpleTransaction(from, to string, amount int) *Transaction {
--
bytes, _ := hex.DecodeString("c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c")
--
}
func NewSimpleTransaction(from, to string, amount int) *Transaction {
--
//the rest amount
txOutput = &TXOutput{4 - int64(amount), from}
--
}
然后在终端中编译并且运行,结果如下
./bc send -from '["juncheng"]' -to '["zhangqiang"]' -amount '["2"]'
./bc printChain
Height:3
PreBlockHash:00000dff1f4d4714570e54b761ef20a05c78d9c4e7ee2f70a896971560e644ef
Timestamp:2022-03-02 04:15:04 PM
Hash:000006309dc9ca1e43e2a8e9c9b5cca1750135306a8bd864b770299bfe71ecbb
Nonce:3299509
Txs:
4416279651b0b2081122ba03435eb6a94332fbcacbf32c95513acf1990e6f3b0
Vins:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
0
juncheng
Vouts
2
zhangqiang
2
juncheng
Height:2
PreBlockHash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Timestamp:2022-03-02 11:11:06 AM
Hash:00000dff1f4d4714570e54b761ef20a05c78d9c4e7ee2f70a896971560e644ef
Nonce:1811875
Txs:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
Vins:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
0
liyuechun
Vouts
6
juncheng
4
liyuechun
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-02 11:10:10 AM
Hash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Nonce:1680697
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
5.6 转账算法逻辑分析
现在我们将上述文件夹再复制一份,并重命名为 part36-transaction-new-transaction-block3
新建交易需要做:
1.返回某个username下所有未花费的金额
伪代码如下:
func UnSpentTransactionsWithAddress(address string) []*Transaction {
return nil
}
2.返回某个user余额和map
其实核心的就是不让转账出错,同时做到账户余额更新
5.7 getbalance - cli
我们先再来定义一个命令行命令,我们可以使用它直接在终端中获取某个地址的余额,这个命令行构建思路和之前几个命令行工具构建思路是一样的
/Users/xxx/go/src/publicChain/part36-transaction-new-transaction/BLC/CLI.go
func printUsage() {
--
fmt.Println("\tgetBalance -address -- output block's information")
}
func (cli CLI) getBalance(address string) {
}
func (cli CLI) Run() {
--
getBalanceCmd := flag.NewFlagSet("getBalance", flag.ExitOnError)
--
getBalanceWithAddress := getBalanceCmd.String("address", "", "inquire one's account")
--
case "getBalance":
err := getBalanceCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
--
if creatBlockChainCmd.Parsed() {
if *getBalanceWithAddress == "" {
fmt.Println("the address shouldn't be null")
printUsage()
os.Exit(1)
}
cli.getBalance(*getBalanceWithAddress)
}
}
编译并运行,即可查看命令行工具已经出现
go build -o bc main.go
./bc
Usage:
creatBlockChain -address --transaction data
send -from FROM -to TO -amount AMOUNT --transaction details
printChain -- output block's information
getBalance -address -- output block's information
./bc getBalance -address "liyuechun"
5.8 遍历和查找UTXO
复制并命名上述文件夹
现在我们要遍历每一个区块中的每一笔交易(未花费的交易),我们写个方法来实现,具体实现代码如下:
先获取迭代器,再通过迭代器获取区块,然后再获取区块前一个区块的哈希值,若前一个区块的哈希值为0终止for循环(说明遍历完了所有区块)
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/Blockchain.go
func (blockchain *Blockchain) UnUTXOs(address string) *Transaction {
blockChainIterator := blockchain.Iterator()
for {
block := blockChainIterator.Next()
fmt.Println(block)
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if hashInt.Cmp(big.NewInt(0)) == 1{
break
}
}
return nil
}
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/CLI.go
然后我们再次调用上述方法,目的是为了获取某个地址的余额
func (cli CLI) getBalance(address string) {
fmt.Println("Address:" + address)
blockchain := BlockChainObject()
defer blockchain.DB.Close()
txs := blockchain.UnSpentTransactionWithAddress(address)
fmt.Println(txs)
}
现在我们来编译一下,并运行,我们看到所有的区块被我们都遍历到了
go build -o bc main.go
./bc getBalance -address "liyuechun"
Address:liyuechun
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc00013c050] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc00013c0a0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc00013c0f0] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
<nil>
现在我们在for循环中继续遍历每一个区块中的交易
我们先来建立两个变量,slice类型的变量用来存放某个地址所有未花费的交易,map类型存放某个hash所对应的消费
接着我们再定义方法用来判断某笔输入输出是否属于给定的地址
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/Transaction-TXInput.go
// judge the current input belong to one's
func (txInput *TXInput) UnlockWithAddress(address string) bool {
return txInput.ScriptSig == address
}
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/Transaction-TXOutput.go
// judge the current output belong to one's
func (txOutput *TXOutput) UnlockScriptPublicKeyWithAddress(address string) bool {
return txOutput.ScriptPublicKey == address
}
在此之前还需写一个函数判断tx是否属于当前coinbase
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/Transaction.go
//judge the current transaction belongs to Coinbase
func (tx *Transaction) IsCoinbaseTransaction() bool {
return len(tx.Vins[0].TxHash) == 0 && tx.Vins[0].Vouts == -1
}
在遍历交易的内部在使用for循环遍历未消费和已消费
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/Blockchain.go
func (blockchain *Blockchain) UnUTXOs(address string) []*TXOutput {
var unUTXOs []*TXOutput
spentTXOutputs := make(map[string][]int)
blockChainIterator := blockchain.Iterator()
for {
block := blockChainIterator.Next()
fmt.Println("\n", block)
for _, tx := range block.Txs {
if tx.IsCoinbaseTransaction() == false {
for _, in := range tx.Vins {
if in.UnlockWithAddress(address) {
key := hex.EncodeToString(in.TxHash)
spentTXOutputs[key] = append(spentTXOutputs[key], in.Vouts)
}
}
}
for index, out := range tx.Vouts {
if out.UnlockScriptPublicKeyWithAddress(address) {
fmt.Println(out)
if spentTXOutputs != nil {
//map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
if len(spentTXOutputs) != 0 {
for txHash, indexSlice := range spentTXOutputs {
for _, i := range indexSlice {
if index == i && txHash == hex.EncodeToString(tx.TxHash) {
continue
} else {
unUTXOs = append(unUTXOs, out)
}
}
}
} else {
unUTXOs = append(unUTXOs, out)
}
}
}
}
}
fmt.Println(spentTXOutputs)
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if hashInt.Cmp(big.NewInt(0)) == 0 {
break
}
}
return unUTXOs
}
最后在更改一下下面代码
/Users/xxx/go/src/publicChain/part37-transaction-new-transaction-unspendTransaction/BLC/CLI.go
func (cli CLI) getBalance(address string) {
fmt.Println("Address:" + address)
blockchain := BlockChainObject()
defer blockchain.DB.Close()
txOutputs := blockchain.UnUTXOs(address)
//fmt.Println("-------------")
for _, out := range txOutputs {
fmt.Println(out)
}
}
在终端中编译并运行
./bc getBalance -address "juncheng"
Address:juncheng
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc00005e0a0] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
&{2 juncheng}
map[c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[0]]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc00005e0f0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{6 juncheng}
map[c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[0]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc00005e140] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
map[c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[0]]
&{2 juncheng}
./bc getBalance -address "liyuechun"
Address:liyuechun
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc0000da050] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
map[]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc0000da0a0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{4 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc0000da0f0] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
&{10 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
&{4 liyuechun}
./bc getBalance -address "zhangqiang"
Address:zhangqiang
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc00005e0a0] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
&{2 zhangqiang}
map[]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc00005e0f0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
map[]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc00005e140] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
map[]
&{2 zhangqiang}
5.9 余额查询
我们惯例将上述文件复制一份,并且新建一个方法专门用于计算某个地址的余额
/Users/xxx/go/src/publicChain/part38-transaction-new-transaction-balance/BLC/Blockchain.go
//require balance
func (blockchain *Blockchain) GetBalance(address string) int64 {
utxos := blockchain.UnUTXOs(address)
var amount int64
for _, out := range utxos {
amount = amount + out.Value
}
return amount
}
同时我们再来改一下命令行工具中的余额查询函数
/Users/xxx/go/src/publicChain/part38-transaction-new-transaction-balance/BLC/CLI.go
func (cli CLI) getBalance(address string) {
fmt.Println("Address:" + address)
blockchain := BlockChainObject()
defer blockchain.DB.Close()
amount := blockchain.GetBalance(address)
fmt.Printf("%s has %d token\n", address, amount)
}
编译并运行,我们看到余额能够正确被查到
./bc getBalance -address "liyuechun"
Address:liyuechun
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc00013c050] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
map[]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc00013c0a0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{4 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc00013c0f0] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
&{10 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
liyuechun has 4 token
5.10 UTXO模型算法优化
现在我们想要建立新的交易,并通能够随时获取某个地址的余额(其实就是要追踪到前一笔交易)
复制并重命名上述文件夹
我们来定义一个新的结构体
/Users/xxx/go/src/publicChain/part39-transaction-new-transaction-send/BLC/UTXO.go
type UTXO struct {
TxHash []byte
Index int
Output *TXOutput
}
然后再更新一下如下代码
/Users/xxx/go/src/publicChain/part39-transaction-new-transaction-send/BLC/Blockchain.go
func (blockchain *Blockchain) UnUTXOs(address string) []*UTXO {
--
} else {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
}
} else {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
}
}
}
--
return unUTXOs
}
//require balance
func (blockchain *Blockchain) GetBalance(address string) int64 {
utxos := blockchain.UnUTXOs(address)
var amount int64
for _, utxo := range utxos {
amount = amount + utxo.Output.Value
}
return amount
}
再更新命令行工具中的代码
/Users/xxx/go/src/publicChain/part39-transaction-new-transaction-send/BLC/BlockchainIterator.go
func (cli CLI) getBalance(address string) {
fmt.Println("Address:" + address)
blockchain := BlockChainObject()
defer blockchain.DB.Close()
amount := blockchain.GetBalance(address)
fmt.Printf("%s has %d token\n", address, amount)
}
编译并运行,结果能够成功打印
./bc getBalance -address "liyuechun"
Address:liyuechun
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc0000940a0] 1646208904 [0 0 6 48 157 201 202 30 67 226 168 233 201 181 204 161 117 1 53 48 106 139 216 100 183 112 41 155 254 113 236 187] 3299509}
map[]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc0000940f0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{4 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc000094140] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
&{10 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
liyuechun has 4 token
5.11 文件分离-优化
复制并重命名上述文件夹
分离将如下函数放入新建的文件中
/Users/xxx/go/src/publicChain/part40-transaction-new-transaction-cli/BLC/CLI_printchain.go
func (cli CLI) printChain() {
if DBExists() == false {
fmt.Println("database didn't exist")
os.Exit(1)
}
blockchain := BlockChainObject()
defer blockchain.DB.Close()
blockchain.PrintChain()
}
同样的理由,CLI文件中的响应代码也转移至下列文件夹中
/Users/xxx/go/src/publicChain/part40-transaction-new-transaction-cli/BLC/CLI_send.go
/Users/xxx/go/src/publicChain/part40-transaction-new-transaction-cli/BLC/CLI_getbalance.go
/Users/xxx/go/src/publicChain/part40-transaction-new-transaction-cli/BLC/CLI_createblockchain.go
并且我们尝试在终端中重新编译并运行,代码能够成功运行
5.12 转账方法优化
依旧是复制重命名上述文件
现在我们想要增加转账(创建一笔交易),我们会调用send方法发送交易信息,交易信息又会调用MineNewClock创建新的区块,在创建新的区块时需要获取交易金额以及从谁到谁,所以会调用NewSimpleTransaction函数,在此函数中,仍然是几大要素;输入、输出、找零,最后再整合成为一笔交易并返回,在形成交易时又需要获取UnUTXOs来得到未花费的金额,这个方法的接收器是blockchain,用它调用就可以了,我们可以选择在NewSimpleTransaction函数中定义一个变量blockchain或者直接增加一个参数,这里我们选择增加参数,那相应的调用此函数的地方都要变更
/Users/xxx/go/src/publicChain/part41-transaction-new-transaction-send/BLC/Blockchain.go
func (blockchain *Blockchain) MineNewBlock(from, to, amount []string) {
--
tx := NewSimpleTransaction(from[0], to[0], value, blockchain)
--
}
上面是大体逻辑,现在我们来构建交易
在此之前我们来定义一个方法专门用来返回未消费余额
/Users/xxx/go/src/publicChain/part41-transaction-new-transaction-send/BLC/Blockchain.go
func (blockchain *Blockchain) FindSpendableUTXOS(from string, amount int) (int, map[string][]int) {
}
定义好了方法我们在下面的函数中调用,我们定义了两个变量money和spendableUTXODic,前者是转账金额,后者是转账的哈希和索引map
/Users/xxx/go/src/publicChain/part41-transaction-new-transaction-send/BLC/Transaction.go
func NewSimpleTransaction(from, to string, amount int, blockchain *Blockchain) *Transaction {
//./bc send -from '["liyuechun"]' -to '["zhangqiang"]' -amount '["2"]'
//[liyuechun]
//[zhangqiang]
//[2]
money, spendableUTXODic := blockchain.FindSpendableUTXOS(from, amount)
//consume
var txInputs []*TXInput
//bytes, _ := hex.DecodeString("c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c")
for txHash, indexSlice := range spendableUTXODic {
for _, index := range indexSlice {
txHashBytes, _ := hex.DecodeString(txHash)
txInput := &TXInput{txHashBytes, index, from}
txInputs = append(txInputs, txInput)
}
}
//transfer
var txOutputs []*TXOutput
txOutput := &TXOutput{int64(amount), to}
txOutputs = append(txOutputs, txOutput)
//the rest amount
txOutput = &TXOutput{int64(money) - int64(amount), from}
txOutputs = append(txOutputs, txOutput)
tx := &Transaction{[]byte{}, txInputs, txOutputs}
// set hash
tx.HashTransaction()
return tx
}
5.12 转账逻辑代码实现
我们来继续完善FindSpendableUTXOS方法,先获取所有的utxo
/Users/xxx/go/src/publicChain/part41-transaction-new-transaction-send/BLC/Blockchain.go
func (blockchain *Blockchain) FindSpendableUTXOS(from string, amount int) (int64, map[string][]int) {
// get all utxos
utxos := blockchain.UnUTXOs(from)
spendAbleUTXO := make(map[string][]int)
// range utxos
var value int64
for _, utxo := range utxos {
value = value + utxo.Output.Value
hash := hex.EncodeToString(utxo.TxHash)
spendAbleUTXO[hash] = append(spendAbleUTXO[hash], utxo.Index)
if value >= int64(amount) {
break
}
}
if value < int64(amount) {
fmt.Printf("%s has an Insufficient balance\n", from)
os.Exit(1)
}
return value, spendAbleUTXO
}
至此,我们所有的代码都构建好了,现在我们来编译并尝试转账
./bc send -from '["liyuechun"]' -to '["zhangqiang"]' -amount '["2"]'
./bc getBalance -address "liyuechun"
Address:liyuechun
&{4 [0 0 0 41 22 169 11 188 245 204 26 38 80 133 220 204 132 180 134 179 244 33 224 30 104 148 102 83 8 64 241 133] [0xc000118050] 1646362045 [0 0 13 185 225 165 218 167 228 150 107 25 157 200 154 28 107 36 182 203 157 45 180 32 129 207 20 122 92 181 88 182] 1514926}
&{2 liyuechun}
map[c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[1]]
&{3 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] [0xc0001180a0] 1646361529 [0 0 0 41 22 169 11 188 245 204 26 38 80 133 220 204 132 180 134 179 244 33 224 30 104 148 102 83 8 64 241 133] 807695}
map[c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[1]]
&{2 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] [0xc0001180f0] 1646190666 [0 0 13 255 31 77 71 20 87 14 84 183 97 239 32 160 92 120 217 196 231 238 47 112 168 150 151 21 96 230 68 239] 1811875}
&{4 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0] c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[1]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc000118140] 1646190610 [0 0 11 166 203 148 112 86 72 96 123 92 122 38 207 234 91 169 33 98 180 88 139 191 225 245 208 83 239 206 255 213] 1680697}
&{10 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0] c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c:[1]]
liyuechun has 16 token
我们看到最终liyuechun有16个token,说明出错了,但是区块中显示liyuechun是2个,正确的应该是juncheng 4,zhangqiang 4,liyuechun 2
5.13 unUTXOs 方法优化
我们打印一下,看看问题出在哪里了
./bc printChain
Height:4
PreBlockHash:0000002916a90bbcf5cc1a265085dccc84b486b3f421e01e689466530840f185
Timestamp:2022-03-04 10:47:25 AM
Hash:00000db9e1a5daa7e4966b199dc89a1c6b24b6cb9d2db42081cf147a5cb558b6
Nonce:1514926
Txs:
d63bf9d0d661769fbef297d0c4b7b44a756cc06ae4c885fb43f0b9e458e60845
Vins:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
1
liyuechun
Vouts
2
zhangqiang
2
liyuechun
Height:3
PreBlockHash:00000dff1f4d4714570e54b761ef20a05c78d9c4e7ee2f70a896971560e644ef
Timestamp:2022-03-04 10:38:49 AM
Hash:0000002916a90bbcf5cc1a265085dccc84b486b3f421e01e689466530840f185
Nonce:807695
Txs:
1b7a55fab37914153107d0ff6b6c679de8e8c301f661339008f41572d749c959
Vins:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
0
juncheng
Vouts
2
zhangqiang
4
juncheng
Height:2
PreBlockHash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Timestamp:2022-03-02 11:11:06 AM
Hash:00000dff1f4d4714570e54b761ef20a05c78d9c4e7ee2f70a896971560e644ef
Nonce:1811875
Txs:
c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c
Vins:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
0
liyuechun
Vouts
6
juncheng
4
liyuechun
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-02 11:10:10 AM
Hash:00000ba6cb94705648607b5c7a26cfea5ba92162b4588bbfe1f5d053efceffd5
Nonce:1680697
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
我们发现数据库中的数据是对的,这就说明只是我们查询余额的时候出错了
我们看看GetBalance方法中的相关值,再顺藤摸瓜,改一下如下代码,然后运行,可以正常得到结果
/Users/xxx/go/src/publicChain/part41-transaction-new-transaction-send/BLC/Blockchain.go
func (blockchain *Blockchain) UnUTXOs(address string) []*UTXO {
var unUTXOs []*UTXO
spentTXOutputs := make(map[string][]int)
blockChainIterator := blockchain.Iterator()
for {
block := blockChainIterator.Next()
fmt.Println("\n", block)
for _, tx := range block.Txs {
if tx.IsCoinbaseTransaction() == false {
for _, in := range tx.Vins {
if in.UnlockWithAddress(address) {
key := hex.EncodeToString(in.TxHash)
spentTXOutputs[key] = append(spentTXOutputs[key], in.Vouts)
}
}
}
work:
for index, out := range tx.Vouts {
if out.UnlockScriptPublicKeyWithAddress(address) {
fmt.Println(out)
if spentTXOutputs != nil {
//map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
if len(spentTXOutputs) != 0 {
var isSpentUTXO bool
for txHash, indexSlice := range spentTXOutputs {
for _, i := range indexSlice {
if index == i && txHash == hex.EncodeToString(tx.TxHash) {
continue work
}
}
}
if isSpentUTXO == false {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
} else {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
}
}
}
fmt.Println(spentTXOutputs)
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if hashInt.Cmp(big.NewInt(0)) == 0 {
break
}
}
return unUTXOs
}
./bc getBalance -address "liyuechun"
./bc getBalance -address "zhangqiang"
./bc getBalance -address "juncheng"
--
liyuechun has 2 token
--
zhangqiang has 4 token
--
juncheng has 4 token
5.14
我们现在再来发起一个交易,测试一下代码,然后打印结果
./bc send -from '["zhangqiang"]' -to '["liyuechun"]' -amount '["1"]'
./bc getBalance -address "zhangqiang"
zhangqiang has 3 token
./bc send -from '["zhangqiang"]' -to '["liyuechun"]' -amount '["2"]'
./bc getBalance -address "zhangqiang"
--
liyuechun has 5 token
--
zhangqiang has 1 token
OK非常棒,截至目前,我们已经可以实现在每一个区块中实现单笔转账
5.14 转账逻辑处理
上述每个区块只包含一笔交易,现在我们尝试来在区块中执行多笔交易。引申出了几个问题:
1.每个区块中应该包含几笔?新的区块怎么通过交易生产等
我们先通过for循环获取所有地址的交易
/Users/xxx/go/src/publicChain/part42-transaction-new-transaction-multi-transaction/BLC/Blockchain.go
func (blockchain *Blockchain) MineNewBlock(from, to, amount []string) {
var txs []*Transaction
for _, address := range from {
value, _ := strconv.Atoi(amount[0])
//1.establish a transaction
tx := NewSimpleTransaction(address, to[0], value, blockchain)
fmt.Println(tx)
txs = append(txs, tx)
}
--
}
现在我们在终端中删去数据库和bc文件,重新编译一下并运行,结果能够成功打印
./bc creatBlockChain -address "liyuechun"
./bc send -from '["liyuechun"]' -to '["juncheng"]' -amount '["2"]'
./bc printChain
Height:2
PreBlockHash:00000739c540bd42da33b52d3930732adaeac21acd78be0c0354c23c23b3b901
Timestamp:2022-03-05 03:31:48 PM
Hash:000007b4ea035d82d7a7c2f78e2c72125e717140ca2b5e5fcab8a26e07307a10
Nonce:2212744
Txs:
054100e4d142a7975e3bf78de475397437575a2d55d739c2d9c4b93c3159c504
Vins:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
0
liyuechun
Vouts
2
juncheng
8
liyuechun
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-05 03:29:41 PM
Hash:00000739c540bd42da33b52d3930732adaeac21acd78be0c0354c23c23b3b901
Nonce:1230532
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
./bc getBalance -address "juncheng"
Address:liyuechun
&{2 [0 0 7 57 197 64 189 66 218 51 181 45 57 48 115 42 218 234 194 26 205 120 190 12 3 84 194 60 35 179 185 1] [0xc000150050] 1646465508 [0 0 7 180 234 3 93 130 215 167 194 247 142 44 114 18 94 113 113 64 202 43 94 95 202 184 162 110 7 48 122 16] 2212744}
&{8 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
&{1 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0xc0001500a0] 1646465381 [0 0 7 57 197 64 189 66 218 51 181 45 57 48 115 42 218 234 194 26 205 120 190 12 3 84 194 60 35 179 185 1] 1230532}
&{10 liyuechun}
map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
liyuechun has 8 token
我们尝试发送如下两笔交易时会出错,问题在于之前的交易并没有和新的交易建立联系,我们来更改下代码,加一个交易参数:txs
/Users/xxx/go/src/publicChain/part42-transaction-new-transaction-multi-transaction/BLC/Transaction.go
func NewSimpleTransaction(from, to string, amount int, blockchain *Blockchain, txs []*Transaction) *Transaction {
money, spendableUTXODic := blockchain.FindSpendableUTXOS(from, amount,txs)
--
}
/Users/xxx/go/src/publicChain/part42-transaction-new-transaction-multi-transaction/BLC/Blockchain.go
func (blockchain *Blockchain) MineNewBlock(from, to, amount []string) {
var txs []*Transaction
for index, address := range from {
value, _ := strconv.Atoi(amount[index])
//1.establish a transaction
tx := NewSimpleTransaction(address, to[index], value, blockchain, txs)
fmt.Println(tx)
txs = append(txs, tx)
}
--
}
func (blockchain *Blockchain) FindSpendableUTXOS(from string, amount int, txs []*Transaction) (int64, map[string][]int) {
utxos := blockchain.UnUTXOs(from, txs)
--
}
func (blockchain *Blockchain) UnUTXOs(address string,txs []*Transaction) []*UTXO {
var unUTXOs []*UTXO
spentTXOutputs := make(map[string][]int)
for _, tx := range txs {
if tx.IsCoinbaseTransaction() == false {
for _, in := range tx.Vins {
if in.UnlockWithAddress(address) {
key := hex.EncodeToString(in.TxHash)
spentTXOutputs[key] = append(spentTXOutputs[key], in.Vouts)
}
}
}
}
for _, tx := range txs {
work1:
for index, out := range tx.Vouts {
if out.UnlockScriptPublicKeyWithAddress(address) {
if len(spentTXOutputs) == 0 {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
} else {
for hash, indexSlice := range spentTXOutputs {
txHashStr := hex.EncodeToString(tx.TxHash)
if hash == txHashStr {
var isUnSpentUTXO bool
for _, outIndex := range indexSlice {
if index == outIndex {
isUnSpentUTXO = true
continue work1
}
if isUnSpentUTXO == false {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
} else {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
}
}
}
}
blockChainIterator := blockchain.Iterator()
for {
block := blockChainIterator.Next()
fmt.Println("\n", block)
for i := len(block.Txs) - 1; i >= 0; i-- {
tx := block.Txs[i]
if tx.IsCoinbaseTransaction() == false {
for _, in := range tx.Vins {
if in.UnlockWithAddress(address) {
key := hex.EncodeToString(in.TxHash)
spentTXOutputs[key] = append(spentTXOutputs[key], in.Vouts)
}
}
}
work2:
for index, out := range tx.Vouts {
if out.UnlockScriptPublicKeyWithAddress(address) {
fmt.Println(out)
if spentTXOutputs != nil {
//map[5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1:[0]]
if len(spentTXOutputs) != 0 {
var isSpentUTXO bool
for txHash, indexSlice := range spentTXOutputs {
for _, i := range indexSlice {
if index == i && txHash == hex.EncodeToString(tx.TxHash) {
continue work2
}
}
}
if isSpentUTXO == false {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
} else {
utxo := &UTXO{tx.TxHash, index, out}
unUTXOs = append(unUTXOs, utxo)
}
}
}
}
}
fmt.Println(spentTXOutputs)
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if hashInt.Cmp(big.NewInt(0)) == 0 {
break
}
}
return unUTXOs
}
现在在终端中编译再执行,能够成功执行
./bc send -from '["liyuechun","juncheng"]' -to '["juncheng","liyuechun"]' -amount '["3","4"]'
./bc printChain
Height:3
PreBlockHash:0000078046464724c23bf1b7ceb398ac16dc87a4a506b5f495e95f8888d58495
Timestamp:2022-03-05 05:56:40 PM
Hash:00000342700893d52978bd4743914b7bf6da0e8f647b24049581d51a7dc48e4f
Nonce:331759
Txs:
5f760ef0b4c1a1bb55a0dd8052089e7d1429e4f405f7fb80407eec612b41a9cc
Vins:
054100e4d142a7975e3bf78de475397437575a2d55d739c2d9c4b93c3159c504
1
liyuechun
Vouts
3
juncheng
5
liyuechun
3c9fc018281ac6ddb95b416ec05d46c704b4ef717b176154884638d465761221
Vins:
5f760ef0b4c1a1bb55a0dd8052089e7d1429e4f405f7fb80407eec612b41a9cc
0
juncheng
054100e4d142a7975e3bf78de475397437575a2d55d739c2d9c4b93c3159c504
0
juncheng
Vouts
4
liyuechun
1
juncheng
Height:2
PreBlockHash:00000bb873aaf6287b6e618a3965635eceee48bd66d63ed8c742220b368844d0
Timestamp:2022-03-05 05:56:20 PM
Hash:0000078046464724c23bf1b7ceb398ac16dc87a4a506b5f495e95f8888d58495
Nonce:1479221
Txs:
054100e4d142a7975e3bf78de475397437575a2d55d739c2d9c4b93c3159c504
Vins:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
0
liyuechun
Vouts
2
juncheng
8
liyuechun
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Timestamp:2022-03-05 05:56:00 PM
Hash:00000bb873aaf6287b6e618a3965635eceee48bd66d63ed8c742220b368844d0
Nonce:4336573
Txs:
5a6ee5c721941fb8d62d6458356d8aa67514cec473dea0a04943f69cec6912f1
Vins:
-1
Genesis data
Vouts
10
liyuechun
./bc getBalance -address "liyuechun"
./bc getBalance -address "juncheng"
liyuechun has 9 toke
juncheng has 1 token
Fine,以上就是关于交易的全部内容
|