diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa312312..87312900 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ jobs: test: strategy: matrix: - go-version: [1.17.x, 1.18.x, 1.19.x] + go-version: [1.19.x, 1.20.x, 1.21.x, 1.22.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/README.md b/README.md index 8446d5df..675fa907 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Solana SDK library for Go -[![GoDoc](https://pkg.go.dev/badge/github.com/gagliardetto/solana-go?status.svg)](https://pkg.go.dev/github.com/gagliardetto/solana-go@v1.8.4?tab=doc) +[![GoDoc](https://pkg.go.dev/badge/github.com/gagliardetto/solana-go?status.svg)](https://pkg.go.dev/github.com/gagliardetto/solana-go@v1.10.0?tab=doc) [![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/gagliardetto/solana-go?include_prereleases&label=release-tag)](https://github.com/gagliardetto/solana-go/releases) [![Build Status](https://github.com/gagliardetto/solana-go/workflows/tests/badge.svg?branch=main)](https://github.com/gagliardetto/solana-go/actions?query=branch%3Amain) [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gagliardetto/solana-go/main)](https://www.tickgit.com/browse?repo=github.com/gagliardetto/solana-go&branch=main) @@ -83,7 +83,7 @@ Thanks! ## Current development status -There is currently **no stable release**. The SDK is actively developed and latest is `v1.8.4` which is an `alpha` release. +There is currently **no stable release**. The SDK is actively developed and latest is `v1.10.0` which is an `alpha` release. The RPC and WS client implementation is based on [this RPC spec](https://github.com/solana-labs/solana/blob/c2435363f39723cef59b91322f3b6a815008af29/docs/src/developing/clients/jsonrpc-api.md). @@ -100,7 +100,7 @@ The RPC and WS client implementation is based on [this RPC spec](https://github. ```bash $ cd my-project -$ go get github.com/gagliardetto/solana-go@v1.8.4 +$ go get github.com/gagliardetto/solana-go@v1.10.0 ``` ## Pretty-Print transactions/instructions @@ -296,7 +296,7 @@ func exampleFromGetTransaction() { endpoint := rpc.TestNet_RPC client := rpc.New(endpoint) - txSig := solana.MustSignatureFromBase58("3pByJJ2ff7EQANKd2bgetmnYQxknk3QUib1xLMnrg6aCvg5hS78peaGMoceC9AFckomqrsgo38DpzrG2LPW9zj3g") + txSig := solana.MustSignatureFromBase58("3hZorctJtD3QLCRV3zF6JM6FDbFR5kAvsuKEG1RH9rWdz8YgnDzAvMWZFjdJgoL8KSNzZnx7aiExm1JEMC8KHfyy") { out, err := client.GetTransaction( context.TODO(), @@ -327,12 +327,15 @@ func decodeSystemTransfer(tx *solana.Transaction) { // Find the program address of this instruction: progKey, err := tx.ResolveProgramIDIndex(i0.ProgramIDIndex) - if if err != nil { + if err != nil { panic(err) } - // FInd the accounts of this instruction: - accounts := i0.ResolveInstructionAccounts(&tx.Message) + // Find the accounts of this instruction: + accounts, err := i0.ResolveInstructionAccounts(&tx.Message) + if err != nil { + panic(err) + } // Feed the accounts and data to the system program parser // OR see below for alternative parsing when you DON'T know @@ -616,7 +619,7 @@ func main() { // Create a new RPC client: client := rpc.New(rpc.TestNet_RPC) - // Airdrop 5 SOL to the new account: + // Airdrop 1 SOL to the new account: out, err := client.RequestAirdrop( context.TODO(), account.PublicKey(), @@ -744,7 +747,7 @@ func main() { amount := uint64(3333) if true { - // Airdrop 5 sol to the account so it will have something to transfer: + // Airdrop 1 sol to the account so it will have something to transfer: out, err := rpcClient.RequestAirdrop( context.TODO(), accountFrom.PublicKey(), diff --git a/go.mod b/go.mod index 0d254afe..ea13b3ae 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,55 @@ module github.com/gagliardetto/solana-go -go 1.16 +go 1.19 require ( - github.com/gagliardetto/binary v0.7.7 + github.com/gagliardetto/binary v0.8.0 github.com/gagliardetto/gofuzz v1.2.2 github.com/gagliardetto/treeout v0.1.4 + github.com/google/uuid v1.6.0 +) + +require ( + cloud.google.com/go v0.56.0 // indirect + github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/blendle/zapdriver v1.3.1 // indirect + github.com/daaku/go.zipexe v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.4.2 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.11 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/afero v1.1.2 // indirect + github.com/spf13/cast v1.3.0 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/appengine v1.6.5 // indirect + google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940 // indirect + google.golang.org/grpc v1.28.0 // indirect + google.golang.org/protobuf v1.23.0 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - contrib.go.opencensus.io/exporter/stackdriver v0.13.4 // indirect filippo.io/edwards25519 v1.0.0-rc.1 github.com/AlekSi/pointer v1.1.0 github.com/GeertJohan/go.rice v1.0.0 @@ -30,9 +70,8 @@ require ( github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 - github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 + github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 github.com/stretchr/testify v1.7.0 - github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect go.mongodb.org/mongo-driver v1.11.0 go.opencensus.io v0.22.5 // indirect go.uber.org/ratelimit v0.2.0 diff --git a/go.sum b/go.sum index f5686ee2..f43f230c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -23,9 +22,6 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk= -contrib.go.opencensus.io/exporter/stackdriver v0.13.4 h1:ksUxwH3OD5sxkjzEqGxNTl+Xjsmu3BnC/300MhSVTSc= -contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -46,8 +42,6 @@ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgp github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -76,8 +70,6 @@ github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79 h1:+HRtcJejUYA/2rnyTMbOaZ4g7f4aVuFduTV/03dbpLY= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -89,8 +81,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= -github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= +github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= +github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -145,6 +137,8 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -172,7 +166,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -184,7 +177,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -303,8 +295,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -315,15 +307,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E= -github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -342,30 +327,21 @@ go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tq go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -423,7 +399,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -464,7 +439,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -515,17 +489,13 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -546,7 +516,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -559,7 +528,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -567,7 +535,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -585,9 +552,7 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= diff --git a/message.go b/message.go index ad1c6d77..7ca6cb64 100644 --- a/message.go +++ b/message.go @@ -749,6 +749,26 @@ func (m Message) Account(index uint16) (PublicKey, error) { return PublicKey{}, fmt.Errorf("account index not found %d", index) } +// GetAccountIndex returns the index of the given account (first occurrence of the account). +func (m Message) GetAccountIndex(account PublicKey) (uint16, error) { + err := m.checkPreconditions() + if err != nil { + return 0, err + } + accountKeys, err := m.GetAllKeys() + if err != nil { + return 0, err + } + + for idx, a := range accountKeys { + if a.Equals(account) { + return uint16(idx), nil + } + } + + return 0, fmt.Errorf("account not found: %s", account) +} + func (m Message) HasAccount(account PublicKey) (bool, error) { err := m.checkPreconditions() if err != nil { diff --git a/programs/address-lookup-table/address-lookup.go b/programs/address-lookup-table/address-lookup.go index c25ed83a..cbd8fd9c 100644 --- a/programs/address-lookup-table/address-lookup.go +++ b/programs/address-lookup-table/address-lookup.go @@ -104,7 +104,12 @@ func (a *AddressLookupTableState) UnmarshalWithDecoder(decoder *bin.Decoder) (er numSerializedAddresses := serializedAddressesNumBytes / 32 if serializedAddressesNumBytes%32 != 0 { - return fmt.Errorf("lookup table is invalid") + // cut off the remaining bytes + // skip the difference + if err := decoder.Discard(serializedAddressesNumBytes % 32); err != nil { + return fmt.Errorf("failed to discard remaining bytes: %w", err) + } + // return fmt.Errorf("lookup table is invalid; serialized addresses are not a multiple of 32 bytes, with %d bytes remaining", serializedAddressesNumBytes%32) } if numSerializedAddresses > LOOKUP_TABLE_MAX_ADDRESSES { return fmt.Errorf("lookup table is invalid: max addresses exceeded (%d > %d)", numSerializedAddresses, LOOKUP_TABLE_MAX_ADDRESSES) @@ -128,7 +133,7 @@ func (a *AddressLookupTableState) UnmarshalWithDecoder(decoder *bin.Decoder) (er a.Addresses[i] = address } if decoder.Remaining() != 0 { - return fmt.Errorf("failed to read all addresses: remaining %d bytes", decoder.Remaining()) + // return fmt.Errorf("failed to read all addresses: remaining %d bytes", decoder.Remaining()) } return nil } diff --git a/rpc/client_test.go b/rpc/client_test.go index 7b646565..33c08b17 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -23,6 +23,7 @@ import ( "encoding/base64" stdjson "encoding/json" "fmt" + "math/big" "testing" "github.com/AlekSi/pointer" @@ -34,7 +35,7 @@ import ( ) func TestClient_GetAccountInfo(t *testing.T) { - responseBody := `{"context":{"slot":83986105},"value":{"data":["dGVzdA==","base64"],"executable":true,"lamports":999999,"owner":"11111111111111111111111111111111","rentEpoch":207}}` + responseBody := `{"context":{"slot":83986105},"value":{"data":["dGVzdA==","base64"],"executable":true,"lamports":999999,"owner":"11111111111111111111111111111111","rentEpoch":18446744073709551615}}` server, closer := mockJSONRPC(t, stdjson.RawMessage(wrapIntoRPC(responseBody))) defer closer() client := New(server.URL) @@ -44,9 +45,14 @@ func TestClient_GetAccountInfo(t *testing.T) { out, err := client.GetAccountInfo(context.Background(), pubKey) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getAccountInfo", "params": []interface{}{ @@ -56,9 +62,10 @@ func TestClient_GetAccountInfo(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) + rentEpoch, _ := new(big.Int).SetString("18446744073709551615", 10) assert.Equal(t, &GetAccountInfoResult{ RPCContext: RPCContext{ @@ -75,7 +82,7 @@ func TestClient_GetAccountInfo(t *testing.T) { }, }, Executable: true, - RentEpoch: 207, + RentEpoch: rentEpoch, }, }, out) } @@ -109,9 +116,14 @@ func TestClient_GetAccountInfoWithOpts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getAccountInfo", "params": []interface{}{ @@ -127,12 +139,12 @@ func TestClient_GetAccountInfoWithOpts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) } func TestClient_GetConfirmedSignaturesForAddress2(t *testing.T) { - server, closer := mockJSONRPC(t, stdjson.RawMessage(`{"jsonrpc":"2.0","result":[{"err":null,"memo":null,"signature":"mgw5vw4tnbou1wVStKckVcVncbpRwfZPcMNbVBoigbSPXBMa3857CNzhwoCkRzM5K7nG32wcbpVJDHttQeBRaHB","slot":1}],"id":0}`)) + server, closer := mockJSONRPC(t, stdjson.RawMessage(`{"jsonrpc":"2.0","result":[{"err":null,"memo":null,"signature":"mgw5vw4tnbou1wVStKckVcVncbpRwfZPcMNbVBoigbSPXBMa3857CNzhwoCkRzM5K7nG32wcbpVJDHttQeBRaHB","slot":1}],"id":null}`)) defer closer() client := New(server.URL) @@ -141,9 +153,14 @@ func TestClient_GetConfirmedSignaturesForAddress2(t *testing.T) { out, err := client.GetConfirmedSignaturesForAddress2(context.Background(), account, &GetConfirmedSignaturesForAddress2Opts{Limit: &limit}) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getConfirmedSignaturesForAddress2", "params": []interface{}{ @@ -151,7 +168,7 @@ func TestClient_GetConfirmedSignaturesForAddress2(t *testing.T) { map[string]interface{}{"limit": float64(1)}, }, }, - server.RequestBody(t), + reqBody, ) expected := []*TransactionSignature{ @@ -162,7 +179,7 @@ func TestClient_GetConfirmedSignaturesForAddress2(t *testing.T) { } func TestClient_GetConfirmedTransaction(t *testing.T) { - server, closer := mockJSONRPC(t, stdjson.RawMessage(`{"jsonrpc":"2.0","result":{"meta":{"err":null,"fee":5000,"innerInstructions":[],"logMessages":[],"postBalances":[],"preBalances":[],"status":{"Ok":null}},"slot":48291656,"transaction":["AcpmPgtaSCzI2vuOUXduljmnoc1zIqMETzEJ8zmF+\/yy2AABHMNonpVleveVw4a4Fo7LUDWtxo2FkyzFr2x9DQIBAAMB47aX3y9Dfp+\/ycSDXt0Ph3TfZQBqPSXMQYToKtUtr5kNhniVeV7Las6qkeV8d0rksxV9de0GF7p4nzQUVEnrWwEEBAECAwAEdGVzdA==","base64"]},"id":0}`)) + server, closer := mockJSONRPC(t, stdjson.RawMessage(`{"jsonrpc":"2.0","result":{"meta":{"err":null,"fee":5000,"innerInstructions":[],"logMessages":[],"postBalances":[],"preBalances":[],"status":{"Ok":null}},"slot":48291656,"transaction":["AcpmPgtaSCzI2vuOUXduljmnoc1zIqMETzEJ8zmF+\/yy2AABHMNonpVleveVw4a4Fo7LUDWtxo2FkyzFr2x9DQIBAAMB47aX3y9Dfp+\/ycSDXt0Ph3TfZQBqPSXMQYToKtUtr5kNhniVeV7Las6qkeV8d0rksxV9de0GF7p4nzQUVEnrWwEEBAECAwAEdGVzdA==","base64"]},"id":null}`)) defer closer() client := New(server.URL) @@ -172,9 +189,14 @@ func TestClient_GetConfirmedTransaction(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getConfirmedTransaction", "params": []interface{}{ @@ -182,7 +204,7 @@ func TestClient_GetConfirmedTransaction(t *testing.T) { "json", }, }, - server.RequestBody(t), + reqBody, ) signature, err := solana.SignatureFromBase58("53hoZ98EsCMA6L63GWM65M3Bd3WqA4LxD8bcJkbKoKWhbJFqX9M1WZ4fSjt8bYyZn21NwNnV2A25zirBni9Qk6LR") @@ -265,9 +287,14 @@ func TestClient_GetRecentBlockhash(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getRecentBlockhash", "params": []interface{}{ @@ -276,7 +303,7 @@ func TestClient_GetRecentBlockhash(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -302,9 +329,14 @@ func TestClient_GetBalance(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBalance", "params": []interface{}{ @@ -314,7 +346,7 @@ func TestClient_GetBalance(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) assert.Equal(t, @@ -340,9 +372,14 @@ func TestClient_GetBlock(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlock", "params": []interface{}{ @@ -352,7 +389,7 @@ func TestClient_GetBlock(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) // TODO: @@ -507,9 +544,14 @@ func TestClient_GetBlockWithOpts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlock", "params": []interface{}{ @@ -523,7 +565,7 @@ func TestClient_GetBlockWithOpts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) // TODO: @@ -542,9 +584,14 @@ func TestClient_GetBlockHeight(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlockHeight", "params": []interface{}{ @@ -553,7 +600,7 @@ func TestClient_GetBlockHeight(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -574,14 +621,19 @@ func TestClient_GetBlockProduction(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlockProduction", "params": []interface{}{}, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -615,9 +667,14 @@ func TestClient_GetBlockProductionWithOpts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlockProduction", "params": []interface{}{ @@ -631,7 +688,7 @@ func TestClient_GetBlockProductionWithOpts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) } @@ -649,16 +706,21 @@ func TestClient_GetBlockCommitment(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlockCommitment", "params": []interface{}{ float64(block), }, }, - server.RequestBody(t), + reqBody, ) expected := map[string]interface{}{ @@ -720,9 +782,14 @@ func TestClient_GetBlocks(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlocks", "params": []interface{}{ @@ -733,7 +800,7 @@ func TestClient_GetBlocks(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -759,9 +826,14 @@ func TestClient_GetBlocksWithLimit(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlocksWithLimit", "params": []interface{}{ @@ -772,7 +844,7 @@ func TestClient_GetBlocksWithLimit(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -795,16 +867,21 @@ func TestClient_GetBlockTime(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getBlockTime", "params": []interface{}{ float64(block), }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -815,7 +892,7 @@ func TestClient_GetBlockTime(t *testing.T) { } func TestClient_GetClusterNodes(t *testing.T) { - responseBody := `[{"featureSet":743297851,"gossip":"162.55.111.250:8001","pubkey":"DMeohMfD3JzmYZA34jL9iiTXp5N7tpAR3rAoXMygdH3U","rpc":"135.181.114.15:8005","shredVersion":18122,"tpu":"162.55.111.250:8004","version":"1.7.3"},{"featureSet":743297851,"gossip":"136.243.131.82:8000","pubkey":"59TSbYfnbb4zx4xf54ApjE8fJRhwzTiSjh9vdHfgyg1U","rpc":"136.243.131.82:8899","shredVersion":18122,"tpu":"136.243.131.82:8003","version":"1.7.3"},{"featureSet":743297851,"gossip":"135.181.114.15:8001","pubkey":"7vu7Q2d4uu9V4xnySHXieeyWvoNh37321kqTd2ATuoj6","rpc":"135.181.114.15:8005","shredVersion":18122,"tpu":"135.181.114.15:8006","version":"1.7.3"}]` + responseBody := `[{"featureSet":3580551090,"gossip":"34.147.255.155:8000","pubkey":"hyp3Eo67t6FgeuWg5Qxbeme8NPXJPXXdKT4iJ4DsLf2","pubsub":"34.147.255.155:8900","rpc":"34.147.255.155:8899","shredVersion":50093,"tpu":"34.147.255.155:8009","tpuQuic":"34.147.255.155:8015","version":"1.17.22"},{"featureSet":3746964731,"gossip":"162.19.222.39:8001","pubkey":"EvnRmnMrd69kFdbLMxWkTn1icZ7DCceRhvmb2SJXqDo4","pubsub":"162.19.222.39:8900","rpc":"162.19.222.39:8899","shredVersion":50093,"tpu":"208.91.106.87:8005","tpuQuic":"208.91.106.87:8011","version":"1.17.27"},{"featureSet":3746964731,"gossip":"205.209.104.74:8000","pubkey":"J87afqF2bDQQLTQpks4SdF7hXPr96SPTdJ28UJXXWr9N","pubsub":"205.209.104.74:8900","rpc":"205.209.104.74:8899","shredVersion":50093,"tpu":"205.209.104.74:8003","tpuQuic":"205.209.104.74:8009","version":"1.17.27"}]` server, closer := mockJSONRPC(t, stdjson.RawMessage(wrapIntoRPC(responseBody))) defer closer() client := New(server.URL) @@ -825,13 +902,18 @@ func TestClient_GetClusterNodes(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getClusterNodes", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -853,9 +935,14 @@ func TestClient_GetEpochInfo(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getEpochInfo", "params": []interface{}{ @@ -864,7 +951,7 @@ func TestClient_GetEpochInfo(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := map[string]interface{}{ @@ -892,13 +979,18 @@ func TestClient_GetEpochSchedule(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getEpochSchedule", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -921,9 +1013,14 @@ func TestClient_GetFeeCalculatorForBlockhash(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getFeeCalculatorForBlockhash", "params": []interface{}{ @@ -933,7 +1030,7 @@ func TestClient_GetFeeCalculatorForBlockhash(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -954,13 +1051,18 @@ func TestClient_GetFeeRateGovernor(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getFeeRateGovernor", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -982,9 +1084,14 @@ func TestClient_GetFees(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getFees", "params": []interface{}{ @@ -993,7 +1100,7 @@ func TestClient_GetFees(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1014,13 +1121,18 @@ func TestClient_GetFirstAvailableBlock(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getFirstAvailableBlock", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1041,13 +1153,18 @@ func TestClient_GetGenesisHash(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getGenesisHash", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1068,13 +1185,18 @@ func TestClient_GetHealth(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getHealth", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1095,13 +1217,18 @@ func TestClient_GetIdentity(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getIdentity", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1123,9 +1250,14 @@ func TestClient_GetInflationGovernor(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getInflationGovernor", "params": []interface{}{ @@ -1134,7 +1266,7 @@ func TestClient_GetInflationGovernor(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1155,13 +1287,18 @@ func TestClient_GetInflationRate(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getInflationRate", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1196,9 +1333,14 @@ func TestClient_GetInflationReward(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getInflationReward", "params": []interface{}{ @@ -1211,7 +1353,7 @@ func TestClient_GetInflationReward(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1235,9 +1377,14 @@ func TestClient_GetLargestAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getLargestAccounts", "params": []interface{}{ @@ -1247,7 +1394,7 @@ func TestClient_GetLargestAccounts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := &GetLargestAccountsResult{ @@ -1364,9 +1511,14 @@ func TestClient_GetLeaderSchedule(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getLeaderSchedule", "params": []interface{}{ @@ -1377,7 +1529,7 @@ func TestClient_GetLeaderSchedule(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1398,13 +1550,18 @@ func TestClient_GetMaxRetransmitSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getMaxRetransmitSlot", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1425,13 +1582,18 @@ func TestClient_GetMaxShredInsertSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getMaxShredInsertSlot", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1455,9 +1617,14 @@ func TestClient_GetMinimumBalanceForRentExemption(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getMinimumBalanceForRentExemption", "params": []interface{}{ @@ -1467,7 +1634,7 @@ func TestClient_GetMinimumBalanceForRentExemption(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1491,16 +1658,21 @@ func TestClient_GetMultipleAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getMultipleAccounts", "params": []interface{}{ []interface{}{pubkeyString}, }, }, - server.RequestBody(t), + reqBody, ) expected := &GetMultipleAccountsResult{ @@ -1521,7 +1693,7 @@ func TestClient_GetMultipleAccounts(t *testing.T) { rawDataEncoding: solana.EncodingBase64, }, Executable: true, - RentEpoch: 207, + RentEpoch: big.NewInt(207), }, }, } @@ -1564,9 +1736,14 @@ func TestClient_GetProgramAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getProgramAccounts", "params": []interface{}{ @@ -1589,7 +1766,7 @@ func TestClient_GetProgramAccounts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := GetProgramAccountsResult{ @@ -1606,7 +1783,7 @@ func TestClient_GetProgramAccounts(t *testing.T) { rawDataEncoding: solana.EncodingBase64, }, Executable: true, - RentEpoch: 206, + RentEpoch: big.NewInt(206), }, }, } @@ -1627,16 +1804,21 @@ func TestClient_GetRecentPerformanceSamples(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getRecentPerformanceSamples", "params": []interface{}{ float64(limit), }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1657,13 +1839,18 @@ func TestClient_GetSnapshotSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSnapshotSlot", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1701,9 +1888,14 @@ func TestClient_GetSignaturesForAddress(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSignaturesForAddress", "params": []interface{}{ @@ -1717,7 +1909,7 @@ func TestClient_GetSignaturesForAddress(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1743,9 +1935,14 @@ func TestClient_GetSignatureStatuses(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSignatureStatuses", "params": []interface{}{ @@ -1758,7 +1955,7 @@ func TestClient_GetSignatureStatuses(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1780,9 +1977,14 @@ func TestClient_GetSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSlot", "params": []interface{}{ @@ -1791,7 +1993,7 @@ func TestClient_GetSlot(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1813,9 +2015,14 @@ func TestClient_GetSlotLeader(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSlotLeader", "params": []interface{}{ @@ -1824,7 +2031,7 @@ func TestClient_GetSlotLeader(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1849,9 +2056,14 @@ func TestClient_GetSlotLeaders(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSlotLeaders", "params": []interface{}{ @@ -1859,7 +2071,7 @@ func TestClient_GetSlotLeaders(t *testing.T) { float64(limit), }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1878,9 +2090,14 @@ func TestClient_GetSupply(t *testing.T) { out, err := client.GetSupply(context.Background(), CommitmentFinalized) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSupply", "params": []interface{}{ @@ -1890,7 +2107,7 @@ func TestClient_GetSupply(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1915,9 +2132,14 @@ func TestClient_GetSupply_CommitmentMax(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSupply", "params": []interface{}{ @@ -1927,7 +2149,7 @@ func TestClient_GetSupply_CommitmentMax(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1953,9 +2175,14 @@ func TestClient_GetSupply_ExcludeNonCirculatingAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getSupply", "params": []interface{}{ @@ -1965,7 +2192,7 @@ func TestClient_GetSupply_ExcludeNonCirculatingAccounts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -1991,9 +2218,14 @@ func TestClient_GetTokenLargestAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTokenLargestAccounts", "params": []interface{}{ @@ -2003,7 +2235,7 @@ func TestClient_GetTokenLargestAccounts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2029,9 +2261,14 @@ func TestClient_GetTokenSupply(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTokenSupply", "params": []interface{}{ @@ -2041,7 +2278,7 @@ func TestClient_GetTokenSupply(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2072,9 +2309,14 @@ func TestClient_GetTransaction(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTransaction", "params": []interface{}{ @@ -2086,7 +2328,7 @@ func TestClient_GetTransaction(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) blockTimeSeconds := int64(1624821990) @@ -2180,9 +2422,14 @@ func TestClient_GetParsedTransaction(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTransaction", "params": []interface{}{ @@ -2193,7 +2440,7 @@ func TestClient_GetParsedTransaction(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) assert.Equal(t, uint64(2), out.Meta.InnerInstructions[0].Index) @@ -2228,9 +2475,14 @@ func TestClient_GetTransactionCount(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTransactionCount", "params": []interface{}{ @@ -2239,7 +2491,7 @@ func TestClient_GetTransactionCount(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2260,13 +2512,18 @@ func TestClient_GetVersion(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getVersion", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2292,9 +2549,14 @@ func TestClient_GetVoteAccounts(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getVoteAccounts", "params": []interface{}{ @@ -2304,7 +2566,7 @@ func TestClient_GetVoteAccounts(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2325,13 +2587,18 @@ func TestClient_MinimumLedgerSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "minimumLedgerSlot", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2359,9 +2626,14 @@ func TestClient_RequestAirdrop(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "requestAirdrop", "params": []interface{}{ @@ -2372,7 +2644,7 @@ func TestClient_RequestAirdrop(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2400,9 +2672,14 @@ func TestClient_GetStakeActivation(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getStakeActivation", "params": []interface{}{ @@ -2413,7 +2690,7 @@ func TestClient_GetStakeActivation(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2439,9 +2716,14 @@ func TestClient_GetTokenAccountBalance(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTokenAccountBalance", "params": []interface{}{ @@ -2451,7 +2733,7 @@ func TestClient_GetTokenAccountBalance(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2486,9 +2768,14 @@ func TestClient_GetTokenAccountsByDelegate(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTokenAccountsByDelegate", "params": []interface{}{ @@ -2502,7 +2789,7 @@ func TestClient_GetTokenAccountsByDelegate(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2537,9 +2824,14 @@ func TestClient_GetTokenAccountsByOwner(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getTokenAccountsByOwner", "params": []interface{}{ @@ -2553,7 +2845,7 @@ func TestClient_GetTokenAccountsByOwner(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2644,9 +2936,14 @@ func TestClient_IsBlockhashValid(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "isBlockhashValid", "params": []interface{}{ @@ -2656,7 +2953,7 @@ func TestClient_IsBlockhashValid(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) assert.Equal(t, @@ -2685,9 +2982,14 @@ func TestClient_GetFeeForMessage(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getFeeForMessage", "params": []interface{}{ @@ -2697,7 +2999,7 @@ func TestClient_GetFeeForMessage(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2718,13 +3020,18 @@ func TestClient_GetHighestSnapshotSlot(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getHighestSnapshotSlot", }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2746,9 +3053,14 @@ func TestClient_GetLatestBlockhash(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getLatestBlockhash", "params": []interface{}{ @@ -2757,7 +3069,7 @@ func TestClient_GetLatestBlockhash(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) @@ -2785,9 +3097,14 @@ func TestClient_GetRecentPrioritizationFees(t *testing.T) { ) require.NoError(t, err) + // the ID is random, so we can't assert it; let's check that it is set, and then remove it + reqBody := server.RequestBody(t) + assert.NotNil(t, reqBody["id"]) + reqBody["id"] = any(nil) + assert.Equal(t, map[string]interface{}{ - "id": float64(0), + "id": any(nil), "jsonrpc": "2.0", "method": "getRecentPrioritizationFees", "params": []interface{}{ @@ -2797,7 +3114,7 @@ func TestClient_GetRecentPrioritizationFees(t *testing.T) { }, }, }, - server.RequestBody(t), + reqBody, ) expected := mustJSONToInterface([]byte(responseBody)) diff --git a/rpc/getClusterNodes.go b/rpc/getClusterNodes.go index 559c6831..f89b2910 100644 --- a/rpc/getClusterNodes.go +++ b/rpc/getClusterNodes.go @@ -38,6 +38,12 @@ type GetClusterNodesResult struct { // TPU network address for the node. TPU *string `json:"tpu,omitempty"` + // TPU QUIC network address for the node. + TPUQUIC *string `json:"tpuQuic,omitempty"` + + // RPC WebSocket network address for the node, or empty if the WebSocket RPC service is not enabled. + PubSub *string `json:"pubsub,omitempty"` + // JSON RPC network address for the node, or empty if the JSON RPC service is not enabled. RPC *string `json:"rpc,omitempty"` diff --git a/rpc/getParsedTransaction.go b/rpc/getParsedTransaction.go index 5542222d..cfc79ff1 100644 --- a/rpc/getParsedTransaction.go +++ b/rpc/getParsedTransaction.go @@ -9,7 +9,12 @@ import ( ) type GetParsedTransactionOpts struct { + // Desired commitment. "processed" is not supported. If parameter not provided, the default is "finalized". Commitment CommitmentType `json:"commitment,omitempty"` + + // Max transaction version to return in responses. + // If the requested block contains a transaction with a higher version, an error will be returned. + MaxSupportedTransactionVersion *uint64 } type GetParsedTransactionResult struct { @@ -30,6 +35,9 @@ func (cl *Client) GetParsedTransaction( if opts.Commitment != "" { obj["commitment"] = opts.Commitment } + if opts.MaxSupportedTransactionVersion != nil { + obj["maxSupportedTransactionVersion"] = *opts.MaxSupportedTransactionVersion + } } obj["encoding"] = solana.EncodingJSONParsed params = append(params, obj) diff --git a/rpc/jsonrpc/jsonrpc.go b/rpc/jsonrpc/jsonrpc.go index 38ceff01..6a48ec95 100644 --- a/rpc/jsonrpc/jsonrpc.go +++ b/rpc/jsonrpc/jsonrpc.go @@ -9,8 +9,10 @@ import ( "fmt" "net/http" "reflect" + "sync/atomic" "github.com/davecgh/go-spew/spew" + "github.com/google/uuid" jsoniter "github.com/json-iterator/go" ) @@ -165,7 +167,7 @@ type RPCClient interface { type RPCRequest struct { Method string `json:"method"` Params interface{} `json:"params,omitempty"` - ID int `json:"id"` + ID any `json:"id"` JSONRPC string `json:"jsonrpc"` } @@ -177,8 +179,8 @@ func NewRequest(method string, params ...interface{}) *RPCRequest { Method: method, Params: Params(params...), JSONRPC: jsonrpcVersion, + ID: newID(), } - return request } @@ -197,7 +199,7 @@ type RPCResponse struct { JSONRPC string `json:"jsonrpc"` Result stdjson.RawMessage `json:"result,omitempty"` Error *RPCError `json:"error,omitempty"` - ID int `json:"id"` + ID any `json:"id"` } // RPCError represents a JSON-RPC error object if an RPC error occurred. @@ -277,23 +279,32 @@ type RPCClientOpts struct { type RPCResponses []*RPCResponse // AsMap returns the responses as map with response id as key. -func (res RPCResponses) AsMap() map[int]*RPCResponse { - resMap := make(map[int]*RPCResponse, 0) +func (res RPCResponses) AsMap() map[any]*RPCResponse { + resMap := make(map[any]*RPCResponse, 0) for _, r := range res { - resMap[r.ID] = r + actualID := r.ID + if actualID != nil { + if asFloat, ok := actualID.(stdjson.Number); ok { + asInt64, err := asFloat.Int64() + if err == nil { + actualID = int(asInt64) + } + } else { + resMap[actualID] = r + } + } + resMap[actualID] = r } - return resMap } // GetByID returns the response object of the given id, nil if it does not exist. -func (res RPCResponses) GetByID(id int) *RPCResponse { +func (res RPCResponses) GetByID(id any) *RPCResponse { for _, r := range res { if r.ID == id { return r } } - return nil } @@ -519,11 +530,32 @@ func (client *rpcClient) doCall( return rpcResponse, nil } +var UseIntegerID = false + +var integerID = new(atomic.Uint64) + +var useFixedID = false + +const defaultFixedID = 1 + +func newID() any { + if useFixedID { + return defaultFixedID + } + if UseIntegerID { + return integerID.Add(1) + } + return uuid.New().String() +} + func (client *rpcClient) doCallWithCallbackOnHTTPResponse( ctx context.Context, RPCRequest *RPCRequest, callback func(*http.Request, *http.Response) error, ) error { + if RPCRequest != nil && RPCRequest.ID == nil { + RPCRequest.ID = newID() + } httpRequest, err := client.newRequest(ctx, RPCRequest) if err != nil { if httpRequest != nil { @@ -559,7 +591,6 @@ func (client *rpcClient) doBatchCall(ctx context.Context, rpcRequest []*RPCReque decoder.DisallowUnknownFields() decoder.UseNumber() err = decoder.Decode(&rpcResponse) - // parsing error if err != nil { // if we have some http error, return it @@ -661,6 +692,9 @@ func Params(params ...interface{}) interface{} { // // The function works as you would expect it from json.Unmarshal() func (RPCResponse *RPCResponse) GetObject(toType interface{}) error { + if RPCResponse == nil { + return errors.New("rpc response is nil") + } rv := reflect.ValueOf(toType) if rv.Kind() != reflect.Ptr { return fmt.Errorf("expected a pointer, got a value: %s", reflect.TypeOf(toType)) diff --git a/rpc/jsonrpc/jsonrpc_test.go b/rpc/jsonrpc/jsonrpc_test.go index e9e764f1..ddcc5f9b 100644 --- a/rpc/jsonrpc/jsonrpc_test.go +++ b/rpc/jsonrpc/jsonrpc_test.go @@ -12,6 +12,7 @@ import ( "testing" . "github.com/onsi/gomega" + "github.com/stretchr/testify/require" ) // needed to retrieve requests that arrived at httpServer for further investigation @@ -72,96 +73,98 @@ func TestRpcClient_Call(t *testing.T) { Ingredients: []string{"rum", "cola"}, } + useFixedID = true + rpcClient.Call(context.Background(), "missingParam") - Expect((<-requestChan).body).To(Equal(`{"method":"missingParam","id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"missingParam","id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "nullParam", nil) - Expect((<-requestChan).body).To(Equal(`{"method":"nullParam","params":[null],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"nullParam","params":[null],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "nullParams", nil, nil) - Expect((<-requestChan).body).To(Equal(`{"method":"nullParams","params":[null,null],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"nullParams","params":[null,null],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "emptyParams", []interface{}{}) - Expect((<-requestChan).body).To(Equal(`{"method":"emptyParams","params":[],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"emptyParams","params":[],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "emptyAnyParams", []string{}) - Expect((<-requestChan).body).To(Equal(`{"method":"emptyAnyParams","params":[],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"emptyAnyParams","params":[],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "emptyObject", struct{}{}) - Expect((<-requestChan).body).To(Equal(`{"method":"emptyObject","params":{},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"emptyObject","params":{},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "emptyObjectList", []struct{}{{}, {}}) - Expect((<-requestChan).body).To(Equal(`{"method":"emptyObjectList","params":[{},{}],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"emptyObjectList","params":[{},{}],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "boolParam", true) - Expect((<-requestChan).body).To(Equal(`{"method":"boolParam","params":[true],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"boolParam","params":[true],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "boolParams", true, false, true) - Expect((<-requestChan).body).To(Equal(`{"method":"boolParams","params":[true,false,true],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"boolParams","params":[true,false,true],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "stringParam", "Alex") - Expect((<-requestChan).body).To(Equal(`{"method":"stringParam","params":["Alex"],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"stringParam","params":["Alex"],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "stringParams", "JSON", "RPC") - Expect((<-requestChan).body).To(Equal(`{"method":"stringParams","params":["JSON","RPC"],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"stringParams","params":["JSON","RPC"],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "numberParam", 123) - Expect((<-requestChan).body).To(Equal(`{"method":"numberParam","params":[123],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"numberParam","params":[123],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "numberParams", 123, 321) - Expect((<-requestChan).body).To(Equal(`{"method":"numberParams","params":[123,321],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"numberParams","params":[123,321],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "floatParam", 1.23) - Expect((<-requestChan).body).To(Equal(`{"method":"floatParam","params":[1.23],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"floatParam","params":[1.23],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "floatParams", 1.23, 3.21) - Expect((<-requestChan).body).To(Equal(`{"method":"floatParams","params":[1.23,3.21],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"floatParams","params":[1.23,3.21],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "manyParams", "Alex", 35, true, nil, 2.34) - Expect((<-requestChan).body).To(Equal(`{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "emptyMissingPublicFieldObject", struct{ name string }{name: "Alex"}) - Expect((<-requestChan).body).To(Equal(`{"method":"emptyMissingPublicFieldObject","params":{},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"emptyMissingPublicFieldObject","params":{},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "singleStruct", person) - Expect((<-requestChan).body).To(Equal(`{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "singlePointerToStruct", &person) - Expect((<-requestChan).body).To(Equal(`{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) pp := &person rpcClient.Call(context.Background(), "doublePointerStruct", &pp) - Expect((<-requestChan).body).To(Equal(`{"method":"doublePointerStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"doublePointerStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "multipleStructs", person, &drink) - Expect((<-requestChan).body).To(Equal(`{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "singleStructInArray", []interface{}{person}) - Expect((<-requestChan).body).To(Equal(`{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "namedParameters", map[string]interface{}{ "name": "Alex", "age": 35, }) - Expect((<-requestChan).body).To(Equal(`{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "anonymousStructNoTags", struct { Name string Age int }{"Alex", 33}) - Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "anonymousStructWithTags", struct { Name string `json:"name"` Age int `json:"age"` }{"Alex", 33}) - Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":1,"jsonrpc":"2.0"}`)) rpcClient.Call(context.Background(), "structWithNullField", struct { Name string `json:"name"` Address *string `json:"address"` }{"Alex", nil}) - Expect((<-requestChan).body).To(Equal(`{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":0,"jsonrpc":"2.0"}`)) + Expect((<-requestChan).body).To(Equal(`{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":1,"jsonrpc":"2.0"}`)) } func TestRpcClient_CallBatch(t *testing.T) { @@ -646,14 +649,14 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) { Expect(res).To(BeNil())*/ // result string is ok - responseBody = `[{"result": "ok","id":0}]` + responseBody = `[{"result": "ok","id":1}]` res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ NewRequest("something", 1, 2, 3), }) <-requestChan Expect(err).To(BeNil()) Expect(res[0].Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok"))))) - Expect(res[0].ID).To(Equal(0)) + Expect(res[0].ID).To(Equal(stdjson.Number("1"))) // result with error null is ok responseBody = `[{"result": "ok", "error": null}]` @@ -719,7 +722,7 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) { // check results var p *Person - responseBody = `[{"id":0, "result": {"name": "Alex", "age": 35}}, {"id":2, "result": {"name": "Lena", "age": 2}}]` + responseBody = `[{"id":1, "result": {"name": "Alex", "age": 35}}, {"id":2, "result": {"name": "Lena", "age": 2}}]` res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ NewRequest("something", 1, 2, 3), }) @@ -728,16 +731,18 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) { Expect(err).To(BeNil()) Expect(res[0].Error).To(BeNil()) - Expect(res[0].ID).To(Equal(0)) + Expect(res[0].ID).To(Equal(stdjson.Number("1"))) Expect(res[1].Error).To(BeNil()) - Expect(res[1].ID).To(Equal(2)) + Expect(res[1].ID).To(Equal(stdjson.Number("2"))) err = res[0].GetObject(&p) + require.NoError(t, err) Expect(p.Name).To(Equal("Alex")) Expect(p.Age).To(Equal(35)) err = res[1].GetObject(&p) + require.NoError(t, err) Expect(p.Name).To(Equal("Lena")) Expect(p.Age).To(Equal(2)) @@ -778,8 +783,10 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) { resMap := res.AsMap() var int1 int64 + require.NotNil(t, resMap[1]) resMap[1].GetObject(&int1) var int123 int64 + require.NotNil(t, resMap[123]) resMap[123].GetObject(&int123) Expect(int1).To(Equal(int64(1))) Expect(int123).To(Equal(int64(123))) @@ -931,7 +938,7 @@ func TestRpcClient_CallFor(t *testing.T) { rpcClient := NewClient(httpServer.URL) i := 0 - responseBody = `{"result":3,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":3,"id":1,"jsonrpc":"2.0"}` err := rpcClient.CallFor(context.Background(), &i, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -939,7 +946,7 @@ func TestRpcClient_CallFor(t *testing.T) { /* i = 3 - responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&i, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -947,20 +954,20 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(i).To(Equal(3)) var pi *int - responseBody = `{"result":4,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":4,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(pi, "something", 1, 2, 3) <-requestChan Expect(err).NotTo(BeNil()) Expect(pi).To(BeNil()) - responseBody = `{"result":4,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":4,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&pi, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) Expect(*pi).To(Equal(4)) *pi = 3 - responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&pi, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -968,7 +975,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(pi).To(BeNil()) p := &Person{} - responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(p, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -976,7 +983,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p).NotTo(BeNil()) var p2 *Person - responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(p2, "something", 1, 2, 3) <-requestChan Expect(err).NotTo(BeNil()) @@ -984,7 +991,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p2).To(BeNil()) p3 := Person{} - responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&p3, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -992,7 +999,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p).NotTo(BeNil()) p = &Person{Age: 35} - responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(p, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -1001,7 +1008,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p.Age).To(Equal(35)) p2 = nil - responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(p2, "something", 1, 2, 3) <-requestChan Expect(err).NotTo(BeNil()) @@ -1009,7 +1016,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p2).To(BeNil()) p2 = nil - responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&p2, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -1018,7 +1025,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p2.Name).To(Equal("Alex")) p3 = Person{Age: 35} - responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&p3, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -1027,7 +1034,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p.Age).To(Equal(35)) p3 = Person{Age: 35} - responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&p3, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) @@ -1036,7 +1043,7 @@ func TestRpcClient_CallFor(t *testing.T) { Expect(p.Age).To(Equal(35)) var intArray []int - responseBody = `{"result":[1, 2, 3],"id":0,"jsonrpc":"2.0"}` + responseBody = `{"result":[1, 2, 3],"id":1,"jsonrpc":"2.0"}` err = rpcClient.CallFor(&intArray, "something", 1, 2, 3) <-requestChan Expect(err).To(BeNil()) diff --git a/rpc/types.go b/rpc/types.go index ca51d050..d601b21a 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -21,6 +21,7 @@ import ( "encoding/base64" stdjson "encoding/json" "fmt" + "math/big" bin "github.com/gagliardetto/binary" @@ -214,6 +215,8 @@ type TransactionMeta struct { Rewards []BlockReward `json:"rewards"` LoadedAddresses LoadedAddresses `json:"loadedAddresses"` + + ComputeUnitsConsumed *uint64 `json:"computeUnitsConsumed"` } type InnerInstruction struct { @@ -292,7 +295,7 @@ type Account struct { Executable bool `json:"executable"` // The epoch at which this account will next owe rent - RentEpoch uint64 `json:"rentEpoch"` + RentEpoch *big.Int `json:"rentEpoch"` } type DataBytesOrJSON struct { diff --git a/rpc/ws/accountSubscribe.go b/rpc/ws/accountSubscribe.go index 807efa1c..8c54229a 100644 --- a/rpc/ws/accountSubscribe.go +++ b/rpc/ws/accountSubscribe.go @@ -92,6 +92,23 @@ func (sw *AccountSubscription) Recv() (*AccountResult, error) { } } +func (sw *AccountSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *AccountSubscription) Response() <-chan *AccountResult { + typedChan := make(chan *AccountResult, 1) + go func(ch chan *AccountResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*AccountResult) + }(typedChan) + return typedChan +} + func (sw *AccountSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/blockSubscribe.go b/rpc/ws/blockSubscribe.go index 559a269c..e1530f11 100644 --- a/rpc/ws/blockSubscribe.go +++ b/rpc/ws/blockSubscribe.go @@ -67,6 +67,10 @@ type BlockSubscribeOpts struct { // Whether to populate the rewards array. If parameter not provided, the default includes rewards. Rewards *bool + + // Max transaction version to return in responses. + // If the requested block contains a transaction with a higher version, an error will be returned. + MaxSupportedTransactionVersion *uint64 } // NOTE: Unstable, disabled by default @@ -114,6 +118,9 @@ func (cl *Client) BlockSubscribe( if opts.Rewards != nil { obj["rewards"] = opts.Rewards } + if opts.MaxSupportedTransactionVersion != nil { + obj["maxSupportedTransactionVersion"] = *opts.MaxSupportedTransactionVersion + } if len(obj) > 0 { params = append(params, obj) } @@ -150,6 +157,23 @@ func (sw *BlockSubscription) Recv() (*BlockResult, error) { } } +func (sw *BlockSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *BlockSubscription) Response() <-chan *BlockResult { + typedChan := make(chan *BlockResult, 1) + go func(ch chan *BlockResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*BlockResult) + }(typedChan) + return typedChan +} + func (sw *BlockSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/client.go b/rpc/ws/client.go index b1ca6224..261b4532 100644 --- a/rpc/ws/client.go +++ b/rpc/ws/client.go @@ -84,9 +84,16 @@ func ConnectWithOptions(ctx context.Context, rpcEndpoint string, opt *Options) ( if opt != nil && opt.HttpHeader != nil && len(opt.HttpHeader) > 0 { httpHeader = opt.HttpHeader } - c.conn, _, err = dialer.DialContext(ctx, rpcEndpoint, httpHeader) + var resp *http.Response + c.conn, resp, err = dialer.DialContext(ctx, rpcEndpoint, httpHeader) if err != nil { - return nil, fmt.Errorf("new ws client: dial: %w", err) + if resp != nil { + body, _ := io.ReadAll(resp.Body) + err = fmt.Errorf("new ws client: dial: %w, status: %s, body: %q", err, resp.Status, string(body)) + } else { + err = fmt.Errorf("new ws client: dial: %w", err) + } + return nil, err } c.connCtx, c.connCtxCancel = context.WithCancel(context.Background()) diff --git a/rpc/ws/logsSubscribe.go b/rpc/ws/logsSubscribe.go index 548a3ef2..1a9b5e03 100644 --- a/rpc/ws/logsSubscribe.go +++ b/rpc/ws/logsSubscribe.go @@ -116,6 +116,23 @@ func (sw *LogSubscription) Recv() (*LogResult, error) { } } +func (sw *LogSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *LogSubscription) Response() <-chan *LogResult { + typedChan := make(chan *LogResult, 1) + go func(ch chan *LogResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*LogResult) + }(typedChan) + return typedChan +} + func (sw *LogSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/programSubscribe.go b/rpc/ws/programSubscribe.go index 8639395f..fb94491a 100644 --- a/rpc/ws/programSubscribe.go +++ b/rpc/ws/programSubscribe.go @@ -95,6 +95,23 @@ func (sw *ProgramSubscription) Recv() (*ProgramResult, error) { } } +func (sw *ProgramSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *ProgramSubscription) Response() <-chan *ProgramResult { + typedChan := make(chan *ProgramResult, 1) + go func(ch chan *ProgramResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*ProgramResult) + }(typedChan) + return typedChan +} + func (sw *ProgramSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/rootSubscribe.go b/rpc/ws/rootSubscribe.go index 2d519dca..f54f30f5 100644 --- a/rpc/ws/rootSubscribe.go +++ b/rpc/ws/rootSubscribe.go @@ -51,6 +51,23 @@ func (sw *RootSubscription) Recv() (*RootResult, error) { } } +func (sw *RootSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *RootSubscription) Response() <-chan *RootResult { + typedChan := make(chan *RootResult, 1) + go func(ch chan *RootResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*RootResult) + }(typedChan) + return typedChan +} + func (sw *RootSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/slotSubscribe.go b/rpc/ws/slotSubscribe.go index d79aed36..05096bad 100644 --- a/rpc/ws/slotSubscribe.go +++ b/rpc/ws/slotSubscribe.go @@ -54,6 +54,23 @@ func (sw *SlotSubscription) Recv() (*SlotResult, error) { } } +func (sw *SlotSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *SlotSubscription) Response() <-chan *SlotResult { + typedChan := make(chan *SlotResult, 1) + go func(ch chan *SlotResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*SlotResult) + }(typedChan) + return typedChan +} + func (sw *SlotSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/slotsUpdatesSubscribe.go b/rpc/ws/slotsUpdatesSubscribe.go index 7aebb6c9..45712434 100644 --- a/rpc/ws/slotsUpdatesSubscribe.go +++ b/rpc/ws/slotsUpdatesSubscribe.go @@ -86,6 +86,23 @@ func (sw *SlotsUpdatesSubscription) Recv() (*SlotsUpdatesResult, error) { } } +func (sw *SlotsUpdatesSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *SlotsUpdatesSubscription) Response() <-chan *SlotsUpdatesResult { + typedChan := make(chan *SlotsUpdatesResult, 1) + go func(ch chan *SlotsUpdatesResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*SlotsUpdatesResult) + }(typedChan) + return typedChan +} + func (sw *SlotsUpdatesSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/rpc/ws/subscription.go b/rpc/ws/subscription.go index c1bebf6d..c209cdaf 100644 --- a/rpc/ws/subscription.go +++ b/rpc/ws/subscription.go @@ -61,4 +61,6 @@ func (s *Subscription) Unsubscribe() { func (s *Subscription) unsubscribe(err error) { s.closeFunc(err) + close(s.stream) + close(s.err) } diff --git a/rpc/ws/voteSubscribe.go b/rpc/ws/voteSubscribe.go index 9fc526bc..1d4aebc6 100644 --- a/rpc/ws/voteSubscribe.go +++ b/rpc/ws/voteSubscribe.go @@ -68,6 +68,23 @@ func (sw *VoteSubscription) Recv() (*VoteResult, error) { } } +func (sw *VoteSubscription) Err() <-chan error { + return sw.sub.err +} + +func (sw *VoteSubscription) Response() <-chan *VoteResult { + typedChan := make(chan *VoteResult, 1) + go func(ch chan *VoteResult) { + // TODO: will this subscription yield more than one result? + d, ok := <-sw.sub.stream + if !ok { + return + } + ch <- d.(*VoteResult) + }(typedChan) + return typedChan +} + func (sw *VoteSubscription) Unsubscribe() { sw.sub.Unsubscribe() } diff --git a/transaction.go b/transaction.go index 3d2293a6..e64ab4b0 100644 --- a/transaction.go +++ b/transaction.go @@ -73,6 +73,10 @@ func (t *Transaction) ResolveProgramIDIndex(programIDIndex uint16) (PublicKey, e return t.Message.ResolveProgramIDIndex(programIDIndex) } +func (t *Transaction) GetAccountIndex(account PublicKey) (uint16, error) { + return t.Message.GetAccountIndex(account) +} + // TransactionFromDecoder decodes a transaction from a decoder. func TransactionFromDecoder(decoder *bin.Decoder) (*Transaction, error) { var out *Transaction