diff --git a/app/console.go b/app/console.go index 8460868..5865e61 100644 --- a/app/console.go +++ b/app/console.go @@ -127,4 +127,5 @@ func createHotWallet(ctx *ishell.Context, app *App) { emoji.Println(":credit_card: New hot wallet created!") emoji.Println(":raising_hand: Name: " + wallet.Name) emoji.Println(":mailbox: Address: " + wallet.Wallet.Public().Repr()) + emoji.Println(":fist: Emoji Address: " + wallet.Wallet.Public().Emoji()) } diff --git a/blockchain/wallet.go b/blockchain/wallet.go index 0afcf24..ac4fe49 100644 --- a/blockchain/wallet.go +++ b/blockchain/wallet.go @@ -10,6 +10,7 @@ import ( "math/big" c "github.com/ubclaunchpad/cumulus/common/constants" + "github.com/ubclaunchpad/cumulus/moj" ) const ( @@ -56,7 +57,8 @@ func (a Address) Repr() string { // Emoji returns the users address as a string of emojis (TODO). func (a Address) Emoji() string { - return a.Repr() + result, _ := moj.EncodeHex(a.Repr()) + return result } // Marshal converts an Address to a byte slice. diff --git a/moj/moj.go b/moj/moj.go new file mode 100644 index 0000000..14b48dc --- /dev/null +++ b/moj/moj.go @@ -0,0 +1,48 @@ +package moj + +import ( + "errors" + "fmt" + "math/big" + "strings" + + "gopkg.in/kyokomi/emoji.v1" +) + +// packageBase constants define the base number system underlying the package. +const packageBase = 16 +const packageBaseStringFmt = "%X" + +// EncodeInt encodes an integer as a string of emojis. +func EncodeInt(i int) (string, error) { + return EncodeHex(fmt.Sprintf(packageBaseStringFmt, i)) +} + +// EncodeBigInt encodes a big integer as a string of emojis. +func EncodeBigInt(i *big.Int) (string, error) { + return EncodeHex(i.Text(packageBase)) +} + +// EncodeHex encodes a hex string as a string of emojis. +func EncodeHex(h string) (string, error) { + var emojiString string + for _, char := range h { + switch { + // Handle numeric rune. + case char >= '0' && char <= '9': + emojiString = emojiString + emojiRuneMap[char] + // Handle uppercase rune. + case char >= 'A' && char <= 'F': + emojiString = emojiString + emojiRuneMap[char] + // Handle lower case rune (map to uppercase). + case char >= 'a' && char <= 'f': + emojiString = emojiString + emojiRuneMap[char-32] + // Other characters raise error. + default: + return emojiString, errors.New("hex string is malformed") + } + } + + // Remove white space appended by emoji package :/. + return strings.TrimSpace(emoji.Sprint(emojiString)), nil +} diff --git a/moj/moj_codemap.go b/moj/moj_codemap.go new file mode 100644 index 0000000..298dd1c --- /dev/null +++ b/moj/moj_codemap.go @@ -0,0 +1,21 @@ +package moj + +// Mapping from hex character to escape code. +var emojiRuneMap = map[rune]string{ + '0': ":fist:", + '1': ":point_up:", + '2': ":v:", + '3': ":cactus:", + '4': ":+1:", + '5': ":hand:", + '6': ":runner:", + '7': ":clap:", + '8': ":mushroom:", + '9': ":tulip:", + 'A': ":raised_hands:", + 'B': ":metal:", + 'C': ":clock2:", + 'D': ":helicopter:", + 'E': ":anchor:", + 'F': ":meat_on_bone:", +} diff --git a/moj/moj_test.go b/moj/moj_test.go new file mode 100644 index 0000000..3aa6537 --- /dev/null +++ b/moj/moj_test.go @@ -0,0 +1,114 @@ +package moj + +import ( + "math" + "math/big" + "testing" + + "gopkg.in/kyokomi/emoji.v1" + + "github.com/stretchr/testify/assert" +) + +// encoder takes a single rune and encodes an emoji. +type encoder interface { + encode(r rune) (string, error) +} + +type intEncoder struct{} +type hexEncoder struct{} +type bigEncoder struct{} + +func (e intEncoder) encode(r rune) (string, error) { + switch { + case r >= 'A': + return EncodeInt(int(r) - 55) + default: + return EncodeInt(int(r) - 48) + } +} + +func (e hexEncoder) encode(r rune) (string, error) { + switch { + case r >= 'A': + return EncodeHex(string(r)) + default: + return EncodeHex(string(r)) + } +} + +func (e bigEncoder) encode(r rune) (string, error) { + switch { + case r >= 'A': + return EncodeBigInt(big.NewInt(int64(r - 55))) + default: + return EncodeBigInt(big.NewInt(int64(r - 48))) + } +} + +func BasicTestRun(e encoder, t *testing.T) { + var result string + var err error + for r, moj := range emojiRuneMap { + result, err = e.encode(r) + assert.Nil(t, err) + // Encoding converts to the unicode symbol. + // Use the codemap to make sure we got the + // right unicode symbol. + assert.Equal(t, result, emoji.CodeMap()[moj]) + } +} + +func TestEncodeIntBasic(t *testing.T) { + BasicTestRun(intEncoder{}, t) +} + +func TestEncodeHexBasic(t *testing.T) { + BasicTestRun(hexEncoder{}, t) +} + +func TestEncodeBigBasic(t *testing.T) { + BasicTestRun(bigEncoder{}, t) +} + +func TestEncodeIntAdvanced(t *testing.T) { + + // 7f + result, _ := EncodeInt(math.MaxInt8) + expected := "👏 🍖" + assert.Equal(t, result, expected) + + // 7fff + result, _ = EncodeInt(math.MaxInt16) + expected = "👏 🍖 🍖 🍖" + assert.Equal(t, result, expected) + + // 7fff_ffff + result, _ = EncodeInt(math.MaxInt32) + expected = "👏 🍖 🍖 🍖 🍖 🍖 🍖 🍖" + assert.Equal(t, result, expected) +} + +func TestEncodeHexAdvanced(t *testing.T) { + + result, _ := EncodeHex("abcdef1234567890") + println(result) + expected := "🙌 🤘 🕑 🚁 ⚓️ 🍖 ☝️ ✌️ 🌵 👍 ✋ 🏃 👏 🍄 🌷 ✊" + assert.Equal(t, result, expected) + + result, _ = EncodeHex("badf00d") + expected = "🤘 🙌 🚁 🍖 ✊ ✊ 🚁" + assert.Equal(t, result, expected) + + result, _ = EncodeHex("abadd00d") + expected = "🙌 🤘 🙌 🚁 🚁 ✊ ✊ 🚁" + assert.Equal(t, result, expected) + + result, _ = EncodeHex("deadbeef") + expected = "🚁 ⚓️ 🙌 🚁 🤘 ⚓️ ⚓️ 🍖" + assert.Equal(t, result, expected) + + result, _ = EncodeHex("70ffee") + expected = "👏 ✊ 🍖 🍖 ⚓️ ⚓️" + assert.Equal(t, result, expected) +}