Skip to content

Commit 65e9a39

Browse files
committed
fix: updated the sourceAddressRoot calculation
1 parent d7fe6c6 commit 65e9a39

4 files changed

Lines changed: 218 additions & 10 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Run the tests with:
9090
The test need a
9191

9292
```
93-
go test ./...
93+
go test -v ./...
9494
```
9595

9696
## Release process (if applicable)

internal/xrp/transaction.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,28 @@ func (tx *transaction) sourceAddressesRoot() (string, error) {
105105

106106
sourceAddresses := make([]common.Hash, 0, 1)
107107
for _, node := range meta.AffectedNodes {
108-
modifiedNode := node.ModifiedNode
109-
if modifiedNode.LedgerEntryType != "AccountRoot" || modifiedNode.FinalFields.Account == "" {
108+
// ModifiedNode and DeletedNode share the same structure and can both
109+
// represent source addresses with decreased balances. CreatedNode entries
110+
// only have increased balances (from zero) and are not source addresses.
111+
var change nodeChange
112+
switch {
113+
case node.ModifiedNode.LedgerEntryType != "":
114+
change = node.ModifiedNode
115+
case node.DeletedNode.LedgerEntryType != "":
116+
change = node.DeletedNode
117+
default:
118+
continue
119+
}
120+
121+
if change.LedgerEntryType != "AccountRoot" || change.FinalFields.Account == "" {
110122
continue
111123
}
112124

113125
var balance string
114126
finalVal := big.NewInt(0)
115127
var check bool
116-
if len(modifiedNode.FinalFields.Balance) > 0 {
117-
err = json.Unmarshal(modifiedNode.FinalFields.Balance, &balance)
128+
if len(change.FinalFields.Balance) > 0 {
129+
err = json.Unmarshal(change.FinalFields.Balance, &balance)
118130
if err != nil {
119131
return "", fmt.Errorf("unable to unmarshal final balance: %w", err)
120132
}
@@ -125,8 +137,8 @@ func (tx *transaction) sourceAddressesRoot() (string, error) {
125137
}
126138

127139
previousVal := big.NewInt(0)
128-
if len(modifiedNode.PreviousFields.Balance) > 0 {
129-
err = json.Unmarshal(modifiedNode.PreviousFields.Balance, &balance)
140+
if len(change.PreviousFields.Balance) > 0 {
141+
err = json.Unmarshal(change.PreviousFields.Balance, &balance)
130142
if err != nil {
131143
return "", fmt.Errorf("unable to unmarshal previous balance: %w", err)
132144
}
@@ -138,7 +150,7 @@ func (tx *transaction) sourceAddressesRoot() (string, error) {
138150

139151
diff := new(big.Int).Sub(finalVal, previousVal)
140152
if diff.Cmp(big.NewInt(0)) < 0 {
141-
hashedAddress := crypto.Keccak256Hash(crypto.Keccak256Hash([]byte(modifiedNode.FinalFields.Account)).Bytes())
153+
hashedAddress := crypto.Keccak256Hash(crypto.Keccak256Hash([]byte(change.FinalFields.Account)).Bytes())
142154
sourceAddresses = append(sourceAddresses, hashedAddress)
143155
}
144156
}

internal/xrp/transaction_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package xrp
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestSourceAddressesRoot(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
metaData string
14+
expected string
15+
}{
16+
{
17+
// Real mainnet tx 72F485775B8BE0E5E0F398CB29BAD007EE9E29E450649778FCED98E735BFAE44
18+
// Payment that creates (funds) a new account.
19+
// Sender rnJrjec2vrTJAAQUTMTjj7U6xdXrk9N4mT balance decreased (ModifiedNode).
20+
// Receiver r3iE6gLEVq6XXN8ddPWxffcYKJLxG2tC51 created with balance 15000000 (CreatedNode).
21+
name: "fund account",
22+
metaData: `{
23+
"AffectedNodes": [
24+
{
25+
"ModifiedNode": {
26+
"FinalFields": {
27+
"Account": "rnJrjec2vrTJAAQUTMTjj7U6xdXrk9N4mT",
28+
"Balance": "38695141402626",
29+
"Flags": 0,
30+
"OwnerCount": 0,
31+
"Sequence": 99628112
32+
},
33+
"LedgerEntryType": "AccountRoot",
34+
"LedgerIndex": "32CC7D5F026163A313EF59E0A610D136F9D66DFE6B5F7C926C68790C384F1C4C",
35+
"PreviousFields": {
36+
"Balance": "38695156408626",
37+
"Sequence": 99628111
38+
},
39+
"PreviousTxnID": "17C7E445DA7EFE3F5FCF67AE6270E29AD4FF147BFB6C3527F370EAEB9DC00C47",
40+
"PreviousTxnLgrSeq": 103489700
41+
}
42+
},
43+
{
44+
"CreatedNode": {
45+
"LedgerEntryType": "AccountRoot",
46+
"LedgerIndex": "71BED8C87259463266008FE80FAF3A0B89EB3577C7DD474173A1C8F6142572C2",
47+
"NewFields": {
48+
"Account": "r3iE6gLEVq6XXN8ddPWxffcYKJLxG2tC51",
49+
"Balance": "15000000",
50+
"Sequence": 103489700
51+
}
52+
}
53+
}
54+
],
55+
"TransactionIndex": 57,
56+
"TransactionResult": "tesSUCCESS"
57+
}`,
58+
expected: "e9f8d46cc405736e946e2e299ae9106ef494b7454f802028be9fe665398e69e3",
59+
},
60+
{
61+
// Real mainnet tx 3C2B0D8C1938271D9A95E657DD7B3D38FE77E84338AB63DFFF5A6079D944EFD0
62+
// AccountDelete that removes rGbHCiVTbyFH9gJ4mJCPXgLgjpgnTjTeko.
63+
// Destination rwn3XsPQcJcEASeT4fMUditHPrFnUDU83t balance increased (ModifiedNode).
64+
// Deleted account rGbHCiVTbyFH9gJ4mJCPXgLgjpgnTjTeko balance 1999980 -> 0 (DeletedNode).
65+
name: "account delete",
66+
metaData: `{
67+
"AffectedNodes": [
68+
{
69+
"ModifiedNode": {
70+
"FinalFields": {
71+
"Account": "rwn3XsPQcJcEASeT4fMUditHPrFnUDU83t",
72+
"Balance": "503599950",
73+
"Flags": 0,
74+
"OwnerCount": 23,
75+
"Sequence": 92601466
76+
},
77+
"LedgerEntryType": "AccountRoot",
78+
"LedgerIndex": "980E43BF431AC9B314AB1BB76A1E038E88B35051214FA2282ADB78D9913E4ABE",
79+
"PreviousFields": {
80+
"Balance": "501799970"
81+
},
82+
"PreviousTxnID": "F8AD2B7752FEE1E9877AE65CDE5446C8DEB2B2CD51A177050D446C9CDAFB58EC",
83+
"PreviousTxnLgrSeq": 103489665
84+
}
85+
},
86+
{
87+
"DeletedNode": {
88+
"FinalFields": {
89+
"Account": "rGbHCiVTbyFH9gJ4mJCPXgLgjpgnTjTeko",
90+
"Balance": "0",
91+
"Flags": 536870912,
92+
"OwnerCount": 0,
93+
"PreviousTxnID": "66FDFD95D19BD9414DED96C1B7884839121D55AD1EDF116228AF4EC556C687D5",
94+
"PreviousTxnLgrSeq": 103488743,
95+
"Sequence": 103488586
96+
},
97+
"LedgerEntryType": "AccountRoot",
98+
"LedgerIndex": "9EBD91EE34022E785E3679CA1116D18353484D9CCAB63EAC878048FDB1650BE5",
99+
"PreviousFields": {
100+
"Balance": "1999980",
101+
"Sequence": 103488585
102+
}
103+
}
104+
}
105+
],
106+
"TransactionIndex": 56,
107+
"TransactionResult": "tesSUCCESS"
108+
}`,
109+
expected: "97f123575f0fa54abec71fbc3740802261b15252e51f0051b11e39dec51c5602",
110+
},
111+
{
112+
// Real mainnet tx C94C88F70799DE3DEA6C5ECD65729EBAA5A6DC086D38E4BD40CA236A508BF056
113+
// Regular payment between two existing accounts.
114+
// Sender rpKrHjNFVPLbnvQj7WZiawLc6QKkeyMsau balance decreased (ModifiedNode).
115+
// Receiver ryouhapPYV5KNHmFUKrjNqsjxhnxvQiVt balance increased (ModifiedNode).
116+
name: "regular payment",
117+
metaData: `{
118+
"AffectedNodes": [
119+
{
120+
"ModifiedNode": {
121+
"FinalFields": {
122+
"Account": "ryouhapPYV5KNHmFUKrjNqsjxhnxvQiVt",
123+
"Balance": "2532322665",
124+
"Domain": "78616D616E2E617070",
125+
"EmailHash": "5B0F31F3678A837376CD437DAA2FCC77",
126+
"Flags": 1048576,
127+
"OwnerCount": 81,
128+
"RegularKey": "r33MBTjz1hzaZSsyvRwHRmaMn9S5xpp9w6",
129+
"Sequence": 80617032,
130+
"TicketCount": 50
131+
},
132+
"LedgerEntryType": "AccountRoot",
133+
"LedgerIndex": "9185B96A67C38FC7BBA39BC44BB6CC9C19DBAE20778A2E3BBB0EBB60BE5F7E0E",
134+
"PreviousFields": {
135+
"Balance": "2532272615"
136+
},
137+
"PreviousTxnID": "9B265D9B1849D197391D0AB667248C1BE7FF531B2C15F5ECFEEF7C2793CB4CD2",
138+
"PreviousTxnLgrSeq": 103490053
139+
}
140+
},
141+
{
142+
"ModifiedNode": {
143+
"FinalFields": {
144+
"Account": "rpKrHjNFVPLbnvQj7WZiawLc6QKkeyMsau",
145+
"Balance": "70977048",
146+
"Flags": 0,
147+
"OwnerCount": 4,
148+
"Sequence": 102610436
149+
},
150+
"LedgerEntryType": "AccountRoot",
151+
"LedgerIndex": "961A230EEDA5BFF71046208002265EF37AD05F481EA1A210DCC0C86713106E1C",
152+
"PreviousFields": {
153+
"Balance": "71027110",
154+
"Sequence": 102610435
155+
},
156+
"PreviousTxnID": "DA6253D7825FA8C3E966624B45422B093C82006CA9C5057C179D9DFE68694884",
157+
"PreviousTxnLgrSeq": 103490056
158+
}
159+
}
160+
],
161+
"TransactionIndex": 0,
162+
"TransactionResult": "tesSUCCESS"
163+
}`,
164+
expected: "2f0cdffefa7f270919a31d1898cb2129eaa255ac75ed22d89155466f93254c1b",
165+
},
166+
{
167+
name: "no affected nodes",
168+
metaData: `{
169+
"AffectedNodes": [],
170+
"TransactionResult": "tesSUCCESS"
171+
}`,
172+
expected: "",
173+
},
174+
}
175+
176+
for _, tt := range tests {
177+
t.Run(tt.name, func(t *testing.T) {
178+
tx := transaction{MetaData: json.RawMessage(tt.metaData)}
179+
result, err := tx.sourceAddressesRoot()
180+
require.NoError(t, err)
181+
require.Equal(t, tt.expected, result)
182+
})
183+
}
184+
}

internal/xrp/types.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,27 @@ type meta struct {
4141
}
4242

4343
type affectedNodes struct {
44-
ModifiedNode modifiedNode `json:"ModifiedNode"`
44+
ModifiedNode nodeChange `json:"ModifiedNode"`
45+
CreatedNode createdNode `json:"CreatedNode"`
46+
DeletedNode nodeChange `json:"DeletedNode"`
4547
}
4648

47-
type modifiedNode struct {
49+
// nodeChange represents a ModifiedNode or DeletedNode entry in transaction
50+
// metadata. Both share the same structure with FinalFields and PreviousFields.
51+
type nodeChange struct {
4852
FinalFields fields `json:"FinalFields"`
4953
PreviousFields fields `json:"PreviousFields"`
5054
LedgerEntryType string `json:"LedgerEntryType"`
5155
}
5256

57+
// createdNode represents a CreatedNode entry in transaction metadata.
58+
// Unlike modified and deleted nodes, it uses NewFields instead of
59+
// FinalFields/PreviousFields.
60+
type createdNode struct {
61+
NewFields fields `json:"NewFields"`
62+
LedgerEntryType string `json:"LedgerEntryType"`
63+
}
64+
5365
type fields struct {
5466
Account string `json:"Account"`
5567
Balance json.RawMessage `json:"Balance"`

0 commit comments

Comments
 (0)