Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xdrill for ledgerCloseMeta #5568

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions exp/xdrill/ledger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package xdrill

import (
"encoding/base64"
"fmt"
"time"

"github.com/stellar/go/exp/xdrill/utils"
"github.com/stellar/go/xdr"
)

type Ledger struct {
ledger *xdr.LedgerCloseMeta
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this struct definition would not work outside of the xdrill package. Although the Ledger struct is exported, the ledger field remains private to external packages. So, if you would not be able to construct a Ledger instance outside of the xdrill package because you would not be able to reference the ledger field.

To fix this issue you can make the ledger field public:

type Ledger struct {
	Ledger *xdr.LedgerCloseMeta
}

Alternatively, you could create a type definition:

type Ledger xdr.LedgerCloseMeta

func (l Ledger) Sequence() uint32 {
	return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.LedgerSeq)
}

Then to call the helper function you would cast your xdr.LedgerCloseMeta value into an xdrill.Ledger value so you could call the helper function:

package app

import (
	"github.com/stellar/go/exp/xdrill"
	"github.com/stellar/go/xdr"
)

func doSomething(ledger xdr.LedgerCloseMeta) {
  l := xrill.Ledger(ledger)
  seq := l.Sequence()
  ...
}

Or you could make the helper functions take the ledger parameter as the first argument:

func LedgerSequence(ledger xdr.LedgerCloseMeta) uint32 {
	return uint32(ledger.LedgerHeaderHistoryEntry().Header.LedgerSeq)
}

IMO the cleanest API would be define helper functions like these in the xdr package directly on the LedgerCloseMeta type. I think I missed the discussion about this decision. What was the motivation for defining a type equivalent to xdr.LedgerCloseMeta in the xdrill package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah oops yeah it should be public

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to mention one other possible solution, you could leave the ledger field as private and expose a public constructor:

type Ledger struct {
	ledger *xdr.LedgerCloseMeta
}

func NewLedger(ledger xdr.LedgerCloseMeta) Ledger {
   return Ledger{ledger: &ledger}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the motivation for defining a type equivalent to xdr.LedgerCloseMeta in the xdrill package?

Talked in slack. tl;dr - it's nicer for users as a separate package because they would be given a subset of only XDRill functions instead of all the functions/fields within xdr and ingest

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated functions to accept lcm as a param instead of the Ledger struct
d45b0fd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved xdrill ledger functions to go/ingest/ledger
40d5057


func (l Ledger) Sequence() uint32 {
return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.LedgerSeq)
}

func (l Ledger) ID() int64 {
return utils.NewID(int32(l.Sequence()), 0, 0).ToInt64()
}

func (l Ledger) Hash() string {
return utils.HashToHexString(l.ledger.LedgerHeaderHistoryEntry().Hash)
}

func (l Ledger) PreviousHash() string {
return utils.HashToHexString(l.ledger.PreviousLedgerHash())
}

func (l Ledger) CloseTime() int64 {
return l.ledger.LedgerCloseTime()
}

func (l Ledger) ClosedAt() time.Time {
return time.Unix(l.CloseTime(), 0).UTC()
}

func (l Ledger) TotalCoins() int64 {
return int64(l.ledger.LedgerHeaderHistoryEntry().Header.TotalCoins)
}

func (l Ledger) FeePool() int64 {
return int64(l.ledger.LedgerHeaderHistoryEntry().Header.FeePool)
}

func (l Ledger) BaseFee() uint32 {
return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.BaseFee)
}

func (l Ledger) BaseReserve() uint32 {
return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.BaseReserve)
}

func (l Ledger) MaxTxSetSize() uint32 {
return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.MaxTxSetSize)
}

func (l Ledger) LedgerVersion() uint32 {
return uint32(l.ledger.LedgerHeaderHistoryEntry().Header.LedgerVersion)
}

func (l Ledger) SorobanFeeWrite1Kb() *int64 {
lcmV1, ok := l.ledger.GetV1()
if !ok {
return nil
}

extV1, ok := lcmV1.Ext.GetV1()
if !ok {
return nil
}

result := int64(extV1.SorobanFeeWrite1Kb)

return &result
}

func (l Ledger) TotalByteSizeOfBucketList() *uint64 {
lcmV1, ok := l.ledger.GetV1()
if !ok {
return nil
}

result := uint64(lcmV1.TotalByteSizeOfBucketList)

return &result
}

func (l Ledger) NodeID() *string {
LedgerCloseValueSignature, ok := l.ledger.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature()
if !ok {
return nil

}
nodeID, ok := utils.GetAddress(LedgerCloseValueSignature.NodeId)
if !ok {
return nil
}

return &nodeID
}

func (l Ledger) Signature() *string {
LedgerCloseValueSignature, ok := l.ledger.LedgerHeaderHistoryEntry().Header.ScpValue.Ext.GetLcValueSignature()
if !ok {
return nil
}

result := base64.StdEncoding.EncodeToString(LedgerCloseValueSignature.Signature)

return &result
}

// Add docstring to larger, more complicated functions
func (l Ledger) TransactionCounts() (successTxCount, failedTxCount int32, ok bool) {
var results []xdr.TransactionResultMeta

transactions := getTransactionSet(l)
results = l.ledger.TxProcessing()
txCount := len(transactions)
if txCount != len(results) {
return 0, 0, false
}

for i := 0; i < txCount; i++ {
if results[i].Result.Successful() {
successTxCount++
} else {
failedTxCount++
}
}

return successTxCount, failedTxCount, true
}

// Add docstring to larger, more complicated functions
func (l Ledger) OperationCounts() (operationCount, txSetOperationCount int32, ok bool) {
var results []xdr.TransactionResultMeta

transactions := getTransactionSet(l)
results = l.ledger.TxProcessing()

txCount := len(transactions)
if txCount != len(results) {
return 0, 0, false
}

for i := 0; i < txCount; i++ {
operations := transactions[i].Operations()
numberOfOps := int32(len(operations))
txSetOperationCount += numberOfOps

// for successful transactions, the operation count is based on the operations results slice
if results[i].Result.Successful() {
operationResults, ok := results[i].Result.OperationResults()
if !ok {
return 0, 0, false
}

operationCount += int32(len(operationResults))
}

}

return operationCount, txSetOperationCount, true
}

func getTransactionSet(l Ledger) (transactionProcessing []xdr.TransactionEnvelope) {
switch l.ledger.V {
case 0:
return l.ledger.V0.TxSet.Txs
case 1:
switch l.ledger.V1.TxSet.V {
case 0:
return getTransactionPhase(l.ledger.V1.TxSet.V1TxSet.Phases)
default:
panic(fmt.Sprintf("unsupported LedgerCloseMeta.V1.TxSet.V: %d", l.ledger.V1.TxSet.V))
}
default:
panic(fmt.Sprintf("unsupported LedgerCloseMeta.V: %d", l.ledger.V))
}
}

Check failure on line 184 in exp/xdrill/ledger.go

View workflow job for this annotation

GitHub Actions / golangci

missing return (typecheck)

func getTransactionPhase(transactionPhase []xdr.TransactionPhase) (transactionEnvelope []xdr.TransactionEnvelope) {
transactionSlice := []xdr.TransactionEnvelope{}
for _, phase := range transactionPhase {
switch phase.V {
case 0:
components := phase.MustV0Components()
for _, component := range components {
switch component.Type {
case 0:
transactionSlice = append(transactionSlice, component.TxsMaybeDiscountedFee.Txs...)

default:
panic(fmt.Sprintf("unsupported TxSetComponentType: %d", component.Type))
}

}
default:
panic(fmt.Sprintf("unsupported TransactionPhase.V: %d", phase.V))
}
}

return transactionSlice
}
162 changes: 162 additions & 0 deletions exp/xdrill/ledger_test.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How exhaustive do we want the testing to be?

Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package xdrill

import (
"testing"
"time"

"github.com/stellar/go/exp/xdrill/utils"
"github.com/stellar/go/xdr"
"github.com/stretchr/testify/assert"
)

func TestLedger(t *testing.T) {
ledger := Ledger{
ledger: ledgerTestInput(),
}

assert.Equal(t, uint32(30578981), ledger.Sequence())
assert.Equal(t, int64(131335723340005376), ledger.ID())
assert.Equal(t, "26932dc4d84b5fabe9ae744cb43ce4c6daccf98c86a991b2a14945b1adac4d59", ledger.Hash())
assert.Equal(t, "f63c15d0eaf48afbd751a4c4dfade54a3448053c47c5a71d622668ae0cc2a208", ledger.PreviousHash())
assert.Equal(t, int64(1594584547), ledger.CloseTime())
assert.Equal(t, time.Time(time.Date(2020, time.July, 12, 20, 9, 7, 0, time.UTC)), ledger.ClosedAt())
assert.Equal(t, int64(1054439020873472865), ledger.TotalCoins())
assert.Equal(t, int64(18153766209161), ledger.FeePool())
assert.Equal(t, uint32(100), ledger.BaseFee())
assert.Equal(t, uint32(5000000), ledger.BaseReserve())
assert.Equal(t, uint32(1000), ledger.MaxTxSetSize())
assert.Equal(t, uint32(13), ledger.LedgerVersion())

var ok bool

freeWrite := ledger.SorobanFeeWrite1Kb()
assert.Equal(t, int64(12), *freeWrite)

bucketSize := ledger.TotalByteSizeOfBucketList()
assert.Equal(t, uint64(56), *bucketSize)

nodeID := ledger.NodeID()
assert.Equal(t, "GARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA76O", *nodeID)

signature := ledger.Signature()
assert.Equal(t, "9g==", *signature)

var success int32
var failed int32
success, failed, ok = ledger.TransactionCounts()
assert.Equal(t, true, ok)
assert.Equal(t, int32(1), success)
assert.Equal(t, int32(1), failed)

success, failed, ok = ledger.OperationCounts()
assert.Equal(t, true, ok)
assert.Equal(t, int32(1), success)
assert.Equal(t, int32(13), failed)

}

func ledgerTestInput() (lcm *xdr.LedgerCloseMeta) {
lcm = &xdr.LedgerCloseMeta{
V: 1,
V1: &xdr.LedgerCloseMetaV1{
Ext: xdr.LedgerCloseMetaExt{
V: 1,
V1: &xdr.LedgerCloseMetaExtV1{
SorobanFeeWrite1Kb: xdr.Int64(12),
},
},
LedgerHeader: xdr.LedgerHeaderHistoryEntry{
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},
Header: xdr.LedgerHeader{
LedgerSeq: 30578981,
TotalCoins: 1054439020873472865,
FeePool: 18153766209161,
BaseFee: 100,
BaseReserve: 5000000,
MaxTxSetSize: 1000,
LedgerVersion: 13,
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},
ScpValue: xdr.StellarValue{
Ext: xdr.StellarValueExt{
V: 1,
LcValueSignature: &xdr.LedgerCloseValueSignature{
NodeId: xdr.NodeId{
Type: 0,
Ed25519: &xdr.Uint256{34},
},
Signature: []byte{0xf6},
},
},
CloseTime: 1594584547,
},
},
},
TotalByteSizeOfBucketList: xdr.Uint64(56),
TxSet: xdr.GeneralizedTransactionSet{
V: 0,
V1TxSet: &xdr.TransactionSetV1{
Phases: []xdr.TransactionPhase{
{
V: 0,
V0Components: &[]xdr.TxSetComponent{
{
Type: 0,
TxsMaybeDiscountedFee: &xdr.TxSetComponentTxsMaybeDiscountedFee{
Txs: []xdr.TransactionEnvelope{
utils.CreateSampleTx(0, 3),
utils.CreateSampleTx(1, 10),
},
},
},
},
},
},
},
},
TxProcessing: []xdr.TransactionResultMeta{
{
Result: xdr.TransactionResultPair{
Result: xdr.TransactionResult{
Result: xdr.TransactionResultResult{
Code: xdr.TransactionResultCodeTxSuccess,
Results: &[]xdr.OperationResult{
{
Code: xdr.OperationResultCodeOpInner,
Tr: &xdr.OperationResultTr{
Type: xdr.OperationTypeCreateAccount,
CreateAccountResult: &xdr.CreateAccountResult{
Code: 0,
},
},
},
},
},
},
},
},
{
Result: xdr.TransactionResultPair{
Result: xdr.TransactionResult{
Result: xdr.TransactionResultResult{
Code: xdr.TransactionResultCodeTxFailed,
Results: &[]xdr.OperationResult{
{
Code: xdr.OperationResultCodeOpInner,
Tr: &xdr.OperationResultTr{
Type: xdr.OperationTypeCreateAccount,
CreateAccountResult: &xdr.CreateAccountResult{
Code: 0,
},
},
},
},
},
},
},
},
},
},
}

return lcm
}
Loading
Loading