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 2 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
248 changes: 248 additions & 0 deletions exp/xdrill/ledger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
package xdrill

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

"github.com/stellar/go/exp/xdrill/utils"
"github.com/stellar/go/keypair"
"github.com/stellar/go/txnbuild"
"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, bool) {
lcmV1, ok := l.ledger.GetV1()
if !ok {
return 0, false
}

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

return int64(extV1.SorobanFeeWrite1Kb), true
}

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

return uint64(lcmV1.TotalByteSizeOfBucketList), true
}

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

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

return nodeID, true
}

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

return base64.StdEncoding.EncodeToString(LedgerCloseValueSignature.Signature), true
}

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

transactions := getTransactionSet(l)
switch l.ledger.V {
case 0:
results = l.ledger.V0.TxProcessing
case 1:
results = l.ledger.V1.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)
switch l.ledger.V {
case 0:
results = l.ledger.V0.TxProcessing
case 1:
results = l.ledger.V1.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 190 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
}

func CreateSampleTx(sequence int64, operationCount int) xdr.TransactionEnvelope {
kp, err := keypair.Random()
PanicOnError(err)

operations := []txnbuild.Operation{}
operationType := &txnbuild.BumpSequence{
BumpTo: 0,
}
for i := 0; i < operationCount; i++ {
operations = append(operations, operationType)
}

sourceAccount := txnbuild.NewSimpleAccount(kp.Address(), int64(0))
tx, err := txnbuild.NewTransaction(
txnbuild.TransactionParams{
SourceAccount: &sourceAccount,
Operations: operations,
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewInfiniteTimeout()},
},
)
PanicOnError(err)

env := tx.ToXDR()
return env
}

// PanicOnError is a function that panics if the provided error is not nil
func PanicOnError(err error) {
if err != nil {
panic(err)
}
}
Loading
Loading