diff --git a/action/action_deserializer_test.go b/action/action_deserializer_test.go index ca97cef043..ed6c49a399 100644 --- a/action/action_deserializer_test.go +++ b/action/action_deserializer_test.go @@ -10,6 +10,7 @@ import ( "encoding/hex" "testing" + "github.com/iotexproject/go-pkgs/hash" "github.com/iotexproject/iotex-proto/golang/iotextypes" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -34,8 +35,12 @@ func TestActionDeserializer(t *testing.T) { r.Equal(_signByte, se.Signature()) r.Zero(se.Encoding()) + // use valid signature and reset se.Hash se.signature = _validSig + se.hash = hash.ZeroHash256 + se.Hash() se1, err := (&Deserializer{}).ActionToSealedEnvelope(se.Proto()) + se1.Hash() r.NoError(err) r.Equal(se, se1) } diff --git a/action/sealedenvelope.go b/action/sealedenvelope.go index c586c9ab76..1527a56604 100644 --- a/action/sealedenvelope.go +++ b/action/sealedenvelope.go @@ -5,6 +5,7 @@ import ( "github.com/iotexproject/go-pkgs/crypto" "github.com/iotexproject/go-pkgs/hash" + "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-proto/golang/iotextypes" "github.com/pkg/errors" "go.uber.org/zap" @@ -22,6 +23,8 @@ type SealedEnvelope struct { evmNetworkID uint32 srcPubkey crypto.PublicKey signature []byte + srcAddress address.Address + hash hash.Hash256 } // envelopeHash returns the raw hash of embedded Envelope (this is the hash to be signed) @@ -48,6 +51,17 @@ func (sealed *SealedEnvelope) envelopeHash() (hash.Hash256, error) { // Hash returns the hash value of SealedEnvelope. // an all-0 return value means the transaction is invalid func (sealed *SealedEnvelope) Hash() (hash.Hash256, error) { + if sealed.hash == hash.ZeroHash256 { + hashVal, hashErr := sealed.calcHash() + if hashErr == nil { + sealed.hash = hashVal + } + return sealed.hash, hashErr + } + return sealed.hash, nil +} + +func (sealed *SealedEnvelope) calcHash() (hash.Hash256, error) { switch sealed.encoding { case iotextypes.Encoding_ETHEREUM_RLP: act, ok := sealed.Action().(EthCompatibleAction) @@ -69,6 +83,14 @@ func (sealed *SealedEnvelope) Hash() (hash.Hash256, error) { // SrcPubkey returns the source public key func (sealed *SealedEnvelope) SrcPubkey() crypto.PublicKey { return sealed.srcPubkey } +// SenderAddress returns address of the source public key +func (sealed *SealedEnvelope) SenderAddress() address.Address { + if sealed.srcAddress == nil { + sealed.srcAddress = sealed.srcPubkey.Address() + } + return sealed.srcAddress +} + // Signature returns signature bytes func (sealed *SealedEnvelope) Signature() []byte { sig := make([]byte, len(sealed.signature)) @@ -141,6 +163,8 @@ func (sealed *SealedEnvelope) LoadProto(pbAct *iotextypes.Action) error { sealed.signature = make([]byte, sigSize) copy(sealed.signature, pbAct.GetSignature()) sealed.encoding = encoding + sealed.hash = hash.ZeroHash256 + sealed.srcAddress = nil return nil } diff --git a/action/sealedenvelope_test.go b/action/sealedenvelope_test.go index 850cf0b425..7358de6de7 100644 --- a/action/sealedenvelope_test.go +++ b/action/sealedenvelope_test.go @@ -156,7 +156,6 @@ func TestSealedEnvelope_Proto(t *testing.T) { req.Contains(se2.LoadProto(se.Proto()).Error(), v.err) } - se.signature = _validSig for _, v := range []struct { enc iotextypes.Encoding hash string @@ -164,17 +163,22 @@ func TestSealedEnvelope_Proto(t *testing.T) { {0, "0562e100b057804ee3cb4fa906a897852aa8075013a02ef1e229360f1e5ee339"}, {1, "d5dc789026c12cc69f1ea7997fbe0aa1bcc02e85176848c7b2ecf4da6b4560d0"}, } { + se, err = createSealedEnvelope(0) + se.signature = _validSig se.encoding = v.enc req.NoError(se2.LoadProto(se.Proto())) if v.enc > 0 { se.evmNetworkID = config.EVMNetworkID() } + h, _ := se.Hash() + req.Equal(v.hash, hex.EncodeToString(h[:])) + se.SenderAddress() + _, _ = se2.Hash() + se2.SenderAddress() req.Equal(se, se2) tsf2, ok := se2.Envelope.Action().(*Transfer) req.True(ok) req.Equal(tsf, tsf2) - h, _ := se.Hash() - req.Equal(v.hash, hex.EncodeToString(h[:])) } } diff --git a/actpool/actpool_test.go b/actpool/actpool_test.go index 4f7e5fd5d4..8a6ab1a765 100644 --- a/actpool/actpool_test.go +++ b/actpool/actpool_test.go @@ -902,7 +902,15 @@ func TestActPool_GetUnconfirmedActs(t *testing.T) { require.Equal([]action.SealedEnvelope(nil), acts) acts = ap.GetUnconfirmedActs(_addr1) - require.Equal([]action.SealedEnvelope{tsf1, tsf3, tsf4, tsf5}, acts) + validated := []action.SealedEnvelope{tsf1, tsf3, tsf4, tsf5} + require.Equal(len(acts), len(validated)) + for i := 0; i < len(acts); i++ { + hashVal1, hashErr1 := validated[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := acts[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } } func TestActPool_GetActionByHash(t *testing.T) { @@ -1089,7 +1097,15 @@ func TestActPool_SpeedUpAction(t *testing.T) { appliedActionList = append(appliedActionList, bestAction) } // tsf1 is replaced by tsf3 with higher gas price - require.Equal(appliedActionList, []action.SealedEnvelope{tsf3, tsf2}) + validated := []action.SealedEnvelope{tsf3, tsf2} + require.Equal(len(appliedActionList), len(validated)) + for i := 0; i < len(appliedActionList); i++ { + hashVal1, hashErr1 := validated[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := appliedActionList[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } } // Helper function to return the correct pending nonce just in case of empty queue diff --git a/blockchain/blockdao/blockdao_test.go b/blockchain/blockdao/blockdao_test.go index c2e92be90c..42d6caecb9 100644 --- a/blockchain/blockdao/blockdao_test.go +++ b/blockchain/blockdao/blockdao_test.go @@ -203,10 +203,24 @@ func TestBlockDAO(t *testing.T) { require.Equal(tipBlk.Height(), height) blk, err := dao.GetBlock(hash) require.NoError(err) - require.Equal(tipBlk, blk) + require.Equal(len(blk.Actions), len(tipBlk.Actions)) + for i := 0; i < len(blk.Actions); i++ { + hashVal1, hashErr1 := blk.Actions[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := tipBlk.Actions[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } blk, err = dao.GetBlockByHeight(height) require.NoError(err) - require.Equal(tipBlk, blk) + require.Equal(len(blk.Actions), len(tipBlk.Actions)) + for i := 0; i < len(blk.Actions); i++ { + hashVal1, hashErr1 := blk.Actions[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := tipBlk.Actions[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } r, err := dao.GetReceipts(height) require.NoError(err) require.Equal(len(receipts[i]), len(r)) @@ -329,10 +343,24 @@ func TestBlockDAO(t *testing.T) { require.Equal(tipHeight, height) blk, err := dao.GetBlock(h) require.NoError(err) - require.Equal(tipBlk, blk) + require.Equal(len(blk.Actions), len(tipBlk.Actions)) + for i := 0; i < len(blk.Actions); i++ { + hashVal1, hashErr1 := blk.Actions[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := tipBlk.Actions[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } blk, err = dao.GetBlockByHeight(height) require.NoError(err) - require.Equal(tipBlk, blk) + require.Equal(len(blk.Actions), len(tipBlk.Actions)) + for i := 0; i < len(blk.Actions); i++ { + hashVal1, hashErr1 := blk.Actions[i].Hash() + require.NoError(hashErr1) + hashVal2, hashErr2 := tipBlk.Actions[i].Hash() + require.NoError(hashErr2) + require.Equal(hashVal1, hashVal2) + } // test BlockDAO's API, 2nd loop to test LRU cache for i := 0; i < 2; i++ {