aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-10-31 19:37:43 +0800
committerobscuren <geffobscura@gmail.com>2014-10-31 19:37:43 +0800
commitfd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b (patch)
tree5f60296ff025ec08962cdd2b4f6bbcfd1479cfdb /crypto
parent3ee0461cb5b6e4a5e2d287180afbdb681805a662 (diff)
downloaddexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar.gz
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar.bz2
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar.lz
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar.xz
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.tar.zst
dexon-fd9da72536b73351bbcdc1e9dbbbb8c0e4bfb21b.zip
ethcrypto => crypto
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.go47
-rw-r--r--crypto/crypto_test.go36
-rw-r--r--crypto/key_manager.go130
-rw-r--r--crypto/key_store.go113
-rw-r--r--crypto/keypair.go58
-rw-r--r--crypto/keyring.go123
-rw-r--r--crypto/keys_test.go122
-rw-r--r--crypto/mnemonic.go60
-rw-r--r--crypto/mnemonic_test.go74
-rw-r--r--crypto/mnemonic_words.go1630
10 files changed, 2393 insertions, 0 deletions
diff --git a/crypto/crypto.go b/crypto/crypto.go
new file mode 100644
index 000000000..e10a9e81f
--- /dev/null
+++ b/crypto/crypto.go
@@ -0,0 +1,47 @@
+package crypto
+
+import (
+ "crypto/sha256"
+
+ "code.google.com/p/go.crypto/ripemd160"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/obscuren/secp256k1-go"
+ "github.com/obscuren/sha3"
+)
+
+// TODO refactor, remove (bin)
+func Sha3(data []byte) []byte {
+ d := sha3.NewKeccak256()
+ d.Write(data)
+
+ return d.Sum(nil)
+}
+
+// Creates an ethereum address given the bytes and the nonce
+func CreateAddress(b []byte, nonce uint64) []byte {
+ return Sha3(ethutil.NewValue([]interface{}{b, nonce}).Encode())[12:]
+}
+
+func Sha256(data []byte) []byte {
+ hash := sha256.Sum256(data)
+
+ return hash[:]
+}
+
+func Ripemd160(data []byte) []byte {
+ ripemd := ripemd160.New()
+ ripemd.Write(data)
+
+ return ripemd.Sum(nil)
+}
+
+func Ecrecover(data []byte) []byte {
+ var in = struct {
+ hash []byte
+ sig []byte
+ }{data[:32], data[32:]}
+
+ r, _ := secp256k1.RecoverPubkey(in.hash, in.sig)
+
+ return r
+}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
new file mode 100644
index 000000000..af62a02a2
--- /dev/null
+++ b/crypto/crypto_test.go
@@ -0,0 +1,36 @@
+package crypto
+
+import (
+ "bytes"
+ "encoding/hex"
+ "testing"
+)
+
+// These tests are sanity checks.
+// They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256
+// and that the sha3 library uses keccak-f permutation.
+
+func TestSha3(t *testing.T) {
+ msg := []byte("abc")
+ exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
+ checkhash(t, "Sha3-256", Sha3, msg, exp)
+}
+
+func TestSha256(t *testing.T) {
+ msg := []byte("abc")
+ exp, _ := hex.DecodeString("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
+ checkhash(t, "Sha256", Sha256, msg, exp)
+}
+
+func TestRipemd160(t *testing.T) {
+ msg := []byte("abc")
+ exp, _ := hex.DecodeString("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")
+ checkhash(t, "Ripemd160", Ripemd160, msg, exp)
+}
+
+func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) {
+ sum := f(msg)
+ if bytes.Compare(exp, sum) != 0 {
+ t.Errorf("hash %s returned wrong result.\ngot: %x\nwant: %x", name, sum, exp)
+ }
+}
diff --git a/crypto/key_manager.go b/crypto/key_manager.go
new file mode 100644
index 000000000..cc2b9ff90
--- /dev/null
+++ b/crypto/key_manager.go
@@ -0,0 +1,130 @@
+package crypto
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+)
+
+type KeyManager struct {
+ keyRing *KeyRing
+ session string
+ keyStore KeyStore // interface
+ keyRings map[string]*KeyRing // cache
+ keyPair *KeyPair
+}
+
+func NewDBKeyManager(db ethutil.Database) *KeyManager {
+ return &KeyManager{keyStore: &DBKeyStore{db: db}, keyRings: make(map[string]*KeyRing)}
+}
+
+func NewFileKeyManager(basedir string) *KeyManager {
+ return &KeyManager{keyStore: &FileKeyStore{basedir: basedir}, keyRings: make(map[string]*KeyRing)}
+}
+
+func (k *KeyManager) KeyPair() *KeyPair {
+ return k.keyPair
+}
+
+func (k *KeyManager) KeyRing() *KeyPair {
+ return k.keyPair
+}
+
+func (k *KeyManager) PrivateKey() []byte {
+ return k.keyPair.PrivateKey
+}
+
+func (k *KeyManager) PublicKey() []byte {
+ return k.keyPair.PublicKey
+}
+
+func (k *KeyManager) Address() []byte {
+ return k.keyPair.Address()
+}
+
+func (k *KeyManager) save(session string, keyRing *KeyRing) error {
+ err := k.keyStore.Save(session, keyRing)
+ if err != nil {
+ return err
+ }
+ k.keyRings[session] = keyRing
+ return nil
+}
+
+func (k *KeyManager) load(session string) (*KeyRing, error) {
+ keyRing, found := k.keyRings[session]
+ if !found {
+ var err error
+ keyRing, err = k.keyStore.Load(session)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return keyRing, nil
+}
+
+func cursorError(cursor int, len int) error {
+ return fmt.Errorf("cursor %d out of range (0..%d)", cursor, len)
+}
+
+func (k *KeyManager) reset(session string, cursor int, keyRing *KeyRing) error {
+ if cursor >= keyRing.Len() {
+ return cursorError(cursor, keyRing.Len())
+ }
+ lock := &sync.Mutex{}
+ lock.Lock()
+ defer lock.Unlock()
+ err := k.save(session, keyRing)
+ if err != nil {
+ return err
+ }
+ k.session = session
+ k.keyRing = keyRing
+ k.keyPair = keyRing.GetKeyPair(cursor)
+ return nil
+}
+
+func (k *KeyManager) SetCursor(cursor int) error {
+ if cursor >= k.keyRing.Len() {
+ return cursorError(cursor, k.keyRing.Len())
+ }
+ k.keyPair = k.keyRing.GetKeyPair(cursor)
+ return nil
+}
+
+func (k *KeyManager) Init(session string, cursor int, force bool) error {
+ var keyRing *KeyRing
+ if !force {
+ var err error
+ keyRing, err = k.load(session)
+ if err != nil {
+ return err
+ }
+ }
+ if keyRing == nil {
+ keyRing = NewGeneratedKeyRing(1)
+ }
+ return k.reset(session, cursor, keyRing)
+}
+
+func (k *KeyManager) InitFromSecretsFile(session string, cursor int, secretsfile string) error {
+ keyRing, err := NewKeyRingFromFile(secretsfile)
+ if err != nil {
+ return err
+ }
+ return k.reset(session, cursor, keyRing)
+}
+
+func (k *KeyManager) InitFromString(session string, cursor int, secrets string) error {
+ keyRing, err := NewKeyRingFromString(secrets)
+ if err != nil {
+ return err
+ }
+ return k.reset(session, cursor, keyRing)
+}
+
+func (k *KeyManager) Export(dir string) error {
+ fileKeyStore := FileKeyStore{dir}
+ return fileKeyStore.Save(k.session, k.keyRing)
+}
diff --git a/crypto/key_store.go b/crypto/key_store.go
new file mode 100644
index 000000000..04560a04e
--- /dev/null
+++ b/crypto/key_store.go
@@ -0,0 +1,113 @@
+package crypto
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+)
+
+type KeyStore interface {
+ Load(string) (*KeyRing, error)
+ Save(string, *KeyRing) error
+}
+
+type DBKeyStore struct {
+ db ethutil.Database
+}
+
+const dbKeyPrefix = "KeyRing"
+
+func (k *DBKeyStore) dbKey(session string) []byte {
+ return []byte(fmt.Sprintf("%s%s", dbKeyPrefix, session))
+}
+
+func (k *DBKeyStore) Save(session string, keyRing *KeyRing) error {
+ k.db.Put(k.dbKey(session), keyRing.RlpEncode())
+ return nil
+}
+
+func (k *DBKeyStore) Load(session string) (*KeyRing, error) {
+ data, err := k.db.Get(k.dbKey(session))
+ if err != nil {
+ return nil, nil
+ }
+ var keyRing *KeyRing
+ keyRing, err = NewKeyRingFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ // if empty keyRing is found we return nil, no error
+ if keyRing.Len() == 0 {
+ return nil, nil
+ }
+ return keyRing, nil
+}
+
+type FileKeyStore struct {
+ basedir string
+}
+
+func (k *FileKeyStore) Save(session string, keyRing *KeyRing) error {
+ var content []byte
+ var err error
+ var privateKeys []string
+ var publicKeys []string
+ var mnemonics []string
+ var addresses []string
+ keyRing.Each(func(keyPair *KeyPair) {
+ privateKeys = append(privateKeys, ethutil.Bytes2Hex(keyPair.PrivateKey))
+ publicKeys = append(publicKeys, ethutil.Bytes2Hex(keyPair.PublicKey))
+ addresses = append(addresses, ethutil.Bytes2Hex(keyPair.Address()))
+ mnemonics = append(mnemonics, keyPair.Mnemonic())
+ })
+
+ basename := session
+ if session == "" {
+ basename = "default"
+ }
+
+ path := path.Join(k.basedir, basename)
+ content = []byte(strings.Join(privateKeys, "\n"))
+ err = ioutil.WriteFile(path+".prv", content, 0600)
+ if err != nil {
+ return err
+ }
+
+ content = []byte(strings.Join(publicKeys, "\n"))
+ err = ioutil.WriteFile(path+".pub", content, 0644)
+ if err != nil {
+ return err
+ }
+
+ content = []byte(strings.Join(addresses, "\n"))
+ err = ioutil.WriteFile(path+".addr", content, 0644)
+ if err != nil {
+ return err
+ }
+
+ content = []byte(strings.Join(mnemonics, "\n"))
+ err = ioutil.WriteFile(path+".mne", content, 0600)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *FileKeyStore) Load(session string) (*KeyRing, error) {
+ basename := session
+ if session == "" {
+ basename = "default"
+ }
+ secfile := path.Join(k.basedir, basename+".prv")
+ _, err := os.Stat(secfile)
+ // if file is not found then we return nil, no error
+ if err != nil {
+ return nil, nil
+ }
+ return NewKeyRingFromFile(secfile)
+}
diff --git a/crypto/keypair.go b/crypto/keypair.go
new file mode 100644
index 000000000..d02875ded
--- /dev/null
+++ b/crypto/keypair.go
@@ -0,0 +1,58 @@
+package crypto
+
+import (
+ "strings"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/obscuren/secp256k1-go"
+)
+
+type KeyPair struct {
+ PrivateKey []byte
+ PublicKey []byte
+ address []byte
+ mnemonic string
+ // The associated account
+ // account *StateObject
+}
+
+func GenerateNewKeyPair() *KeyPair {
+ _, prv := secp256k1.GenerateKeyPair()
+ keyPair, _ := NewKeyPairFromSec(prv) // swallow error, this one cannot err
+ return keyPair
+}
+
+func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) {
+ pubkey, err := secp256k1.GeneratePubKey(seckey)
+ if err != nil {
+ return nil, err
+ }
+
+ return &KeyPair{PrivateKey: seckey, PublicKey: pubkey}, nil
+}
+
+func (k *KeyPair) Address() []byte {
+ if k.address == nil {
+ k.address = Sha3(k.PublicKey[1:])[12:]
+ }
+ return k.address
+}
+
+func (k *KeyPair) Mnemonic() string {
+ if k.mnemonic == "" {
+ k.mnemonic = strings.Join(MnemonicEncode(ethutil.Bytes2Hex(k.PrivateKey)), " ")
+ }
+ return k.mnemonic
+}
+
+func (k *KeyPair) AsStrings() (string, string, string, string) {
+ return k.Mnemonic(), ethutil.Bytes2Hex(k.Address()), ethutil.Bytes2Hex(k.PrivateKey), ethutil.Bytes2Hex(k.PublicKey)
+}
+
+func (k *KeyPair) RlpEncode() []byte {
+ return k.RlpValue().Encode()
+}
+
+func (k *KeyPair) RlpValue() *ethutil.Value {
+ return ethutil.NewValue(k.PrivateKey)
+}
diff --git a/crypto/keyring.go b/crypto/keyring.go
new file mode 100644
index 000000000..eab13dbc4
--- /dev/null
+++ b/crypto/keyring.go
@@ -0,0 +1,123 @@
+package crypto
+
+import (
+ "fmt"
+ "io/ioutil"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+)
+
+type KeyRing struct {
+ keys []*KeyPair
+}
+
+func NewKeyRing() *KeyRing {
+ return &KeyRing{}
+}
+
+func (k *KeyRing) AddKeyPair(keyPair *KeyPair) {
+ k.keys = append(k.keys, keyPair)
+}
+
+func (k *KeyRing) GetKeyPair(i int) *KeyPair {
+ if len(k.keys) > i {
+ return k.keys[i]
+ }
+
+ return nil
+}
+
+func (k *KeyRing) Empty() bool {
+ return k.Len() == 0
+}
+
+func (k *KeyRing) Len() int {
+ return len(k.keys)
+}
+
+func (k *KeyRing) Each(f func(*KeyPair)) {
+ for _, keyPair := range k.keys {
+ f(keyPair)
+ }
+}
+
+func NewGeneratedKeyRing(len int) *KeyRing {
+ keyRing := NewKeyRing()
+ for i := 0; i < len; i++ {
+ keyRing.AddKeyPair(GenerateNewKeyPair())
+ }
+ return keyRing
+}
+
+func NewKeyRingFromFile(secfile string) (*KeyRing, error) {
+ var content []byte
+ var err error
+ content, err = ioutil.ReadFile(secfile)
+ if err != nil {
+ return nil, err
+ }
+ keyRing, err := NewKeyRingFromString(string(content))
+ if err != nil {
+ return nil, err
+ }
+ return keyRing, nil
+}
+
+func NewKeyRingFromString(content string) (*KeyRing, error) {
+ secretStrings := strings.Split(content, "\n")
+ var secrets [][]byte
+ for _, secretString := range secretStrings {
+ secret := secretString
+ words := strings.Split(secretString, " ")
+ if len(words) == 24 {
+ secret = MnemonicDecode(words)
+ } else if len(words) != 1 {
+ return nil, fmt.Errorf("Unrecognised key format")
+ }
+
+ if len(secret) != 0 {
+ secrets = append(secrets, ethutil.Hex2Bytes(secret))
+ }
+ }
+
+ return NewKeyRingFromSecrets(secrets)
+}
+
+func NewKeyRingFromSecrets(secs [][]byte) (*KeyRing, error) {
+ keyRing := NewKeyRing()
+ for _, sec := range secs {
+ keyPair, err := NewKeyPairFromSec(sec)
+ if err != nil {
+ return nil, err
+ }
+ keyRing.AddKeyPair(keyPair)
+ }
+ return keyRing, nil
+}
+
+func NewKeyRingFromBytes(data []byte) (*KeyRing, error) {
+ var secrets [][]byte
+ it := ethutil.NewValueFromBytes(data).NewIterator()
+ for it.Next() {
+ secret := it.Value().Bytes()
+ secrets = append(secrets, secret)
+ }
+ keyRing, err := NewKeyRingFromSecrets(secrets)
+ if err != nil {
+ return nil, err
+ }
+ return keyRing, nil
+}
+
+func (k *KeyRing) RlpEncode() []byte {
+ return k.RlpValue().Encode()
+}
+
+func (k *KeyRing) RlpValue() *ethutil.Value {
+ v := ethutil.EmptyValue()
+ k.Each(func(keyPair *KeyPair) {
+ v.Append(keyPair.RlpValue())
+ })
+ return v
+}
diff --git a/crypto/keys_test.go b/crypto/keys_test.go
new file mode 100644
index 000000000..46e40110c
--- /dev/null
+++ b/crypto/keys_test.go
@@ -0,0 +1,122 @@
+package crypto
+
+import (
+ "github.com/ethereum/go-ethereum/ethdb"
+ // "io/ioutil"
+ "fmt"
+ "os"
+ "path"
+ "testing"
+)
+
+// test if persistence layer works
+func TestDBKeyManager(t *testing.T) {
+ memdb, _ := ethdb.NewMemDatabase()
+ keyManager0 := NewDBKeyManager(memdb)
+ err := keyManager0.Init("", 0, false)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ keyManager1 := NewDBKeyManager(memdb)
+ err = keyManager1.Init("", 0, false)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
+ t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ }
+ err = keyManager1.Init("", 0, true)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) {
+ t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ }
+}
+
+func TestFileKeyManager(t *testing.T) {
+ basedir0 := "/tmp/ethtest0"
+ os.RemoveAll(basedir0)
+ os.Mkdir(basedir0, 0777)
+
+ keyManager0 := NewFileKeyManager(basedir0)
+ err := keyManager0.Init("", 0, false)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+
+ keyManager1 := NewFileKeyManager(basedir0)
+
+ err = keyManager1.Init("", 0, false)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
+ t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ }
+
+ err = keyManager1.Init("", 0, true)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) {
+ t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ }
+}
+
+// cursor errors
+func TestCursorErrors(t *testing.T) {
+ memdb, _ := ethdb.NewMemDatabase()
+ keyManager0 := NewDBKeyManager(memdb)
+ err := keyManager0.Init("", 0, false)
+ err = keyManager0.Init("", 1, false)
+ if err == nil {
+ t.Error("Expected cursor error")
+ }
+ err = keyManager0.SetCursor(1)
+ if err == nil {
+ t.Error("Expected cursor error")
+ }
+}
+
+func TestExportImport(t *testing.T) {
+ memdb, _ := ethdb.NewMemDatabase()
+ keyManager0 := NewDBKeyManager(memdb)
+ err := keyManager0.Init("", 0, false)
+ basedir0 := "/tmp/ethtest0"
+ os.RemoveAll(basedir0)
+ os.Mkdir(basedir0, 0777)
+ keyManager0.Export(basedir0)
+
+ keyManager1 := NewFileKeyManager(basedir0)
+ err = keyManager1.Init("", 0, false)
+ if err != nil {
+ t.Error("Unexpected error: ", err)
+ }
+ fmt.Printf("keyRing: %v\n", keyManager0.KeyPair())
+ fmt.Printf("keyRing: %v\n", keyManager1.KeyPair())
+ if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
+ t.Error("Expected private keys %x, %x, to be identical via export to filestore basedir", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ }
+ path.Join("")
+
+ // memdb, _ = ethdb.NewMemDatabase()
+ // keyManager2 := NewDBKeyManager(memdb)
+ // err = keyManager2.InitFromSecretsFile("", 0, path.Join(basedir0, "default.prv"))
+ // if err != nil {
+ // t.Error("Unexpected error: ", err)
+ // }
+ // if string(keyManager0.PrivateKey()) != string(keyManager2.PrivateKey()) {
+ // t.Error("Expected private keys %s, %s, to be identical via export/import prv", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ // }
+
+ // memdb, _ = ethdb.NewMemDatabase()
+ // keyManager3 := NewDBKeyManager(memdb)
+ // err = keyManager3.InitFromSecretsFile("", 0, path.Join(basedir0, "default.mne"))
+ // if err != nil {
+ // t.Error("Unexpected error: ", err)
+ // }
+ // if string(keyManager0.PrivateKey()) != string(keyManager3.PrivateKey()) {
+ // t.Error("Expected private keys %s, %s, to be identical via export/import mnemonic file", keyManager0.PrivateKey(), keyManager1.PrivateKey())
+ // }
+}
diff --git a/crypto/mnemonic.go b/crypto/mnemonic.go
new file mode 100644
index 000000000..0d690f245
--- /dev/null
+++ b/crypto/mnemonic.go
@@ -0,0 +1,60 @@
+package crypto
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// TODO: See if we can refactor this into a shared util lib if we need it multiple times
+func IndexOf(slice []string, value string) int64 {
+ for p, v := range slice {
+ if v == value {
+ return int64(p)
+ }
+ }
+ return -1
+}
+
+func MnemonicEncode(message string) []string {
+ var out []string
+ n := int64(len(MnemonicWords))
+
+ for i := 0; i < len(message); i += (len(message) / 8) {
+ x := message[i : i+8]
+ bit, _ := strconv.ParseInt(x, 16, 64)
+ w1 := (bit % n)
+ w2 := ((bit / n) + w1) % n
+ w3 := ((bit / n / n) + w2) % n
+ out = append(out, MnemonicWords[w1], MnemonicWords[w2], MnemonicWords[w3])
+ }
+ return out
+}
+
+func MnemonicDecode(wordsar []string) string {
+ var out string
+ n := int64(len(MnemonicWords))
+
+ for i := 0; i < len(wordsar); i += 3 {
+ word1 := wordsar[i]
+ word2 := wordsar[i+1]
+ word3 := wordsar[i+2]
+ w1 := IndexOf(MnemonicWords, word1)
+ w2 := IndexOf(MnemonicWords, word2)
+ w3 := IndexOf(MnemonicWords, word3)
+
+ y := (w2 - w1) % n
+ z := (w3 - w2) % n
+
+ // Golang handles modulo with negative numbers different then most languages
+ // The modulo can be negative, we don't want that.
+ if z < 0 {
+ z += n
+ }
+ if y < 0 {
+ y += n
+ }
+ x := w1 + n*(y) + n*n*(z)
+ out += fmt.Sprintf("%08x", x)
+ }
+ return out
+}
diff --git a/crypto/mnemonic_test.go b/crypto/mnemonic_test.go
new file mode 100644
index 000000000..beff476e0
--- /dev/null
+++ b/crypto/mnemonic_test.go
@@ -0,0 +1,74 @@
+package crypto
+
+import (
+ "testing"
+)
+
+func TestMnDecode(t *testing.T) {
+ words := []string{
+ "ink",
+ "balance",
+ "gain",
+ "fear",
+ "happen",
+ "melt",
+ "mom",
+ "surface",
+ "stir",
+ "bottle",
+ "unseen",
+ "expression",
+ "important",
+ "curl",
+ "grant",
+ "fairy",
+ "across",
+ "back",
+ "figure",
+ "breast",
+ "nobody",
+ "scratch",
+ "worry",
+ "yesterday",
+ }
+ encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
+ result := MnemonicDecode(words)
+ if encode != result {
+ t.Error("We expected", encode, "got", result, "instead")
+ }
+}
+func TestMnEncode(t *testing.T) {
+ encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
+ result := []string{
+ "ink",
+ "balance",
+ "gain",
+ "fear",
+ "happen",
+ "melt",
+ "mom",
+ "surface",
+ "stir",
+ "bottle",
+ "unseen",
+ "expression",
+ "important",
+ "curl",
+ "grant",
+ "fairy",
+ "across",
+ "back",
+ "figure",
+ "breast",
+ "nobody",
+ "scratch",
+ "worry",
+ "yesterday",
+ }
+ words := MnemonicEncode(encode)
+ for i, word := range words {
+ if word != result[i] {
+ t.Error("Mnenonic does not match:", words, result)
+ }
+ }
+}
diff --git a/crypto/mnemonic_words.go b/crypto/mnemonic_words.go
new file mode 100644
index 000000000..ebd0d2690
--- /dev/null
+++ b/crypto/mnemonic_words.go
@@ -0,0 +1,1630 @@
+package crypto
+
+var MnemonicWords []string = []string{
+ "like",
+ "just",
+ "love",
+ "know",
+ "never",
+ "want",
+ "time",
+ "out",
+ "there",
+ "make",
+ "look",
+ "eye",
+ "down",
+ "only",
+ "think",
+ "heart",
+ "back",
+ "then",
+ "into",
+ "about",
+ "more",
+ "away",
+ "still",
+ "them",
+ "take",
+ "thing",
+ "even",
+ "through",
+ "long",
+ "always",
+ "world",
+ "too",
+ "friend",
+ "tell",
+ "try",
+ "hand",
+ "thought",
+ "over",
+ "here",
+ "other",
+ "need",
+ "smile",
+ "again",
+ "much",
+ "cry",
+ "been",
+ "night",
+ "ever",
+ "little",
+ "said",
+ "end",
+ "some",
+ "those",
+ "around",
+ "mind",
+ "people",
+ "girl",
+ "leave",
+ "dream",
+ "left",
+ "turn",
+ "myself",
+ "give",
+ "nothing",
+ "really",
+ "off",
+ "before",
+ "something",
+ "find",
+ "walk",
+ "wish",
+ "good",
+ "once",
+ "place",
+ "ask",
+ "stop",
+ "keep",
+ "watch",
+ "seem",
+ "everything",
+ "wait",
+ "got",
+ "yet",
+ "made",
+ "remember",
+ "start",
+ "alone",
+ "run",
+ "hope",
+ "maybe",
+ "believe",
+ "body",
+ "hate",
+ "after",
+ "close",
+ "talk",
+ "stand",
+ "own",
+ "each",
+ "hurt",
+ "help",
+ "home",
+ "god",
+ "soul",
+ "new",
+ "many",
+ "two",
+ "inside",
+ "should",
+ "true",
+ "first",
+ "fear",
+ "mean",
+ "better",
+ "play",
+ "another",
+ "gone",
+ "change",
+ "use",
+ "wonder",
+ "someone",
+ "hair",
+ "cold",
+ "open",
+ "best",
+ "any",
+ "behind",
+ "happen",
+ "water",
+ "dark",
+ "laugh",
+ "stay",
+ "forever",
+ "name",
+ "work",
+ "show",
+ "sky",
+ "break",
+ "came",
+ "deep",
+ "door",
+ "put",
+ "black",
+ "together",
+ "upon",
+ "happy",
+ "such",
+ "great",
+ "white",
+ "matter",
+ "fill",
+ "past",
+ "please",
+ "burn",
+ "cause",
+ "enough",
+ "touch",
+ "moment",
+ "soon",
+ "voice",
+ "scream",
+ "anything",
+ "stare",
+ "sound",
+ "red",
+ "everyone",
+ "hide",
+ "kiss",
+ "truth",
+ "death",
+ "beautiful",
+ "mine",
+ "blood",
+ "broken",
+ "very",
+ "pass",
+ "next",
+ "forget",
+ "tree",
+ "wrong",
+ "air",
+ "mother",
+ "understand",
+ "lip",
+ "hit",
+ "wall",
+ "memory",
+ "sleep",
+ "free",
+ "high",
+ "realize",
+ "school",
+ "might",
+ "skin",
+ "sweet",
+ "perfect",
+ "blue",
+ "kill",
+ "breath",
+ "dance",
+ "against",
+ "fly",
+ "between",
+ "grow",
+ "strong",
+ "under",
+ "listen",
+ "bring",
+ "sometimes",
+ "speak",
+ "pull",
+ "person",
+ "become",
+ "family",
+ "begin",
+ "ground",
+ "real",
+ "small",
+ "father",
+ "sure",
+ "feet",
+ "rest",
+ "young",
+ "finally",
+ "land",
+ "across",
+ "today",
+ "different",
+ "guy",
+ "line",
+ "fire",
+ "reason",
+ "reach",
+ "second",
+ "slowly",
+ "write",
+ "eat",
+ "smell",
+ "mouth",
+ "step",
+ "learn",
+ "three",
+ "floor",
+ "promise",
+ "breathe",
+ "darkness",
+ "push",
+ "earth",
+ "guess",
+ "save",
+ "song",
+ "above",
+ "along",
+ "both",
+ "color",
+ "house",
+ "almost",
+ "sorry",
+ "anymore",
+ "brother",
+ "okay",
+ "dear",
+ "game",
+ "fade",
+ "already",
+ "apart",
+ "warm",
+ "beauty",
+ "heard",
+ "notice",
+ "question",
+ "shine",
+ "began",
+ "piece",
+ "whole",
+ "shadow",
+ "secret",
+ "street",
+ "within",
+ "finger",
+ "point",
+ "morning",
+ "whisper",
+ "child",
+ "moon",
+ "green",
+ "story",
+ "glass",
+ "kid",
+ "silence",
+ "since",
+ "soft",
+ "yourself",
+ "empty",
+ "shall",
+ "angel",
+ "answer",
+ "baby",
+ "bright",
+ "dad",
+ "path",
+ "worry",
+ "hour",
+ "drop",
+ "follow",
+ "power",
+ "war",
+ "half",
+ "flow",
+ "heaven",
+ "act",
+ "chance",
+ "fact",
+ "least",
+ "tired",
+ "children",
+ "near",
+ "quite",
+ "afraid",
+ "rise",
+ "sea",
+ "taste",
+ "window",
+ "cover",
+ "nice",
+ "trust",
+ "lot",
+ "sad",
+ "cool",
+ "force",
+ "peace",
+ "return",
+ "blind",
+ "easy",
+ "ready",
+ "roll",
+ "rose",
+ "drive",
+ "held",
+ "music",
+ "beneath",
+ "hang",
+ "mom",
+ "paint",
+ "emotion",
+ "quiet",
+ "clear",
+ "cloud",
+ "few",
+ "pretty",
+ "bird",
+ "outside",
+ "paper",
+ "picture",
+ "front",
+ "rock",
+ "simple",
+ "anyone",
+ "meant",
+ "reality",
+ "road",
+ "sense",
+ "waste",
+ "bit",
+ "leaf",
+ "thank",
+ "happiness",
+ "meet",
+ "men",
+ "smoke",
+ "truly",
+ "decide",
+ "self",
+ "age",
+ "book",
+ "form",
+ "alive",
+ "carry",
+ "escape",
+ "damn",
+ "instead",
+ "able",
+ "ice",
+ "minute",
+ "throw",
+ "catch",
+ "leg",
+ "ring",
+ "course",
+ "goodbye",
+ "lead",
+ "poem",
+ "sick",
+ "corner",
+ "desire",
+ "known",
+ "problem",
+ "remind",
+ "shoulder",
+ "suppose",
+ "toward",
+ "wave",
+ "drink",
+ "jump",
+ "woman",
+ "pretend",
+ "sister",
+ "week",
+ "human",
+ "joy",
+ "crack",
+ "grey",
+ "pray",
+ "surprise",
+ "dry",
+ "knee",
+ "less",
+ "search",
+ "bleed",
+ "caught",
+ "clean",
+ "embrace",
+ "future",
+ "king",
+ "son",
+ "sorrow",
+ "chest",
+ "hug",
+ "remain",
+ "sat",
+ "worth",
+ "blow",
+ "daddy",
+ "final",
+ "parent",
+ "tight",
+ "also",
+ "create",
+ "lonely",
+ "safe",
+ "cross",
+ "dress",
+ "evil",
+ "silent",
+ "bone",
+ "fate",
+ "perhaps",
+ "anger",
+ "class",
+ "scar",
+ "snow",
+ "tiny",
+ "tonight",
+ "continue",
+ "control",
+ "dog",
+ "edge",
+ "mirror",
+ "month",
+ "suddenly",
+ "comfort",
+ "given",
+ "loud",
+ "quickly",
+ "gaze",
+ "plan",
+ "rush",
+ "stone",
+ "town",
+ "battle",
+ "ignore",
+ "spirit",
+ "stood",
+ "stupid",
+ "yours",
+ "brown",
+ "build",
+ "dust",
+ "hey",
+ "kept",
+ "pay",
+ "phone",
+ "twist",
+ "although",
+ "ball",
+ "beyond",
+ "hidden",
+ "nose",
+ "taken",
+ "fail",
+ "float",
+ "pure",
+ "somehow",
+ "wash",
+ "wrap",
+ "angry",
+ "cheek",
+ "creature",
+ "forgotten",
+ "heat",
+ "rip",
+ "single",
+ "space",
+ "special",
+ "weak",
+ "whatever",
+ "yell",
+ "anyway",
+ "blame",
+ "job",
+ "choose",
+ "country",
+ "curse",
+ "drift",
+ "echo",
+ "figure",
+ "grew",
+ "laughter",
+ "neck",
+ "suffer",
+ "worse",
+ "yeah",
+ "disappear",
+ "foot",
+ "forward",
+ "knife",
+ "mess",
+ "somewhere",
+ "stomach",
+ "storm",
+ "beg",
+ "idea",
+ "lift",
+ "offer",
+ "breeze",
+ "field",
+ "five",
+ "often",
+ "simply",
+ "stuck",
+ "win",
+ "allow",
+ "confuse",
+ "enjoy",
+ "except",
+ "flower",
+ "seek",
+ "strength",
+ "calm",
+ "grin",
+ "gun",
+ "heavy",
+ "hill",
+ "large",
+ "ocean",
+ "shoe",
+ "sigh",
+ "straight",
+ "summer",
+ "tongue",
+ "accept",
+ "crazy",
+ "everyday",
+ "exist",
+ "grass",
+ "mistake",
+ "sent",
+ "shut",
+ "surround",
+ "table",
+ "ache",
+ "brain",
+ "destroy",
+ "heal",
+ "nature",
+ "shout",
+ "sign",
+ "stain",
+ "choice",
+ "doubt",
+ "glance",
+ "glow",
+ "mountain",
+ "queen",
+ "stranger",
+ "throat",
+ "tomorrow",
+ "city",
+ "either",
+ "fish",
+ "flame",
+ "rather",
+ "shape",
+ "spin",
+ "spread",
+ "ash",
+ "distance",
+ "finish",
+ "image",
+ "imagine",
+ "important",
+ "nobody",
+ "shatter",
+ "warmth",
+ "became",
+ "feed",
+ "flesh",
+ "funny",
+ "lust",
+ "shirt",
+ "trouble",
+ "yellow",
+ "attention",
+ "bare",
+ "bite",
+ "money",
+ "protect",
+ "amaze",
+ "appear",
+ "born",
+ "choke",
+ "completely",
+ "daughter",
+ "fresh",
+ "friendship",
+ "gentle",
+ "probably",
+ "six",
+ "deserve",
+ "expect",
+ "grab",
+ "middle",
+ "nightmare",
+ "river",
+ "thousand",
+ "weight",
+ "worst",
+ "wound",
+ "barely",
+ "bottle",
+ "cream",
+ "regret",
+ "relationship",
+ "stick",
+ "test",
+ "crush",
+ "endless",
+ "fault",
+ "itself",
+ "rule",
+ "spill",
+ "art",
+ "circle",
+ "join",
+ "kick",
+ "mask",
+ "master",
+ "passion",
+ "quick",
+ "raise",
+ "smooth",
+ "unless",
+ "wander",
+ "actually",
+ "broke",
+ "chair",
+ "deal",
+ "favorite",
+ "gift",
+ "note",
+ "number",
+ "sweat",
+ "box",
+ "chill",
+ "clothes",
+ "lady",
+ "mark",
+ "park",
+ "poor",
+ "sadness",
+ "tie",
+ "animal",
+ "belong",
+ "brush",
+ "consume",
+ "dawn",
+ "forest",
+ "innocent",
+ "pen",
+ "pride",
+ "stream",
+ "thick",
+ "clay",
+ "complete",
+ "count",
+ "draw",
+ "faith",
+ "press",
+ "silver",
+ "struggle",
+ "surface",
+ "taught",
+ "teach",
+ "wet",
+ "bless",
+ "chase",
+ "climb",
+ "enter",
+ "letter",
+ "melt",
+ "metal",
+ "movie",
+ "stretch",
+ "swing",
+ "vision",
+ "wife",
+ "beside",
+ "crash",
+ "forgot",
+ "guide",
+ "haunt",
+ "joke",
+ "knock",
+ "plant",
+ "pour",
+ "prove",
+ "reveal",
+ "steal",
+ "stuff",
+ "trip",
+ "wood",
+ "wrist",
+ "bother",
+ "bottom",
+ "crawl",
+ "crowd",
+ "fix",
+ "forgive",
+ "frown",
+ "grace",
+ "loose",
+ "lucky",
+ "party",
+ "release",
+ "surely",
+ "survive",
+ "teacher",
+ "gently",
+ "grip",
+ "speed",
+ "suicide",
+ "travel",
+ "treat",
+ "vein",
+ "written",
+ "cage",
+ "chain",
+ "conversation",
+ "date",
+ "enemy",
+ "however",
+ "interest",
+ "million",
+ "page",
+ "pink",
+ "proud",
+ "sway",
+ "themselves",
+ "winter",
+ "church",
+ "cruel",
+ "cup",
+ "demon",
+ "experience",
+ "freedom",
+ "pair",
+ "pop",
+ "purpose",
+ "respect",
+ "shoot",
+ "softly",
+ "state",
+ "strange",
+ "bar",
+ "birth",
+ "curl",
+ "dirt",
+ "excuse",
+ "lord",
+ "lovely",
+ "monster",
+ "order",
+ "pack",
+ "pants",
+ "pool",
+ "scene",
+ "seven",
+ "shame",
+ "slide",
+ "ugly",
+ "among",
+ "blade",
+ "blonde",
+ "closet",
+ "creek",
+ "deny",
+ "drug",
+ "eternity",
+ "gain",
+ "grade",
+ "handle",
+ "key",
+ "linger",
+ "pale",
+ "prepare",
+ "swallow",
+ "swim",
+ "tremble",
+ "wheel",
+ "won",
+ "cast",
+ "cigarette",
+ "claim",
+ "college",
+ "direction",
+ "dirty",
+ "gather",
+ "ghost",
+ "hundred",
+ "loss",
+ "lung",
+ "orange",
+ "present",
+ "swear",
+ "swirl",
+ "twice",
+ "wild",
+ "bitter",
+ "blanket",
+ "doctor",
+ "everywhere",
+ "flash",
+ "grown",
+ "knowledge",
+ "numb",
+ "pressure",
+ "radio",
+ "repeat",
+ "ruin",
+ "spend",
+ "unknown",
+ "buy",
+ "clock",
+ "devil",
+ "early",
+ "false",
+ "fantasy",
+ "pound",
+ "precious",
+ "refuse",
+ "sheet",
+ "teeth",
+ "welcome",
+ "add",
+ "ahead",
+ "block",
+ "bury",
+ "caress",
+ "content",
+ "depth",
+ "despite",
+ "distant",
+ "marry",
+ "purple",
+ "threw",
+ "whenever",
+ "bomb",
+ "dull",
+ "easily",
+ "grasp",
+ "hospital",
+ "innocence",
+ "normal",
+ "receive",
+ "reply",
+ "rhyme",
+ "shade",
+ "someday",
+ "sword",
+ "toe",
+ "visit",
+ "asleep",
+ "bought",
+ "center",
+ "consider",
+ "flat",
+ "hero",
+ "history",
+ "ink",
+ "insane",
+ "muscle",
+ "mystery",
+ "pocket",
+ "reflection",
+ "shove",
+ "silently",
+ "smart",
+ "soldier",
+ "spot",
+ "stress",
+ "train",
+ "type",
+ "view",
+ "whether",
+ "bus",
+ "energy",
+ "explain",
+ "holy",
+ "hunger",
+ "inch",
+ "magic",
+ "mix",
+ "noise",
+ "nowhere",
+ "prayer",
+ "presence",
+ "shock",
+ "snap",
+ "spider",
+ "study",
+ "thunder",
+ "trail",
+ "admit",
+ "agree",
+ "bag",
+ "bang",
+ "bound",
+ "butterfly",
+ "cute",
+ "exactly",
+ "explode",
+ "familiar",
+ "fold",
+ "further",
+ "pierce",
+ "reflect",
+ "scent",
+ "selfish",
+ "sharp",
+ "sink",
+ "spring",
+ "stumble",
+ "universe",
+ "weep",
+ "women",
+ "wonderful",
+ "action",
+ "ancient",
+ "attempt",
+ "avoid",
+ "birthday",
+ "branch",
+ "chocolate",
+ "core",
+ "depress",
+ "drunk",
+ "especially",
+ "focus",
+ "fruit",
+ "honest",
+ "match",
+ "palm",
+ "perfectly",
+ "pillow",
+ "pity",
+ "poison",
+ "roar",
+ "shift",
+ "slightly",
+ "thump",
+ "truck",
+ "tune",
+ "twenty",
+ "unable",
+ "wipe",
+ "wrote",
+ "coat",
+ "constant",
+ "dinner",
+ "drove",
+ "egg",
+ "eternal",
+ "flight",
+ "flood",
+ "frame",
+ "freak",
+ "gasp",
+ "glad",
+ "hollow",
+ "motion",
+ "peer",
+ "plastic",
+ "root",
+ "screen",
+ "season",
+ "sting",
+ "strike",
+ "team",
+ "unlike",
+ "victim",
+ "volume",
+ "warn",
+ "weird",
+ "attack",
+ "await",
+ "awake",
+ "built",
+ "charm",
+ "crave",
+ "despair",
+ "fought",
+ "grant",
+ "grief",
+ "horse",
+ "limit",
+ "message",
+ "ripple",
+ "sanity",
+ "scatter",
+ "serve",
+ "split",
+ "string",
+ "trick",
+ "annoy",
+ "blur",
+ "boat",
+ "brave",
+ "clearly",
+ "cling",
+ "connect",
+ "fist",
+ "forth",
+ "imagination",
+ "iron",
+ "jock",
+ "judge",
+ "lesson",
+ "milk",
+ "misery",
+ "nail",
+ "naked",
+ "ourselves",
+ "poet",
+ "possible",
+ "princess",
+ "sail",
+ "size",
+ "snake",
+ "society",
+ "stroke",
+ "torture",
+ "toss",
+ "trace",
+ "wise",
+ "bloom",
+ "bullet",
+ "cell",
+ "check",
+ "cost",
+ "darling",
+ "during",
+ "footstep",
+ "fragile",
+ "hallway",
+ "hardly",
+ "horizon",
+ "invisible",
+ "journey",
+ "midnight",
+ "mud",
+ "nod",
+ "pause",
+ "relax",
+ "shiver",
+ "sudden",
+ "value",
+ "youth",
+ "abuse",
+ "admire",
+ "blink",
+ "breast",
+ "bruise",
+ "constantly",
+ "couple",
+ "creep",
+ "curve",
+ "difference",
+ "dumb",
+ "emptiness",
+ "gotta",
+ "honor",
+ "plain",
+ "planet",
+ "recall",
+ "rub",
+ "ship",
+ "slam",
+ "soar",
+ "somebody",
+ "tightly",
+ "weather",
+ "adore",
+ "approach",
+ "bond",
+ "bread",
+ "burst",
+ "candle",
+ "coffee",
+ "cousin",
+ "crime",
+ "desert",
+ "flutter",
+ "frozen",
+ "grand",
+ "heel",
+ "hello",
+ "language",
+ "level",
+ "movement",
+ "pleasure",
+ "powerful",
+ "random",
+ "rhythm",
+ "settle",
+ "silly",
+ "slap",
+ "sort",
+ "spoken",
+ "steel",
+ "threaten",
+ "tumble",
+ "upset",
+ "aside",
+ "awkward",
+ "bee",
+ "blank",
+ "board",
+ "button",
+ "card",
+ "carefully",
+ "complain",
+ "crap",
+ "deeply",
+ "discover",
+ "drag",
+ "dread",
+ "effort",
+ "entire",
+ "fairy",
+ "giant",
+ "gotten",
+ "greet",
+ "illusion",
+ "jeans",
+ "leap",
+ "liquid",
+ "march",
+ "mend",
+ "nervous",
+ "nine",
+ "replace",
+ "rope",
+ "spine",
+ "stole",
+ "terror",
+ "accident",
+ "apple",
+ "balance",
+ "boom",
+ "childhood",
+ "collect",
+ "demand",
+ "depression",
+ "eventually",
+ "faint",
+ "glare",
+ "goal",
+ "group",
+ "honey",
+ "kitchen",
+ "laid",
+ "limb",
+ "machine",
+ "mere",
+ "mold",
+ "murder",
+ "nerve",
+ "painful",
+ "poetry",
+ "prince",
+ "rabbit",
+ "shelter",
+ "shore",
+ "shower",
+ "soothe",
+ "stair",
+ "steady",
+ "sunlight",
+ "tangle",
+ "tease",
+ "treasure",
+ "uncle",
+ "begun",
+ "bliss",
+ "canvas",
+ "cheer",
+ "claw",
+ "clutch",
+ "commit",
+ "crimson",
+ "crystal",
+ "delight",
+ "doll",
+ "existence",
+ "express",
+ "fog",
+ "football",
+ "gay",
+ "goose",
+ "guard",
+ "hatred",
+ "illuminate",
+ "mass",
+ "math",
+ "mourn",
+ "rich",
+ "rough",
+ "skip",
+ "stir",
+ "student",
+ "style",
+ "support",
+ "thorn",
+ "tough",
+ "yard",
+ "yearn",
+ "yesterday",
+ "advice",
+ "appreciate",
+ "autumn",
+ "bank",
+ "beam",
+ "bowl",
+ "capture",
+ "carve",
+ "collapse",
+ "confusion",
+ "creation",
+ "dove",
+ "feather",
+ "girlfriend",
+ "glory",
+ "government",
+ "harsh",
+ "hop",
+ "inner",
+ "loser",
+ "moonlight",
+ "neighbor",
+ "neither",
+ "peach",
+ "pig",
+ "praise",
+ "screw",
+ "shield",
+ "shimmer",
+ "sneak",
+ "stab",
+ "subject",
+ "throughout",
+ "thrown",
+ "tower",
+ "twirl",
+ "wow",
+ "army",
+ "arrive",
+ "bathroom",
+ "bump",
+ "cease",
+ "cookie",
+ "couch",
+ "courage",
+ "dim",
+ "guilt",
+ "howl",
+ "hum",
+ "husband",
+ "insult",
+ "led",
+ "lunch",
+ "mock",
+ "mostly",
+ "natural",
+ "nearly",
+ "needle",
+ "nerd",
+ "peaceful",
+ "perfection",
+ "pile",
+ "price",
+ "remove",
+ "roam",
+ "sanctuary",
+ "serious",
+ "shiny",
+ "shook",
+ "sob",
+ "stolen",
+ "tap",
+ "vain",
+ "void",
+ "warrior",
+ "wrinkle",
+ "affection",
+ "apologize",
+ "blossom",
+ "bounce",
+ "bridge",
+ "cheap",
+ "crumble",
+ "decision",
+ "descend",
+ "desperately",
+ "dig",
+ "dot",
+ "flip",
+ "frighten",
+ "heartbeat",
+ "huge",
+ "lazy",
+ "lick",
+ "odd",
+ "opinion",
+ "process",
+ "puzzle",
+ "quietly",
+ "retreat",
+ "score",
+ "sentence",
+ "separate",
+ "situation",
+ "skill",
+ "soak",
+ "square",
+ "stray",
+ "taint",
+ "task",
+ "tide",
+ "underneath",
+ "veil",
+ "whistle",
+ "anywhere",
+ "bedroom",
+ "bid",
+ "bloody",
+ "burden",
+ "careful",
+ "compare",
+ "concern",
+ "curtain",
+ "decay",
+ "defeat",
+ "describe",
+ "double",
+ "dreamer",
+ "driver",
+ "dwell",
+ "evening",
+ "flare",
+ "flicker",
+ "grandma",
+ "guitar",
+ "harm",
+ "horrible",
+ "hungry",
+ "indeed",
+ "lace",
+ "melody",
+ "monkey",
+ "nation",
+ "object",
+ "obviously",
+ "rainbow",
+ "salt",
+ "scratch",
+ "shown",
+ "shy",
+ "stage",
+ "stun",
+ "third",
+ "tickle",
+ "useless",
+ "weakness",
+ "worship",
+ "worthless",
+ "afternoon",
+ "beard",
+ "boyfriend",
+ "bubble",
+ "busy",
+ "certain",
+ "chin",
+ "concrete",
+ "desk",
+ "diamond",
+ "doom",
+ "drawn",
+ "due",
+ "felicity",
+ "freeze",
+ "frost",
+ "garden",
+ "glide",
+ "harmony",
+ "hopefully",
+ "hunt",
+ "jealous",
+ "lightning",
+ "mama",
+ "mercy",
+ "peel",
+ "physical",
+ "position",
+ "pulse",
+ "punch",
+ "quit",
+ "rant",
+ "respond",
+ "salty",
+ "sane",
+ "satisfy",
+ "savior",
+ "sheep",
+ "slept",
+ "social",
+ "sport",
+ "tuck",
+ "utter",
+ "valley",
+ "wolf",
+ "aim",
+ "alas",
+ "alter",
+ "arrow",
+ "awaken",
+ "beaten",
+ "belief",
+ "brand",
+ "ceiling",
+ "cheese",
+ "clue",
+ "confidence",
+ "connection",
+ "daily",
+ "disguise",
+ "eager",
+ "erase",
+ "essence",
+ "everytime",
+ "expression",
+ "fan",
+ "flag",
+ "flirt",
+ "foul",
+ "fur",
+ "giggle",
+ "glorious",
+ "ignorance",
+ "law",
+ "lifeless",
+ "measure",
+ "mighty",
+ "muse",
+ "north",
+ "opposite",
+ "paradise",
+ "patience",
+ "patient",
+ "pencil",
+ "petal",
+ "plate",
+ "ponder",
+ "possibly",
+ "practice",
+ "slice",
+ "spell",
+ "stock",
+ "strife",
+ "strip",
+ "suffocate",
+ "suit",
+ "tender",
+ "tool",
+ "trade",
+ "velvet",
+ "verse",
+ "waist",
+ "witch",
+ "aunt",
+ "bench",
+ "bold",
+ "cap",
+ "certainly",
+ "click",
+ "companion",
+ "creator",
+ "dart",
+ "delicate",
+ "determine",
+ "dish",
+ "dragon",
+ "drama",
+ "drum",
+ "dude",
+ "everybody",
+ "feast",
+ "forehead",
+ "former",
+ "fright",
+ "fully",
+ "gas",
+ "hook",
+ "hurl",
+ "invite",
+ "juice",
+ "manage",
+ "moral",
+ "possess",
+ "raw",
+ "rebel",
+ "royal",
+ "scale",
+ "scary",
+ "several",
+ "slight",
+ "stubborn",
+ "swell",
+ "talent",
+ "tea",
+ "terrible",
+ "thread",
+ "torment",
+ "trickle",
+ "usually",
+ "vast",
+ "violence",
+ "weave",
+ "acid",
+ "agony",
+ "ashamed",
+ "awe",
+ "belly",
+ "blend",
+ "blush",
+ "character",
+ "cheat",
+ "common",
+ "company",
+ "coward",
+ "creak",
+ "danger",
+ "deadly",
+ "defense",
+ "define",
+ "depend",
+ "desperate",
+ "destination",
+ "dew",
+ "duck",
+ "dusty",
+ "embarrass",
+ "engine",
+ "example",
+ "explore",
+ "foe",
+ "freely",
+ "frustrate",
+ "generation",
+ "glove",
+ "guilty",
+ "health",
+ "hurry",
+ "idiot",
+ "impossible",
+ "inhale",
+ "jaw",
+ "kingdom",
+ "mention",
+ "mist",
+ "moan",
+ "mumble",
+ "mutter",
+ "observe",
+ "ode",
+ "pathetic",
+ "pattern",
+ "pie",
+ "prefer",
+ "puff",
+ "rape",
+ "rare",
+ "revenge",
+ "rude",
+ "scrape",
+ "spiral",
+ "squeeze",
+ "strain",
+ "sunset",
+ "suspend",
+ "sympathy",
+ "thigh",
+ "throne",
+ "total",
+ "unseen",
+ "weapon",
+ "weary",
+}