diff --git a/address.go b/address.go new file mode 100644 index 0000000..8c26e3a --- /dev/null +++ b/address.go @@ -0,0 +1,66 @@ +package main + +import ( + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "log" + + "golang.org/x/crypto/ripemd160" +) + +const version = byte(0x00) + +// Wallet ... +type Wallet struct { + PrivateKey []byte + PublicKey []byte +} + +// GetAddress returns wallet address +func (w Wallet) GetAddress() []byte { + publicSHA256 := sha256.Sum256(w.PublicKey) + + 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 ... +func NewWallet() *Wallet { + private, public := newKeyPair() + wallet := Wallet{private, public} + + return &wallet +} + +func newKeyPair() ([]byte, []byte) { + curve := elliptic.P256() + private, x, y, err := elliptic.GenerateKey(curve, rand.Reader) + if err != nil { + log.Panic(err) + } + + public := append(x.Bytes(), y.Bytes()...) + + return private, public +} + +// Checksum ... +func checksum(payload []byte) []byte { + firstSHA := sha256.Sum256(payload) + secondSHA := sha256.Sum256(firstSHA[:]) + + return secondSHA[:4] +} diff --git a/base58.go b/base58.go new file mode 100644 index 0000000..b9ccdce --- /dev/null +++ b/base58.go @@ -0,0 +1,35 @@ +package main + +import ( + "math/big" +) + +var alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + +// Base58Encode encodes a byte array to Base58 +func Base58Encode(input []byte) []byte { + var result []byte + + x := big.NewInt(0) + x.SetBytes(input) + + base := big.NewInt(int64(len(alphabet))) + zero := big.NewInt(0) + mod := &big.Int{} + + for x.Cmp(zero) != 0 { + x.DivMod(x, base, mod) + result = append(result, alphabet[mod.Int64()]) + } + + ReverseBytes(result) + for c := range input { + if c == 0x00 { + result = append([]byte{alphabet[0]}, result...) + } else { + break + } + } + + return result +} diff --git a/cli.go b/cli.go index 1270c6e..6476822 100644 --- a/cli.go +++ b/cli.go @@ -17,6 +17,11 @@ func (cli *CLI) createBlockchain(address string) { fmt.Println("Done!") } +func (cli *CLI) createWallet() { + wallet := NewWallet() + fmt.Printf("Your address: %s\n", wallet.GetAddress()) +} + func (cli *CLI) getBalance(address string) { bc := NewBlockchain(address) defer bc.db.Close() @@ -83,6 +88,7 @@ func (cli *CLI) Run() { getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) + createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError) sendCmd := flag.NewFlagSet("send", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) @@ -103,6 +109,11 @@ func (cli *CLI) Run() { if err != nil { log.Panic(err) } + case "createwallet": + err := createWalletCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } case "printchain": err := printChainCmd.Parse(os.Args[2:]) if err != nil { @@ -134,6 +145,10 @@ func (cli *CLI) Run() { cli.createBlockchain(*createBlockchainAddress) } + if createWalletCmd.Parsed() { + cli.createWallet() + } + if printChainCmd.Parsed() { cli.printChain() } diff --git a/utils.go b/utils.go index 7f5e727..aecf919 100644 --- a/utils.go +++ b/utils.go @@ -16,3 +16,10 @@ func IntToHex(num int64) []byte { return buff.Bytes() } + +// ReverseBytes reverses a byte array +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] + } +}