如果交易顺利完成,转账发起人的“未花费输出”被消耗掉变成了花费状态,而转账接收人to得到了一笔新的“未花费输出”,之后他自己需要转账时,查询自己的未花费输出,即可使用这笔钱。
最后需要对交易进行签名,表示交易确实是由发起人本人发起(私钥签名),而不是被第三人冒充。
6.Transaction的签名和验证 6.1 签名交易的有效性需要首先建立在发起人签名的基础上,防止他人冒充转账或者发起人抵赖,blockchain_go中交易签名实现如下:
// SignTransaction signs inputs of a Transaction func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) { prevTXs := make(map[string]Transaction) for _, vin := range tx.Vin { prevTX, err := bc.FindTransaction(vin.Txid) if err != nil { log.Panic(err) } prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX } tx.Sign(privKey, prevTXs) } // Sign signs each input of a Transaction func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) { if tx.IsCoinbase() { return } for _, vin := range tx.Vin { if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil { log.Panic("ERROR: Previous transaction is not correct") } } txCopy := tx.TrimmedCopy() for inID, vin := range txCopy.Vin { prevTx := prevTXs[hex.EncodeToString(vin.Txid)] txCopy.Vin[inID].Signature = nil txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash dataToSign := fmt.Sprintf("%x\n", txCopy) r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign)) if err != nil { log.Panic(err) } signature := append(r.Bytes(), s.Bytes()...) tx.Vin[inID].Signature = signature txCopy.Vin[inID].PubKey = nil } }交易输入的签名信息是放在TXInput中的signature字段,其中需要包括用户的pubkey,用于之后的验证。需要对每一个输入做签名。
6.2 验证交易签名是发生在交易产生时,交易完成后,Transaction会把交易广播给邻居。节点在进行挖矿时,会整理一段时间的所有交易信息,将这些信息打包进入新的区块,成功加入区块链以后,这个交易就得到了最终的确认。但是在挖矿节点打包交易前,需要对交易的有效性做验证,以防虚假数据,验证实现如下:
// MineBlock mines a new block with the provided transactions func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block { var lastHash []byte var lastHeight int for _, tx := range transactions { // TODO: ignore transaction if it's not valid if bc.VerifyTransaction(tx) != true { log.Panic("ERROR: Invalid transaction") } } ... ... ... return block } // VerifyTransaction verifies transaction input signatures func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool { if tx.IsCoinbase() { return true } prevTXs := make(map[string]Transaction) for _, vin := range tx.Vin { prevTX, err := bc.FindTransaction(vin.Txid) if err != nil { log.Panic(err) } prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX } return tx.Verify(prevTXs) } // Verify verifies signatures of Transaction inputs func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { if tx.IsCoinbase() { return true } for _, vin := range tx.Vin { if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil { log.Panic("ERROR: Previous transaction is not correct") } } txCopy := tx.TrimmedCopy() curve := elliptic.P256() for inID, vin := range tx.Vin { prevTx := prevTXs[hex.EncodeToString(vin.Txid)] txCopy.Vin[inID].Signature = nil txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash r := big.Int{} s := big.Int{} sigLen := len(vin.Signature) r.SetBytes(vin.Signature[:(sigLen / 2)]) s.SetBytes(vin.Signature[(sigLen / 2):]) x := big.Int{} y := big.Int{} keyLen := len(vin.PubKey) x.SetBytes(vin.PubKey[:(keyLen / 2)]) y.SetBytes(vin.PubKey[(keyLen / 2):]) dataToVerify := fmt.Sprintf("%x\n", txCopy) rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y} if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false { return false } txCopy.Vin[inID].PubKey = nil } return true }可以看到验证的时候也是每个交易的每个TXInput都单独进行验证,和签名过程很相似,需要构造相同的交易数据txCopy,验证时会用到签名设置的TxInput.PubKeyHash生成一个原始的PublicKey,将前面的signature分拆后通过ecdsa.Verify进行验证。
7.总结以上简单分析和整理了blockchain_go中的交易和UTXO机制的实现过程,加深了区块链中的挖矿,交易和转账的基础技术原理的理解。