-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Paul Bellamy
committed
Nov 30, 2022
1 parent
4a97f1d
commit 7f27d79
Showing
5 changed files
with
252 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package methods | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/creachadair/jrpc2" | ||
"github.com/creachadair/jrpc2/code" | ||
"github.com/creachadair/jrpc2/handler" | ||
|
||
"github.com/stellar/go/clients/stellarcore" | ||
proto "github.com/stellar/go/protocols/stellarcore" | ||
"github.com/stellar/go/support/log" | ||
"github.com/stellar/go/xdr" | ||
) | ||
|
||
type GetLedgerEntryRequest struct { | ||
Key string `json:"key"` | ||
} | ||
|
||
type GetLedgerEntryResponse struct { | ||
XDR string `json:"xdr"` | ||
LastModifiedLedger int64 `json:"lastModifiedLedgerSeq,string"` | ||
LatestLedger int64 `json:"latestLedger,string"` | ||
} | ||
|
||
// NewGetLedgerEntryHandler returns a json rpc handler to retrieve a contract data ledger entry from stellar cre | ||
func NewGetLedgerEntryHandler(logger *log.Entry, coreClient *stellarcore.Client) jrpc2.Handler { | ||
return handler.New(func(ctx context.Context, request GetLedgerEntryRequest) (GetLedgerEntryResponse, error) { | ||
var key xdr.LedgerKey | ||
if err := xdr.SafeUnmarshalBase64(request.Key, &key); err != nil { | ||
logger.WithError(err).WithField("request", request). | ||
Info("could not unmarshal ledgerKey from getLedgerEntry request") | ||
return GetLedgerEntryResponse{}, &jrpc2.Error{ | ||
Code: code.InvalidParams, | ||
Message: "cannot unmarshal key value", | ||
} | ||
} | ||
|
||
coreResponse, err := coreClient.GetLedgerEntry(ctx, key) | ||
if err != nil { | ||
logger.WithError(err).WithField("request", request). | ||
Info("could not submit getLedgerEntry request to core") | ||
return GetLedgerEntryResponse{}, &jrpc2.Error{ | ||
Code: code.InternalError, | ||
Message: "could not submit request to core", | ||
} | ||
} | ||
|
||
if coreResponse.State == proto.DeadState { | ||
return GetLedgerEntryResponse{}, &jrpc2.Error{ | ||
Code: code.InvalidRequest, | ||
Message: "not found", | ||
} | ||
} | ||
|
||
var ledgerEntry xdr.LedgerEntry | ||
if err = xdr.SafeUnmarshalBase64(coreResponse.Entry, &ledgerEntry); err != nil { | ||
logger.WithError(err).WithField("request", request). | ||
WithField("response", coreResponse). | ||
Info("could not parse ledger entry") | ||
return GetLedgerEntryResponse{}, &jrpc2.Error{ | ||
Code: code.InternalError, | ||
Message: "could not parse core response", | ||
} | ||
} | ||
|
||
response := GetLedgerEntryResponse{ | ||
LastModifiedLedger: int64(ledgerEntry.LastModifiedLedgerSeq), | ||
LatestLedger: coreResponse.Ledger, | ||
} | ||
if response.XDR, err = xdr.MarshalBase64(ledgerEntry.Data); err != nil { | ||
logger.WithError(err).WithField("request", request). | ||
WithField("response", coreResponse). | ||
Info("could not serialize ledger entry data") | ||
return GetLedgerEntryResponse{}, &jrpc2.Error{ | ||
Code: code.InternalError, | ||
Message: "could not serialize ledger entry data", | ||
} | ||
} | ||
|
||
return response, nil | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package test | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
"time" | ||
|
||
"github.com/creachadair/jrpc2" | ||
"github.com/creachadair/jrpc2/code" | ||
"github.com/creachadair/jrpc2/jhttp" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/stellar/go/keypair" | ||
"github.com/stellar/go/txnbuild" | ||
"github.com/stellar/go/xdr" | ||
"github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/methods" | ||
) | ||
|
||
func TestGetLedgerEntryNotFound(t *testing.T) { | ||
test := NewTest(t) | ||
|
||
ch := jhttp.NewChannel(test.server.URL, nil) | ||
client := jrpc2.NewClient(ch, nil) | ||
|
||
sourceAccount := keypair.Root(StandaloneNetworkPassphrase).Address() | ||
contractID := getContractID(t, sourceAccount, testSalt, StandaloneNetworkPassphrase) | ||
keyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ | ||
Type: xdr.LedgerEntryTypeContractData, | ||
ContractData: &xdr.LedgerKeyContractData{ | ||
ContractId: contractID, | ||
Key: getContractCodeLedgerKey(), | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
request := methods.GetLedgerEntryRequest{ | ||
Key: keyB64, | ||
} | ||
|
||
var result methods.GetLedgerEntryResponse | ||
jsonRPCErr := client.CallResult(context.Background(), "getLedgerEntry", request, &result).(*jrpc2.Error) | ||
assert.Equal(t, "not found", jsonRPCErr.Message) | ||
assert.Equal(t, code.InvalidRequest, jsonRPCErr.Code) | ||
} | ||
|
||
func TestGetLedgerEntryInvalidParams(t *testing.T) { | ||
test := NewTest(t) | ||
|
||
ch := jhttp.NewChannel(test.server.URL, nil) | ||
client := jrpc2.NewClient(ch, nil) | ||
|
||
request := methods.GetLedgerEntryRequest{ | ||
Key: "<>@@#$", | ||
} | ||
|
||
var result methods.GetLedgerEntryResponse | ||
jsonRPCErr := client.CallResult(context.Background(), "getLedgerEntry", request, &result).(*jrpc2.Error) | ||
assert.Equal(t, "cannot unmarshal key value", jsonRPCErr.Message) | ||
assert.Equal(t, code.InvalidParams, jsonRPCErr.Code) | ||
} | ||
|
||
func TestGetLedgerEntryDeadlineError(t *testing.T) { | ||
test := NewTest(t) | ||
test.coreClient.HTTP = &http.Client{ | ||
Timeout: time.Microsecond, | ||
} | ||
|
||
ch := jhttp.NewChannel(test.server.URL, nil) | ||
client := jrpc2.NewClient(ch, nil) | ||
|
||
sourceAccount := keypair.Root(StandaloneNetworkPassphrase).Address() | ||
contractID := getContractID(t, sourceAccount, testSalt, StandaloneNetworkPassphrase) | ||
keyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ | ||
Type: xdr.LedgerEntryTypeContractData, | ||
ContractData: &xdr.LedgerKeyContractData{ | ||
ContractId: contractID, | ||
Key: getContractCodeLedgerKey(), | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
request := methods.GetLedgerEntryRequest{ | ||
Key: keyB64, | ||
} | ||
|
||
var result methods.GetLedgerEntryResponse | ||
jsonRPCErr := client.CallResult(context.Background(), "getLedgerEntry", request, &result).(*jrpc2.Error) | ||
assert.Equal(t, "could not submit request to core", jsonRPCErr.Message) | ||
assert.Equal(t, code.InternalError, jsonRPCErr.Code) | ||
} | ||
|
||
func TestGetLedgerEntrySucceeds(t *testing.T) { | ||
test := NewTest(t) | ||
|
||
ch := jhttp.NewChannel(test.server.URL, nil) | ||
client := jrpc2.NewClient(ch, nil) | ||
|
||
kp := keypair.Root(StandaloneNetworkPassphrase) | ||
account := txnbuild.NewSimpleAccount(kp.Address(), 0) | ||
|
||
// Install and create the contract first | ||
for _, op := range []txnbuild.Operation{ | ||
createInstallContractCodeOperation(t, account.AccountID, testContract, true), | ||
createCreateContractOperation(t, account.AccountID, testContract, StandaloneNetworkPassphrase, true), | ||
} { | ||
assertSendTransaction(t, client, kp, txnbuild.TransactionParams{ | ||
SourceAccount: &account, | ||
IncrementSequenceNum: true, | ||
Operations: []txnbuild.Operation{op}, | ||
BaseFee: txnbuild.MinBaseFee, | ||
Preconditions: txnbuild.Preconditions{ | ||
TimeBounds: txnbuild.NewInfiniteTimeout(), | ||
}, | ||
}) | ||
} | ||
|
||
sourceAccount := keypair.Root(StandaloneNetworkPassphrase).Address() | ||
contractID := getContractID(t, sourceAccount, testSalt, StandaloneNetworkPassphrase) | ||
keyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ | ||
Type: xdr.LedgerEntryTypeContractData, | ||
ContractData: &xdr.LedgerKeyContractData{ | ||
ContractId: contractID, | ||
Key: getContractCodeLedgerKey(), | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
request := methods.GetLedgerEntryRequest{ | ||
Key: keyB64, | ||
} | ||
|
||
var result methods.GetLedgerEntryResponse | ||
err = client.CallResult(context.Background(), "getLedgerEntry", request, &result) | ||
assert.NoError(t, err) | ||
assert.Greater(t, result.LatestLedger, int64(0)) | ||
assert.GreaterOrEqual(t, result.LatestLedger, result.LastModifiedLedger) | ||
var scVal xdr.ScVal | ||
assert.NoError(t, xdr.SafeUnmarshalBase64(result.XDR, &scVal)) | ||
assert.Equal(t, testContract, scVal.MustObj().MustContractCode().MustWasmId()) | ||
} |