Skip to content

Commit a6048da

Browse files
committed
Make private/public key strong type (iotexproject#699)
* Make private/public key strong type * Address comments and add tests for keypair pkg * Return invalid public/private key length as an error instead of panic
1 parent 0edc985 commit a6048da

35 files changed

+454
-163
lines changed

actpool/actpool.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/iotexproject/iotex-core/iotxaddress"
1919
"github.com/iotexproject/iotex-core/logger"
2020
"github.com/iotexproject/iotex-core/pkg/hash"
21+
"github.com/iotexproject/iotex-core/pkg/keypair"
2122
"github.com/iotexproject/iotex-core/proto"
2223
)
2324

@@ -208,7 +209,8 @@ func (ap *actPool) AddVote(vote *action.Vote) error {
208209
return errors.Wrapf(ErrActPool, "insufficient space for vote")
209210
}
210211

211-
voter, _ := iotxaddress.GetAddress(vote.SelfPubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
212+
selfPublicKey, _ := vote.SelfPublicKey()
213+
voter, _ := iotxaddress.GetAddress(selfPublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
212214
// Wrap vote as an action
213215
action := &iproto.ActionPb{Action: &iproto.ActionPb_Vote{vote.ConvertToVotePb()}}
214216
return ap.addAction(voter.RawAddress, action, hash, vote.Nonce)
@@ -289,7 +291,11 @@ func (ap *actPool) validateVote(vote *action.Vote) error {
289291
logger.Error().Msg("Error when validating vote")
290292
return errors.Wrapf(ErrActPool, "oversized data")
291293
}
292-
voter, err := iotxaddress.GetAddress(vote.SelfPubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
294+
selfPublicKey, err := vote.SelfPublicKey()
295+
if err != nil {
296+
return err
297+
}
298+
voter, err := iotxaddress.GetAddress(selfPublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
293299
if err != nil {
294300
logger.Error().Err(err).Msg("Error when validating vote")
295301
return errors.Wrapf(err, "invalid voter address")
@@ -306,9 +312,13 @@ func (ap *actPool) validateVote(vote *action.Vote) error {
306312
logger.Error().Err(err).Msg("Error when validating vote")
307313
return errors.Wrapf(err, "invalid nonce value")
308314
}
309-
if len(vote.VotePubkey) > 0 {
315+
votePublicKey, err := vote.VotePublicKey()
316+
if err != nil {
317+
return err
318+
}
319+
if votePublicKey != keypair.ZeroPublicKey {
310320
// Reject vote if votee is not a candidate
311-
votee, err := iotxaddress.GetAddress(vote.VotePubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
321+
votee, err := iotxaddress.GetAddress(votePublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
312322
if err != nil {
313323
logger.Error().
314324
Err(err).Hex("voter", vote.SelfPubkey[:]).Hex("votee", vote.VotePubkey).

actpool/actpool_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/iotexproject/iotex-core/config"
2222
"github.com/iotexproject/iotex-core/iotxaddress"
2323
"github.com/iotexproject/iotex-core/logger"
24+
"github.com/iotexproject/iotex-core/pkg/keypair"
2425
pb "github.com/iotexproject/iotex-core/proto"
2526
"github.com/iotexproject/iotex-core/test/mock/mock_blockchain"
2627
"github.com/iotexproject/iotex-core/test/util"
@@ -222,7 +223,7 @@ func TestActPool_AddActs(t *testing.T) {
222223
replaceTsf, _ := signedTransfer(addr1, addr2, uint64(1), big.NewInt(1))
223224
err = ap.AddTsf(replaceTsf)
224225
require.Equal(ErrNonce, errors.Cause(err))
225-
replaceVote := action.NewVote(4, addr1.PublicKey, []byte{})
226+
replaceVote := action.NewVote(4, addr1.PublicKey, keypair.ZeroPublicKey)
226227
replaceVote, _ = replaceVote.Sign(addr1)
227228
require.Equal(ErrNonce, errors.Cause(err))
228229
// Case IV: Nonce is too large
@@ -580,11 +581,11 @@ func TestActPool_Reset(t *testing.T) {
580581
require.Nil(err)
581582
tsf21, _ := signedTransfer(addr4, addr5, uint64(1), big.NewInt(10))
582583
vote22, _ := signedVote(addr4, addr4, uint64(2))
583-
vote23 := action.NewVote(3, addr4.PublicKey, []byte{})
584+
vote23 := action.NewVote(3, addr4.PublicKey, keypair.ZeroPublicKey)
584585
vote23, err = vote23.Sign(addr4)
585586
vote24, _ := signedVote(addr5, addr5, uint64(1))
586587
tsf25, _ := signedTransfer(addr5, addr4, uint64(2), big.NewInt(10))
587-
vote26 := action.NewVote(3, addr5.PublicKey, []byte{})
588+
vote26 := action.NewVote(3, addr5.PublicKey, keypair.ZeroPublicKey)
588589
vote26, err = vote26.Sign(addr5)
589590

590591
err = ap1.AddTsf(tsf21)

blockchain/action/transfer.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/iotexproject/iotex-core/logger"
2222
"github.com/iotexproject/iotex-core/pkg/enc"
2323
"github.com/iotexproject/iotex-core/pkg/hash"
24+
"github.com/iotexproject/iotex-core/pkg/keypair"
2425
"github.com/iotexproject/iotex-core/pkg/version"
2526
"github.com/iotexproject/iotex-core/proto"
2627
)
@@ -41,7 +42,7 @@ type (
4142
Sender string
4243
Recipient string
4344
Payload []byte
44-
SenderPublicKey []byte
45+
SenderPublicKey keypair.PublicKey
4546
Signature []byte
4647
IsCoinbase bool
4748
// Coinbase transfer is not expected to be received from the network but can only be generated by block producer
@@ -107,7 +108,7 @@ func (tsf *Transfer) ByteStream() []byte {
107108
stream = append(stream, tsf.Sender...)
108109
stream = append(stream, tsf.Recipient...)
109110
stream = append(stream, tsf.Payload...)
110-
stream = append(stream, tsf.SenderPublicKey...)
111+
stream = append(stream, tsf.SenderPublicKey[:]...)
111112
// Signature = Sign(hash(ByteStream())), so not included
112113
if tsf.IsCoinbase {
113114
stream = append(stream, 1)
@@ -126,7 +127,7 @@ func (tsf *Transfer) ConvertToTransferPb() *iproto.TransferPb {
126127
Sender: tsf.Sender,
127128
Recipient: tsf.Recipient,
128129
Payload: tsf.Payload,
129-
SenderPubKey: tsf.SenderPublicKey,
130+
SenderPubKey: tsf.SenderPublicKey[:],
130131
Signature: tsf.Signature,
131132
IsCoinbase: tsf.IsCoinbase,
132133
}
@@ -146,7 +147,7 @@ func (tsf *Transfer) ToJSON() *explorer.Transfer {
146147
Sender: tsf.Sender,
147148
Recipient: tsf.Recipient,
148149
Payload: hex.EncodeToString(tsf.Payload),
149-
SenderPubKey: hex.EncodeToString(tsf.SenderPublicKey),
150+
SenderPubKey: keypair.EncodePublicKey(tsf.SenderPublicKey),
150151
Signature: hex.EncodeToString(tsf.Signature),
151152
IsCoinbase: tsf.IsCoinbase,
152153
}
@@ -184,8 +185,7 @@ func (tsf *Transfer) ConvertFromTransferPb(pbTx *iproto.TransferPb) {
184185
}
185186
tsf.Payload = nil
186187
tsf.Payload = pbTx.Payload
187-
tsf.SenderPublicKey = nil
188-
tsf.SenderPublicKey = pbTx.SenderPubKey
188+
copy(tsf.SenderPublicKey[:], pbTx.SenderPubKey)
189189
tsf.Signature = nil
190190
tsf.Signature = pbTx.Signature
191191
tsf.IsCoinbase = pbTx.IsCoinbase
@@ -206,12 +206,12 @@ func NewTransferFromJSON(jsonTsf *explorer.Transfer) (*Transfer, error) {
206206
return nil, err
207207
}
208208
tsf.Payload = payload
209-
senderPubKey, err := hex.DecodeString(jsonTsf.SenderPubKey)
209+
senderPubKey, err := keypair.StringToPubKeyBytes(jsonTsf.SenderPubKey)
210210
if err != nil {
211211
logger.Error().Err(err).Msg("Fail to create a new Transfer from TransferJSON")
212212
return nil, err
213213
}
214-
tsf.SenderPublicKey = senderPubKey
214+
copy(tsf.SenderPublicKey[:], senderPubKey)
215215
signature, err := hex.DecodeString(jsonTsf.Signature)
216216
if err != nil {
217217
logger.Error().Err(err).Msg("Fail to create a new Transfer from TransferJSON")

blockchain/action/vote.go

+29-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/iotexproject/iotex-core/logger"
2121
"github.com/iotexproject/iotex-core/pkg/enc"
2222
"github.com/iotexproject/iotex-core/pkg/hash"
23+
"github.com/iotexproject/iotex-core/pkg/keypair"
2324
"github.com/iotexproject/iotex-core/pkg/version"
2425
"github.com/iotexproject/iotex-core/proto"
2526
)
@@ -42,17 +43,27 @@ type Vote struct {
4243
}
4344

4445
// NewVote returns a Vote instance
45-
func NewVote(nonce uint64, selfPubKey []byte, votePubKey []byte) *Vote {
46+
func NewVote(nonce uint64, selfPubKey keypair.PublicKey, votePubKey keypair.PublicKey) *Vote {
4647
pbVote := &iproto.VotePb{
4748
Version: version.ProtocolVersion,
4849

4950
Nonce: nonce,
50-
SelfPubkey: selfPubKey,
51-
VotePubkey: votePubKey,
51+
SelfPubkey: selfPubKey[:],
52+
VotePubkey: votePubKey[:],
5253
}
5354
return &Vote{pbVote}
5455
}
5556

57+
// SelfPublicKey returns the self public key of the vote
58+
func (v *Vote) SelfPublicKey() (keypair.PublicKey, error) {
59+
return keypair.BytesToPublicKey(v.SelfPubkey)
60+
}
61+
62+
// VotePublicKey returns the vote public key of the vote
63+
func (v *Vote) VotePublicKey() (keypair.PublicKey, error) {
64+
return keypair.BytesToPublicKey(v.VotePubkey)
65+
}
66+
5667
// TotalSize returns the total size of this Vote
5768
func (v *Vote) TotalSize() uint32 {
5869
size := TimestampSizeInBytes
@@ -86,16 +97,24 @@ func (v *Vote) ConvertToVotePb() *iproto.VotePb {
8697
}
8798

8899
// ToJSON converts Vote to VoteJSON
89-
func (v *Vote) ToJSON() *explorer.Vote {
100+
func (v *Vote) ToJSON() (*explorer.Vote, error) {
90101
// used by account-based model
102+
voterPubKey, err := keypair.BytesToPubKeyString(v.SelfPubkey)
103+
if err != nil {
104+
return nil, err
105+
}
106+
voteePubKey, err := keypair.BytesToPubKeyString(v.VotePubkey)
107+
if err != nil {
108+
return nil, err
109+
}
91110
vote := &explorer.Vote{
92111
Version: int64(v.Version),
93112
Nonce: int64(v.Nonce),
94-
VoterPubKey: hex.EncodeToString(v.SelfPubkey),
95-
VoteePubKey: hex.EncodeToString(v.VotePubkey),
113+
VoterPubKey: voterPubKey,
114+
VoteePubKey: voteePubKey,
96115
Signature: hex.EncodeToString(v.Signature),
97116
}
98-
return vote
117+
return vote, nil
99118
}
100119

101120
// Serialize returns a serialized byte stream for the Transfer
@@ -114,13 +133,13 @@ func NewVoteFromJSON(jsonVote *explorer.Vote) (*Vote, error) {
114133
v.Version = uint32(jsonVote.Version)
115134
// used by account-based model
116135
v.Nonce = uint64(jsonVote.Nonce)
117-
voterPubKey, err := hex.DecodeString(jsonVote.VoterPubKey)
136+
voterPubKey, err := keypair.StringToPubKeyBytes(jsonVote.VoterPubKey)
118137
if err != nil {
119138
logger.Error().Err(err).Msg("Fail to create a new Vote from VoteJSON")
120139
return nil, err
121140
}
122141
v.SelfPubkey = voterPubKey
123-
voteePubKey, err := hex.DecodeString(jsonVote.VoteePubKey)
142+
voteePubKey, err := keypair.StringToPubKeyBytes(jsonVote.VoteePubKey)
124143
if err != nil {
125144
logger.Error().Err(err).Msg("Fail to create a new Vote from VoteJSON")
126145
return nil, err
@@ -155,7 +174,7 @@ func (v *Vote) Hash() hash.Hash32B {
155174
// Sign signs the Vote using sender's private key
156175
func (v *Vote) Sign(sender *iotxaddress.Address) (*Vote, error) {
157176
// check the sender is correct
158-
if !bytes.Equal(v.SelfPubkey, sender.PublicKey) {
177+
if !bytes.Equal(v.SelfPubkey, sender.PublicKey[:]) {
159178
return nil, errors.Wrapf(ErrVoteError, "signing pubKey %x does not match with Vote pubKey %x",
160179
v.SelfPubkey, sender.PublicKey)
161180
}

blockchain/block.go

+14-13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/iotexproject/iotex-core/logger"
2121
"github.com/iotexproject/iotex-core/pkg/enc"
2222
"github.com/iotexproject/iotex-core/pkg/hash"
23+
"github.com/iotexproject/iotex-core/pkg/keypair"
2324
"github.com/iotexproject/iotex-core/pkg/version"
2425
"github.com/iotexproject/iotex-core/proto"
2526
)
@@ -33,15 +34,15 @@ type Payee struct {
3334
// BlockHeader defines the struct of block header
3435
// make sure the variable type and order of this struct is same as "BlockHeaderPb" in blockchain.pb.go
3536
type BlockHeader struct {
36-
version uint32 // version
37-
chainID uint32 // this chain's ID
38-
height uint64 // block height
39-
timestamp uint64 // timestamp
40-
prevBlockHash hash.Hash32B // hash of previous block
41-
txRoot hash.Hash32B // merkle root of all transactions
42-
stateRoot hash.Hash32B // merkle root of all states
43-
blockSig []byte // block signature
44-
Pubkey []byte // block miner's public key
37+
version uint32 // version
38+
chainID uint32 // this chain's ID
39+
height uint64 // block height
40+
timestamp uint64 // timestamp
41+
prevBlockHash hash.Hash32B // hash of previous block
42+
txRoot hash.Hash32B // merkle root of all transactions
43+
stateRoot hash.Hash32B // merkle root of all states
44+
blockSig []byte // block signature
45+
Pubkey keypair.PublicKey // block miner's public key
4546

4647
}
4748

@@ -98,7 +99,7 @@ func (b *Block) ByteStreamHeader() []byte {
9899
stream = append(stream, b.Header.prevBlockHash[:]...)
99100
stream = append(stream, b.Header.txRoot[:]...)
100101
stream = append(stream, b.Header.stateRoot[:]...)
101-
stream = append(stream, b.Header.Pubkey...)
102+
stream = append(stream, b.Header.Pubkey[:]...)
102103
return stream
103104
}
104105

@@ -168,7 +169,7 @@ func (b *Block) ConvertFromBlockHeaderPb(pbBlock *iproto.BlockPb) {
168169
copy(b.Header.txRoot[:], pbBlock.GetHeader().GetTxRoot())
169170
copy(b.Header.stateRoot[:], pbBlock.GetHeader().GetStateRoot())
170171
b.Header.blockSig = pbBlock.GetHeader().GetSignature()
171-
b.Header.Pubkey = pbBlock.GetHeader().GetPubkey()
172+
copy(b.Header.Pubkey[:], pbBlock.GetHeader().GetPubkey())
172173
}
173174

174175
// ConvertFromBlockPb converts BlockPb to Block
@@ -235,8 +236,8 @@ func (b *Block) HashBlock() hash.Hash32B {
235236

236237
// SignBlock allows signer to sign the block b
237238
func (b *Block) SignBlock(signer *iotxaddress.Address) error {
238-
if signer.PrivateKey == nil {
239-
return errors.New("The private key is nil")
239+
if signer.PrivateKey == keypair.ZeroPrivateKey {
240+
return errors.New("The private key is empty")
240241
}
241242
b.Header.Pubkey = signer.PublicKey
242243
blkHash := b.HashBlock()

blockchain/block_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,27 @@ func TestMerkle(t *testing.T) {
5858
require.NotNil(cbtsf4)
5959

6060
// verify tx hash
61-
hash0, _ := hex.DecodeString("38628817384f0c67ea3396529162e8d7cb4491c227c6a8eaaaf8828bc11614fd")
61+
hash0, _ := hex.DecodeString("f752082c2c52249ebe0faca08819745b5567cfecdf40ef76b2a3cc73b427874f")
6262
actual := cbtsf0.Hash()
6363
require.Equal(hash0, actual[:])
6464
t.Logf("actual hash = %x", actual[:])
6565

66-
hash1, _ := hex.DecodeString("ce5dd70b032614bd8e3d82b257b478eb49a4da90d531e7db6cd4eebc98ed149d")
66+
hash1, _ := hex.DecodeString("408a62a55eb9eaf6cebe37dd3816e0c9a367281540e8a3254f1b51e441b555d7")
6767
actual = cbtsf1.Hash()
6868
require.Equal(hash1, actual[:])
6969
t.Logf("actual hash = %x", actual[:])
7070

71-
hash2, _ := hex.DecodeString("164914612e8628e0de69ea4e4184e63d46439ceb46c46fc60a43b636277dc1e9")
71+
hash2, _ := hex.DecodeString("5170d22f3d357208849f6e0356cdc04f85c6ff73c9c5720f8e50c616440983d0")
7272
actual = cbtsf2.Hash()
7373
require.Equal(hash2, actual[:])
7474
t.Logf("actual hash = %x", actual[:])
7575

76-
hash3, _ := hex.DecodeString("73e0a24bb15687ffb98c254f617db7e7d59963f020de38b1a7c284ffde7dc8d5")
76+
hash3, _ := hex.DecodeString("8eda19d0e16cbb541f2c6251acd83d3847dd69ed9531b8a9335011cfc5ab26c0")
7777
actual = cbtsf3.Hash()
7878
require.Equal(hash3, actual[:])
7979
t.Logf("actual hash = %x", actual[:])
8080

81-
hash4, _ := hex.DecodeString("e1c72a310432a0040bedb40ee8ea885fc93258f19687e271a2e2c2fb62723474")
81+
hash4, _ := hex.DecodeString("5aa0107cb37e63b1276e889ab8e071cabf5611606560fd5e8d6a3786eec35265")
8282
actual = cbtsf4.Hash()
8383
require.Equal(hash4, actual[:])
8484
t.Logf("actual hash = %x", actual[:])

blockchain/blockchain.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/iotexproject/iotex-core/iotxaddress"
2121
"github.com/iotexproject/iotex-core/logger"
2222
"github.com/iotexproject/iotex-core/pkg/hash"
23+
"github.com/iotexproject/iotex-core/pkg/keypair"
2324
"github.com/iotexproject/iotex-core/pkg/lifecycle"
2425
"github.com/iotexproject/iotex-core/pkg/version"
2526
"github.com/iotexproject/iotex-core/state"
@@ -473,7 +474,7 @@ func (bc *blockchain) MintNewBlock(tsf []*action.Transfer, vote []*action.Vote,
473474
tsf = append(tsf, action.NewCoinBaseTransfer(big.NewInt(int64(bc.genesis.BlockReward)), producer.RawAddress))
474475

475476
blk := NewBlock(bc.chainID, bc.tipHeight+1, bc.tipHash, tsf, vote)
476-
if producer.PrivateKey == nil {
477+
if producer.PrivateKey == keypair.ZeroPrivateKey {
477478
logger.Warn().Msg("Unsigned block...")
478479
return blk, nil
479480
}

blockchain/blockdao.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -504,13 +504,21 @@ func putVotes(dao *blockDAO, blk *Block, batch db.KVStoreBatch) error {
504504
for _, vote := range blk.Votes {
505505
voteHash := vote.Hash()
506506

507-
SenderAddress, err := iotxaddress.GetAddress(vote.SelfPubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
507+
selfPublicKey, err := vote.SelfPublicKey()
508+
if err != nil {
509+
return err
510+
}
511+
SenderAddress, err := iotxaddress.GetAddress(selfPublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
508512
if err != nil {
509513
return errors.Wrapf(err, " to get sender address for pubkey %x", vote.SelfPubkey)
510514
}
511515
Sender := SenderAddress.RawAddress
512516

513-
RecipientAddress, err := iotxaddress.GetAddress(vote.VotePubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
517+
votePublicKey, err := vote.VotePublicKey()
518+
if err != nil {
519+
return err
520+
}
521+
RecipientAddress, err := iotxaddress.GetAddress(votePublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
514522
if err != nil {
515523
return errors.Wrapf(err, " to get recipient address for pubkey %x", vote.VotePubkey)
516524
}

blockchain/blockvalidator.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ func (v *validator) Validate(blk *Block, tipHeight uint64, tipHash hash.Hash32B)
123123
for _, vote := range blk.Votes {
124124
go func(vote *action.Vote, correctVote *uint64) {
125125
defer wg.Done()
126-
address, err := iotxaddress.GetAddress(vote.SelfPubkey, iotxaddress.IsTestnet, iotxaddress.ChainID)
126+
selfPublicKey, err := vote.SelfPublicKey()
127+
if err != nil {
128+
return
129+
}
130+
address, err := iotxaddress.GetAddress(selfPublicKey, iotxaddress.IsTestnet, iotxaddress.ChainID)
127131
if err != nil {
128132
return
129133
}

0 commit comments

Comments
 (0)