Skip to content

Commit 07d197a

Browse files
authored
xdrill for ledgerCloseMeta (#5568)
1 parent bd7c1ad commit 07d197a

File tree

5 files changed

+384
-0
lines changed

5 files changed

+384
-0
lines changed

ingest/ledger/ledger.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package ledger
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"time"
7+
8+
"github.com/stellar/go/xdr"
9+
)
10+
11+
func Sequence(l xdr.LedgerCloseMeta) uint32 {
12+
return uint32(l.LedgerHeaderHistoryEntry().Header.LedgerSeq)
13+
}
14+
15+
func Hash(l xdr.LedgerCloseMeta) string {
16+
return l.LedgerHeaderHistoryEntry().Hash.HexString()
17+
}
18+
19+
func PreviousHash(l xdr.LedgerCloseMeta) string {
20+
return l.PreviousLedgerHash().HexString()
21+
}
22+
23+
func CloseTime(l xdr.LedgerCloseMeta) int64 {
24+
return l.LedgerCloseTime()
25+
}
26+
27+
func ClosedAt(l xdr.LedgerCloseMeta) time.Time {
28+
return time.Unix(l.LedgerCloseTime(), 0).UTC()
29+
}
30+
31+
func TotalCoins(l xdr.LedgerCloseMeta) int64 {
32+
return int64(l.LedgerHeaderHistoryEntry().Header.TotalCoins)
33+
}
34+
35+
func FeePool(l xdr.LedgerCloseMeta) int64 {
36+
return int64(l.LedgerHeaderHistoryEntry().Header.FeePool)
37+
}
38+
39+
func BaseFee(l xdr.LedgerCloseMeta) uint32 {
40+
return uint32(l.LedgerHeaderHistoryEntry().Header.BaseFee)
41+
}
42+
43+
func BaseReserve(l xdr.LedgerCloseMeta) uint32 {
44+
return uint32(l.LedgerHeaderHistoryEntry().Header.BaseReserve)
45+
}
46+
47+
func MaxTxSetSize(l xdr.LedgerCloseMeta) uint32 {
48+
return uint32(l.LedgerHeaderHistoryEntry().Header.MaxTxSetSize)
49+
}
50+
51+
func LedgerVersion(l xdr.LedgerCloseMeta) uint32 {
52+
return uint32(l.LedgerHeaderHistoryEntry().Header.LedgerVersion)
53+
}
54+
55+
func SorobanFeeWrite1Kb(l xdr.LedgerCloseMeta) (int64, bool) {
56+
lcmV1, ok := l.GetV1()
57+
if !ok {
58+
return 0, false
59+
}
60+
61+
extV1, ok := lcmV1.Ext.GetV1()
62+
if !ok {
63+
return 0, false
64+
}
65+
66+
return int64(extV1.SorobanFeeWrite1Kb), true
67+
}
68+
69+
func TotalByteSizeOfBucketList(l xdr.LedgerCloseMeta) (uint64, bool) {
70+
lcmV1, ok := l.GetV1()
71+
if !ok {
72+
return 0, false
73+
}
74+
75+
return uint64(lcmV1.TotalByteSizeOfBucketList), true
76+
}
77+
78+
func NodeID(l xdr.LedgerCloseMeta) (string, error) {
79+
LedgerCloseValueSignature, ok := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature()
80+
if !ok {
81+
return "", fmt.Errorf("could not get LedgerCloseValueSignature")
82+
83+
}
84+
return LedgerCloseValueSignature.NodeId.GetAddress()
85+
}
86+
87+
func Signature(l xdr.LedgerCloseMeta) (string, bool) {
88+
LedgerCloseValueSignature, ok := l.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature()
89+
if !ok {
90+
return "", false
91+
}
92+
93+
return base64.StdEncoding.EncodeToString(LedgerCloseValueSignature.Signature), true
94+
}
95+
96+
// TransactionCounts calculates and returns the number of successful and total transactions
97+
func TransactionCounts(l xdr.LedgerCloseMeta) (successTxCount, totalTxCount uint32) {
98+
transactions := l.TransactionEnvelopes()
99+
results, err := l.TxProcessing()
100+
if err != nil {
101+
panic(err)
102+
}
103+
104+
txCount := len(transactions)
105+
if txCount != len(results) {
106+
panic("transaction count and number of TransactionResultMeta not equal")
107+
}
108+
109+
for i := 0; i < txCount; i++ {
110+
if results[i].Result.Successful() {
111+
successTxCount++
112+
}
113+
}
114+
115+
return successTxCount, uint32(txCount)
116+
}
117+
118+
// OperationCounts calculates and returns the number of successful operations and the total operations within
119+
// a LedgerCloseMeta
120+
func OperationCounts(l xdr.LedgerCloseMeta) (successfulOperationCount, totalOperationCount uint32) {
121+
transactions := l.TransactionEnvelopes()
122+
results, err := l.TxProcessing()
123+
if err != nil {
124+
panic(err)
125+
}
126+
127+
for i, result := range results {
128+
operations := transactions[i].OperationsCount()
129+
totalOperationCount += operations
130+
131+
// for successful transactions, the operation count is based on the operations results slice
132+
if result.Result.Successful() {
133+
operationResults, ok := result.Result.OperationResults()
134+
if !ok {
135+
panic("could not get OperationResults")
136+
}
137+
138+
successfulOperationCount += uint32(len(operationResults))
139+
}
140+
}
141+
142+
return successfulOperationCount, totalOperationCount
143+
}

ingest/ledger/ledger_test.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package ledger
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stellar/go/keypair"
8+
"github.com/stellar/go/txnbuild"
9+
"github.com/stellar/go/xdr"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestLedger(t *testing.T) {
14+
ledger := ledgerTestInput()
15+
16+
assert.Equal(t, uint32(30578981), Sequence(ledger))
17+
assert.Equal(t, "26932dc4d84b5fabe9ae744cb43ce4c6daccf98c86a991b2a14945b1adac4d59", Hash(ledger))
18+
assert.Equal(t, "f63c15d0eaf48afbd751a4c4dfade54a3448053c47c5a71d622668ae0cc2a208", PreviousHash(ledger))
19+
assert.Equal(t, int64(1594584547), CloseTime(ledger))
20+
assert.Equal(t, time.Time(time.Date(2020, time.July, 12, 20, 9, 7, 0, time.UTC)), ClosedAt(ledger))
21+
assert.Equal(t, int64(1054439020873472865), TotalCoins(ledger))
22+
assert.Equal(t, int64(18153766209161), FeePool(ledger))
23+
assert.Equal(t, uint32(100), BaseFee(ledger))
24+
assert.Equal(t, uint32(5000000), BaseReserve(ledger))
25+
assert.Equal(t, uint32(1000), MaxTxSetSize(ledger))
26+
assert.Equal(t, uint32(13), LedgerVersion(ledger))
27+
28+
var ok bool
29+
var freeWrite int64
30+
freeWrite, ok = SorobanFeeWrite1Kb(ledger)
31+
assert.Equal(t, true, ok)
32+
assert.Equal(t, int64(12), freeWrite)
33+
34+
var bucketSize uint64
35+
bucketSize, ok = TotalByteSizeOfBucketList(ledger)
36+
assert.Equal(t, true, ok)
37+
assert.Equal(t, uint64(56), bucketSize)
38+
39+
var nodeID string
40+
var err error
41+
nodeID, err = NodeID(ledger)
42+
assert.Equal(t, nil, err)
43+
assert.Equal(t, "GARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA76O", nodeID)
44+
45+
var signature string
46+
signature, ok = Signature(ledger)
47+
assert.Equal(t, true, ok)
48+
assert.Equal(t, "9g==", signature)
49+
50+
var success uint32
51+
var total uint32
52+
success, total = TransactionCounts(ledger)
53+
assert.Equal(t, uint32(1), success)
54+
assert.Equal(t, uint32(2), total)
55+
56+
success, total = OperationCounts(ledger)
57+
assert.Equal(t, uint32(1), success)
58+
assert.Equal(t, uint32(13), total)
59+
}
60+
61+
func ledgerTestInput() (lcm xdr.LedgerCloseMeta) {
62+
lcm = xdr.LedgerCloseMeta{
63+
V: 1,
64+
V1: &xdr.LedgerCloseMetaV1{
65+
Ext: xdr.LedgerCloseMetaExt{
66+
V: 1,
67+
V1: &xdr.LedgerCloseMetaExtV1{
68+
SorobanFeeWrite1Kb: xdr.Int64(12),
69+
},
70+
},
71+
LedgerHeader: xdr.LedgerHeaderHistoryEntry{
72+
Hash: xdr.Hash{0x26, 0x93, 0x2d, 0xc4, 0xd8, 0x4b, 0x5f, 0xab, 0xe9, 0xae, 0x74, 0x4c, 0xb4, 0x3c, 0xe4, 0xc6, 0xda, 0xcc, 0xf9, 0x8c, 0x86, 0xa9, 0x91, 0xb2, 0xa1, 0x49, 0x45, 0xb1, 0xad, 0xac, 0x4d, 0x59},
73+
Header: xdr.LedgerHeader{
74+
LedgerSeq: 30578981,
75+
TotalCoins: 1054439020873472865,
76+
FeePool: 18153766209161,
77+
BaseFee: 100,
78+
BaseReserve: 5000000,
79+
MaxTxSetSize: 1000,
80+
LedgerVersion: 13,
81+
PreviousLedgerHash: xdr.Hash{0xf6, 0x3c, 0x15, 0xd0, 0xea, 0xf4, 0x8a, 0xfb, 0xd7, 0x51, 0xa4, 0xc4, 0xdf, 0xad, 0xe5, 0x4a, 0x34, 0x48, 0x5, 0x3c, 0x47, 0xc5, 0xa7, 0x1d, 0x62, 0x26, 0x68, 0xae, 0xc, 0xc2, 0xa2, 0x8},
82+
ScpValue: xdr.StellarValue{
83+
Ext: xdr.StellarValueExt{
84+
V: 1,
85+
LcValueSignature: &xdr.LedgerCloseValueSignature{
86+
NodeId: xdr.NodeId{
87+
Type: 0,
88+
Ed25519: &xdr.Uint256{34},
89+
},
90+
Signature: []byte{0xf6},
91+
},
92+
},
93+
CloseTime: 1594584547,
94+
},
95+
},
96+
},
97+
TotalByteSizeOfBucketList: xdr.Uint64(56),
98+
TxSet: xdr.GeneralizedTransactionSet{
99+
V: 0,
100+
V1TxSet: &xdr.TransactionSetV1{
101+
Phases: []xdr.TransactionPhase{
102+
{
103+
V: 0,
104+
V0Components: &[]xdr.TxSetComponent{
105+
{
106+
Type: 0,
107+
TxsMaybeDiscountedFee: &xdr.TxSetComponentTxsMaybeDiscountedFee{
108+
Txs: []xdr.TransactionEnvelope{
109+
createSampleTx(3),
110+
createSampleTx(10),
111+
},
112+
},
113+
},
114+
},
115+
},
116+
},
117+
},
118+
},
119+
TxProcessing: []xdr.TransactionResultMeta{
120+
{
121+
Result: xdr.TransactionResultPair{
122+
Result: xdr.TransactionResult{
123+
Result: xdr.TransactionResultResult{
124+
Code: xdr.TransactionResultCodeTxSuccess,
125+
Results: &[]xdr.OperationResult{
126+
{
127+
Code: xdr.OperationResultCodeOpInner,
128+
Tr: &xdr.OperationResultTr{
129+
Type: xdr.OperationTypeCreateAccount,
130+
CreateAccountResult: &xdr.CreateAccountResult{
131+
Code: 0,
132+
},
133+
},
134+
},
135+
},
136+
},
137+
},
138+
},
139+
},
140+
{
141+
Result: xdr.TransactionResultPair{
142+
Result: xdr.TransactionResult{
143+
Result: xdr.TransactionResultResult{
144+
Code: xdr.TransactionResultCodeTxFailed,
145+
Results: &[]xdr.OperationResult{
146+
{
147+
Code: xdr.OperationResultCodeOpInner,
148+
Tr: &xdr.OperationResultTr{
149+
Type: xdr.OperationTypeCreateAccount,
150+
CreateAccountResult: &xdr.CreateAccountResult{
151+
Code: 0,
152+
},
153+
},
154+
},
155+
},
156+
},
157+
},
158+
},
159+
},
160+
},
161+
},
162+
}
163+
164+
return lcm
165+
}
166+
167+
func createSampleTx(operationCount int) xdr.TransactionEnvelope {
168+
kp, err := keypair.Random()
169+
panicOnError(err)
170+
171+
operations := []txnbuild.Operation{}
172+
operationType := &txnbuild.BumpSequence{
173+
BumpTo: 0,
174+
}
175+
for i := 0; i < operationCount; i++ {
176+
operations = append(operations, operationType)
177+
}
178+
179+
sourceAccount := txnbuild.NewSimpleAccount(kp.Address(), int64(0))
180+
tx, err := txnbuild.NewTransaction(
181+
txnbuild.TransactionParams{
182+
SourceAccount: &sourceAccount,
183+
Operations: operations,
184+
BaseFee: txnbuild.MinBaseFee,
185+
Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewInfiniteTimeout()},
186+
},
187+
)
188+
panicOnError(err)
189+
190+
env := tx.ToXDR()
191+
return env
192+
}
193+
194+
// PanicOnError is a function that panics if the provided error is not nil
195+
func panicOnError(err error) {
196+
if err != nil {
197+
panic(err)
198+
}
199+
}

xdr/ledger_close_meta.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,15 @@ func (l LedgerCloseMeta) EvictedPersistentLedgerEntries() ([]LedgerEntry, error)
156156
panic(fmt.Sprintf("Unsupported LedgerCloseMeta.V: %d", l.V))
157157
}
158158
}
159+
160+
// TxProcessing returns the TransactionResultMeta in this ledger
161+
func (l LedgerCloseMeta) TxProcessing() ([]TransactionResultMeta, error) {
162+
switch l.V {
163+
case 0:
164+
return l.MustV0().TxProcessing, nil
165+
case 1:
166+
return l.MustV1().TxProcessing, nil
167+
default:
168+
return []TransactionResultMeta{}, fmt.Errorf("Unsupported LedgerCloseMeta.V: %d", l.V)
169+
}
170+
}

xdr/node_id.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package xdr
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/stellar/go/strkey"
7+
)
8+
9+
func (n NodeId) GetAddress() (string, error) {
10+
switch n.Type {
11+
case PublicKeyTypePublicKeyTypeEd25519:
12+
ed, ok := n.GetEd25519()
13+
if !ok {
14+
return "", fmt.Errorf("could not get NodeID.Ed25519")
15+
}
16+
raw := make([]byte, 32)
17+
copy(raw, ed[:])
18+
encodedAddress, err := strkey.Encode(strkey.VersionByteAccountID, raw)
19+
if err != nil {
20+
return "", err
21+
}
22+
return encodedAddress, nil
23+
default:
24+
return "", fmt.Errorf("unknown NodeId.PublicKeyType")
25+
}
26+
}

xdr/transaction_envelope.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,7 @@ func (e TransactionEnvelope) Memo() Memo {
242242
panic("unsupported transaction type: " + e.Type.String())
243243
}
244244
}
245+
246+
func (e TransactionEnvelope) OperationsCount() uint32 {
247+
return uint32(len(e.Operations()))
248+
}

0 commit comments

Comments
 (0)