Skip to content

Commit

Permalink
Revert changes done to crypto to make it less random (#154)
Browse files Browse the repository at this point in the history
* Revert "Issue 70 (#153)"

This reverts commit a20ac58.

* Deprecate Bech32AddressWithLength, Bech32Address, P2SHAddress, P2PKHAddressWithLength, P2PKHAddress

---------

Co-authored-by: Jonathan Schweder <jonathanschweder@amazon.com>
  • Loading branch information
jaswdr and Jonathan Schweder authored Oct 10, 2023
1 parent a20ac58 commit d2fb71c
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 64 deletions.
125 changes: 83 additions & 42 deletions crypto.go
Original file line number Diff line number Diff line change
@@ -1,80 +1,121 @@
package faker

import (
"strings"
)

// Crypto is a faker struct for generating bitcoin data
type Crypto struct {
Faker *Faker
}

var (
bitcoinAddresses = []string{
"1JyxpLZzvYP2TyXaQV3J3vwajJz4hbxtRC",
"1JyVFJVUNx8RQrjyNCGDe7BQ62wPyxU8bC",
"1JVcPDeBfGP5PmNZwnJSfms7hKLncMSenV",
"1FwiYqdgLH6w5XdB9QXgZGi7ZHGiyUYucT",
"1H3nknk2Pdav9LjXyfLd8umqPC57ZQbN4",
"1DMwH1FMx2yJ67az7ZTJqViBD6iz3vQkUK",
"1JzvBX9Q86LbEcBxT58npYXS31QexVVMGG",
"1HBTfs2QLK459tQrdpeQs4stR25GWwJTui",
"1NZMxDpqB1ehEdvGJeAs9Gdmh3dXfUfAZB",
"19ePAsmdkM4u9e3euzfnQa1AXEoD2UgmEj",
}
bitcoinMin = 26
bitcoinMax = 35
ethLen = 42
ethPrefix = "0x"
)

etheriumAddresses = []string{
"0x83e1e8f10092d42db425D81c2e99f312a7E011aA",
"0xd3E823D4C999e4ef9c92835eEF6906E519C13251",
"0x4e3adfcdD456DDe868B1225aA0ed103Dd188B5F6",
"0xbC39DCa632f8f7f2A94B095d48bcEE779d961728",
"0xE0A6c75e545947E7Bb4dde2D8182762a4C698E5c",
"0x45C20Fd8F6B07359750f92B79f1C41754Bd09Ac3",
"0xA882bE0b4C10E91c3565EE01878A48F9B940f2c5",
"0x8c2B7B23f01fcAD2946A3C214c4D96338A5eFD6D",
"0x271253c6B815a07506719116262c1673692eD76E",
"0x1e887dC08ba56e369E68987F9D82b44065677c87",
// Checks whether the ascii value provided is in the exclusion for bitcoin.
func (Crypto) isInExclusionZone(ascii int) bool {
switch ascii {
// Ascii for uppercase letter "O", uppercase letter "I", lowercase letter "l", and the number "0"
case 48, 73, 79, 108:
return true
}
)
return false
}

// BitcoinAddress returns a valid address of either Bech32, P2PKH, or P2SH type.
func (c Crypto) BitcoinAddress() string {
return c.Faker.RandomStringElement(bitcoinAddresses)
// algorithmRange decides whether to get digit, uppercase, or lowercase. returns the ascii range to do IntBetween on
func (c Crypto) algorithmRange() (int, int) {
dec := c.Faker.IntBetween(0, 2)
if dec == 0 {
// digit
return 48, 57
} else if dec == 1 {
// upper
return 65, 90
}
// lower
return 97, 122
}

// EtheriumAddress returns a valid hexadecimal ethereum address of 42 characters.
func (c Crypto) EtheriumAddress() string {
return c.Faker.RandomStringElement(etheriumAddresses)
// generateBicoinAddress returns a bitcoin address with a given prefix and length
func (c Crypto) generateBicoinAddress(length int, prefix string, f *Faker) string {
address := []string{prefix}

for i := 0; i < length; i++ {
asciiStart, asciiEnd := c.algorithmRange()
val := f.IntBetween(asciiStart, asciiEnd)
if c.isInExclusionZone(val) {
val++
}
address = append(address, string(rune(val)))
}
return strings.Join(address, "")
}

// P2PKHAddress generates a P2PKH bitcoin address.
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) P2PKHAddress() string {
return "1" + c.BitcoinAddress()[1:]
length := c.Faker.IntBetween(bitcoinMin, bitcoinMax)
// subtract 1 for prefix
return c.generateBicoinAddress(length-1, "1", c.Faker)
}

// P2PKHAddressWithLength generates a P2PKH bitcoin address with specified length.
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) P2PKHAddressWithLength(length int) string {
return "1" + c.P2PKHAddress()[1:length-1]
return c.generateBicoinAddress(length-1, "1", c.Faker)
}

// P2SHAddress generates a P2SH bitcoin address.
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) P2SHAddress() string {
return "3" + c.BitcoinAddress()[1:]
length := c.Faker.IntBetween(bitcoinMin, bitcoinMax)
// subtract 1 for prefix
return c.generateBicoinAddress(length-1, "3", c.Faker)
}

// P2SHAddressWithLength generates a P2PKH bitcoin address with specified length.
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) P2SHAddressWithLength(length int) string {
return "3" + c.P2SHAddress()[1:length-1]
return c.generateBicoinAddress(length-1, "3", c.Faker)
}

// Bech32Address generates a Bech32 bitcoin address
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) Bech32Address() string {
return "bc1" + c.BitcoinAddress()[3:]
length := c.Faker.IntBetween(bitcoinMin, bitcoinMax)
// subtract 1 for prefix
return c.generateBicoinAddress(length-3, "bc1", c.Faker)
}

// Bech32AddressWithLength generates a Bech32 bitcoin address with specified length.
// Deprecated: Use BitcoinAddress instead.
// Deprecated: Please use BitcoinAddress instead.
func (c Crypto) Bech32AddressWithLength(length int) string {
return "bc1" + c.Bech32Address()[3:length-3]
return c.generateBicoinAddress(length-3, "bc1", c.Faker)
}

// BitcoinAddress returns an address of either Bech32, P2PKH, or P2SH type.
func (c Crypto) BitcoinAddress() string {
dec := c.Faker.IntBetween(0, 2)
if dec == 0 {
return c.Bech32Address()
} else if dec == 1 {
return c.P2SHAddress()
}
return c.P2PKHAddress()
}

// EtheriumAddress returns a hexadecimal ethereum address of 42 characters.
func (c Crypto) EtheriumAddress() string {
address := []string{ethPrefix}

for i := 0; i < ethLen-2; i++ {
asciiStart, asciiEnd := c.algorithmRange()
val := c.Faker.IntBetween(asciiStart, asciiEnd)
address = append(address, string(rune(val)))
}
return strings.Join(address, "")
}
182 changes: 160 additions & 22 deletions crypto_test.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,199 @@
package faker

import (
"fmt"
"strings"
"testing"
)

func TestBitcoinAddress(t *testing.T) {
var (
bannedBitcoin = []string{"O", "I", "l", "0"}
validBitcoinPrefix = map[string]string{
"p2pkh": "1",
"p2sh": "3",
"bech32": "bc1",
}
validEthPrefix = "0x"
)

type GeneratorMock struct {
local int
}

func (g GeneratorMock) Intn(_ int) int {
return g.local
}

func (g GeneratorMock) Int() int {
return g.local
}

type TestCaseAlnum struct {
desc string
localInt int
assert func(t *testing.T, a int, b int)
}

type TestCaseRandomBitcoin struct {
desc string
localInt int
expectedSubstring string
}

func TestIsInExclusionZone(t *testing.T) {
c := New().Crypto()
addr := c.BitcoinAddress()
Expect(t, false, addr == "")
for _, address := range bannedBitcoin {
Expect(t, true, c.isInExclusionZone(int(rune(address[0]))))
}
// take any banned rune and + 1 it to get a valid character
Expect(t, false, c.isInExclusionZone(int(rune(bannedBitcoin[0][0]))+1))
}

func TestEtheriumAddress(t *testing.T) {
func TestGenerateBicoinAddress(t *testing.T) {
c := New().Crypto()
addr := c.EtheriumAddress()
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "0x"))
length := c.Faker.IntBetween(5, 10)
Expect(t, length+1, len(c.generateBicoinAddress(length, "a", c.Faker)))
}

func TestP2PKHAddress(t *testing.T) {
c := New().Crypto()
addr := c.P2PKHAddress()
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "1"))
Expect(t, true, len(addr) >= bitcoinMin)
Expect(t, true, len(addr) <= bitcoinMax)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["p2pkh"]))
for i := 0; i < len(bannedBitcoin); i++ {
Expect(t, true, !strings.Contains(addr, bannedBitcoin[i]))
}
}

func TestP2PKHAddressWithLength(t *testing.T) {
c := New().Crypto()
addr := c.P2PKHAddressWithLength(10)
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "1"))
length := c.Faker.IntBetween(26, 62)
addr := c.P2PKHAddressWithLength(length)
Expect(t, true, len(addr) == length)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["p2pkh"]))
}

func TestP2SHAddress(t *testing.T) {
c := New().Crypto()
addr := c.P2SHAddress()
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "3"))
Expect(t, true, len(addr) >= bitcoinMin)
Expect(t, true, len(addr) <= bitcoinMax)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["p2sh"]))
for i := 0; i < len(bannedBitcoin); i++ {
Expect(t, true, !strings.Contains(addr, bannedBitcoin[i]))
}
}

func TestP2SHAddressWithLength(t *testing.T) {
c := New().Crypto()
addr := c.P2SHAddressWithLength(10)
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "3"))
length := c.Faker.IntBetween(26, 62)
addr := c.P2SHAddressWithLength(length)
Expect(t, true, len(addr) == length)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["p2sh"]))
}

func TestBech32Address(t *testing.T) {
c := New().Crypto()
addr := c.Bech32Address()
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "bc1"))
Expect(t, true, len(addr) >= bitcoinMin)
Expect(t, true, len(addr) <= bitcoinMax)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["bech32"]))
for i := 0; i < len(bannedBitcoin); i++ {
Expect(t, true, !strings.Contains(addr, bannedBitcoin[i]))
}
}

func TestBech32AddressWithLength(t *testing.T) {
c := New().Crypto()
addr := c.Bech32AddressWithLength(10)
Expect(t, false, addr == "")
Expect(t, true, strings.HasPrefix(addr, "bc1"))
length := c.Faker.IntBetween(26, 62)
addr := c.Bech32AddressWithLength(length)
Expect(t, true, len(addr) == length)
Expect(t, true, strings.HasPrefix(addr, validBitcoinPrefix["bech32"]))
}

func TestEtheriumAddress(t *testing.T) {
c := New().Crypto()
addr := c.EtheriumAddress()
Expect(t, true, len(addr) == ethLen)
Expect(t, true, strings.HasPrefix(addr, ethPrefix))
}

func TestAlgorithmRange(t *testing.T) {
for k, tc := range []TestCaseAlnum{
{
// The Description of the test case
desc: "Test Get Digit 0-9",
localInt: 0,
// Our anticipated result
assert: func(t *testing.T, a int, b int) {
Expect(t, true, a == int('0'))
Expect(t, true, b == int('9'))
},
},
{
desc: "Test Get Uppercase A-Z",
localInt: 1,
assert: func(t *testing.T, a int, b int) {
Expect(t, true, a == int('A'))
Expect(t, true, b == int('Z'))
},
},
{
desc: "Test Get Lowercase a-z",
localInt: 2,
assert: func(t *testing.T, a int, b int) {
Expect(t, true, a == int('a'))
Expect(t, true, b == int('z'))
},
},
} {
t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.desc), func(t *testing.T) {
// Use our mock here instead of using a seed.
gen := GeneratorMock{}
gen.local = tc.localInt
// populate the generator with our mock as it is an interface.
c := Faker{Generator: gen}
a, b := c.Crypto().algorithmRange()
tc.assert(t, a, b)
})
}
}

func TestRandomBitcoin(t *testing.T) {
for k, tc := range []TestCaseRandomBitcoin{
{
// The Description of the test case
desc: "Test Get Bech32",
localInt: 0,
// Our anticipated result
expectedSubstring: "bc1",
},
{
// The Description of the test case
desc: "Test Get P2SH",
localInt: 1,
// Our anticipated result
expectedSubstring: "3",
},
{
// The Description of the test case
desc: "Test Get P2PKH",
localInt: 2,
// Our anticipated result
expectedSubstring: "1",
},
} {
t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.desc), func(t *testing.T) {
// Use our mock here instead of using a seed.
gen := GeneratorMock{}
gen.local = tc.localInt
// populate the generator with our mock as it is an interface.
c := Faker{Generator: gen}
rs := c.Crypto().BitcoinAddress()
Expect(t, true, strings.HasPrefix(rs, tc.expectedSubstring))
Expect(t, true, len(rs) >= bitcoinMin)
Expect(t, true, len(rs) <= bitcoinMax)
})
}
}

0 comments on commit d2fb71c

Please sign in to comment.