生成交易
1.根据地址遍历区块链,获取未花费的utxo和余额balance
2.根据未花费的utxo和转账金额生成input的集合vins(input:消费的记录)
3.生成output的集合vouts(output:接收方收到的余额,和给发送方找零。output:入账的记录)
4.设置交易hash
5.对交易进行签名
签名
1.复制一份新的交易对象(input的签名和公钥置空)
2.对复制的交易进行hash,获取签名需要的hash
(
获取input的output所在的交易
设置vin的公钥为utxo的pubhash
对交易进行hash
)
3.对input进行签名
type Client struct {
Blockchain *blockchain.Blockchain
}
type Blockchain struct {
Tip []byte //最新区块的hash
DB *bolt.DB //"github.com/boltdb/bolt"
}
type UTXO struct {
Hash []byte //交易哈希
Index int //索引
Output *TXOutput //未花费的output
}
type TXOutput struct {
//金额
Value int
//ripemd160
PubHash []byte
}
type TXInput struct {
//交易的Hash
TxHash []byte
//存储TXOutput在Vout里面的索引
Vout int
//数字签名
ScriptSig []byte
//公钥
PubKey []byte
}
/*
创建交易
*/
func (client *Client)createTransaction(priKey *ecdsa.PrivateKey,to string,value int) (tx *transaction.Transaction,msg string,err error) {
/*
从数据库查询最新的hash
并将数据库设值给blockchain
*/
client.getBlockchainAndSetToClient()
/*
将私钥转换成地址
*/
//将私钥转换成公钥
publicKey := append(priKey.X.Bytes(),priKey.Y.Bytes()...)
//ripemd160(hash256(publicKey))
hash256 := sha256.Sum256(publicKey)
rmd160 := ripemd160.New()
rmd160.Write(hash256[:])
rmd:= rmd160.Sum(nil)
//getAddressChecksum
hash256 := sha256.Sum256(rmd)
hash256 = sha256.Sum256(hash256[:])
checkSum := hash256[:4]
//拼接version、ripemd160、checksum
from := append([]byte{0x00},rmd160...)
from = append(attr,addrCheckSum...)
/*
获取utxo和balance
*/
utxos,balance := client.GetUTXOs(from,value)
//调用方法,创建交易
tx,msg,err = CreateTransaction(balance,utxos,value,priKey,from,to)
//调用方法,对交易签名
msg,err = tx.SignTransaction(utxos,priKey)
return
}
创建交易前的准备
/*
获取utxo和balance
*/
func (client Client) GetUTXOs(from string,value int) (utxos []*transaction.UTXO,balance int) {
//获取区块链迭代器
blcIterator := &BlockchainIterator{NextHash: client.Blockchain.Tip, DB: client.Blockchain.DB}
//循环获取区块,直到val大于等于传入的value或区块为创世区块
var b = &block.Block{}
var mapInput = make(map[string]*transaction.TXInput)
var mapTXO = make(map[string]*transaction.UTXO)
for {
//金额大于等于转账的金额 或 创世块 退出
if (value != -1 && balance >= value) || (b != nil && b.Height == 1) {
break
}
b = blcIterator.Next()
//调用方法,获取address到该区块的output、input
mapInput,mapTXO = b.GetAddrOutInPut(from,mapInput,mapTXO)
//调用方法,计算UTXO并返回output
balance,utxos = getUTXOAndReturnOutput(mapInput,mapTXO)
}
return
}
/*
获取address在该区块的output、input
*/
func (b *Block) GetAddrOutInPut(address string,mapInput map[string]*TXInput,maptxo map[string]*UTXO) ( map[string]*TXInput, map[string]*UTXO) {
if b == nil {
return nil,nil
}
//遍历交易
for _, tx := range b.Txs {
txHash := hex.EncodeToString(tx.TxHash)
for _,input := range tx.Vins {
//取出map address的input
if !input.UnLockWithAddress(address) {
continue
}
inHash := hex.EncodeToString(input.TxHash)
mapInput[inHash] = input
}
//取出map address的output
for i, output := range tx.Vouts {
//调用方法判断是否该address的output
if !output.UnLockWithAddress(address) {
continue
}
txo := &UTXO{Hash: tx.TxHash, Index: i, Output: output}
maptxo[txHash] = txo
}
}
return mapInput,maptxo
}
/*
根据address解锁(判断是否该address的input)
*/
func (input *TXInput) UnLockWithAddress(address string) bool {
//base58解码
addressBytes := Base58Decode([]byte(address))
//从address截取rmd160
rmd160 := addressBytes[1 : len(addressBytes) - 4]
//将input的公钥变成ripemd160
inputRMD := Ripemd160Hash(input.PubKey)
//log.Printf("rmd160: %x",rmd160)
//log.Printf("inputRMD: %x",inputRMD)
return bytes.Compare(rmd160,inputRMD) == 0
}
/*
ripemd160(hash256(publicKey))
*/
func Ripemd160Hash(publicKey []byte) []byte {
//hash256 publicKey
hash256 := sha256.Sum256(publicKey)
//ripemd160
rmd160 := ripemd160.New()
rmd160.Write(hash256[:])
return rmd160.Sum(nil)
}
/*
获取下一个block
*/
func (iterator *BlockchainIterator) Next() (block *block.Block) {
iterator.DB.View(func(tx *bolt.Tx) error {
var err error
//获取表
b := tx.Bucket([]byte(BlockTableName))
if b == nil {
return errors.New(BlockTableName + " is nil")
}
//从DB获取区块
//获取区块
blockBytes := b.Get(key)
decoder := gob.NewDecoder(bytes.NewReader(blockBytes))
decoder.Decode(block)
//更新迭代器的下一个指针
iterator.NextHash = block.PrevBlockHash
return err
})
return
}
/*
获取区块链并设置给client
*/
func (client *Client) getBlockchainAndSetToClient() {
//从数据库查询最新的hash
//并将数据库设值给blockchain
blc = &Blockchain{}
//打开或创建数据库
var db *bolt.DB //"github.com/boltdb/bolt"
db,err = bolt.Open("blockchain.db",0600,&bolt.Options{Timeout: 1 * time.Second})
if err != nil {
log.Println(err)
return
}
blc.DB = db
//查询最新的hash
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("blocks"))
if b == nil {
log.Println("blocks is nil")
return
}
blc.Tip = b.Get([]byte("l"))
return nil
})
client.Blockchain = blc
}
/*
根据address解锁(判断是否该address的output)
*/
func (output *TXOutput) UnLockWithAddress(address string) bool {
//base58解码
addressBytes := utils.Base58Decode([]byte(address))
//从address截取rmd160
rmd160 := addressBytes[1 : len(address) - 4]
return bytes.Compare(rmd160,output.PubHash) == 0
}
/*
base58解码
*/
func Base58Decode(input []byte) (output []byte) {
if len(input) == 0 {
return
}
result := big.NewInt(0)
zeroBytes := 0
for b := range input {
if b == 0x00 {
zeroBytes++
}
}
payload := input[zeroBytes:]
for _,b := range payload {
charIndex := bytes.IndexByte(base58Alphabet,b)
result.Mul(result,big.NewInt(58))
result.Add(result,big.NewInt(int64(charIndex)))
}
output = result.Bytes()
output = append(bytes.Repeat([]byte{byte(0x00)},zeroBytes),output...)
return
}
创建交易
/*
生成交易
*/
func CreateTransaction(balance int,utxos []*UTXO, value int,priKey *ecdsa.PrivateKey,from string,to string) (tx *Transaction,msg string,err error) {
if balance < value {
msg = "余额不足,无法交易"
log.Println(msg)
return
}
if len(utxos) < 1 {
msg = "余额不足,无法交易"
log.Println(msg)
return
}
//生成vins
pubKey := append(priKey.X.Bytes(),priKey.Y.Bytes()...)
//log.Println("公钥:",pubKey)
var vins []*TXInput
for _, utxo := range utxos {
input := NewTxInput(utxo.Hash,utxo.Index,pubKey)
vins = append(vins, input)
}
//调用方法,生成vouts
var vouts []*TXOutput
output := NewTxOutput(value,to) //接收方
vouts = append(vouts, output)
if balance > value {//找零
balanceOutput := NewTxOutput(balance - value,from)
vouts = append(vouts, balanceOutput)
}
//生成交易
tx = &Transaction{Vins: vins,Vouts: vouts}
//调用方法,生成交易hash
err = tx.hashTransaction()
if err != nil {
log.Println(err)
return
}
return
}
/*
设置交易hash
*/
func (tx *Transaction) hashTransaction() (err error) {
//序列化交易对象
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err = encoder.Encode(tx)
if err != nil {
log.Println(err)
return err
}
//hash256
txHash := sha256.Sum256(result.Bytes())
tx.TxHash = txHash[:]
return nil
}
/*
生成output
*/
func NewTxOutput(val int, address string) *TXOutput {
output := &TXOutput{Value: val}
output.lock(address)
return output
}
/*
*/
func (output *TXOutput) lock(address string) {
//base58解码
addressBytes := Base58Decode([]byte(address))
//从address截取rmd160
rmd160 := addressBytes[1 : len(address) - 4]
output.PubHash = rmd160
}
/*
生成input
*/
func NewTxInput(txhash []byte,index int, pubKey []byte) *TXInput {
input := &TXInput{TxHash:txhash,Vout: index,ScriptSig: nil,PubKey: pubKey}
return input
}
签名
/*
对交易签名
*/
func (tx *Transaction) SignTransaction(utxos []*UTXO, priKey *ecdsa.PrivateKey) (msg string,err error) {
//调用方法,复制transaction
copyTx := tx.trimmedCopy()
//遍历input
for i, vin := range copyTx.Vins {
//调用方法对copyTx进行hash,获取签名需要的hash
var signHash []byte
signHash,msg = copyTx.getHashForSignOrVerify(utxos,*vin)
//对input进行签名
r,s,err := ecdsa.Sign(rand.Reader,priKey,signHash)
if err != nil {
log.Println(err)
return "", err
}
signature := append(r.Bytes(),s.Bytes()...)
tx.Vins[i].ScriptSig = signature
}
return
}
/*
签名,验证签名前的准备
*/
func (tx *Transaction)getHashForSignOrVerify(utxos []*UTXO,vin TXInput) (hashByte []byte, msg string) {
//获取input的output所在的交易
var utxo *UTXO
for _, u := range utxos {
if bytes.Compare(u.Hash,vin.TxHash) == 0 && u.Index == vin.Vout {
utxo = u
break
}
}
if utxo == nil {
msg = "交易错误,根据input的信息未能找到对应的utxo"
log.Println(msg,vin.TxHash)
return
}
//确保vin签名为空
vin.ScriptSig = nil
//设置vin的公钥为utxo的pubhash
vin.PubKey = utxo.Output.PubHash
//获取签名需要的hash,调用方法对copyTx哈希
hashByte = tx.txHashForSignHash()
if hashByte == nil {
msg = "获取签名需要的hash失败"
log.Println(msg)
return
}
return
}
/*
对交易进行hash,获取进行签名的hash
*/
func (tx *Transaction) txHashForSignHash() []byte {
copyTx := *tx
copyTx.TxHash = []byte{}
err := copyTx.hashTransaction()
if err != nil {
return nil
}
return copyTx.TxHash
}
/*
设置交易hash
*/
func (tx *Transaction) hashTransaction() (err error) {
//序列化交易对象
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err = encoder.Encode(tx)
if err != nil {
log.Println(err)
return err
}
//hash256
txHash := sha256.Sum256(result.Bytes())
tx.TxHash = txHash[:]
return nil
}
/*
拷贝一份新的transaction用于签名
*/
func (tx *Transaction) trimmedCopy() Transaction {
var inputs []*TXInput
var outputs []*TXOutput
for _, vin := range tx.Vins {
inputs = append(inputs, &TXInput{TxHash: vin.TxHash,Vout: vin.Vout,ScriptSig: nil,PubKey: nil} )
}
for _, vout := range tx.Vouts {
outputs = append(outputs, &TXOutput{Value: vout.Value,PubHash: vout.PubHash})
}
return Transaction{TxHash: tx.TxHash,Vins: inputs,Vouts: outputs}
}