0
点赞
收藏
分享

微信扫一扫

golang 区块链:创建交易并对交易进行签名

穆风1818 2022-02-16 阅读 85

生成交易

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}
}
举报

相关推荐

0 条评论