golang 区块链:验证签名

齐昊苍
2023-12-01

1.遍历交易集合txs获取全部的消费记录inputMap
2.从区块链获取inputMap的消费记录input对应的output
3.传入output的集合,逐笔验证签名

验证:
1.复制一份新的交易对象(input的签名和公钥置空)
2.对复制的交易进行hash,获取签名需要的hash

获取input的output所在的交易
设置vin的公钥为utxo的pubhash
对交易进行hash

3.验证签名

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)verifyTxs(txs []*transaction.Transaction) (validTxs []*transaction.Transaction,msg string, err error) {
	/*
	从数据库查询最新的hash
	并将数据库设值给blockchain
	*/
	client.getBlockchainAndSetToClient()
	iterator := client.Blockchain.Iterator()
	//遍历txs获取全部inputMap
	var inputMap = make(map[string]*transaction.TXInput)
	for _, tx := range txs {
		for _, vin := range tx.Vins {
			inputMap[string(vin.TxHash)] = vin
			//log.Printf("传入的vin:%+v\n",vin)
		}
	}

	//调用方法,根据inputMap获取全部utxos
	var utxos []*transaction.UTXO
	utxos,msg,err = getUTXOsByInputMap(iterator,inputMap)

	//遍历txs,逐比验证
	for _, tx := range txs {
		isValid,_ := tx.VerifySign(utxos)
		if !isValid {
			continue
		}

		//有效交易添加到返回值
		validTxs = append(validTxs, tx)
	}

	return
}

/*
new 一个迭代器
 */
func (blc *Blockchain) Iterator() *BlockchainIterator {
	if blc.DB == nil {
		return nil
	}
	return &BlockchainIterator{NextHash: blc.Tip, DB: blc.DB}
}

验证交易的签名

/*
验证交易的签名
 */
func (tx *Transaction) VerifySign(utxos []*UTXO) (isValid bool,msg string) {
	copyTx := tx.trimmedCopy()
	curve := elliptic.P256()

	//遍历input
	for _, vin := range tx.Vins {
		//调用方法对copyTx进行hash,获取签名需要的hash
		var signHash []byte
		signHash,msg = copyTx.getHashForSignOrVerify(utxos,*vin)
		if msg != "" {
			log.Println(msg)
			return
		}

		//签名的r,s
		r := big.Int{}
		s := big.Int{}
		sigLen := len(vin.ScriptSig)
		r.SetBytes(vin.ScriptSig[:(sigLen/2)])
		s.SetBytes(vin.ScriptSig[(sigLen/2):])

		//公钥对象
		x := big.Int{}
		y := big.Int{}
		keyLen := len(vin.PubKey)
		x.SetBytes(vin.PubKey[:(keyLen/2)])
		y.SetBytes(vin.PubKey[(keyLen/2):])
		pubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}

		//log.Println("vin:", vin)
		//log.Println("公钥:", vin.PubKey)

		isValid = ecdsa.Verify(&pubKey,signHash,&r,&s)
		if !isValid {
			msg = "签名验证失败"
			log.Println(msg)
			return
		}
	}
	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}
}

根据inputMap获取全部utxos

/*
根据inputMap获取全部utxos
 */
func getUTXOsByInputMap(iterator *BlockchainIterator,inputMap map[string]*transaction.TXInput) (utxos []*transaction.UTXO,msg string, err error) {
	for  {
		if len(inputMap) == 0 {
			return
		}
		var b *block.Block
		b,msg,err = iterator.Next()
		if msg != "" || err != nil {
			log.Println(msg,err)
			return
		}

		//遍历区块,
		for _, tx := range b.Txs {
			input := inputMap[string(tx.TxHash)]
			if input == nil {
				continue
			}
			if input.Vout > len(tx.Vouts) {
				continue
			}
			//如果input公钥的rmd160不等于output的rmd160,则不符合条件
			if bytes.Compare(wallet.Ripemd160Hash(input.TxHash),tx.Vouts[input.Vout].PubHash) == 0 {
				continue
			}
			//符合的output,添加到返回值
			utxos = append(utxos, &transaction.UTXO{Hash: tx.TxHash,Index: input.Vout,Output: tx.Vouts[input.Vout]})

			//删除inputMap对应的input
			delete(inputMap,string(tx.TxHash))
		}
	}
}

/*
获取下一个block
 */
func (iterator *BlockchainIterator) Next() (block *block.Block,msg string, err error) {
	err = iterator.DB.View(func(tx *bolt.Tx) error {
		//获取表
		b := tx.Bucket([]byte("blocks"))
		if b == nil {
			log.Println(err)
			return errors.New(BlockTableName + " is nil")
		}
		//从DB获取区块
		block,msg,err = GetBlockFromDB(b,iterator.NextHash)
		if err != nil {
			log.Println(err)
			return err
		}
		if block == nil {
			return nil
		}

		//更新迭代器的下一个指针
		iterator.NextHash = block.PrevBlockHash
		return err
	})
	return
}

/*
从数据库获取区块
*/
func GetBlockFromDB(b *bolt.Bucket,key []byte) (blockStruct *block.Block, msg string,err error) {
	//获取区块
	blockBytes := b.Get(key)
	if blockBytes == nil {
		msg = "DB:"+string(key)+"没有对应的block"
		log.Println(msg)
		return
	}
	blockStruct,err = DeserializeBlock(blockBytes)
	if err != nil {
		log.Println(err)
	}
	return
}

/*
反序列化
 */
func DeserializeBlock(blockBytes []byte) (*Block,error) {
	var b Block

	decoder := gob.NewDecoder(bytes.NewReader(blockBytes))
	err := decoder.Decode(&b)

	return &b,err
}

获取区块链对象

/*
获取区块链并设置给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
}
 类似资料: