Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet): supporting Ed25519 curve in wallet #1484

Merged
merged 7 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading