<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                源代碼地址:[https://github.com/daleboy/blockchain5](https://github.com/daleboy/blockchain5) # 前言 在上一篇中,我們把一個由用戶定義的任意字符串當成是地址,現在我們將要實現一個跟比特幣一樣的真實地址。 # 比特幣地址 比特幣地址是基于加密算法的組合創建的密鑰,實際上是將公鑰表示成人類可讀的形式,并且保證這個世界上沒有人其他人可以取走你的幣,除非拿到你的私鑰。 # 公鑰加密 公鑰加密(public-key cryptography)算法使用的是成對的密鑰:公鑰和私鑰。 本質上,比特幣錢包也只不過是這樣的密鑰對而已。當你安裝一個錢包應用,或是使用一個比特幣客戶端來生成一個新地址時,它就會為你生成一對密鑰。在比特幣中,誰擁有了私鑰,誰就可以控制所以發送到這個公鑰的幣。 私鑰和公鑰只不過是隨機的字節序列,因此它們無法在屏幕上打印,人類也無法通過肉眼去讀取。這就是為什么比特幣使用了一個轉換算法(公鑰加密),將公鑰轉化為一個人類可讀的字符串(也就是我們看到的地址)。 公鑰和私鑰必須確保生成真正的隨機字節。比特幣采用橢圓曲線產生私鑰。 比特幣使用 Base58 算法將公鑰轉換成人類可讀的形式(公鑰加密算法)。這個算法跟著名的 Base64 很類似,區別在于它使用了更短的字母表:為了避免一些利用字母相似性的攻擊,從字母表中移除了一些字母。也就是,沒有這些符號:0(零),O(大寫的 o),I(大寫的i),l(小寫的 L),因為這幾個字母看著很像。另外,也沒有 + 和 / 符號。 # 每一筆交易輸入都會由創建交易的人簽名 比特幣使用的是 ECDSA(Elliptic Curve Digital Signature Algorithm)算法來對交易進行簽名,我們也會使用該算法。 在數學和密碼學中,有一個數字簽名(digital signature)的概念,算法可以保證: 1. 當數據從發送方傳送到接收方時,數據不會被修改; 2. 數據由某一確定的發送方創建; 3. 發送方無法否認發送過數據這一事實。 通過在數據上應用簽名算法(也就是對數據進行簽名),你就可以得到一個簽名,這個簽名晚些時候會被驗證。生成數字簽名需要一個私鑰,而驗證簽名需要一個公鑰。簽名有點類似于印章,比方說我做了一幅畫,完了用印章一蓋,就說明了這幅畫是我的作品。給數據生成簽名,就是給數據蓋了章。 為了對數據進行簽名,我們需要下面兩樣東西: 1. 要簽名的數據 2. 私鑰 應用簽名算法可以生成一個簽名,并且這個簽名會被存儲在交易輸入中。為了對一個簽名進行驗證,我們需要以下三樣東西: 1. 被簽名的數據 2. 簽名 3. 公鑰 簡單來說,驗證過程可以被描述為:檢查簽名是由被簽名數據加上私鑰得來,并且公鑰恰好是由該私鑰生成。 > 數據簽名并不是加密,你無法從一個簽名重新構造出數據。這有點像哈希:你在數據上運行一個哈希算法,然后得到一個該數據的唯一表示。簽名與哈希的區別在于密鑰對:有了密鑰對,才有簽名驗證。但是密鑰對也可以被用于加密數據:私鑰用于加密,公鑰用于解密數據。不過比特幣并不使用加密算法。 **在比特幣中,每一筆交易輸入都會由創建交易的人簽名。**,創始區塊例外,因為創始區塊是沒有輸入的,所以也不需要簽名。在被放入到一個塊之前,必須要對每一筆交易進行驗證。除了一些其他步驟,驗證意味著: 1. 檢查交易輸入有權使用來自之前交易的輸出(同一人) 2. 檢查交易簽名是正確的 ![](https://img.kancloud.cn/3d/f7/3df7a318c4e0ef6106df8c8fe75335ee_700x300.png) # 實現錢包地址 ## 錢包 錢包結構如下: ~~~ //Wallet 錢包結構 type Wallet struct { PrivateKey ecdsa.PrivateKey//錢包私鑰,誰擁有私鑰,誰就擁有錢包 PublicKey []byte } //Wallets 多個錢包 type Wallets struct { Wallets map[string]*Wallet//value為struct類型,一般用map存儲 } //NewWallet 創建一個錢包 func NewWallet() *Wallet { private, public := newKeyPair() wallet := Wallet{private, public} return &wallet } //newKeyPair 創建公私鑰對 func newKeyPair() (ecdsa.PrivateKey, []byte) { curve := elliptic.P256() private, err := ecdsa.GenerateKey(curve, rand.Reader)//生成私鑰 //公鑰從私鑰生成:將兩個slice拼接到一起(這種方式使用append只能用兩個參數,第二個參數的名稱后需要加三個點) pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...) return *private, pubKey } ~~~ ## 生成一個地址:將公鑰轉換為Base58地址 ~~~ //GetAddress address=version(1個字節)+public key hash(32個字節)+checksum(4字節) func (w Wallet) GetAddress() []byte { pubKeyHash := HashPubKey(w.PublicKey) //version:生成算法的版本的前綴 versionedPayload := append([]byte{version}, pubKeyHash...) checksum := checksum(versionedPayload) fullPayload := append(versionedPayload, checksum...) address := Base58Encode(fullPayload) return address } //HashPubKey 公鑰哈希轉為Base58編碼 func HashPubKey(pubKey []byte) []byte { publicSHA256 := sha256.Sum256(pubKey) RIPEMD160Hasher := ripemd160.New() _, err := RIPEMD160Hasher.Write(publicSHA256[:]) publicRIPEMD160 := RIPEMD160Hasher.Sum(nil) return publicRIPEMD160 } //校驗值:4字節 func checksum(payload []byte) []byte { firstSHA := sha256.Sum256(payload) secondSHA := sha256.Sum256(firstSHA[:])//兩次哈希計算 //addressChecksumLen=4,這里只截取secondSHA的4個字節長度切片 return secondSHA[:addressChecksumLen] } ~~~ ## 修改輸入和輸出來使用地址 交易中輸入者的身份,必須和輸入中引用的輸出者的身份相同(只有自己才有權處置自己的資產) ~~~ ype TXInput struct { Txid []byte Vout int//引用的輸出在原輸出列表中的編號 Signature []byte//輸入的簽名 PubKey []byte//公鑰 } //UsesKey 是否可以解鎖輸入中的輸出 func (in *TXInput) UsesKey(pubKeyHash []byte) bool { lockingHash := HashPubKey(in.PubKey) return bytes.Compare(lockingHash, pubKeyHash) == 0 } type TXOutput struct { Value int PubKeyHash []byte//公鑰哈希,并不存儲公鑰本身 } //Lock 設置輸出中的公鑰哈希,鎖定輸出 func (out *TXOutput) Lock(address []byte) { pubKeyHash := Base58Decode(address)//解碼出公鑰 pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4]//除掉version和checksum得到公鑰哈希 out.PubKeyHash = pubKeyHash } //IsLockedWithKey 檢查輸出的擁有者,不直接檢查公鑰是否相同,而是檢查公鑰的哈希是否相同 func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool { return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0 } ~~~ ## 交易簽名 (1)交易必須被交易發起者使用自己的私鑰進行簽名。因為這是比特幣里面保證發送方不會花費屬于其他人的幣的唯一方式。 (2)簽名必須是有效的。如果一個簽名是無效的,那么這筆交易就會被認為是無效的,因此,這筆交易也就無法被加到區塊鏈中。 (3)我們現在離實現交易簽名還差一件事情:用于簽名的數據。一筆交易的哪些部分需要簽名?又或者說,要對完整的交易進行簽名?選擇簽名的數據相當重要。因為用于簽名的這個數據,必須要包含能夠唯一識別數據的信息。比如,如果僅僅對輸出值進行簽名并沒有什么意義,因為簽名不會考慮發送方和接收方。 考慮到交易解鎖的是之前的輸出,然后重新分配里面的價值,并鎖定新的輸出,那么必須要簽名以下數據: 1. 存儲在已解鎖輸出的公鑰哈希。它識別了一筆交易的“發送方”。(from) 2. 存儲在新的鎖定輸出里面的公鑰哈希。它識別了一筆交易的“接收方”。(to) 3. 新的輸出值。 > 在比特幣中,鎖定/解鎖邏輯被存儲在腳本中,它們被分別存儲在輸入和輸出的`ScriptSig`和`ScriptPubKey`字段。由于比特幣允許這樣不同類型的腳本,它對`ScriptPubKey`的整個內容進行了簽名。 可以看到,我們不需要對存儲在輸入里面的公鑰簽名。因此,在比特幣里, 所簽名的并不是一個交易,而是一個去除部分內容的輸入副本,輸入里面存儲了被引用輸出的`ScriptPubKey`。 可以看到,我們不需要對存儲在輸入里面的公鑰簽名。因此,在比特幣里, 所簽名的并不是一個交易,而是一個去除部分內容的輸入副本,輸入里面存儲了被引用輸出的`ScriptPubKey`。 ~~~ //Sign 對當前交易進行簽名,需要把輸入所引用的輸出交易prevTXs作為參數進行處理 func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) { if tx.IsCoinbase() {//coinbase交易沒有實際輸入,所以沒有無需簽名 return } txCopy := tx.TrimmedCopy()//將會被簽署的是修剪后的當前交易的交易副本,而不是一個完整交易: for inID, vin := range txCopy.Vin {//迭代副本中的每一個輸入 prevTx := prevTXs[hex.EncodeToString(vin.Txid)] //在每個輸入中,`Signature`被設置為`nil`(Signature僅僅是一個雙重檢驗,所以沒有必要放進來) txCopy.Vin[inID].Signature = nil //`pubKey`被設置為所引用輸出的`PubKeyHash txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash txCopy.ID = txCopy.Hash() txCopy.Vin[inID].PubKey = nil r, s, err := ecdsa.Sign(rand.Reader, &privKey, txCopy.ID)//簽名的是交易副本的ID(即交易副本的哈希) signature := append(r.Bytes(), s.Bytes()...)//一個 ECDSA 簽名就是一對數字。連接切片,構建簽名 //**副本中每一個輸入是被分開簽名的** //盡管這對于我們的應用并不十分緊要,但是比特幣允許交易包含引用了不同地址的輸入 tx.Vin[inID].Signature = signature } } //TrimmedCopy 獲得修剪后的交易副本 func (tx *Transaction) TrimmedCopy() Transaction { var inputs []TXInput var outputs []TXOutput for _, vin := range tx.Vin { //包含了所有的輸入和輸出,但是`TXInput.Signature`和`TXIput.PubKey`被設置為`nil` //在調用這個方法后,會用引用的前一個交易的輸出的PubKeyHash,取代這里的PubKey inputs = append(inputs, TXInput{vin.Txid, vin.Vout, nil, nil}) } for _, vout := range tx.Vout { outputs = append(outputs, TXOutput{vout.Value, vout.PubKeyHash}) } txCopy := Transaction{tx.ID, inputs, outputs} return txCopy } ~~~ ## 驗證簽名 ~~~ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { if?tx.IsCoinbase()?{ returntrue ????} 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 txCopy.ID = txCopy.Hash() txCopy.Vin[inID].PubKey = nil //解包存儲在`TXInput.Signature`和`TXInput.PubKey`中的值 //一個簽名就是一對長度相同的數字。 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):]) //從輸入提取的公鑰創建一個rawPubKey rawPubKey := ecdsa.PublicKey{curve, &x, &y} //使用公鑰驗證副本的簽名,是否私鑰簽名檔結果一致(&r和&s是私鑰簽名txCopy.ID的結果) if ecdsa.Verify(&rawPubKey, txCopy.ID, &r, &s) == false { return false } } return true } ~~~ ## 獲得之前的交易 ~~~ //FindTransaction 根據交易ID,獲得交易 func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) { bci := bc.Iterator() for { block := bci.Next() for _, tx := range block.Transactions { if bytes.Compare(tx.ID, ID) == 0 { return *tx, nil } } if len(block.PrevBlockHash) == 0 { break } } return Transaction{}, errors.New("沒有找到交易") } //SignTransaction 簽名之前的交易 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)//輸入中保存了引用輸出交易的ID,由此可以查到之前的交易 prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX } tx.Sign(privKey, prevTXs) } //VerifyTransaction 驗證之前交易的簽名 func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool { prevTXs := make(map[string]Transaction) for _, vin := range tx.Vin { prevTX, err := bc.FindTransaction(vin.Txid)//輸入中保存了引用輸出交易的ID,由此可以查到之前的交易 prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX } return tx.Verify(prevTXs) } ~~~ ## 對當前交易進行簽名和驗證交易簽名 簽名在`NewUTXOTransaction`中進行: ~~~ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction { ... tx := Transaction{nil, inputs, outputs}//inputs將簽名部分設為了nil tx.ID = tx.Hash() bc.SignTransaction(&tx, wallet.PrivateKey)//通過錢包的私鑰進行簽名 return &tx } ~~~ 在一筆交易被放入一個塊之前進行驗證: ~~~ func (bc *Blockchain) MineBlock(transactions []*Transaction) { var lastHash []byte for _, tx := range transactions { if bc.VerifyTransaction(tx) != true { log.Panic("ERROR: 非法交易。") } } ... } ~~~ ## 運行測試 >1、創建兩個錢包地址 ![](https://img.kancloud.cn/da/48/da48ceea9c0ac6dc91b5bf14a204b8a4_725x179.png) >2、創建新的區塊鏈 ![](https://img.kancloud.cn/41/03/41038cc30d37a9ce081d7918387b0ba2_1126x203.png) >3、轉賬 ![](https://img.kancloud.cn/41/d1/41d13df5d4d1618135f0dbf5158b7451_1560x113.png) >4、轉賬后各賬戶余額查詢 ![](https://img.kancloud.cn/db/b5/dbb5e2ba790098c7bffd52bf10af4567_1054x170.png) >5、打印區塊鏈 ![](https://img.kancloud.cn/23/d5/23d57ede9c779eebcb3bce1197fe564f_1927x961.png) 可以看到,轉賬交易有兩個輸出,一個是收款人獲得的幣,一個是給發送者的找零。 >6、打印本地所有錢包地址 ![](https://img.kancloud.cn/b9/3a/b93aa9b4cbcb4d3e82eebf32c3d6dece_508x174.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看