6.钱包
6.1 加密过程
为了建立钱包与账户金额的关系,我们简要介绍一下钱包和加密过程,创建一个钱包地址
1.生成一对公钥和私钥
2.获取地址,可以公钥进行Base58进行编码
3.转账过程:拿到被转账者的地址后,将其反编码变成公钥,将公钥和数据进行签名
4.通过私钥进行解密,解密是单方向的,只有用私钥的人才能进行解密
6.2 sha256
将某个字符串转化为哈希字符串
/Users/xxx/go/src/publicChain/part43-sha256/main.go
func main() {
hasher := sha256.New()
hasher.Write([]byte("https://blog.csdn.net/weixin_51487151?type=blog"))
bytes := hasher.Sum(nil)
fmt.Printf("%x\n", bytes)
}
运行
d44efe3429970454d5212fabecb2ffb52367d725b8dd4c7b1f341df93303828a
6.3 ripemd160
将某个字符串转化为哈希字符串,这个比上面那个更短
以太坊地址
/Users/xxx/go/src/publicChain/part44-ripemd160/main.go
func main() {
hasher := ripemd160.New()
hasher.Write([]byte("https://blog.csdn.net/weixin_51487151?type=blog"))
bytes := hasher.Sum(nil)
fmt.Printf("%x\n", bytes)
}
运行
dfa00792a7e62eea204f20539cf269dae9eb6cb1
6.4 base58加密解密
base58可以将字节数组转换成一个大数进行存储
我们来写一下代码部分
/Users/xxx/go/src/publicChain/part45-base58/BLC/base58.go
var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
func Base58Encode(input []byte) []byte {
var result []byte
x := big.NewInt(0).SetBytes(input)
base := big.NewInt(int64(len(b58Alphabet)))
zero := big.NewInt(0)
mod := &big.Int{}
for x.Cmp(zero) != 0 {
x.DivMod(x, base, mod)
result = append(result, b58Alphabet[mod.Int64()])
}
ReverseBytes(result)
for b := range input {
if b == 0x00 {
result = append([]byte{b58Alphabet[0]}, result...)
} else {
break
}
}
return result
}
func Base58Decode(input []byte) []byte {
result := big.NewInt(0)
zeroBytes := 0
for b := range input {
if b == 0x00 {
zeroBytes++
}
}
payload := input[zeroBytes:]
for _, b := range payload {
charIndex := bytes.IndexByte(b58Alphabet, b)
result.Mul(result, big.NewInt(58))
result.Add(result, big.NewInt(int64(charIndex)))
}
decoded := result.Bytes()
decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...)
return decoded
}
/Users/xxx/go/src/publicChain/part45-base58/BLC/Utils.go
package BLC
func ReverseBytes(data []byte) {
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
data[i], data[j] = data[j], data[i]
}
}
最后我们再在main函数中测试一下
/Users/xxx/go/src/publicChain/part45-base58/main.go
func main() {
bytes := []byte("https://blog.csdn.net/weixin_51487151?type=blog")
bytes58 := BLC.Base58Encode(bytes)
fmt.Printf("%s\n", bytes58)
bytesStr := BLC.Base58Decode(bytes58)
fmt.Printf("%s\n", bytesStr)
}
运行,加密解密都可以实现
1sML2JBPsdAyCMc45YRYt83ynt8WVS6NS1iLJSNXm8mW7vvXwrddw3sP49vnfzS22
https://blog.csdn.net/weixin_51487151?type=blog
6.5 base64对称加密
/Users/xxx/go/src/publicChain/part46-base64/main.go
func main() {
msg := "Hello, 世界"
encoded := base64.StdEncoding.EncodeToString([]byte(msg))
fmt.Println(encoded)
decded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
fmt.Println("decode error:", err)
return
}
fmt.Println(string(decded))
}
运行
SGVsbG8sIOS4lueVjA==
Hello, 世界
6.6 创建钱包
以上是几种加密方法,现在我们来创建钱包
/Users/xxx/go/src/publicChain/part50-wallet/BLC/Wallet.go
package BLC
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"log"
)
//define a wallet object
type Wallet struct {
//1 private key
PrivateKey ecdsa.PrivateKey
//2 public key
PublicKey []byte
}
//creat a wallet
func NewWallet() *Wallet {
privateKey, publicKey := NewKeyPair()
fmt.Println(&privateKey)
fmt.Println(publicKey)
return &Wallet{privateKey, publicKey}
}
//produce a publicKey through privateKey
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
curve := elliptic.P256()
//create private key
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.X.Bytes()...)
return *private, pubKey
}
在main函数中运行,即可输出
/Users/xxx/go/src/publicChain/part50-wallet/main.go
package main
import (
"publicChain/part50-wallet/BLC"
)
func main() {
BLC.NewWallet()
}
&{{{0xc0000220c0} 21017854596864650218203831960074430375956288853481211304698980906726553655340 59885365380312163380342999904701909361419031424282375945617351012038472067745} 5764075985346365143513693016953536224356111200943861561398090661806759750069}
[46 119 175 35 115 14 170 148 104 124 222 174 232 130 57 124 239 245 77 207 104 159 217 99 59 147 82 154 33 173 152 44 46 119 175 35 115 14 170 148 104 124 222 174 232 130 57 124 239 245 77 207 104 159 217 99 59 147 82 154 33 173 152 44]
6.7 通过公钥生成钱包地址
/Users/xxx/go/src/publicChain/part51-wallet-address/BLC/Wallet.go
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/ripemd160"
"log"
)
const version = byte(0x00)
const addressChecksumLen = 4
type Wallet struct {
//1 private key
PrivateKey ecdsa.PrivateKey
//2 public key
PublicKey []byte
}
//creat a wallet
func NewWallet() *Wallet {
privateKey, publicKey := NewKeyPair()
fmt.Println("\n", &privateKey)
fmt.Println()
fmt.Println(publicKey)
return &Wallet{privateKey, publicKey}
}
//produce a publicKey through privateKey
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.X.Bytes()...)
return *private, pubKey
}
func (w *Wallet) GetAddress() []byte {
//1 hash256.160 publicKey
ripedmd160 := w.Ripemd160(w.PublicKey)
version_ripedmd160Hash := append([]byte{version}, ripedmd160...)
checkSumBytes := CheckSum(version_ripedmd160Hash)
bytes := append(version_ripedmd160Hash, checkSumBytes...)
return Base58Encode(bytes)
}
func CheckSum(payload []byte) []byte {
hash1 := sha256.Sum256(payload)
hash2 := sha256.Sum256(hash1[:])
return hash2[:addressChecksumLen]
}
func (w *Wallet) Ripemd160(publicKey []byte) []byte {
//1.256
hash256 := sha256.New()
hash256.Write(publicKey)
hash := hash256.Sum(nil)
//2.160
ripemd160 := ripemd160.New()
ripemd160.Write(hash)
return ripemd160.Sum(nil)
}
在main函数中编译代码
/Users/xxx/go/src/publicChain/part51-wallet-address/main.go
func main() {
wallet := BLC.NewWallet()
address := wallet.GetAddress()
fmt.Printf("address: %s\n", address)
}
6.8 判断地址是否合法有效
/Users/xxx/go/src/publicChain/part52-wallet-address/BLC/Wallet.go
package BLC
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/ripemd160"
"log"
)
const version = byte(0x00)
const addressChecksumLen = 4
type Wallet struct {
//1 private key
PrivateKey ecdsa.PrivateKey
//2 public key
PublicKey []byte
}
//creat a wallet
func NewWallet() *Wallet {
privateKey, publicKey := NewKeyPair()
fmt.Println("\n", &privateKey)
fmt.Println()
fmt.Println(publicKey)
return &Wallet{privateKey, publicKey}
}
//produce a publicKey through privateKey
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.X.Bytes()...)
return *private, pubKey
}
func (w *Wallet) IsvalidAddress(address []byte) bool {
versionCheckSumBytes := Base58Decode(address)
fmt.Println(versionCheckSumBytes)
CheckSumBytes := versionCheckSumBytes[len(versionCheckSumBytes)-addressChecksumLen:]
versionRipemd160 := versionCheckSumBytes[:len(versionCheckSumBytes)-addressChecksumLen]
//fmt.Println(len(CheckSumBytes))
//fmt.Println(len(versionRipemd160))
checkBytes := CheckSum(versionRipemd160)
if bytes.Compare(CheckSumBytes, checkBytes) == 0 {
return true
}
return false
}
func (w *Wallet) GetAddress() []byte {
//1 hash256.160 publicKey
ripedmd160 := w.Ripemd160(w.PublicKey)
version_ripedmd160Hash := append([]byte{version}, ripedmd160...)
checkSumBytes := CheckSum(version_ripedmd160Hash)
bytes := append(version_ripedmd160Hash, checkSumBytes...)
return Base58Encode(bytes)
}
func CheckSum(payload []byte) []byte {
hash1 := sha256.Sum256(payload)
hash2 := sha256.Sum256(hash1[:])
return hash2[:addressChecksumLen]
}
func (w *Wallet) Ripemd160(publicKey []byte) []byte {
//1.256
hash256 := sha256.New()
hash256.Write(publicKey)
hash := hash256.Sum(nil)
//2.160
ripemd160 := ripemd160.New()
ripemd160.Write(hash)
return ripemd160.Sum(nil)
}
/Users/xxx/go/src/publicChain/go.mod
func main() {
wallet := BLC.NewWallet()
address := wallet.GetAddress()
isValid := wallet.IsvalidAddress(address)
fmt.Printf("address: %s\n", address)
fmt.Printf("%s The Address is %v\n", address, isValid)
}
6.9 Wallets结构
/Users/xxx/go/src/publicChain/part53-wallets/BLC/Wallets.go
package BLC
import "fmt"
type Wallets struct {
Wallets map[string]*Wallet
}
//create collection of wallet
func NewWallets() *Wallets {
wallets := &Wallets{}
wallets.Wallets = make(map[string]*Wallet)
return wallets
}
// 创建一个新钱包
func (w *Wallets) CreateNewWallet() {
wallet := NewWallet()
fmt.Printf("Address:%s\n", wallet.GetAddress())
w.Wallets[string(wallet.GetAddress())] = wallet
}
/Users/xxx/go/src/publicChain/go.mod
func main() {
wallets := BLC.NewWallets()
fmt.Println(wallets.Wallets)
wallets.CreateNewWallet()
wallets.CreateNewWallet()
fmt.Println(wallets.Wallets)
}
Address:1NQD2ZVDtJKNhyzUuk9t2W3TS1owgJsqKA
map[1CpeMqZJCQmVvZ7tmLta5F3QFakvLyGwFP:0xc000026180 1NQD2ZVDtJKNhyzUuk9t2W3TS1owgJsqKA:0xc0000261c0]
6.10 将地址集成到项目里面
将part42-transaction-new-transaction-multi-transaction复制一份并且重命名。并把上面文件夹中的除Utils.go文件中的文件全部拷贝,并且粘贴在BLC文件夹中,把上面Utils.go中的代码复制到本Utils.go文件中
我们现在要增加几个命令行
/Users/xxx/go/src/publicChain/part54-wallets/BLC/CLI_createwallet.go
func (cli *CLI) createWallet() {
wallets := NewWallets()
wallets.CreateNewWallet()
fmt.Println(wallets.Wallets)
}
我们先写一个方法,然后在CLI.go文件中完善一下对应的命令行工具代码
编译并运行
go build -o bc main.go
./bc
creatWallet --create wallet
creatBlockChain -address --transaction data
send -from FROM -to TO -amount AMOUNT --transaction details
printChain -- output block's information
getBalance -address -- output block's information
在转账/查询余额之前让我们先来判断地址是否是有效的
func (cli CLI) Run() {
--
for index, fromAddress := range from {
if IsvalidAddress([]byte(fromAddress)) == false || IsvalidAddress([]byte(to[index])) == false {
fmt.Println("the address is invalid")
os.Exit(1)
}
}
--
if creatBlockChainCmd.Parsed() {
if IsvalidAddress([]byte(*flagcreatBlockChainWithAddress))==false {
fmt.Println("the address is invalid")
printUsage()
os.Exit(1)
}
cli.creatGenesisBlockChain(*flagcreatBlockChainWithAddress)
}
--
if getBalanceCmd.Parsed() {
if IsvalidAddress([]byte(*getBalanceWithAddress)) == false {
fmt.Println("the address is invalid")
printUsage()
os.Exit(1)
}
cli.getBalance(*getBalanceWithAddress)
}
--
}
现在我们编译并提供一个有效地址运行,区块能够正常创建
./bc creatBlockChain -address "1NQD2ZVDtJKNhyzUuk9t2W3TS1owgJsqKA"
[0 234 190 63 133 110 197 141 97 195 225 185 249 38 52 24 62 136 192 190 158 247 35 198 157]
is creating genesis block...
00000ea54fe5db2a27b0ac5d993f34cb512a477f6a15cf7d26a509f7c945b8e1
我们再来删除数据库尝试创建钱包地址,我们看到钱包地址也能够正常创建
./bc creatWallet
Address:1BDQqpLSqucBPYYXKiVApdVfsGBHwrcffd
map[1BDQqpLSqucBPYYXKiVApdVfsGBHwrcffd:0xc000021780]
也可以使用该地址创建区块和查询余额
6.11 addresslists
我们照旧来创建一个getAddressLists命令
先新建一个文件
/Users/xxx/go/src/publicChain/part55-wallets-getaddresslists/BLC/CLI_getaddresslists.go
func (cli *CLI) getAddressLists() []string {
fmt.Println("print all wallets' address")
return nil
}
然后在CLI.go文件中按照模版完善代码
即可在终端中运行获取所有钱包地址
6.12将钱包信息写入到dat文件中
区块能够被遍历,但是地址无法遍历,主要因为地址之前没有联系(也就是没法索引)
我们现在来继续构建方法
/Users/xxx/go/src/publicChain/part55-wallets-getaddresslists/BLC/CLI_createwallet.go
func (cli *CLI) createWallet() {
wallets := NewWallets()
wallets.CreateNewWallet()
// store all wallets
wallets.SaveWallets()
fmt.Println(wallets.WalletsMap)
}
/Users/xxx/go/src/publicChain/part55-wallets-getaddresslists/BLC/Wallets.go
//write the info of wallets to file
func (w *Wallets) SaveWallets() {
var content bytes.Buffer
//the purpose of register is to Serialization
gob.Register(elliptic.P256())
encoder := gob.NewEncoder(&content)
err := encoder.Encode(&w)
if err != nil {
log.Panic(err)
}
// write the serialized data to file
err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
if err != nil {
log.Panic(err)
}
}
/Users/xxx/go/src/publicChain/part55-wallets-getaddresslists/BLC/Wallets.go
//create collection of wallet
func NewWallets() (*Wallets,error) {
if _,err := os.Stat(walletFile);os.IsNotExist(err) {
wallets := &Wallets{}
wallets.WalletsMap = make(map[string]*Wallet)
return &wallets,err
}
fileContent,err := ioutil.ReadFile(walletFile)
if err != nil {
log.Panic(err)
}
var wallets Wallets
gob.Register(elliptic.P256())
decoder:= gob.NewDecoder(bytes.NewReader(fileContent))
err = decoder.Decode(&wallets)
if err != nil {
log.Panic(err)
}
return &wallets,nil
}
func (w *Wallets) CreateNewWallet() {
wallet := NewWallet()
fmt.Printf("Address:%s\n", wallet.GetAddress())
w.WalletsMap[string(wallet.GetAddress())] = wallet
w.SaveWallets()
}
func (cli *CLI) createWallet() {
wallets, _ := NewWallets()
wallets.CreateNewWallet()
fmt.Println(len(wallets.WalletsMap))
}
再去终端中删掉数据库再编译
./bc creatWallet
Address:1D4fs55nGjJ2m7vr91tPnun2TALbjzFEZu
1
./bc creatWallet
Address:1KSCY5L2WFfBLZkkrPV3eR5JVS5UyH95K5
2
./bc creatWallet
Address:12GzqgM7rq3eFnP2LBpSqjgvcmqw5MaDJf
3
6.13 输出所有钱包地址
完善下列文件中的代码
/Users/xxx/go/src/publicChain/part56-wallets-addresslists/BLC/CLI_getaddresslists.go
func (cli *CLI) getAddressLists() []string {
fmt.Println("print all wallets' address")
wallets, _ := NewWallets()
for address, _ := range wallets.WalletsMap {
fmt.Println(address)
}
return nil
}
编译并运行,并且新建一个钱包地址在获取所有钱包地址也能够正常输出
go build -o bc main.go
./bc getAddressLists
print all wallets' address
1D4fs55nGjJ2m7vr91tPnun2TALbjzFEZu
1KSCY5L2WFfBLZkkrPV3eR5JVS5UyH95K5
12GzqgM7rq3eFnP2LBpSqjgvcmqw5MaDJf
6.14 inputs-outputs-update
/Users/xxx/go/src/publicChain/part57-wallets-inputs-outputs-update/BLC/Transaction-TXInput.go
type TXInput struct {
//1 transaction hash
TxHash []byte
//2 Store the index of TXOutput in Vout
Vouts int
Signature []byte
PublicKey []byte //publickkey in wallet
}
// judge the current money belong to one's
func (txInput *TXInput) UnlockRipedmd160Hash(ripedmd160Hash []byte) bool {
PublicKey := Ripedmd160Hash(txInput.PublicKey)
return bytes.Compare(PublicKey, ripedmd160Hash) == 0
}
/Users/xxx/go/src/publicChain/part57-wallets-inputs-outputs-update/BLC/Transaction.go
func NewCoinbaseTransAction(address string) *Transaction {
//consume
txInput := &TXInput{[]byte{}, -1, nil, []byte{}}
txOutput := &TXOutput{10, address}
txCoinbase := &Transaction{[]byte{}, []*TXInput{txInput}, []*TXOutput{txOutput}}
// set hash
txCoinbase.HashTransaction()
return txCoinbase
--
}
func NewCoinbaseTransAction(address string) *Transaction {
//consume
txInput := &TXInput{[]byte{}, -1, nil, []byte{}}
txOutput := NewTXOutput(10, address)
txCoinbase := &Transaction{[]byte{}, []*TXInput{txInput}, []*TXOutput{txOutput}}
// set hash
txCoinbase.HashTransaction()
return txCoinbase
}
/Users/xxx/go/src/publicChain/part57-wallets-inputs-outputs-update/BLC/Transaction-TXOutput.go
func NewTXOutput(value int64, address string) *TXOutput {
txOutput := &TXOutput{value, nil}
//set Ripemd160Hash
txOutput.Lock(address)
return txOutput
}
func (txOutput *TXOutput) Lock(address string) {
publicKey := Base58Decode([]byte(address))
txOutput.Ripemd160Hash = publicKey[1 : len(publicKey)-4]
}
func (txOutput *TXOutput) UnlockScriptPublicKeyWithAddress(address string) bool {
publicKeyHash := Base58Decode([]byte(address))
hash160 := publicKeyHash[1 : len(publicKeyHash)-4]
return bytes.Compare(txOutput.Ripedmd160Hash, hash160) == 0
}
/Users/xxx/go/src/publicChain/part57-wallets-inputs-outputs-update/BLC/Transaction.go
func NewSimpleTransaction(from, to string, amount int, blockchain *Blockchain, txs []*Transaction) *Transaction {
wallets, _ := NewWallets()
wallet := wallets.WalletsMap[from]
money, spendableUTXODic := blockchain.FindSpendableUTXOS(from, amount, txs)
//consume
var txInputs []*TXInput
//bytes, _ := hex.DecodeString("c9fe972ca4e3b90a59ecf1743d493e493284030851a8178ac8fb2389d323488c")
for txHash, indexSlice := range spendableUTXODic {
for _, index := range indexSlice {
txHashBytes, _ := hex.DecodeString(txHash)
txInput := &TXInput{txHashBytes, index, nil,wallet.PublicKey}
txInputs = append(txInputs, txInput)
}
}
//transfer
var txOutputs []*TXOutput
txOutput := NewTXOutput(int64(amount), to)
txOutputs = append(txOutputs, txOutput)
//the rest amount
txOutput = NewTXOutput(int64(money)-int64(amount), from)
txOutputs = append(txOutputs, txOutput)
tx := &Transaction{[]byte{}, txInputs, txOutputs}
// set hash
tx.HashTransaction()
return tx
}
/Users/xxx/go/src/publicChain/part57-wallets-inputs-outputs-update/BLC/Blockchain.go
func (blockchain *Blockchain) UnUTXOs(address string, txs []*Transaction) []*UTXO {
var unUTXOs []*UTXO
spentTXOutputs := make(map[string][]int)
for _, tx := range txs {
if tx.IsCoinbaseTransaction() == false {
for _, in := range tx.Vins {
publicKeyHash := Base58Decode([]byte(address))
ripedmd160Hash := publicKeyHash[1 : len(publicKeyHash)-4]
if in.UnlockRipedmd160Hash(ripedmd160Hash) {
key := hex.EncodeToString(in.TxHash)
spentTXOutputs[key] = append(spentTXOutputs[key], in.Vouts)
}
}
}
}
--
}
…其他所涉及的代码也需要改一下
截至目前,查询、转账、发送交易、创建地址等操作都可以执行,下面是一段测试代码,可成功运行
./bc send -from '["1JX43j1CfkUMyXHj24FobUHWp8Q2PDshtZ"]' -to '["1FZedtfPu1MokBidFs9Mxt6EDo3p9CZ2Kb"]' -amount '["6"]'
./bc getBalance -address "1JX43j1CfkUMyXHj24FobUHWp8Q2PDshtZ"
1JX43j1CfkUMyXHj24FobUHWp8Q2PDshtZ has 4 token
|