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
*/
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
}