From 36fcb9e4ad2552203aa9bbd2c6ba571a2ce5e9fc Mon Sep 17 00:00:00 2001 From: Sam Bukowski Date: Thu, 11 Jul 2024 16:08:43 -0600 Subject: [PATCH] Feature: Add `bech32m` module (#132) * add bech32m module * rename functions, update justfile * add decode, update comments * replace bech32m functions in cli with bech32m module * update error message * remove unused function, rename Encode to EncodeFromBytes * add decode test * update name to DecodeFromString * move indirect import --- justfile | 5 + modules/bech32m/bech32m.go | 121 ++++++++++++++++++ modules/bech32m/bech32m_test.go | 44 +++++++ modules/bech32m/go.mod | 16 +++ modules/bech32m/go.sum | 106 +++++++++++++++ modules/cli/go.mod | 5 +- modules/cli/internal/sequencer/helpers.go | 52 -------- .../cli/internal/sequencer/helpers_test.go | 15 --- modules/cli/internal/sequencer/sequencer.go | 21 +-- .../cli/internal/sequencer/sequencer_test.go | 2 +- modules/cli/internal/sequencer/types.go | 6 +- 11 files changed, 311 insertions(+), 82 deletions(-) create mode 100644 modules/bech32m/bech32m.go create mode 100644 modules/bech32m/bech32m_test.go create mode 100644 modules/bech32m/go.mod create mode 100644 modules/bech32m/go.sum diff --git a/justfile b/justfile index 0249aaa..2e0ae24 100644 --- a/justfile +++ b/justfile @@ -17,6 +17,7 @@ install-cli binary_name=default_binary_name: test: cd modules/cli && go test ./... cd modules/go-sequencer-client && go test ./... + cd modules/bech32m && go test ./... alias t := test # unit tests with coverage report that opens in browser @@ -25,6 +26,8 @@ test-cov: cd modules/cli && go tool cover -html=coverage.out cd modules/go-sequencer-client && go test ./... -coverprofile=coverage.out cd modules/go-sequencer-client && go tool cover -html=coverage.out + cd modules/bech32m && go test ./... -coverprofile=coverage.out + cd modules/mech32m && go tool cover -html=coverage.out # run unit and integration tests, and tests that require tty. test-all: test test-integration-cli @@ -50,6 +53,7 @@ cleanup-integration-tests: fmt: cd modules/cli && go fmt ./... cd modules/go-sequencer-client && go fmt ./... + cd modules/bech32m && go fmt ./... alias f := fmt default_lang := 'all' @@ -67,6 +71,7 @@ alias l := lint _lint-go: cd modules/cli && golangci-lint run cd modules/go-sequencer-client && golangci-lint run + cd modules/bech32m && golangci-lint run [no-exit-message] _lint-md: diff --git a/modules/bech32m/bech32m.go b/modules/bech32m/bech32m.go new file mode 100644 index 0000000..91f13c9 --- /dev/null +++ b/modules/bech32m/bech32m.go @@ -0,0 +1,121 @@ +package bech32m + +import ( + "crypto/ed25519" + "crypto/sha256" + "fmt" + + "github.com/btcsuite/btcd/btcutil/bech32" + log "github.com/sirupsen/logrus" +) + +type Bech32MAddress struct { + address string + prefix string + bytes [20]byte +} + +// String returns the bech32m address as a string +func (a *Bech32MAddress) String() string { + return a.address +} + +// Prefix returns the prefix of the bech32m address +func (a *Bech32MAddress) Prefix() string { + return a.prefix +} + +// Bytes returns the underlying bytes for the bech32m address as a [20]byte array +func (a *Bech32MAddress) Bytes() [20]byte { + return a.bytes +} + +// Validate verifies that a string in a valid bech32m address. It +// will return nil if the address is valid, otherwise it will return an error. +func Validate(address string) error { + prefix, byteAddress, version, err := bech32.DecodeGeneric(address) + if err != nil { + return fmt.Errorf("address must be a bech32 encoded string") + } + if version != bech32.VersionM { + return fmt.Errorf("address must be a bech32m address") + } + byteAddress, err = bech32.ConvertBits(byteAddress, 5, 8, false) + if err != nil { + return fmt.Errorf("failed to convert address to 8 bit") + } + if prefix == "" { + return fmt.Errorf("address must have prefix") + } + if len(byteAddress) != 20 { + return fmt.Errorf("address must decode to a 20 length byte array: got len %d", len(byteAddress)) + } + + return nil +} + +// EncodeFromBytes creates a *Bech32MAddress from a [20]byte array and string +// prefix. +func EncodeFromBytes(prefix string, data [20]byte) (*Bech32MAddress, error) { + // Convert the data from 8-bit groups to 5-bit + convertedBytes, err := bech32.ConvertBits(data[:], 8, 5, true) + if err != nil { + return nil, fmt.Errorf("failed to convert bits from 8-bit groups to 5-bit groups: %v", err) + } + + // Encode the data as bech32m + address, err := bech32.EncodeM(prefix, convertedBytes) + if err != nil { + return nil, fmt.Errorf("failed to encode address as bech32m: %v", err) + } + + return &Bech32MAddress{ + address: address, + prefix: prefix, + bytes: data, + }, nil +} + +// EncodeFromPublicKey takes an ed25519 public key and string prefix and encodes +// them into a *Bech32MAddress. +func EncodeFromPublicKey(prefix string, pubkey ed25519.PublicKey) (*Bech32MAddress, error) { + hash := sha256.Sum256(pubkey) + var addr [20]byte + copy(addr[:], hash[:20]) + address, err := EncodeFromBytes(prefix, addr) + if err != nil { + log.WithError(err).Error("Error encoding address") + return nil, err + } + return address, nil +} + +// DecodeFromString decodes a bech32m string into a string prefix and the underlying +// [20]byte array originally used to encode the address. It also checks if the +// address is a bech32m address and not a different bech32 version. +func DecodeFromString(address string) (string, [20]byte, error) { + prefix, bytes, version, err := bech32.DecodeGeneric(address) + if err != nil { + var defaultBytes [20]byte + copy(defaultBytes[:], bytes) + return prefix, defaultBytes, fmt.Errorf("failed to decode address") + } + + if version != bech32.VersionM { + var defaultBytes [20]byte + copy(defaultBytes[:], bytes) + return prefix, defaultBytes, fmt.Errorf("address must be a bech32m address") + } + + convertedBytes, err := bech32.ConvertBits(bytes, 5, 8, false) + if err != nil { + var defaultBytes [20]byte + copy(defaultBytes[:], convertedBytes) + return prefix, defaultBytes, fmt.Errorf("failed to convert address bytes to 8 bit") + } + + var addrBytes [20]byte + copy(addrBytes[:], convertedBytes) + + return prefix, addrBytes, nil +} diff --git a/modules/bech32m/bech32m_test.go b/modules/bech32m/bech32m_test.go new file mode 100644 index 0000000..14c3b14 --- /dev/null +++ b/modules/bech32m/bech32m_test.go @@ -0,0 +1,44 @@ +package bech32m + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" +) + +const bech32MAddress = "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" +const bech32MAddressBytes = "1c0c490f1b5528d8173c5de46d131160e4b2c0c3" +const fromPubAddress = "astria1x66v8ph5x8z95vxw6uxmyg5xahkfg0tk8lvrvf" +const pubKey = "88787e29db8d5247c6adfac9909b56e6b2705c3120b2e3885e8ec8aa416a10f1" +const testPrefix = "astria" + +func TestValidate(t *testing.T) { + err := Validate(bech32MAddress) + assert.Nil(t, err) +} +func TestEncode(t *testing.T) { + bytes, _ := hex.DecodeString(bech32MAddressBytes) + var addrBytes [20]byte + copy(addrBytes[:], bytes) + addr, _ := EncodeFromBytes(testPrefix, addrBytes) + + assert.Equal(t, bech32MAddress, addr.String()) + assert.Equal(t, testPrefix, addr.Prefix()) + assert.Equal(t, addrBytes, addr.Bytes()) +} + +func TestEncodeFromPublicKey(t *testing.T) { + bytes, _ := hex.DecodeString(pubKey) + addr, _ := EncodeFromPublicKey(testPrefix, bytes) + + assert.Equal(t, fromPubAddress, addr.String()) + assert.Equal(t, testPrefix, addr.Prefix()) +} + +func TestDecode(t *testing.T) { + prefix, addr, err := DecodeFromString(bech32MAddress) + assert.Nil(t, err) + assert.Equal(t, prefix, testPrefix) + assert.Equal(t, bech32MAddressBytes, hex.EncodeToString(addr[:])) +} diff --git a/modules/bech32m/go.mod b/modules/bech32m/go.mod new file mode 100644 index 0000000..d4e5922 --- /dev/null +++ b/modules/bech32m/go.mod @@ -0,0 +1,16 @@ +module github.com/astriaorg/astria-cli-go/modules/bech32m + +go 1.22.2 + +require ( + github.com/btcsuite/btcd/btcutil v1.1.5 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.7.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/modules/bech32m/go.sum b/modules/bech32m/go.sum new file mode 100644 index 0000000..94aafb9 --- /dev/null +++ b/modules/bech32m/go.sum @@ -0,0 +1,106 @@ +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/modules/cli/go.mod b/modules/cli/go.mod index 8b88040..8c1909f 100644 --- a/modules/cli/go.mod +++ b/modules/cli/go.mod @@ -6,8 +6,8 @@ require ( buf.build/gen/go/astria/primitives/protocolbuffers/go v1.34.2-20240626163506-691883836b9e.2 buf.build/gen/go/astria/protocol-apis/protocolbuffers/go v1.34.2-20240627184145-2eaea785eb7d.2 github.com/99designs/keyring v1.2.2 + github.com/astriaorg/astria-cli-go/modules/bech32m v0.0.0-00010101000000-000000000000 github.com/astriaorg/astria-cli-go/modules/go-sequencer-client v0.0.0-00010101000000-000000000000 - github.com/btcsuite/btcd/btcutil v1.1.5 github.com/gdamore/tcell/v2 v2.7.4 github.com/pelletier/go-toml/v2 v2.2.2 github.com/pterm/pterm v0.12.79 @@ -21,6 +21,8 @@ require ( replace github.com/astriaorg/astria-cli-go/modules/go-sequencer-client => ../go-sequencer-client +replace github.com/astriaorg/astria-cli-go/modules/bech32m => ../bech32m + require ( atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect @@ -29,6 +31,7 @@ require ( github.com/DataDog/zstd v1.5.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect diff --git a/modules/cli/internal/sequencer/helpers.go b/modules/cli/internal/sequencer/helpers.go index eb2bed9..fadc12b 100644 --- a/modules/cli/internal/sequencer/helpers.go +++ b/modules/cli/internal/sequencer/helpers.go @@ -1,15 +1,9 @@ package sequencer import ( - "crypto/ed25519" "crypto/sha256" - "encoding/hex" - "fmt" primproto "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" - - "github.com/btcsuite/btcd/btcutil/bech32" - log "github.com/sirupsen/logrus" ) // rollupIdFromText converts a string to a RollupId protobuf. @@ -19,49 +13,3 @@ func rollupIdFromText(rollup string) *primproto.RollupId { Inner: hash[:], } } - -// addressFromPublicKey converts an ed25519 public key to a hexadecimal string representation of its address. -func addressFromPublicKey(prefix string, pubkey ed25519.PublicKey) (*Bech32MAddress, error) { - hash := sha256.Sum256(pubkey) - var addr [20]byte - copy(addr[:], hash[:20]) - address, err := Bech32MFromBytes(prefix, addr) - if err != nil { - log.WithError(err).Error("Error encoding address") - return nil, err - } - return address, nil -} - -// privateKeyFromText converts a string representation of a private key to an ed25519.PrivateKey. -// It decodes the private key from hex string format and creates a new ed25519.PrivateKey. -func privateKeyFromText(privkey string) (ed25519.PrivateKey, error) { - privKeyBytes, err := hex.DecodeString(privkey) - if err != nil { - return nil, err - } - from := ed25519.NewKeyFromSeed(privKeyBytes) - return from, nil -} - -// Bech32MFromBytes creates a bech32m address from a [20]byte array and string -// prefix. -func Bech32MFromBytes(prefix string, data [20]byte) (*Bech32MAddress, error) { - // Convert the data from 8-bit groups to 5-bit - converted, err := bech32.ConvertBits(data[:], 8, 5, true) - if err != nil { - return nil, fmt.Errorf("failed to convert bits from 8-bit groups to 5-bit groups: %v", err) - } - - // Encode the data as Bech32m - address, err := bech32.EncodeM(prefix, converted) - if err != nil { - return nil, fmt.Errorf("failed to encode address as bech32m: %v", err) - } - - return &Bech32MAddress{ - Address: address, - Prefix: prefix, - Bytes: data, - }, nil -} diff --git a/modules/cli/internal/sequencer/helpers_test.go b/modules/cli/internal/sequencer/helpers_test.go index 7383df8..e07d5ec 100644 --- a/modules/cli/internal/sequencer/helpers_test.go +++ b/modules/cli/internal/sequencer/helpers_test.go @@ -1,7 +1,6 @@ package sequencer import ( - "crypto/ed25519" "testing" primproto "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" @@ -16,17 +15,3 @@ func TestRollupIdFromText(t *testing.T) { } assert.Equal(t, expected, actual) } - -func TestAddressFromPublicKey(t *testing.T) { - // bech32m address encoded from 1c0c490f1b5528d8173c5de46d131160e4b2c0c3 bytes - expected := "astria1rsxyjrcm255ds9euthjx6yc3vrjt9sxrm9cfgm" - - testFromPrivKey := "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90" - - from, _ := privateKeyFromText(testFromPrivKey) - pub := from.Public().(ed25519.PublicKey) - actual, err := addressFromPublicKey("astria", pub) - assert.NoError(t, err) - - assert.Equal(t, expected, actual.Address) -} diff --git a/modules/cli/internal/sequencer/sequencer.go b/modules/cli/internal/sequencer/sequencer.go index 1e5531c..88e3c1d 100644 --- a/modules/cli/internal/sequencer/sequencer.go +++ b/modules/cli/internal/sequencer/sequencer.go @@ -10,6 +10,7 @@ import ( txproto "buf.build/gen/go/astria/protocol-apis/protocolbuffers/go/astria/protocol/transactions/v1alpha1" "buf.build/gen/go/astria/protocol-apis/protocolbuffers/go/astria_vendored/tendermint/abci" "buf.build/gen/go/astria/protocol-apis/protocolbuffers/go/astria_vendored/tendermint/crypto" + "github.com/astriaorg/astria-cli-go/modules/bech32m" "github.com/astriaorg/astria-cli-go/modules/go-sequencer-client/client" log "github.com/sirupsen/logrus" @@ -28,7 +29,7 @@ func CreateAccount(prefix string) (*Account, error) { log.Debugf("Address bytes: %s", hex.EncodeToString(address[:])) - addr, err := Bech32MFromBytes(prefix, address) + addr, err := bech32m.EncodeFromBytes(prefix, address) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -172,7 +173,7 @@ func Transfer(opts TransferOpts) (*TransferResponse, error) { signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -247,7 +248,7 @@ func InitBridgeAccount(opts InitBridgeOpts) (*InitBridgeResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -325,7 +326,7 @@ func BridgeLock(opts BridgeLockOpts) (*BridgeLockResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -404,7 +405,7 @@ func AddFeeAsset(opts FeeAssetOpts) (*FeeAssetResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -479,7 +480,7 @@ func RemoveFeeAsset(opts FeeAssetOpts) (*FeeAssetResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -554,7 +555,7 @@ func AddIBCRelayer(opts IBCRelayerOpts) (*IBCRelayerResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -629,7 +630,7 @@ func RemoveIBCRelayer(opts IBCRelayerOpts) (*IBCRelayerResponse, error) { // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err @@ -704,7 +705,7 @@ func ChangeSudoAddress(opts ChangeSudoAddressOpts) (*ChangeSudoAddressResponse, // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return &ChangeSudoAddressResponse{}, err @@ -777,7 +778,7 @@ func UpdateValidator(opts UpdateValidatorOpts) (*UpdateValidatorResponse, error) // Get current address nonce signer := client.NewSigner(opts.FromKey) fromAddr := signer.Address() - addr, err := Bech32MFromBytes(opts.AddressPrefix, fromAddr) + addr, err := bech32m.EncodeFromBytes(opts.AddressPrefix, fromAddr) if err != nil { log.WithError(err).Error("Failed to encode address") return nil, err diff --git a/modules/cli/internal/sequencer/sequencer_test.go b/modules/cli/internal/sequencer/sequencer_test.go index eac34b1..a48fde4 100644 --- a/modules/cli/internal/sequencer/sequencer_test.go +++ b/modules/cli/internal/sequencer/sequencer_test.go @@ -17,7 +17,7 @@ func TestCreateAccount(t *testing.T) { assert.NotEmpty(t, account.PublicKey, "Public Key should not be empty") assert.NotEmpty(t, account.PrivateKey, "Private Key should not be empty") - assert.Equal(t, account.Address.Address, account.ToJSONStruct().Address, "Address should match JSON representation") + assert.Equal(t, account.Address.String(), account.ToJSONStruct().Address, "Address should match JSON representation") assert.Equal(t, account.PublicKeyString(), account.ToJSONStruct().PublicKey, "Public Key should match JSON representation") assert.Equal(t, account.PrivateKeyString(), account.ToJSONStruct().PrivateKey, "Private Key should match JSON representation") diff --git a/modules/cli/internal/sequencer/types.go b/modules/cli/internal/sequencer/types.go index 7b38f49..fcdaa67 100644 --- a/modules/cli/internal/sequencer/types.go +++ b/modules/cli/internal/sequencer/types.go @@ -8,7 +8,7 @@ import ( "strconv" primproto "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" - + "github.com/astriaorg/astria-cli-go/modules/bech32m" coretypes "github.com/cometbft/cometbft/rpc/core/types" log "github.com/sirupsen/logrus" ) @@ -25,7 +25,7 @@ func (a *Bech32MAddress) String() string { // Account is the struct that holds the account information. type Account struct { - Address *Bech32MAddress + Address *bech32m.Bech32MAddress PublicKey ed25519.PublicKey PrivateKey ed25519.PrivateKey } @@ -35,7 +35,7 @@ type Account struct { // and returns a pointer to the new Account struct with the address, public key, and private key set. func NewAccountFromPrivKey(prefix string, privkey ed25519.PrivateKey) (*Account, error) { pub := privkey.Public().(ed25519.PublicKey) - addr, err := addressFromPublicKey(prefix, pub) + addr, err := bech32m.EncodeFromPublicKey(prefix, pub) if err != nil { log.WithError(err).Error("Error creating address from public key") return nil, err