基本知识
公钥加密算法使用的是成对的密钥:公钥和私钥,公钥可以公开,私钥不能被公开。比特币钱包实际上是一个密钥对,当你安装 一个钱包应用,或者是使用一个比特币客户端来生成一个新地址是,他就会为你生成一个密钥对。
代码实现
func (cli *CLI) createWallet(nodeID string) {//创建钱包的主函数wallets, _ := NewWallets(nodeID) address := wallets.CreateWallet()wallets.SaveToFile(nodeID)fmt.Printf("Your new address: %s\n", address)}
我们慢慢的分析这个程序,其中的NewWallets()函数如下,在这里先是定义了一个钱包集合,我们利用wallets结构体存储多个钱包,将他们保存到文件中或者从文件中进行加载,每个钱包都保存了一堆公钥和私钥。创建出了一个空的钱包集合后,便开始加载以前的钱包集合文件
func NewWallets(nodeID string) (*Wallets, error) {wallets := Wallets{}wallets.Wallets = make(map[string]*Wallet)err := wallets.LoadFromFile(nodeID)return &wallets, err}type Wallets struct {Wallets map[string]*Wallet}type Wallet struct {PrivateKey ecdsa.PrivateKeyPublicKey []byte}func (ws *Wallets) LoadFromFile(nodeID string) error {walletFile := fmt.Sprintf(walletFile, nodeID) if _, err := os.Stat(walletFile); os.IsNotExist(err) { //判断文件是否存在return err}fileContent, err := ioutil.ReadFile(walletFile)// ReadFile 读取文件中的所有数据,返回读取的数据和遇到的错误。//如果读取成功,则 err 返回 nil,而不是 EOFfunc ReadFile(filename string) ([]byte, error)if err != nil {log.Panic(err)}var wallets Walletsgob.Register(elliptic.P256())//gob是Golang包自带的一个数据结构序列化的编码/解码工具。decoder := gob.NewDecoder(bytes.NewReader(fileContent))err = decoder.Decode(&wallets)//这里应该是一个解码的过程if err != nil {log.Panic(err)}ws.Wallets = wallets.Walletsreturn nil}
再来看一看wallets.CreateWallet()方法,其中的NewWallet()如下, NewWallet()函数创建了一个钱包,我们可以根据公钥打印出相应的钱包对应的地址,然后将钱包存储到钱包集合结构体中
unc (ws *Wallets) CreateWallet() string {wallet := NewWallet()address := fmt.Sprintf("%s", wallet.GetAddress())ws.Wallets[address] = wallet //存储到钱包集合中return address}func NewWallet() *Wallet {private, public := newKeyPair() //得到公钥和私钥wallet := Wallet{private, public} //存储到钱包结构体return &wallet}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.Y.Bytes()...)return *private, pubKey}//由公钥得到地址,具体方法见我的博客用 [“go语言实现比特币地址校验”](/m0_37719047/article/details/81945896) func (w Wallet) GetAddress() []byte {pubKeyHash := HashPubKey(w.PublicKey) versionedPayload := append([]byte{version}, pubKeyHash...)checksum := checksum(versionedPayload)fullPayload := append(versionedPayload, checksum...)address := Base58Encode(fullPayload)return address}
最后将创建好的钱包更新到存储钱包集合的文件中去
func (ws Wallets) SaveToFile(nodeID string) {var content bytes.Buffer//开辟一个内存空间walletFile := fmt.Sprintf(walletFile, nodeID)gob.Register(elliptic.P256())encoder := gob.NewEncoder(&content) //序列化结构体err := encoder.Encode(ws)if err != nil {log.Panic(err)}err = ioutil.WriteFile(walletFile, content.Bytes(), 0644) //将序列化的数据写入到文件中去if err != nil {log.Panic(err)}}
如果我们需要打印钱包集合中所有钱包对应的地址,我们可以利用以下的函数进行遍历。
func (cli *CLI) listAddresses(nodeID string) {wallets, err := NewWallets(nodeID) //加载现有的钱包集合if err != nil {log.Panic(err)}addresses := wallets.GetAddresses()for _, address := range addresses {fmt.Println(address)}}func (ws *Wallets) GetAddresses() []string {var addresses []stringfor address := range ws.Wallets {addresses = append(addresses, address)}return addresses}
通过以上的代码,我们便完成了钱包,实现了 创建钱包和遍历钱包的功能
参考
/