Skip to content

Commit

Permalink
feat(wallet): supporting Ed25519 curve in wallet (pactus-project#1484)
Browse files Browse the repository at this point in the history
Co-authored-by: mj <[email protected]>
  • Loading branch information
akbariandev and mj authored Aug 24, 2024
1 parent 4e7052d commit d30aa03
Show file tree
Hide file tree
Showing 11 changed files with 461 additions and 99 deletions.
3 changes: 3 additions & 0 deletions cmd/wallet/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ func buildNewAddressCmd(parentCmd *cobra.Command) {

if *addressType == wallet.AddressTypeBLSAccount {
addressInfo, err = wlt.NewBLSAccountAddress(label)
} else if *addressType == wallet.AddressTypeEd25519Account {
password := cmd.PromptInput("Password")
addressInfo, err = wlt.NewEd25519AccountAddress(label, password)
} else if *addressType == wallet.AddressTypeValidator {
addressInfo, err = wlt.NewValidatorAddress(label)
} else {
Expand Down
3 changes: 2 additions & 1 deletion crypto/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ func (addr Address) IsTreasuryAddress() bool {

func (addr Address) IsAccountAddress() bool {
return addr.Type() == AddressTypeTreasury ||
addr.Type() == AddressTypeBLSAccount
addr.Type() == AddressTypeBLSAccount ||
addr.Type() == AddressTypeEd25519Account
}

func (addr Address) IsValidatorAddress() bool {
Expand Down
98 changes: 69 additions & 29 deletions crypto/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package crypto_test
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"strings"
"testing"
Expand All @@ -15,109 +14,145 @@ import (
"github.com/stretchr/testify/assert"
)

func TestAddressKeyType(t *testing.T) {
ts := testsuite.NewTestSuite(t)

pub, _ := ts.RandBLSKeyPair()
accAddr := pub.AccountAddress()
valAddr := pub.ValidatorAddress()
func TestTreasuryAddressType(t *testing.T) {
treasury := crypto.TreasuryAddress

assert.True(t, accAddr.IsAccountAddress())
assert.False(t, accAddr.IsValidatorAddress())
assert.False(t, accAddr.IsTreasuryAddress())
assert.False(t, valAddr.IsAccountAddress())
assert.True(t, valAddr.IsValidatorAddress())
assert.False(t, treasury.IsValidatorAddress())
assert.True(t, treasury.IsAccountAddress())
assert.True(t, treasury.IsTreasuryAddress())
assert.NotEqual(t, accAddr, valAddr)
}

func TestString(t *testing.T) {
ts := testsuite.NewTestSuite(t)
func TestAddressType(t *testing.T) {
tests := []struct {
address string
account bool
validator bool
}{
{address: "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr", account: false, validator: true},
{address: "pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x", account: true, validator: false},
{address: "pc1rspm7ps49gar9ft5g0tkl6lhxs8ygeakq87quh3", account: true, validator: false},
}

for _, test := range tests {
addr, _ := crypto.AddressFromString(test.address)

assert.Equal(t, test.account, addr.IsAccountAddress())
assert.Equal(t, test.validator, addr.IsValidatorAddress())
}
}

a, _ := crypto.AddressFromString("pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr")
fmt.Println(a.String())
func TestShortString(t *testing.T) {
ts := testsuite.NewTestSuite(t)

addr1 := ts.RandAccAddress()
assert.Contains(t, addr1.String(), addr1.ShortString())
}

func TestToString(t *testing.T) {
func TestFromString(t *testing.T) {
tests := []struct {
encoded string
err error
result *crypto.Address
encoded string
err error
bytes []byte
addrType crypto.AddressType
}{
{
"000000000000000000000000000000000000000000",
nil,
&crypto.Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
crypto.AddressTypeTreasury,
},
{
"",
bech32m.InvalidLengthError(0),
nil,
0,
},
{
"not_proper_encoded",
bech32m.InvalidSeparatorIndexError(-1),
nil,
0,
},
{
"pc1ioiooi",
bech32m.NonCharsetCharError(105),
nil,
0,
},
{
"pc19p72rf",
bech32m.InvalidLengthError(0),
nil,
0,
},
{
"qc1z0hrct7eflrpw4ccrttxzs4qud2axex4dh8zz75",
crypto.InvalidHRPError("qc"),
nil,
0,
},
{
"pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dg8xaf5",
bech32m.InvalidChecksumError{Expected: "cdzdfr", Actual: "g8xaf5"},
nil,
0,
},
{
"pc1p0hrct7eflrpw4ccrttxzs4qud2axexs2dhdk8",
crypto.InvalidLengthError(20),
nil,
0,
},
{
"pc1y0hrct7eflrpw4ccrttxzs4qud2axex4dksmred",
crypto.InvalidAddressTypeError(4),
nil,
0,
},
{
"PC1P0HRCT7EFLRPW4CCRTTXZS4QUD2AXEX4DCDZDFR", // UPPERCASE
nil,
&crypto.Address{
0x1, 0x7d, 0xc7, 0x85, 0xfb, 0x29, 0xf8, 0xc2, 0xea, 0xe3,
0x3, 0x5a, 0xcc, 0x28, 0x54, 0x1c, 0x6a, 0xba, 0x6c, 0x9a, 0xad,
[]byte{
0x01, 0x7d, 0xc7, 0x85, 0xfb, 0x29, 0xf8, 0xc2, 0xea, 0xe3,
0x03, 0x5a, 0xcc, 0x28, 0x54, 0x1c, 0x6a, 0xba, 0x6c, 0x9a, 0xad,
},
crypto.AddressTypeValidator,
},
{
"pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr",
nil,
&crypto.Address{
0x1, 0x7d, 0xc7, 0x85, 0xfb, 0x29, 0xf8, 0xc2, 0xea, 0xe3,
0x3, 0x5a, 0xcc, 0x28, 0x54, 0x1c, 0x6a, 0xba, 0x6c, 0x9a, 0xad,
[]byte{
0x01, 0x7d, 0xc7, 0x85, 0xfb, 0x29, 0xf8, 0xc2, 0xea, 0xe3,
0x03, 0x5a, 0xcc, 0x28, 0x54, 0x1c, 0x6a, 0xba, 0x6c, 0x9a, 0xad,
},
crypto.AddressTypeValidator,
},
{
"pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x",
nil,
[]byte{
0x02, 0x10, 0x2c, 0x21, 0x72, 0xac, 0x23, 0x5b, 0x08, 0x3e, 0x8a,
0xec, 0x35, 0x88, 0xf8, 0x03, 0x6e, 0x12, 0x15, 0xe5, 0x00,
},
crypto.AddressTypeBLSAccount,
},
{
"pc1rspm7ps49gar9ft5g0tkl6lhxs8ygeakq87quh3",
nil,
[]byte{
0x03, 0x80, 0x77, 0xe0, 0xc2, 0xa5, 0x47, 0x46, 0x54, 0xae,
0x88, 0x7a, 0xed, 0xfd, 0x7e, 0xe6, 0x81, 0xc8, 0x8c, 0xf6, 0xc0,
},
crypto.AddressTypeEd25519Account,
},
}
for no, test := range tests {
addr, err := crypto.AddressFromString(test.encoded)
if test.err == nil {
assert.NoError(t, err, "test %v: unexpected error", no)
assert.Equal(t, *test.result, addr, "test %v: invalid result", no)
assert.Equal(t, test.bytes, addr.Bytes(), "test %v: invalid result", no)
assert.Equal(t, strings.ToLower(test.encoded), addr.String(), "test %v: invalid encode", no)
assert.Equal(t, test.addrType, addr.Type(), "test %v: invalid type", no)
} else {
assert.ErrorIs(t, err, test.err, "test %v: invalid error", no)
}
Expand Down Expand Up @@ -165,6 +200,11 @@ func TestAddressEncoding(t *testing.T) {
"02000102030405060708090a0b0c0d0e0f00010203",
nil,
},
{
21,
"03000102030405060708090a0b0c0d0e0f00010203",
nil,
},
}
for no, test := range tests {
data, _ := hex.DecodeString(test.hex)
Expand Down
9 changes: 8 additions & 1 deletion types/tx/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package tx

import "github.com/pactus-project/pactus/types/tx/payload"
import (
"errors"

"github.com/pactus-project/pactus/types/tx/payload"
)

// ErrInvalidSigner is returned when the signer address is not valid.
var ErrInvalidSigner = errors.New("invalid signer address")

// BasicCheckError is returned when the basic check on the transaction fails.
type BasicCheckError struct {
Expand Down
101 changes: 84 additions & 17 deletions types/tx/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/fxamacker/cbor/v2"
"github.com/pactus-project/pactus/crypto"
"github.com/pactus-project/pactus/crypto/bls"
"github.com/pactus-project/pactus/crypto/ed25519"
"github.com/pactus-project/pactus/crypto/hash"
"github.com/pactus-project/pactus/types/amount"
"github.com/pactus-project/pactus/types/tx/payload"
Expand Down Expand Up @@ -250,18 +251,37 @@ func (tx *Tx) UnmarshalCBOR(bs []byte) error {

// SerializeSize returns the number of bytes it would take to serialize the transaction.
func (tx *Tx) SerializeSize() int {
n := 3 + // one byte version, flag, payload type
4 + // for tx.LockTime
n := 7 + // flag (1) + version (1) + payload type (1) + lock_time (4)
encoding.VarIntSerializeSize(uint64(tx.Fee())) +
encoding.VarStringSerializeSize(tx.Memo())
if tx.Payload() != nil {
n += tx.Payload().SerializeSize()
}
if tx.data.Signature != nil {
n += bls.SignatureSize
switch tx.data.Payload.Signer().Type() {
case crypto.AddressTypeValidator,
crypto.AddressTypeBLSAccount:
n += bls.SignatureSize

case crypto.AddressTypeEd25519Account:
n += ed25519.SignatureSize

case crypto.AddressTypeTreasury:
n += 0
}
}
if tx.data.PublicKey != nil {
n += bls.PublicKeySize
switch tx.data.Payload.Signer().Type() {
case crypto.AddressTypeValidator,
crypto.AddressTypeBLSAccount:
n += bls.PublicKeySize

case crypto.AddressTypeEd25519Account:
n += ed25519.PublicKeySize

case crypto.AddressTypeTreasury:
n += 0
}
}

return n
Expand Down Expand Up @@ -360,27 +380,74 @@ func (tx *Tx) Decode(r io.Reader) error {
return err
}

if !util.IsFlagSet(tx.data.Flags, flagNotSigned) {
sig := new(bls.Signature)
err = sig.Decode(r)
if util.IsFlagSet(tx.data.Flags, flagNotSigned) {
return nil
}

// It is a signed transaction, Decode signatory.
sig, err := tx.decodeSignature(r)
if err != nil {
return err
}
tx.data.Signature = sig

if !tx.IsPublicKeyStriped() {
pub, err := tx.decodePublicKey(r)
if err != nil {
return err
}
tx.data.Signature = sig

if !tx.IsPublicKeyStriped() {
pub := new(bls.PublicKey)
err = pub.Decode(r)
if err != nil {
return err
}
tx.data.PublicKey = pub
}
tx.data.PublicKey = pub
}

return nil
}

func (tx *Tx) decodeSignature(r io.Reader) (crypto.Signature, error) {
switch tx.data.Payload.Signer().Type() {
case crypto.AddressTypeValidator,
crypto.AddressTypeBLSAccount:
sig := new(bls.Signature)
err := sig.Decode(r)

return sig, err

case crypto.AddressTypeEd25519Account:
sig := new(ed25519.Signature)
err := sig.Decode(r)

return sig, err

case crypto.AddressTypeTreasury:
return nil, ErrInvalidSigner

default:
return nil, ErrInvalidSigner
}
}

func (tx *Tx) decodePublicKey(r io.Reader) (crypto.PublicKey, error) {
switch tx.data.Payload.Signer().Type() {
case crypto.AddressTypeValidator,
crypto.AddressTypeBLSAccount:
pub := new(bls.PublicKey)
err := pub.Decode(r)

return pub, err

case crypto.AddressTypeEd25519Account:
pub := new(ed25519.PublicKey)
err := pub.Decode(r)

return pub, err

case crypto.AddressTypeTreasury:
return nil, ErrInvalidSigner

default:
return nil, ErrInvalidSigner
}
}

func (tx *Tx) String() string {
return fmt.Sprintf("{⌘ %v - %v 🏵 %v}",
tx.ID().ShortString(),
Expand Down
Loading

0 comments on commit d30aa03

Please sign in to comment.