Skip to content

Commit

Permalink
Address review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed Apr 3, 2024
1 parent 4a19360 commit 006f1a4
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 14 deletions.
117 changes: 107 additions & 10 deletions cmd/soroban-rpc/internal/methods/simulate_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package methods
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/creachadair/jrpc2"
"github.com/creachadair/jrpc2/handler"
Expand Down Expand Up @@ -34,12 +37,107 @@ type RestorePreamble struct {
TransactionData string `json:"transactionData"` // SorobanTransactionData XDR in base64
MinResourceFee int64 `json:"minResourceFee,string"`
}
type LedgerEntryChangeType int

// LedgerEntryDiff designates a change in a ledger entry. Before and After cannot be be omitted at the same time.
const (
LedgerEntryChangeTypeCreated LedgerEntryChangeType = iota
LedgerEntryChangeTypeUpdated
LedgerEntryChangeTypeDeleted
)

var (
LedgerEntryChangeTypeName = map[LedgerEntryChangeType]string{
LedgerEntryChangeTypeCreated: "created",
LedgerEntryChangeTypeUpdated: "updated",
LedgerEntryChangeTypeDeleted: "deleted",
}
LedgerEntryChangeTypeValue = map[string]LedgerEntryChangeType{
"created": LedgerEntryChangeTypeCreated,
"updated": LedgerEntryChangeTypeUpdated,
"deleted": LedgerEntryChangeTypeDeleted,
}
)

func (l LedgerEntryChangeType) String() string {
return LedgerEntryChangeTypeName[l]
}

func (l LedgerEntryChangeType) MarshalJSON() ([]byte, error) {
return json.Marshal(l.String())
}

func (l *LedgerEntryChangeType) Parse(s string) error {
s = strings.TrimSpace(strings.ToLower(s))
value, ok := LedgerEntryChangeTypeValue[s]
if !ok {
return fmt.Errorf("%q is not a valid ledger entry change type", s)
}
*l = value
return nil
}

func (l *LedgerEntryChangeType) UnmarshalJSON(data []byte) (err error) {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
return l.Parse(s)
}

func (l *LedgerEntryChange) FromXDRDiff(diff preflight.XDRDiff) error {
beforePresent := len(diff.Before) > 0
afterPresent := len(diff.After) > 0
var (
entryXDR []byte
changeType LedgerEntryChangeType
)
switch {
case beforePresent:
entryXDR = diff.Before
if afterPresent {
changeType = LedgerEntryChangeTypeUpdated
} else {
changeType = LedgerEntryChangeTypeDeleted
}
case afterPresent:
entryXDR = diff.After
changeType = LedgerEntryChangeTypeCreated
default:
return errors.New("missing before and after")
}
var entry xdr.LedgerEntry

if err := xdr.SafeUnmarshal(entryXDR, &entry); err != nil {
return err
}
key, err := entry.LedgerKey()
if err != nil {
return err
}
keyB64, err := xdr.MarshalBase64(key)
if err != nil {
return err
}
l.Type = changeType
l.Key = keyB64
if beforePresent {
before := base64.StdEncoding.EncodeToString(diff.Before)
l.Before = &before
}
if afterPresent {
after := base64.StdEncoding.EncodeToString(diff.After)
l.After = &after
}
return nil
}

// LedgerEntryChange designates a change in a ledger entry. Before and After cannot be be omitted at the same time.
// If Before is omitted, it constitutes a creation, if After is omitted, it constitutes a delation.
type LedgerEntryDiff struct {
Before string `json:"before,omitempty"` // LedgerEntry XDR in base64
After string `json:"after,omitempty"` // LedgerEntry XDR in base64
type LedgerEntryChange struct {
Type LedgerEntryChangeType
Key string // LedgerEntryKey in base64
Before *string `json:"before"` // LedgerEntry XDR in base64
After *string `json:"after"` // LedgerEntry XDR in base64
}

type SimulateTransactionResponse struct {
Expand All @@ -50,7 +148,7 @@ type SimulateTransactionResponse struct {
Results []SimulateHostFunctionResult `json:"results,omitempty"` // an array of the individual host function call results
Cost SimulateTransactionCost `json:"cost,omitempty"` // the effective cpu and memory cost of the invoked transaction execution.
RestorePreamble *RestorePreamble `json:"restorePreamble,omitempty"` // If present, it indicates that a prior RestoreFootprint is required
StateDiff []LedgerEntryDiff `json:"stateDiff,omitempty"` // If present, it indicates how the state (ledger entries) will change as a result of the transaction execution.
StateChanges []LedgerEntryChange `json:"stateChanges,omitempty"` // If present, it indicates how the state (ledger entries) will change as a result of the transaction execution.
LatestLedger uint32 `json:"latestLedger"`
}

Expand Down Expand Up @@ -157,10 +255,9 @@ func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.Ledge
}
}

stateDiff := make([]LedgerEntryDiff, len(result.LedgerEntryDiff))
for i := 0; i < len(stateDiff); i++ {
stateDiff[i].Before = base64.StdEncoding.EncodeToString(result.LedgerEntryDiff[i].Before)
stateDiff[i].After = base64.StdEncoding.EncodeToString(result.LedgerEntryDiff[i].After)
stateChanges := make([]LedgerEntryChange, len(result.LedgerEntryDiff))
for i := 0; i < len(stateChanges); i++ {
stateChanges[i].FromXDRDiff(result.LedgerEntryDiff[i])
}

return SimulateTransactionResponse{
Expand All @@ -175,7 +272,7 @@ func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.Ledge
},
LatestLedger: latestLedger,
RestorePreamble: restorePreamble,
StateDiff: stateDiff,
StateChanges: stateChanges,
}
})
}
Expand Down
14 changes: 10 additions & 4 deletions cmd/soroban-rpc/internal/test/simulate_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,18 @@ func TestSimulateTransactionSucceeds(t *testing.T) {
assert.Equal(t, expectedXdr, resultXdr)

// Check state diff
assert.Len(t, result.StateDiff, 1)
assert.Empty(t, result.StateDiff[0].Before)
assert.NotEmpty(t, result.StateDiff[0].After)
assert.Len(t, result.StateChanges, 1)
assert.Nil(t, result.StateChanges[0].Before)
assert.NotNil(t, result.StateChanges[0].After)
assert.Equal(t, methods.LedgerEntryChangeTypeCreated, result.StateChanges[0].Type)
var after xdr.LedgerEntry
assert.NoError(t, xdr.SafeUnmarshalBase64(result.StateDiff[0].After, &after))
assert.NoError(t, xdr.SafeUnmarshalBase64(*result.StateChanges[0].After, &after))
assert.Equal(t, xdr.LedgerEntryTypeContractCode, after.Data.Type)
entryKey, err := after.LedgerKey()
assert.NoError(t, err)
entryKeyB64, err := xdr.MarshalBase64(entryKey)
assert.NoError(t, err)
assert.Equal(t, entryKeyB64, result.StateChanges[0].Key)

// test operation which does not have a source account
withoutSourceAccountOp := createInstallContractCodeOperation("", contractBinary)
Expand Down

0 comments on commit 006f1a4

Please sign in to comment.