区块链项目 - 6 钱包

区块链项目 - 6 钱包,第1张

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

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/2992503.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存