blockchain_go/wallet.go

151 lines
3.0 KiB
Go

package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"fmt"
"io/ioutil"
"log"
"os"
"golang.org/x/crypto/ripemd160"
)
const version = byte(0x00)
const walletFile = "wallet.dat"
// Wallet stores private and public keys
type Wallet struct {
PrivateKey ecdsa.PrivateKey
PublicKey ecdsa.PublicKey
}
// Wallets stores a collection of wallets
type Wallets struct {
Wallets map[string]*Wallet
}
// GetAddress returns wallet address
func (w Wallet) GetAddress() []byte {
public := append(w.PublicKey.X.Bytes(), w.PublicKey.Y.Bytes()...)
publicSHA256 := sha256.Sum256(public)
RIPEMD160Hasher := ripemd160.New()
_, err := RIPEMD160Hasher.Write(publicSHA256[:])
if err != nil {
log.Panic(err)
}
publicRIPEMD160 := RIPEMD160Hasher.Sum(nil)
versionedPayload := append([]byte{version}, publicRIPEMD160...)
checksum := checksum(versionedPayload)
fullPayload := append(versionedPayload, checksum...)
address := Base58Encode(fullPayload)
return address
}
// NewWallet creates and returns a Wallet
func NewWallet() *Wallet {
private, public := newKeyPair()
wallet := Wallet{private, public}
return &wallet
}
func newKeyPair() (ecdsa.PrivateKey, ecdsa.PublicKey) {
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
return *private, private.PublicKey
}
// 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
}
// SaveToFile saves wallets to a file
func (ws Wallets) SaveToFile() {
var content bytes.Buffer
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)
}
}
// LoadFromFile loads wallets from the file
func (ws *Wallets) LoadFromFile() error {
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
}
// 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
}
// NewWallets creates Wallets and fills it from a file if it exists
func NewWallets() (*Wallets, error) {
wallets := Wallets{}
wallets.Wallets = make(map[string]*Wallet)
err := wallets.LoadFromFile()
return &wallets, err
}
// Checksum generates a checksum for a public key
func checksum(payload []byte) []byte {
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])
return secondSHA[:4]
}