Created
March 19, 2021 18:23
-
-
Save tonyfabeen/36e9b9200de6d89af28c4ec3f9f7ba7b to your computer and use it in GitHub Desktop.
Create a new Bitcoin Wallet along with a new address
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/sha256" | |
"encoding/hex" | |
"errors" | |
"fmt" | |
"io" | |
"github.com/shengdoushi/base58" | |
"github.com/tyler-smith/go-bip32" | |
"github.com/tyler-smith/go-bip39" | |
"golang.org/x/crypto/ripemd160" | |
) | |
func hashSha256(data []byte) ([]byte, error) { | |
hasher := sha256.New() | |
_, err := hasher.Write(data) | |
if err != nil { | |
return nil, err | |
} | |
return hasher.Sum(nil), nil | |
} | |
func hashDoubleSha256(data []byte) ([]byte, error) { | |
hash1, err := hashSha256(data) | |
if err != nil { | |
return nil, err | |
} | |
hash2, err := hashSha256(hash1) | |
if err != nil { | |
return nil, err | |
} | |
return hash2, nil | |
} | |
func hashRipeMD160(data []byte) ([]byte, error) { | |
hasher := ripemd160.New() | |
_, err := io.WriteString(hasher, string(data)) | |
if err != nil { | |
return nil, err | |
} | |
return hasher.Sum(nil), nil | |
} | |
func hash160(data []byte) ([]byte, error) { | |
hash1, err := hashSha256(data) | |
if err != nil { | |
return nil, err | |
} | |
hash2, err := hashRipeMD160(hash1) | |
if err != nil { | |
return nil, err | |
} | |
return hash2, nil | |
} | |
type Address []byte | |
func (address Address) ToBase58() string { | |
alphabet := base58.BitcoinAlphabet | |
return base58.Encode(address, alphabet) | |
} | |
func (address Address) ToHex() string { | |
addressEncoded := make([]byte, hex.EncodedLen(len(address))) | |
hex.Encode(addressEncoded, address) | |
return string(addressEncoded) | |
} | |
type Wallet struct { | |
PrivateKey *bip32.Key | |
PublicKey *bip32.Key | |
Mnemonic string | |
} | |
func (wallet *Wallet) GenerateAddress() (Address, error) { | |
// 1 - Take the corresponding public key generated with it (33 bytes, 1 byte 0x02 (y-coord is even), and 32 bytes corresponding to X coordinate) | |
// 2 - Perform SHA-256 hashing on the public key | |
// 3 - Perform RIPEMD-160 hashing on the result of SHA-256 | |
hashedPubKeyXCoordinate, err := hash160(wallet.PublicKey.Key[:32]) | |
if err != nil { | |
return nil, err | |
} | |
// 4 - Add version byte in front of RIPEMD-160 hash (0x00 for Main Network) | |
hashedPubKeyXCoordinateWithNetwork := append([]byte{0x00}, hashedPubKeyXCoordinate...) | |
// 5 - Perform SHA-256 hash on the extended RIPEMD-160 result | |
// 6 - Perform SHA-256 hash on the result of the previous SHA-256 hash | |
doubledHashedSha256, err := hashDoubleSha256(hashedPubKeyXCoordinateWithNetwork) | |
if err != nil { | |
return nil, err | |
} | |
// 7 - Take the first 4 bytes of the second SHA-256 hash. This is the address checksum | |
addressChecksum := doubledHashedSha256[:4] | |
//8 - Add the 4 checksum bytes from stage 7 at the end of extended RIPEMD-160 hash from stage 4. | |
// This is the 25-byte binary Bitcoin Address. | |
address := append(hashedPubKeyXCoordinateWithNetwork, addressChecksum...) | |
return address, nil | |
} | |
func CreateWallet(password string) (*Wallet, error) { | |
if password == "" { | |
return nil, errors.New("password should not be an empty string") | |
} | |
panicWhenError := func(err error) { | |
if err != nil { | |
panic(err) | |
} | |
} | |
entropy, err := bip39.NewEntropy(256) | |
panicWhenError(err) | |
mnemonic, err := bip39.NewMnemonic(entropy) | |
panicWhenError(err) | |
seed := bip39.NewSeed(mnemonic, password) | |
masterKey, err := bip32.NewMasterKey(seed) | |
panicWhenError(err) | |
publicKey := masterKey.PublicKey() | |
wallet := &Wallet{ | |
Mnemonic: mnemonic, | |
PrivateKey: masterKey, | |
PublicKey: publicKey, | |
} | |
return wallet, nil | |
} | |
// Background | |
// https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses | |
// | |
// Validators | |
// http://lenschulwitz.com/base58 | |
// https://www.appdevtools.com/base58-encoder-decoder | |
func main() { | |
wallet, _ := CreateWallet("Secret Passphrase") | |
address, _ := wallet.GenerateAddress() | |
fmt.Println("address in base58", address.ToBase58()) | |
fmt.Println("address in Hex", address.ToHex()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment