Skip to content

Commit 8717ccf

Browse files
rpc: fix the from address calculation (#4593)
* test: add delegation type tests These were skipped intentionally in the previous pull request * rpc: undo all changes to 2023.3.0 * rpc: calculate SenderAddress before `ConvertToEth` `tx.ConvertToEth` silently drops the `ShardID` and `ToShardID` fields. This results in the hash of the transaction changing (either via removal of these fields in the hash calculation or by automatic filling of the values by the node's shard). The different hash calculation results in incorrect sender address calculation. There have been a couple of attempts to fix this issue, which created troubles elsewhere. This pull request reverts to the behaviour seen in 2023.3.0 with a simple edit that calculates the `SenderAddress` before dropping the fields in the call to `ConvertToEth`.
1 parent dd65484 commit 8717ccf

File tree

6 files changed

+177
-84
lines changed

6 files changed

+177
-84
lines changed

Diff for: rpc/blockchain.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,11 @@ func (s *PublicBlockchainService) GetBlockReceipts(
464464
r, err = v2.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()])
465465
case Eth:
466466
if tx, ok := tx.(*types.Transaction); ok {
467-
r, err = v2.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()])
467+
from, err := tx.SenderAddress()
468+
if err != nil {
469+
return nil, err
470+
}
471+
r, err = eth.NewReceipt(from, tx.ConvertToEth(), blockHash, block.NumberU64(), index, rmap[tx.Hash()])
468472
}
469473
default:
470474
return nil, ErrUnknownRPCVersion

Diff for: rpc/eth/types.go

+15-65
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,9 @@ type Transaction struct {
7474
// representation, with the given location metadata set (if available).
7575
// Note that all txs on Harmony are replay protected (post EIP155 epoch).
7676
func NewTransaction(
77-
tx *types.EthTransaction, blockHash common.Hash,
77+
from common.Address, tx *types.EthTransaction, blockHash common.Hash,
7878
blockNumber uint64, timestamp uint64, index uint64,
7979
) (*Transaction, error) {
80-
from := common.Address{}
81-
var err error
82-
if tx.IsEthCompatible() {
83-
from, err = tx.SenderAddress()
84-
} else {
85-
from, err = tx.ConvertToHmy().SenderAddress()
86-
}
87-
if err != nil {
88-
return nil, err
89-
}
9080
v, r, s := tx.RawSignatureValues()
9181

9282
result := &Transaction{
@@ -111,59 +101,10 @@ func NewTransaction(
111101
return result, nil
112102
}
113103

114-
// NewReceipt returns the RPC data for a new receipt. It is unused at the moment.
115-
func NewReceipt(tx *types.EthTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt) (map[string]interface{}, error) {
116-
senderAddr, err := tx.SenderAddress()
117-
if err != nil {
118-
return nil, err
119-
}
120-
104+
// NewReceipt returns the RPC data for a new receipt
105+
func NewReceipt(senderAddr common.Address, tx *types.EthTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt) (map[string]interface{}, error) {
121106
ethTxHash := tx.Hash()
122-
for i, _ := range receipt.Logs {
123-
// Override log txHash with receipt's
124-
receipt.Logs[i].TxHash = ethTxHash
125-
}
126-
127-
fields := map[string]interface{}{
128-
"blockHash": blockHash,
129-
"blockNumber": hexutil.Uint64(blockNumber),
130-
"transactionHash": ethTxHash,
131-
"transactionIndex": hexutil.Uint64(blockIndex),
132-
"from": senderAddr,
133-
"to": tx.To(),
134-
"gasUsed": hexutil.Uint64(receipt.GasUsed),
135-
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
136-
"contractAddress": nil,
137-
"logs": receipt.Logs,
138-
"logsBloom": receipt.Bloom,
139-
}
140-
141-
// Assign receipt status or post state.
142-
if len(receipt.PostState) > 0 {
143-
fields["root"] = hexutil.Bytes(receipt.PostState)
144-
} else {
145-
fields["status"] = hexutil.Uint(receipt.Status)
146-
}
147-
if receipt.Logs == nil {
148-
fields["logs"] = [][]*types.Log{}
149-
}
150-
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
151-
if receipt.ContractAddress != (common.Address{}) {
152-
fields["contractAddress"] = receipt.ContractAddress
153-
}
154-
155-
return fields, nil
156-
}
157-
158-
// NewReceiptFromTransaction returns the RPC data for a new receipt. It is unused at the moment.
159-
func NewReceiptFromTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt) (map[string]interface{}, error) {
160-
senderAddr, err := tx.SenderAddress()
161-
if err != nil {
162-
return nil, err
163-
}
164-
165-
ethTxHash := tx.Hash()
166-
for i, _ := range receipt.Logs {
107+
for i := range receipt.Logs {
167108
// Override log txHash with receipt's
168109
receipt.Logs[i].TxHash = ethTxHash
169110
}
@@ -253,7 +194,11 @@ func blockWithFullTxFromBlock(b *types.Block) (*BlockWithFullTx, error) {
253194
}
254195

255196
for idx, tx := range b.Transactions() {
256-
fmtTx, err := NewTransaction(tx.ConvertToEth(), b.Hash(), b.NumberU64(), b.Time().Uint64(), uint64(idx))
197+
from, err := tx.SenderAddress()
198+
if err != nil {
199+
return nil, err
200+
}
201+
fmtTx, err := NewTransaction(from, tx.ConvertToEth(), b.Hash(), b.NumberU64(), b.Time().Uint64(), uint64(idx))
257202
if err != nil {
258203
return nil, err
259204
}
@@ -270,5 +215,10 @@ func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, e
270215
"tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(),
271216
)
272217
}
273-
return NewTransaction(txs[index].ConvertToEth(), b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
218+
tx := txs[index].ConvertToEth()
219+
from, err := tx.SenderAddress()
220+
if err != nil {
221+
return nil, err
222+
}
223+
return NewTransaction(from, tx, b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
274224
}

Diff for: rpc/pool.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,14 @@ func (s *PublicPoolService) PendingTransactions(
253253
continue // Legacy behavior is to not return error here
254254
}
255255
case Eth:
256-
tx, err = eth.NewTransaction(plainTx.ConvertToEth(), common.Hash{}, 0, 0, 0)
256+
from, err := plainTx.SenderAddress()
257+
if err != nil {
258+
utils.Logger().Debug().
259+
Err(err).
260+
Msgf("%v error at %v", LogTag, "PendingTransactions")
261+
continue // Legacy behavior is to not return error here
262+
}
263+
tx, err = eth.NewTransaction(from, plainTx.ConvertToEth(), common.Hash{}, 0, 0, 0)
257264
if err != nil {
258265
utils.Logger().Debug().
259266
Err(err).

Diff for: rpc/transaction.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,13 @@ func (s *PublicTransactionService) newRPCTransaction(tx *types.Transaction, bloc
236236
}
237237
return NewStructuredResponse(tx)
238238
case Eth:
239-
tx, err := eth.NewTransaction(tx.ConvertToEth(), blockHash, blockNumber, timestamp, index)
239+
// calculate SenderAddress before ConvertToEth
240+
senderAddr, err := tx.SenderAddress()
241+
if err != nil {
242+
DoMetricRPCQueryInfo(GetTransactionByHash, FailedNumber)
243+
return nil, err
244+
}
245+
tx, err := eth.NewTransaction(senderAddr, tx.ConvertToEth(), blockHash, blockNumber, timestamp, index)
240246
if err != nil {
241247
DoMetricRPCQueryInfo(GetTransactionByHash, FailedNumber)
242248
return nil, err
@@ -763,7 +769,12 @@ func (s *PublicTransactionService) GetTransactionReceipt(
763769
return NewStructuredResponse(RPCReceipt)
764770
case Eth:
765771
if tx != nil {
766-
RPCReceipt, err = eth.NewReceiptFromTransaction(tx, blockHash, blockNumber, index, receipt)
772+
// calculate SenderAddress before ConvertToEth
773+
senderAddr, err := tx.SenderAddress()
774+
if err != nil {
775+
return nil, err
776+
}
777+
RPCReceipt, err = eth.NewReceipt(senderAddr, tx.ConvertToEth(), blockHash, blockNumber, index, receipt)
767778
}
768779
if err != nil {
769780
return nil, err

Diff for: rpc/v2/types.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -204,20 +204,20 @@ type UndelegateMsg struct {
204204

205205
// TxReceipt represents a transaction receipt that will serialize to the RPC representation.
206206
type TxReceipt struct {
207-
BlockHash common.Hash `json:"blockHash"`
208-
TransactionHash common.Hash `json:"transactionHash"`
209-
BlockNumber uint64 `json:"blockNumber"`
210-
TransactionIndex uint64 `json:"transactionIndex"`
211-
GasUsed uint64 `json:"gasUsed"`
212-
CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
213-
ContractAddress *common.Address `json:"contractAddress"`
214-
Logs []*types.Log `json:"logs"`
215-
LogsBloom ethtypes.Bloom `json:"logsBloom"`
216-
ShardID uint32 `json:"shardID"`
217-
From string `json:"from"`
218-
To string `json:"to"`
219-
Root hexutil.Bytes `json:"root"`
220-
Status uint `json:"status"`
207+
BlockHash common.Hash `json:"blockHash"`
208+
TransactionHash common.Hash `json:"transactionHash"`
209+
BlockNumber uint64 `json:"blockNumber"`
210+
TransactionIndex uint64 `json:"transactionIndex"`
211+
GasUsed uint64 `json:"gasUsed"`
212+
CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
213+
ContractAddress common.Address `json:"contractAddress"`
214+
Logs []*types.Log `json:"logs"`
215+
LogsBloom ethtypes.Bloom `json:"logsBloom"`
216+
ShardID uint32 `json:"shardID"`
217+
From string `json:"from"`
218+
To string `json:"to"`
219+
Root hexutil.Bytes `json:"root"`
220+
Status uint `json:"status"`
221221
}
222222

223223
// StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation.
@@ -362,6 +362,7 @@ func NewTxReceipt(
362362
sender = senderAddr.String()
363363
receiver = ""
364364
} else {
365+
// Handle response type for regular transaction
365366
sender, err = internal_common.AddressToBech32(senderAddr)
366367
if err != nil {
367368
return nil, err
@@ -403,7 +404,7 @@ func NewTxReceipt(
403404

404405
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
405406
if receipt.ContractAddress != (common.Address{}) {
406-
txReceipt.ContractAddress = &receipt.ContractAddress
407+
txReceipt.ContractAddress = receipt.ContractAddress
407408
}
408409
return txReceipt, nil
409410
}

Diff for: staking/types/delegation_test.go

+120
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,123 @@ func TestNoEarlyUnlock(t *testing.T) {
166166
t.Errorf("should not allow early unlock")
167167
}
168168
}
169+
170+
func TestMaxRateAtLess(t *testing.T) {
171+
// recreate it so that all tests can run
172+
delegation := NewDelegation(delegatorAddr, delegationAmt)
173+
lastEpochInCommittee := big.NewInt(1)
174+
curEpoch := big.NewInt(27)
175+
epoch := big.NewInt(21)
176+
amount := big.NewInt(4000)
177+
delegation.Undelegate(epoch, amount)
178+
initialLength := len(delegation.Undelegations)
179+
180+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, true)
181+
if result.Cmp(big.NewInt(0)) != 0 {
182+
t.Errorf("should not allow unlock before 7")
183+
}
184+
finalLength := len(delegation.Undelegations)
185+
if initialLength != finalLength {
186+
t.Errorf("should not remove undelegations before 7")
187+
}
188+
}
189+
190+
func TestMaxRateAtEqual(t *testing.T) {
191+
// recreate it so that all tests can run
192+
delegation := NewDelegation(delegatorAddr, delegationAmt)
193+
lastEpochInCommittee := big.NewInt(1)
194+
curEpoch := big.NewInt(28)
195+
epoch := big.NewInt(21)
196+
amount := big.NewInt(4000)
197+
delegation.Undelegate(epoch, amount)
198+
initialLength := len(delegation.Undelegations)
199+
200+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, true)
201+
if result.Cmp(big.NewInt(4000)) != 0 {
202+
t.Errorf("should withdraw at 7")
203+
}
204+
finalLength := len(delegation.Undelegations)
205+
if initialLength == finalLength {
206+
t.Errorf("should remove undelegations at 7")
207+
}
208+
}
209+
210+
func TestMaxRateAtExcess(t *testing.T) {
211+
// recreate it so that all tests can run
212+
delegation := NewDelegation(delegatorAddr, delegationAmt)
213+
lastEpochInCommittee := big.NewInt(1)
214+
curEpoch := big.NewInt(29)
215+
epoch := big.NewInt(21)
216+
amount := big.NewInt(4000)
217+
delegation.Undelegate(epoch, amount)
218+
initialLength := len(delegation.Undelegations)
219+
220+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, true)
221+
if result.Cmp(big.NewInt(0)) != 0 {
222+
t.Errorf("should not withdraw at 8")
223+
}
224+
finalLength := len(delegation.Undelegations)
225+
if initialLength == finalLength {
226+
t.Errorf("should remove undelegations at 8")
227+
}
228+
}
229+
230+
func TestNoMaxRateAtLess(t *testing.T) {
231+
// recreate it so that all tests can run
232+
delegation := NewDelegation(delegatorAddr, delegationAmt)
233+
lastEpochInCommittee := big.NewInt(1)
234+
curEpoch := big.NewInt(27)
235+
epoch := big.NewInt(21)
236+
amount := big.NewInt(4000)
237+
delegation.Undelegate(epoch, amount)
238+
initialLength := len(delegation.Undelegations)
239+
240+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, false)
241+
if result.Cmp(big.NewInt(0)) != 0 {
242+
t.Errorf("should not allow unlock before 7")
243+
}
244+
finalLength := len(delegation.Undelegations)
245+
if initialLength != finalLength {
246+
t.Errorf("should not remove undelegations before 7")
247+
}
248+
}
249+
250+
func TestNoMaxRateAtEqual(t *testing.T) {
251+
// recreate it so that all tests can run
252+
delegation := NewDelegation(delegatorAddr, delegationAmt)
253+
lastEpochInCommittee := big.NewInt(1)
254+
curEpoch := big.NewInt(28)
255+
epoch := big.NewInt(21)
256+
amount := big.NewInt(4000)
257+
delegation.Undelegate(epoch, amount)
258+
initialLength := len(delegation.Undelegations)
259+
260+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, false)
261+
if result.Cmp(big.NewInt(4000)) != 0 {
262+
t.Errorf("should withdraw at 7")
263+
}
264+
finalLength := len(delegation.Undelegations)
265+
if initialLength == finalLength {
266+
t.Errorf("should remove undelegations at 7")
267+
}
268+
}
269+
270+
func TestNoMaxRateAtExcess(t *testing.T) {
271+
// recreate it so that all tests can run
272+
delegation := NewDelegation(delegatorAddr, delegationAmt)
273+
lastEpochInCommittee := big.NewInt(1)
274+
curEpoch := big.NewInt(29)
275+
epoch := big.NewInt(21)
276+
amount := big.NewInt(4000)
277+
delegation.Undelegate(epoch, amount)
278+
initialLength := len(delegation.Undelegations)
279+
280+
result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, false)
281+
if result.Cmp(big.NewInt(4000)) != 0 {
282+
t.Errorf("should withdraw at 8")
283+
}
284+
finalLength := len(delegation.Undelegations)
285+
if initialLength == finalLength {
286+
t.Errorf("should remove undelegations at 8")
287+
}
288+
}

0 commit comments

Comments
 (0)