实验目的及要求
了解编程语言的基本逻辑,理解编程语言运行机理。熟悉区块链的原理。能正确使用编程语言搭建区块和区块链,并进行钱包的实现。
实验基本原理
通过课堂理论教学准备,动手使用实验软件平台,完成慕课实验项目的要求。
实验实施环境
Goland & GoSDK
实验概述
编写区块链基本原型,加入钱包的算法。
实验内容
学习第六章 区块链钱包交易
imooc学习地址↓
6-1数字货币地址及身份识别
6-2数据钱包创建交易过程及代码实现
学习总结:
数字货币的地址及身份标识
- 某种途径可以识别出你是交易输出的所有者,称之为身份
- 在比特币中,你的身份(indentity)就是一对(或者多对)公钥(public key)和私钥(private key)
- 所谓的地址,只不过是讲公钥表示成人类可读的形式
公钥算法和数字签名
- 公钥加密(public-key cryptography)算法使用的是成对ide密钥:公钥和私钥
- 公钥并不是敏感性息,可以告诉其他人。但是,私钥绝对不能告诉其他人:所有所有者(owner)才能知道私钥,能够识别,鉴定和证明所有者身份的就是私钥。
实现区块链钱包交易的代码编写
项目目录截图
原型代码
(因为代码量过多,这里只放部分)
wallet.go
package core
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"log"
"golang.org/x/crypto/ripemd160"
)
const version = byte(0x00)
const addressChecksumLen = 4
// Wallet stores private and public keys
type Wallet struct {
PrivateKey ecdsa.PrivateKey
PublicKey []byte
}
// NewWallet creates and returns a Wallet
func NewWallet() *Wallet {
private, public := newKeyPair()
wallet := Wallet{private, public}
return &wallet
}
// GetAddress returns wallet address
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
}
// HashPubKey hashes public key
func HashPubKey(pubKey []byte) []byte {
publicSHA256 := sha256.Sum256(pubKey)
RIPEMD160Hasher := ripemd160.New()
_, err := RIPEMD160Hasher.Write(publicSHA256[:])
if err != nil {
log.Panic(err)
}
publicRIPEMD160 := RIPEMD160Hasher.Sum(nil)
return publicRIPEMD160
}
// ValidateAddress check if address if valid
func ValidateAddress(address string) bool {
pubKeyHash := Base58Decode([]byte(address))
actualChecksum := pubKeyHash[len(pubKeyHash)-addressChecksumLen:]
version := pubKeyHash[0]
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
targetChecksum := checksum(append([]byte{version}, pubKeyHash...))
return bytes.Compare(actualChecksum, targetChecksum) == 0
}
// Checksum generates a checksum for a public key
func checksum(payload []byte) []byte {
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])
return secondSHA[:addressChecksumLen]
}
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
}
wallets.go
package core
import (
"bytes"
"crypto/elliptic"
"encoding/gob"
"fmt"
"io/ioutil"
"log"
"os"
)
const walletFile = "wallet_%s.dat"
// Wallets stores a collection of wallets
type Wallets struct {
Wallets map[string]*Wallet
}
// NewWallets creates Wallets and fills it from a file if it exists
func NewWallets(nodeID string) (*Wallets, error) {
wallets := Wallets{}
wallets.Wallets = make(map[string]*Wallet)
err := wallets.LoadFromFile(nodeID)
return &wallets, err
}
// CreateWallet adds a Wallet to Wallets
func (ws *Wallets) CreateWallet() string {
wallet := NewWallet()
address := fmt.Sprintf("%s", wallet.GetAddress())
ws.Wallets[address] = wallet
return address
}
// GetAddresses returns an array of addresses stored in the wallet file
func (ws *Wallets) GetAddresses() []string {
var addresses []string
for address := range ws.Wallets {
addresses = append(addresses, address)
}
return addresses
}
// GetWallet returns a Wallet by its address
func (ws Wallets) GetWallet(address string) Wallet {
return *ws.Wallets[address]
}
// LoadFromFile loads wallets from the file
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)
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)
}
ws.Wallets = wallets.Wallets
return nil
}
// SaveToFile saves wallets to a file
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)
}
}
Terminal运行结果截图