diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13a2972..b73d3b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,14 +2,14 @@ name: Test on: push: - branches: + branches: - master - feat** - test** - docs** - ref** pull_request: - branches: [ master ] + branches: [master] jobs: test: @@ -18,12 +18,12 @@ jobs: BLOCKFROST_PROJECT_ID: ${{ secrets.BLOCKFROST_PROJECT_ID }} BLOCKFROST_IPFS_PROJECT_ID: ${{ secrets.BLOCKFROST_IPFS_PROJECT_ID }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.17 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.4 - - name: Test - run: go clean -testcache && go test -v + - name: Test + run: go clean -testcache && go test -v diff --git a/api_account.go b/api_account.go index 7f23a19..98b2391 100644 --- a/api_account.go +++ b/api_account.go @@ -31,7 +31,7 @@ type Account struct { Active bool `json:"active"` // Epoch of the most recent action - registration or deregistration - ActiveEpoch int64 `json:"active_epoch"` + ActiveEpoch *int64 `json:"active_epoch"` // Balance of the account in Lovelaces ControlledAmount string `json:"controlled_amount"` @@ -52,7 +52,7 @@ type Account struct { WithdrawableAmount string `json:"withdrawable_amount"` // Bech32 pool ID that owns the account - PoolID string `json:"pool_id"` + PoolID *string `json:"pool_id"` } // AccountRewardsHist return Account reward history @@ -267,11 +267,11 @@ func (c *apiClient) AccountRewardsHistoryAll(ctx context.Context, stakeAddress s } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -339,11 +339,11 @@ func (c *apiClient) AccountHistoryAll(ctx context.Context, address string) <-cha } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -411,11 +411,11 @@ func (c *apiClient) AccountDelegationHistoryAll(ctx context.Context, stakeAddres } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -483,11 +483,11 @@ func (c *apiClient) AccountRegistrationHistoryAll(ctx context.Context, stakeAddr } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -555,11 +555,11 @@ func (c *apiClient) AccountWithdrawalHistoryAll(ctx context.Context, stakeAddres } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -627,11 +627,11 @@ func (c *apiClient) AccountMIRHistoryAll(ctx context.Context, stakeAddress strin } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -698,11 +698,11 @@ func (c *apiClient) AccountAssociatedAddressesAll(ctx context.Context, stakeAddr } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -770,11 +770,11 @@ func (c *apiClient) AccountAssociatedAssetsAll(ctx context.Context, stakeAddress } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_addresses.go b/api_addresses.go index 69b73b0..a76f910 100644 --- a/api_addresses.go +++ b/api_addresses.go @@ -27,7 +27,7 @@ type Address struct { Amount []AddressAmount `json:"amount"` // Stake address that controls the key - StakeAddress string `json:"stake_address"` + StakeAddress *string `json:"stake_address"` // Address era. // Enum: "byron" "shelley" @@ -62,6 +62,7 @@ type AddressTransactions struct { } type AddressUTXO struct { + Address string `json:"address"` // Transaction hash of the UTXO TxHash string `json:"tx_hash"` @@ -73,7 +74,10 @@ type AddressUTXO struct { Block string `json:"block"` // The hash of the transaction output datum - DataHash string `json:"data_hash"` + DataHash *string `json:"data_hash"` + + InlineDatum *string `json:"inline_datum"` + ReferenceScriptHash *string `json:"reference_script_hash"` } type AddressTxResult struct { @@ -162,11 +166,11 @@ func (c *apiClient) AddressTransactionsAll(ctx context.Context, address string) } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -253,11 +257,81 @@ func (c *apiClient) AddressUTXOsAll(ctx context.Context, address string) <-chan } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { + select { + case <-quit: + fetchNextPage = false + default: + jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} + } + } + + close(jobs) + wg.Wait() + }() + return ch +} + +func (c *apiClient) AddressUTXOsAsset(ctx context.Context, address, asset string, query APIQueryParams) (utxos []AddressUTXO, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s/%s", c.server, resourceAddresses, address, resourceUTXOs, asset)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + v := req.URL.Query() + query.From = "" + query.To = "" + v = formatParams(v, query) + req.URL.RawQuery = v.Encode() + + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + + if err = json.NewDecoder(res.Body).Decode(&utxos); err != nil { + return + } + return utxos, nil +} + +func (c *apiClient) AddressUTXOsAssetAll(ctx context.Context, address, asset string) <-chan AddressUTXOResult { + ch := make(chan AddressUTXOResult, c.routines) + jobs := make(chan methodOptions, c.routines) + quit := make(chan bool, 1) + + wg := sync.WaitGroup{} + + for i := 0; i < c.routines; i++ { + wg.Add(1) + go func(jobs chan methodOptions, ch chan AddressUTXOResult, wg *sync.WaitGroup) { + defer wg.Done() + for j := range jobs { + autxo, err := c.AddressUTXOsAsset(j.ctx, address, asset, j.query) + if len(autxo) != j.query.Count || err != nil { + select { + case quit <- true: + default: + } + } + res := AddressUTXOResult{Res: autxo, Err: err} + ch <- res + } + + }(jobs, ch, &wg) + } + go func() { + defer close(ch) + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_addresses_test.go b/api_addresses_test.go index 9015434..196b648 100644 --- a/api_addresses_test.go +++ b/api_addresses_test.go @@ -23,10 +23,12 @@ var ( const testdata = "testdata" func TestAddressUnMarshall(t *testing.T) { + stakeAddress := "stake1ux3u6x5cs388djqz6awnyuvez2f6n8jzjhqq59s4yxhm8jskeh0t9" + want := blockfrost.Address{ Address: "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz", Amount: []blockfrost.AddressAmount{{Unit: "lovelace", Quantity: "0"}}, - StakeAddress: "stake1ux3u6x5cs388djqz6awnyuvez2f6n8jzjhqq59s4yxhm8jskeh0t9", + StakeAddress: &stakeAddress, Type: "shelley", Script: false, } @@ -66,7 +68,7 @@ func WriteGoldenFile(t *testing.T, path string, bytes []byte) { func ReadOrGenerateGoldenFile(t *testing.T, path string, v interface{}) []byte { t.Helper() - b, err := ioutil.ReadFile(path) + b, err := os.ReadFile(path) switch { case errors.Is(err, os.ErrNotExist): if *generate { @@ -152,3 +154,24 @@ func TestAddressUTXOs(t *testing.T) { var want []blockfrost.AddressUTXO testIntUtil(t, fp, &got, &want) } + +func TestAddressUTXOsAsset(t *testing.T) { + addr := "addr1q8zsjx7vxkl4esfejafhxthyew8c54c9ch95gkv3nz37sxrc9ty742qncmffaesxqarvqjmxmy36d9aht2duhmhvekgq3jd3w2" + asset := "d436d9f6b754582f798fe33f4bed12133d47493f78b944b9cc55fd1853756d6d69744c6f64676534393539" + + api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) + + got, err := api.AddressUTXOsAsset( + context.TODO(), + addr, + asset, + blockfrost.APIQueryParams{}, + ) + if err != nil { + t.Fatal(err) + } + + fp := filepath.Join(testdata, strings.ToLower(strings.TrimLeft(t.Name(), "Test"))+".golden") + var want []blockfrost.AddressUTXO + testIntUtil(t, fp, &got, &want) +} diff --git a/api_assets.go b/api_assets.go index 177b1e8..0296c1e 100644 --- a/api_assets.go +++ b/api_assets.go @@ -55,10 +55,24 @@ type Asset struct { // Count of mint and burn transactions MintOrBurnCount int `json:"mint_or_burn_count"` - // On-chain metadata stored in the minting transaction under label 721, - // community discussion around the standard ongoing at https://github.com/cardano-foundation/CIPs/pull/85 - OnchainMetadata AssetOnchainMetadata `json:"onchain_metadata"` - Metadata AssetMetadata `json:"metadata"` + // On-chain metadata which SHOULD adhere to the valid standards, based on which we perform the look up and display the asset (best effort) + OnchainMetadata *AssetOnchainMetadata `json:"onchain_metadata"` + // Enum: "CIP25v1" "CIP25v2" "CIP68v1" + // If on-chain metadata passes validation, we display the standard under which it is valid + OnchainMetadataStandard *string `json:"onchain_metadata_standard"` + // Arbitrary plutus data (CIP68). + OnchainMetadataExtra *string `json:"onchain_metadata_extra"` + // Off-chain metadata fetched from GitHub based on network. + Metadata *AssetMetadata `json:"metadata"` +} + +// Assets minted under a specific policy. +type AssetByPolicy struct { + // Hex-encoded asset full name + Asset string `json:"asset"` + + // Current asset quantity + Quantity string `json:"quantity"` } // AssetHistory contains history of an asset. @@ -102,6 +116,10 @@ type AssetResult struct { Res []Asset Err error } +type AssetByPolicyResult struct { + Res []AssetByPolicy + Err error +} type AssetAddressesAll struct { Res []AssetAddress @@ -109,7 +127,7 @@ type AssetAddressesAll struct { } // Assets returns a paginated list of assets. -func (c *apiClient) Assets(ctx context.Context, query APIQueryParams) (a []Asset, err error) { +func (c *apiClient) Assets(ctx context.Context, query APIQueryParams) (a []AssetByPolicy, err error) { requestUrl, err := url.Parse(fmt.Sprintf("%s/%s", c.server, resourceAssets)) if err != nil { return @@ -135,8 +153,8 @@ func (c *apiClient) Assets(ctx context.Context, query APIQueryParams) (a []Asset } // AssetsAll returns all assets. -func (c *apiClient) AssetsAll(ctx context.Context) <-chan AssetResult { - ch := make(chan AssetResult, c.routines) +func (c *apiClient) AssetsAll(ctx context.Context) <-chan AssetByPolicyResult { + ch := make(chan AssetByPolicyResult, c.routines) jobs := make(chan methodOptions, c.routines) quit := make(chan bool, 1) @@ -144,7 +162,7 @@ func (c *apiClient) AssetsAll(ctx context.Context) <-chan AssetResult { for i := 0; i < c.routines; i++ { wg.Add(1) - go func(jobs chan methodOptions, ch chan AssetResult, wg *sync.WaitGroup) { + go func(jobs chan methodOptions, ch chan AssetByPolicyResult, wg *sync.WaitGroup) { defer wg.Done() for j := range jobs { assets, err := c.Assets(j.ctx, j.query) @@ -154,7 +172,7 @@ func (c *apiClient) AssetsAll(ctx context.Context) <-chan AssetResult { default: } } - res := AssetResult{Res: assets, Err: err} + res := AssetByPolicyResult{Res: assets, Err: err} ch <- res } @@ -162,11 +180,11 @@ func (c *apiClient) AssetsAll(ctx context.Context) <-chan AssetResult { } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -300,11 +318,11 @@ func (c *apiClient) AssetAddressesAll(ctx context.Context, asset string) <-chan } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -317,7 +335,7 @@ func (c *apiClient) AssetAddressesAll(ctx context.Context, asset string) <-chan } // AssetsByPolicy returns list of assets minted under a specific policy. -func (c *apiClient) AssetsByPolicy(ctx context.Context, policyId string) (a []Asset, err error) { +func (c *apiClient) AssetsByPolicy(ctx context.Context, policyId string) (a []AssetByPolicy, err error) { requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s", c.server, resourcePolicyAssets, policyId)) if err != nil { return diff --git a/api_assets_test.go b/api_assets_test.go index f993d87..ca25d92 100644 --- a/api_assets_test.go +++ b/api_assets_test.go @@ -21,11 +21,11 @@ func TestAssetUnmarshal(t *testing.T) { InitialMintTxHash: "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad", MintOrBurnCount: 1, Quantity: "12000", - OnchainMetadata: blockfrost.AssetOnchainMetadata{ + OnchainMetadata: &blockfrost.AssetOnchainMetadata{ Image: "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226", Name: "My NFT token", }, - Metadata: blockfrost.AssetMetadata{ + Metadata: &blockfrost.AssetMetadata{ Name: "nutcoin", Description: "The Nut Coin", Ticker: "nutc", @@ -46,7 +46,7 @@ func TestResourceAssetsIntegration(t *testing.T) { t.Fatal(err) } fp := filepath.Join(testdata, strings.ToLower(strings.TrimLeft(t.Name(), "Test"))+".golden") - want := []blockfrost.Asset{} + want := []blockfrost.AssetByPolicy{} testIntUtil(t, fp, &got, &want) } @@ -112,7 +112,7 @@ func TestResourceAssetsByPolicyIntegration(t *testing.T) { } fp := filepath.Join(testdata, strings.ToLower(strings.TrimLeft(t.Name(), "Test"))+".golden") - want := []blockfrost.Asset{} + want := []blockfrost.AssetByPolicy{} testIntUtil(t, fp, &got, &want) } @@ -136,6 +136,13 @@ func testIntUtil(t *testing.T, fp string, got interface{}, want interface{}, opt } if !reflect.DeepEqual(got, want) { - t.Fatalf("expected %v got %v", want, got) + gotJSON, err := json.Marshal(got) + wantJSON, err := json.Marshal(want) + if err != nil { + t.Fatalf("\nexpected %v \ngot %v", want, got) + } + + t.Fatalf("\nexpected %s \ngot %s", string(wantJSON), string(gotJSON)) + } } diff --git a/api_block.go b/api_block.go index 3f1d7f9..e442770 100644 --- a/api_block.go +++ b/api_block.go @@ -6,6 +6,11 @@ import ( "fmt" "net/http" "net/url" + "sync" +) + +const ( + resourceBlocksAffectedAddresses = "addresses" ) // Block defines content of a block @@ -38,24 +43,42 @@ type Block struct { TxCount int `json:"tx_count"` // Total output within the block in Lovelaces - Output string `json:"output"` + Output *string `json:"output"` // Total fees within the block in Lovelaces - Fees string `json:"fees"` + Fees *string `json:"fees"` // VRF key of the block - BlockVRF string `json:"block_vrf"` + BlockVRF *string `json:"block_vrf"` + + // The hash of the operational certificate of the block producer + OPCert *string `json:"op_cert,omitempty"` // omitempty due to webhook test fixtures + + // The value of the counter used to produce the operational certificate + OPCertCounter *string `json:"op_cert_counter,omitempty"` // omitempty due to webhook test fixtures // Hash of the previous block PreviousBlock string `json:"previous_block"` // Hash of the next block - NextBlock string `json:"next_block"` + NextBlock *string `json:"next_block"` // Number of block confirmations Confirmations int `json:"confirmations"` } +type BlockAffectedAddresses struct { + Address string `json:"address"` + Transactions []struct { + TxHash string `json:"tx_hash"` + } `json:"transactions"` +} + +type BlockAffectedAddressesResult struct { + Res []BlockAffectedAddresses + Err error +} + // BlocksLatest Return the latest block available to the backends, also known as the // tip of the blockchain. func (c *apiClient) BlockLatest(ctx context.Context) (b Block, err error) { @@ -250,3 +273,73 @@ func (c *apiClient) BlocksBySlotAndEpoch(ctx context.Context, slotNumber int, ep } return bl, nil } + +// BlocksAddresses returns list of addresses affected in the specified block with additional information +func (c *apiClient) BlocksAddresses(ctx context.Context, hashOrNumber string, query APIQueryParams) (txs []BlockAffectedAddresses, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", c.server, resourceBlock, hashOrNumber, resourceBlocksAffectedAddresses)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + + v := req.URL.Query() + v = formatParams(v, query) + req.URL.RawQuery = v.Encode() + + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + + if err = json.NewDecoder(res.Body).Decode(&txs); err != nil { + return + } + return txs, nil +} + +func (c *apiClient) BlocksAddressesAll(ctx context.Context, hashOrNumber string) <-chan BlockAffectedAddressesResult { + ch := make(chan BlockAffectedAddressesResult, c.routines) + jobs := make(chan methodOptions, c.routines) + quit := make(chan bool, 1) + + wg := sync.WaitGroup{} + + for i := 0; i < c.routines; i++ { + wg.Add(1) + go func(jobs chan methodOptions, ch chan BlockAffectedAddressesResult, wg *sync.WaitGroup) { + defer wg.Done() + for j := range jobs { + affectedAddresses, err := c.BlocksAddresses(j.ctx, hashOrNumber, j.query) + if len(affectedAddresses) != j.query.Count || err != nil { + select { + case quit <- true: + default: + } + } + res := BlockAffectedAddressesResult{Res: affectedAddresses, Err: err} + ch <- res + } + + }(jobs, ch, &wg) + } + go func() { + defer close(ch) + fetchNextPage := true + for i := 1; fetchNextPage; i++ { + select { + case <-quit: + fetchNextPage = false + default: + jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} + } + } + + close(jobs) + wg.Wait() + }() + return ch +} diff --git a/api_block_test.go b/api_block_test.go index 1a140ef..e1e8709 100644 --- a/api_block_test.go +++ b/api_block_test.go @@ -2,7 +2,9 @@ package blockfrost_test import ( "context" + "path/filepath" "reflect" + "strings" "testing" "github.com/blockfrost/blockfrost-go" @@ -109,3 +111,19 @@ func TestBlockPreviousIntegration(t *testing.T) { t.Fatal("got null struct") } } +func TestBlocksAddressesIntegration(t *testing.T) { + hash := "5ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a" + api := blockfrost.NewAPIClient( + blockfrost.APIClientOptions{}, + ) + got, err := api.BlocksAddresses(context.TODO(), hash, blockfrost.APIQueryParams{}) + + if err != nil { + t.Fatal(err) + } + + fp := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") + want := []blockfrost.BlockAffectedAddresses{} + + testIntUtil(t, fp, &got, &want) +} diff --git a/api_epochs.go b/api_epochs.go index f111015..6203cf2 100644 --- a/api_epochs.go +++ b/api_epochs.go @@ -25,10 +25,15 @@ type EpochStake struct { Amount string `json:"amount"` } +type EpochStakeByPool struct { + StakeAddress string `json:"stake_address"` + Amount string `json:"amount"` +} + // Epoch contains information on an epoch. type Epoch struct { // Sum of all the active stakes within the epoch in Lovelaces - ActiveStake string `json:"active_stake"` + ActiveStake *string `json:"active_stake"` // Number of blocks within the epoch BlockCount int `json:"block_count"` @@ -85,9 +90,9 @@ type EpochParameters struct { // Maximum transaction size MaxTxSize int `json:"max_tx_size"` - + // The maximum Val size - MaxValSize string `json:"max_val_size"` + MaxValSize *string `json:"max_val_size "` // The linear factor for the minimum fee calculation for given epoch MinFeeA int `json:"min_fee_a"` @@ -121,9 +126,40 @@ type EpochParameters struct { // Treasury expansion Tau float32 `json:"tau"` - - // The cost per UTXO word + + // Cost per UTxO word for Alonzo. Cost per UTxO byte for Babbage and later. + // Deprecated: Use CoinsPerUTxOSize instead CoinsPerUtxOWord string `json:"coins_per_utxo_word"` + + // Cost models parameters for Plutus Core scripts + CostModels *interface{} `json:"cost_models"` + + // The per word cost of script memory usage + PriceMem *float32 `json:"price_mem"` + + // The cost of script execution step usage + PriceStep *float32 `json:"price_step"` + + // The maximum number of execution memory allowed to be used in a single transaction + MaxTxExMem *string `json:"max_tx_ex_mem"` + + // The maximum number of execution steps allowed to be used in a single transaction + MaxTxExSteps *string `json:"max_tx_ex_steps"` + + // The maximum number of execution memory allowed to be used in a single block + MaxBlockExMem *string `json:"max_block_ex_mem"` + + // The maximum number of execution steps allowed to be used in a single block + MaxBlockExSteps *string `json:"max_block_ex_steps"` + + // The percentage of the transactions fee which must be provided as collateral when including non-native scripts + CollateralPercent *int `json:"collateral_percent"` + + // The maximum number of collateral inputs allowed in a transaction + MaxCollateralInputs *int `json:"max_collateral_inputs"` + + // Cost per UTxO word for Alonzo. Cost per UTxO byte for Babbage and later. + CoinsPerUTxOSize *string `json:"coins_per_utxo_size"` } type EpochResult struct { @@ -135,6 +171,10 @@ type EpochStakeResult struct { Res []EpochStake Err error } +type EpochStakeByPoolResult struct { + Res []EpochStakeByPool + Err error +} type BlockDistributionResult struct { Res []string @@ -263,11 +303,11 @@ func (c *apiClient) EpochNextAll(ctx context.Context, epochNumber int) <-chan Ep } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -334,11 +374,11 @@ func (c *apiClient) EpochPreviousAll(ctx context.Context, epochNumber int) <-cha } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -405,11 +445,11 @@ func (c *apiClient) EpochStakeDistributionAll(ctx context.Context, epochNumber i } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -422,7 +462,7 @@ func (c *apiClient) EpochStakeDistributionAll(ctx context.Context, epochNumber i } // EpochStakeDistributionByPool returns the active stake distribution for the epoch specified by stake pool. -func (c *apiClient) EpochStakeDistributionByPool(ctx context.Context, epochNumber int, poolId string, query APIQueryParams) (eps []EpochStake, err error) { +func (c *apiClient) EpochStakeDistributionByPool(ctx context.Context, epochNumber int, poolId string, query APIQueryParams) (eps []EpochStakeByPool, err error) { requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%d/%s/%s", c.server, resourceEpochs, epochNumber, resourceEpochsStakes, poolId)) if err != nil { return @@ -449,8 +489,8 @@ func (c *apiClient) EpochStakeDistributionByPool(ctx context.Context, epochNumbe // EpochStakeDistributionByPoolAll fetches all active stake distribution for the epoch specified by stake pool. // Returns a channel of type EpochStakeResult -func (c *apiClient) EpochStakeDistributionByPoolAll(ctx context.Context, epochNumber int, poolId string) <-chan EpochStakeResult { - ch := make(chan EpochStakeResult, c.routines) +func (c *apiClient) EpochStakeDistributionByPoolAll(ctx context.Context, epochNumber int, poolId string) <-chan EpochStakeByPoolResult { + ch := make(chan EpochStakeByPoolResult, c.routines) jobs := make(chan methodOptions, c.routines) quit := make(chan bool, 1) @@ -458,7 +498,7 @@ func (c *apiClient) EpochStakeDistributionByPoolAll(ctx context.Context, epochNu for i := 0; i < c.routines; i++ { wg.Add(1) - go func(jobs chan methodOptions, ch chan EpochStakeResult, wg *sync.WaitGroup) { + go func(jobs chan methodOptions, ch chan EpochStakeByPoolResult, wg *sync.WaitGroup) { defer wg.Done() for j := range jobs { eps, err := c.EpochStakeDistributionByPool(j.ctx, epochNumber, poolId, j.query) @@ -468,7 +508,7 @@ func (c *apiClient) EpochStakeDistributionByPoolAll(ctx context.Context, epochNu default: } } - res := EpochStakeResult{Res: eps, Err: err} + res := EpochStakeByPoolResult{Res: eps, Err: err} ch <- res } @@ -476,11 +516,11 @@ func (c *apiClient) EpochStakeDistributionByPoolAll(ctx context.Context, epochNu } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -547,11 +587,11 @@ func (c *apiClient) EpochBlockDistributionAll(ctx context.Context, epochNumber i } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -618,11 +658,11 @@ func (c *apiClient) EpochBlockDistributionByPoolAll(ctx context.Context, epochNu } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_epochs_test.go b/api_epochs_test.go index ab5716a..611b323 100644 --- a/api_epochs_test.go +++ b/api_epochs_test.go @@ -92,7 +92,7 @@ func TestEpochStakeDistributionByPoolIntegration(t *testing.T) { if err != nil { t.Fatal(err) } - want := []blockfrost.EpochStake{} + want := []blockfrost.EpochStakeByPool{} fp := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") testIntUtil(t, fp, &got, &want) } @@ -125,7 +125,7 @@ func TestEpochBlockDistributionByPoolIntegration(t *testing.T) { func TestEpochParametersIntegration(t *testing.T) { api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) - got, err := api.EpochParameters(context.TODO(), 225) + got, err := api.EpochParameters(context.TODO(), 453) if err != nil { t.Fatal(err) } diff --git a/api_mempool.go b/api_mempool.go new file mode 100644 index 0000000..dddc025 --- /dev/null +++ b/api_mempool.go @@ -0,0 +1,271 @@ +package blockfrost + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "sync" +) + +const ( + resourceMempool = "mempool" + resourceMempoolAddresses = "addresses" +) + +type Mempool struct { + TxHash string `json:"tx_hash"` +} + +type MempoolTransaction struct { + // Count of asset mints and burns within the transaction + AssetMintOrBurnCount int `json:"asset_mint_or_burn_count"` + + // Count of the delegations within the transaction + DelegationCount int `json:"delegation_count"` + + // Deposit within the transaction in Lovelaces + Deposit string `json:"deposit"` + + // Fees of the transaction in Lovelaces + Fees string `json:"fees"` + + // Transaction hash + Hash string `json:"hash"` + + // Left (included) endpoint of the timelock validity intervals + InvalidBefore *string `json:"invalid_before"` + + // Right (excluded) endpoint of the timelock validity intervals + InvalidHereafter *string `json:"invalid_hereafter"` + + // Count of the MIR certificates within the transaction + MirCertCount int `json:"mir_cert_count"` + OutputAmount []struct { + // The quantity of the unit + Quantity string `json:"quantity"` + + // The unit of the value + Unit string `json:"unit"` + } `json:"output_amount"` + + // Count of the stake pool retirement certificates within the transaction + PoolRetireCount int `json:"pool_retire_count"` + + // Count of the stake pool registration and update certificates within the transaction + PoolUpdateCount int `json:"pool_update_count"` + + // Count of redeemers within the transaction + RedeemerCount int `json:"redeemer_count"` + + // Size of the transaction in Bytes + Size int `json:"size"` + + // Count of the stake keys (de)registration and delegation certificates within the transaction + StakeCertCount int `json:"stake_cert_count"` + + // Count of UTXOs within the transaction + UtxoCount int `json:"utxo_count"` + + // Script passed validation + ValidContract bool `json:"valid_contract"` + + // Count of the withdrawals within the transaction + WithdrawalCount int `json:"withdrawal_count"` +} + +type MempoolTransactionOutput struct { + // Output address + Address string `json:"address"` + Amount []TxAmount `json:"amount"` + OutputIndex int `json:"output_index"` + DataHash *string `json:"data_hash"` + InlineDatum *string `json:"inline_datum"` + Collateral bool `json:"collateral"` + ReferenceScriptHash *string `json:"reference_script_hash"` +} +type MempoolTransactionInput struct { + Address string `json:"address"` + OutputIndex float32 `json:"output_index"` + TxHash string `json:"tx_hash"` + Collateral bool `json:"collateral"` + Reference bool `json:"reference"` +} + +type MempoolTransactionRedeemers struct { + TxIndex int `json:"tx_index"` + Purpose string `json:"purpose"` + UnitMem string `json:"unit_mem"` + UnitSteps string `json:"unit_steps"` +} + +type MempoolTransactionContent struct { + Tx MempoolTransaction `json:"tx"` + Inputs []MempoolTransactionInput `json:"inputs"` + Outputs []MempoolTransactionOutput `json:"outputs"` + Redeemers []MempoolTransactionRedeemers `json:"redeemers"` +} + +type MempoolResult struct { + Res []Mempool + Err error +} + +func (c *apiClient) Mempool(ctx context.Context, query APIQueryParams) (a []Mempool, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s", c.server, resourceMempool)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + v := req.URL.Query() + v = formatParams(v, query) + req.URL.RawQuery = v.Encode() + + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + + if err = json.NewDecoder(res.Body).Decode(&a); err != nil { + return + } + return a, nil +} + +// AssetsAll returns all assets. +func (c *apiClient) MempoolAll(ctx context.Context) <-chan MempoolResult { + ch := make(chan MempoolResult, c.routines) + jobs := make(chan methodOptions, c.routines) + quit := make(chan bool, 1) + + wg := sync.WaitGroup{} + + for i := 0; i < c.routines; i++ { + wg.Add(1) + go func(jobs chan methodOptions, ch chan MempoolResult, wg *sync.WaitGroup) { + defer wg.Done() + for j := range jobs { + mempool, err := c.Mempool(j.ctx, j.query) + if len(mempool) != j.query.Count || err != nil { + select { + case quit <- true: + default: + } + } + res := MempoolResult{Res: mempool, Err: err} + ch <- res + } + + }(jobs, ch, &wg) + } + go func() { + defer close(ch) + fetchNextPage := true + for i := 1; fetchNextPage; i++ { + select { + case <-quit: + fetchNextPage = false + default: + jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} + } + } + + close(jobs) + wg.Wait() + }() + return ch +} + +func (c *apiClient) MempoolTx(ctx context.Context, hash string) (tc MempoolTransactionContent, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s", c.server, resourceMempool, hash)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + if err = json.NewDecoder(res.Body).Decode(&tc); err != nil { + return + } + return tc, nil +} + +func (c *apiClient) MempoolByAddress(ctx context.Context, address string, query APIQueryParams) (a []Mempool, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", c.server, resourceMempool, resourceMempoolAddresses, address)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + v := req.URL.Query() + v = formatParams(v, query) + req.URL.RawQuery = v.Encode() + + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + + if err = json.NewDecoder(res.Body).Decode(&a); err != nil { + return + } + return a, nil +} + +// AssetsAll returns all assets. +func (c *apiClient) MempoolByAddressAll(ctx context.Context, address string) <-chan MempoolResult { + ch := make(chan MempoolResult, c.routines) + jobs := make(chan methodOptions, c.routines) + quit := make(chan bool, 1) + + wg := sync.WaitGroup{} + + for i := 0; i < c.routines; i++ { + wg.Add(1) + go func(jobs chan methodOptions, ch chan MempoolResult, wg *sync.WaitGroup) { + defer wg.Done() + for j := range jobs { + mempool, err := c.MempoolByAddress(j.ctx, address, j.query) + if len(mempool) != j.query.Count || err != nil { + select { + case quit <- true: + default: + } + } + res := MempoolResult{Res: mempool, Err: err} + ch <- res + } + + }(jobs, ch, &wg) + } + go func() { + defer close(ch) + fetchNextPage := true + for i := 1; fetchNextPage; i++ { + select { + case <-quit: + fetchNextPage = false + default: + jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} + } + } + + close(jobs) + wg.Wait() + }() + return ch +} diff --git a/api_mempool_test.go b/api_mempool_test.go new file mode 100644 index 0000000..1adf90c --- /dev/null +++ b/api_mempool_test.go @@ -0,0 +1,90 @@ +package blockfrost_test + +import ( + "context" + "path/filepath" + "reflect" + "testing" + + "github.com/blockfrost/blockfrost-go" +) + +func TestMempoolUnmarshal(t *testing.T) { + want := []blockfrost.Mempool{ + {TxHash: "abc"}, + {TxHash: "def"}, + } + fp := filepath.Join(testdata, "json", "mempool", "mempool.json") + got := []blockfrost.Mempool{} + testStructGotWant(t, fp, &got, &want) +} + +func TestMempoolTransactionContentUnmarshal(t *testing.T) { + invalidHereafter := "109798439" + want := blockfrost.MempoolTransactionContent{ + Tx: blockfrost.MempoolTransaction{ + Hash: "1f96d3824eb2aeeb0b09b99748bb70ac681e0cae6e37e01c43958b79ca69c986", + OutputAmount: []struct { + Quantity string `json:"quantity"` + + // The unit of the value + Unit string `json:"unit"` + }{ + { + Unit: "lovelace", + Quantity: "2837715", + }, + }, + Fees: "369133", + Deposit: "0", + Size: 4683, + InvalidBefore: nil, + InvalidHereafter: &invalidHereafter, + UtxoCount: 2, + ValidContract: true, + }, + Inputs: []blockfrost.MempoolTransactionInput{{ + Address: "addr1vx9wkegx062xmmdzfd69jz6dt48p5mse4v35mml5h6ceznq8ap8fz", + TxHash: "7aa461f4f924586864c74141d457e70cfb26b2b5b9cfea4c5d5580f037ef41da", + OutputIndex: 0, + Collateral: false, + Reference: false, + }}, + Outputs: []blockfrost.MempoolTransactionOutput{ + { + Address: "addr1vx9wkegx062xmmdzfd69jz6dt48p5mse4v35mml5h6ceznq8ap8fz", + Amount: []blockfrost.TxAmount{{ + Unit: "lovelace", + Quantity: "2837715", + }, { + Unit: "6787a47e9f73efe4002d763337140da27afa8eb9a39413d2c39d4286524144546f6b656e73", + Quantity: "15000", + }}, + OutputIndex: 0, + DataHash: nil, + InlineDatum: nil, + ReferenceScriptHash: nil, + Collateral: false, + }}, + Redeemers: nil, + } + fp := filepath.Join(testdata, "json", "mempool", "transaction.json") + got := blockfrost.MempoolTransactionContent{} + testStructGotWant(t, fp, &got, &want) +} + +func TestMempoolIntegration(t *testing.T) { + api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) + + got, err := api.Mempool(context.TODO(), blockfrost.APIQueryParams{}) + if err != nil { + t.Fatal(err) + } + + var want []blockfrost.Mempool + if reflect.TypeOf(got) != reflect.TypeOf(want) { + t.Fatalf("Expected type []blockfrost.Mempool, got type %T", got) + } +} + +// MempoolTx and MempoolByAddress need mocked server to provide static mempool transaction diff --git a/api_metadata.go b/api_metadata.go index e478032..e6259cd 100644 --- a/api_metadata.go +++ b/api_metadata.go @@ -18,9 +18,9 @@ const ( // MetadataTxLabel return Transaction metadata labels // List of all used transaction metadata labels. type MetadataTxLabel struct { - Label string `json:"label"` - Cip10 string `json:"cip10"` - Count string `json:"count"` + Label string `json:"label"` + Cip10 *string `json:"cip10"` + Count string `json:"count"` } // MetadataTxContentInJSON Transaction metadata content raw in JSON @@ -30,14 +30,14 @@ type MetadataTxContentInJSON struct { TxHash string `json:"tx_hash"` // string or object or Array of any or integer or number or boolean Nullable // Content of the JSON metadata - JSONMetadata interface{} `json:"json_metadata"` + JSONMetadata *interface{} `json:"json_metadata"` } // MetadataTxContentInCBOR return Transaction metadata content in CBOR // Transaction metadata per label. type MetadataTxContentInCBOR struct { - TxHash string `json:"tx_hash"` - CborMetadata string `json:"cbor_metadata"` + TxHash string `json:"tx_hash"` + Metadata *string `json:"metadata"` } type MetadataTxLabelResult struct { @@ -114,11 +114,11 @@ func (c *apiClient) MetadataTxLabelsAll(ctx context.Context) <-chan MetadataTxLa } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -187,11 +187,11 @@ func (c *apiClient) MetadataTxContentInJSONAll(ctx context.Context, label string } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -260,11 +260,11 @@ func (c *apiClient) MetadataTxContentInCBORAll(ctx context.Context, label string } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_nutlink.go b/api_nutlink.go index 4fbdf3c..8d91c35 100644 --- a/api_nutlink.go +++ b/api_nutlink.go @@ -147,11 +147,11 @@ func (c *apiClient) TickersAll(ctx context.Context, address string) <-chan Ticke } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -219,11 +219,11 @@ func (c *apiClient) TickerRecordsAll(ctx context.Context, ticker string) <-chan } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -289,11 +289,11 @@ func (c *apiClient) AddressTickerRecordsAll(ctx context.Context, address string, } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_pool.go b/api_pool.go index 3e31dce..302722a 100644 --- a/api_pool.go +++ b/api_pool.go @@ -74,24 +74,24 @@ type PoolHistory struct { // PoolMetadata return Stake pool metadata // Stake pool registration metadata. type PoolMetadata struct { - PoolID string `json:"pool_id"` - Hex string `json:"hex"` - URL string `json:"url"` - Hash string `json:"hash"` - Ticker string `json:"ticker"` - Name string `json:"name"` - Description string `json:"description"` - Homepage string `json:"homepage"` + PoolID string `json:"pool_id"` + Hex string `json:"hex"` + URL *string `json:"url"` + Hash *string `json:"hash"` + Ticker *string `json:"ticker"` + Name *string `json:"name"` + Description *string `json:"description"` + Homepage *string `json:"homepage"` } // PoolRelay return Stake pool relays // Relays of a stake pool. type PoolRelay struct { - Ipv4 string `json:"ipv4"` - Ipv6 string `json:"ipv6"` - DNS string `json:"dns"` - DNSSrv string `json:"dns_srv"` - Port int `json:"port"` + Ipv4 *string `json:"ipv4"` + Ipv6 *string `json:"ipv6"` + DNS *string `json:"dns"` + DNSSrv *string `json:"dns_srv"` + Port int `json:"port"` } // PoolDelegator return Stake pool delegators @@ -205,11 +205,11 @@ func (c *apiClient) PoolsAll(ctx context.Context) <-chan PoolsResult { } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -277,11 +277,11 @@ func (c *apiClient) PoolsRetiredAll(ctx context.Context) <-chan PoolsRetiredResu } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -350,11 +350,11 @@ func (c *apiClient) PoolsRetiringAll(ctx context.Context) <-chan PoolsRetiringRe } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -445,11 +445,11 @@ func (c *apiClient) PoolHistoryAll(ctx context.Context, poolId string) <-chan Po } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -565,11 +565,11 @@ func (c *apiClient) PoolDelegatorsAll(ctx context.Context, poolId string) <-chan } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -637,11 +637,11 @@ func (c *apiClient) PoolBlocksAll(ctx context.Context, poolId string) <-chan Poo } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -709,11 +709,11 @@ func (c *apiClient) PoolUpdatesAll(ctx context.Context, poolId string) <-chan Po } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_script.go b/api_script.go index c704bb9..7ec1f29 100644 --- a/api_script.go +++ b/api_script.go @@ -12,6 +12,10 @@ import ( const ( resourceScripts = "scripts" resourceRedeemers = "redeemers" + // resourceScriptsJson = "json" + // resourceScriptsCbor = "cbor" + // resourceDatum = "datum" + // resourceDatumCbor = "cbor" ) // Script contains information about a script @@ -23,7 +27,7 @@ type Script struct { Type string `json:"type"` // The size of the CBOR serialised script, if a Plutus script - SerialisedSize int `json:"serialised_size"` + SerialisedSize *int `json:"serialised_size"` } // ScriptRedeemer contains information about a script redeemer. @@ -120,11 +124,11 @@ func (c *apiClient) ScriptsAll(ctx context.Context) <-chan ScriptAllResult { } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } @@ -210,11 +214,11 @@ func (c *apiClient) ScriptRedeemersAll(ctx context.Context, address string) <-ch } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/api_script_test.go b/api_script_test.go index 8f740e1..f9609eb 100644 --- a/api_script_test.go +++ b/api_script_test.go @@ -3,9 +3,10 @@ package blockfrost_test import ( "context" "encoding/json" - "io/ioutil" + "os" "path/filepath" "reflect" + "strings" "testing" "github.com/blockfrost/blockfrost-go" @@ -25,7 +26,7 @@ func TestScriptUnmarshal(t *testing.T) { } func testStructGotWant(t *testing.T, fp string, got interface{}, want interface{}) { - bytes, err := ioutil.ReadFile(fp) + bytes, err := os.ReadFile(fp) if err != nil { t.Fatalf("failed to open json test file %s with err %v", fp, err) } @@ -63,20 +64,15 @@ func TestUnmarshalRedeemer(t *testing.T) { func TestIntegrationResourceRedeemers(t *testing.T) { api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) - scripts, err := api.Scripts(context.TODO(), blockfrost.APIQueryParams{}) - testErrorHelper(t, err) - if len(scripts) == 0 { - t.Fatal("Failed to fetch scripts") - } - - got, err := api.ScriptRedeemers(context.TODO(), scripts[0].ScriptHash, blockfrost.APIQueryParams{}) + got, err := api.ScriptRedeemers(context.TODO(), "4f590a3d80ae0312bad0b64d540c3ff5080e77250e9dbf5011630016", blockfrost.APIQueryParams{}) if err != nil { t.Fatal(err) } - if reflect.DeepEqual(got, []blockfrost.ScriptRedeemer{}) { - t.Logf("got null %+v", got) - } + fp := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") + want := []blockfrost.ScriptRedeemer{} + + testIntUtil(t, fp, &got, &want) } func TestIntegrationResourceScripts(t *testing.T) { diff --git a/api_transaction_test.go b/api_transaction_test.go index d48c041..8de312e 100644 --- a/api_transaction_test.go +++ b/api_transaction_test.go @@ -9,7 +9,12 @@ import ( "github.com/blockfrost/blockfrost-go" ) +var tx_cbor = []byte("84a800848258205a19a31ee27cc8311f5144d121fb7f10e6e0702feb0df5ffc7f99dc33bda470f00825820becc111316602fb09d4d2b2e8a3b7ff7b2d1841175fbfe2942e14e463a940d2b01825820bdff90a5ed9604e7d42cdbe412d2a0368207835e22a552244728a8259b2d62080082582063baa1897526b6df1f64955885d8f8231328e6a6382fdbcd8cee1f6002f13c6902018383581d710449932f9da0258d220c39f803ceb8e2c45fdc605e8dd42f35558b58821a042c1d80a1581c800df05a0cc6b6f0d28aaa1812135bd9eebfbf5e8e80fd47da9989eba14c537061636542756442696430015820634e97b64773663ac25f44f43136a7b1458ce109915f1635f859aac731118d6c8258390102bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b3cf104c3f49a0337f2396862fd9374cbf1e2d3704c7c41dffb7c6f69821a00118f32a1581c29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6a1434d494e1a0099498e8258390102bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b3cf104c3f49a0337f2396862fd9374cbf1e2d3704c7c41dffb7c6f691a005f3895021a000979180758205852e0a4c24247a3cc059ab6f6875e7060f58d493d4c4c909ba354e0dcd3f8870b5820ee5ce4de6e28e2c8c0ca47e6fb617fe4c89046434ccbf4bbc4d914c2ea947b5c0d81825820bdff90a5ed9604e7d42cdbe412d2a0368207835e22a552244728a8259b2d620800108258390102bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b3cf104c3f49a0337f2396862fd9374cbf1e2d3704c7c41dffb7c6f691a041de7dc111a000e35a4a30381591974591971010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199911191919199119999111191919191999111991191919191919911991199999111119191919199911199911199999999111111119911999991111199991111991199119911991199119911991199119911919191919191919191919191919191919191999911119911919111119191919191a982c0049119119119119111911192999a983c80b909a983f00091129999a983300d099838999a837a83c9840008021a9aa84480a80b11000998389991199ab9a3371200400212402122026604c60c600a605800c60c6a02a660e26601aa02a004a66a611c026604ea03000621200226605a60c66603aa03000660c600a2c2660e26604ea030006660e2666a0dea0f26a6aa11202a02c440020fc6601aa02a0042660e2666a0dea0f26a6aa11202a02c440020fc660e26601aa02a004660e26601e6603aa030006004660106a05c60c600aa028426a60fc002444a6666a60cc0342c2660e26601e6603aa030a028004660e26605a00200e6601000200626604ea0300062c2a6666a60c402c2a66a6114026644666ae68cdc480100084680847009806800a40042a66a6a10402605802a2610e022c442a66a6a1080200226112022c46442a66a6a10e0200226a6aa114026a6aa11402a0044400444a666a610002002426a610a02002444660f06602800c004660f0660686a06a60d400c01c660f0666a0ec0d200290011a9aa848009a9aa84800a80411000912999a98430080090b10b0999a83c0359981a180d00724004603400442c2660e8666a0e40ca6605c60280109001180a0011a9aa846009a9aa84600a80211000912999a984100800909a9843808009111983d1980b0030011983d1981b1a81b9836003008199a83c035800a400442c2c442611c022c266aa11202602c006602c0022a66a6a10402605802a26110022c4646442a66a6a10c020022a666a60fa6a6aa11202a00644002426a610402002444660ea66022a00c004660ea660626a06460cea00c016666a0e60cc002900110b0b1109847008b09a9aa84380a800910010980a0008b0b0b299a9a840809a815091199aa83111299a984680a99a9a83a981418148011084800884700899802181398148010008800800991a981c8009111111111005280a8983f0b110a99a9a841808008801110a99a9a842808008a999a983e00d109a984080800911299a984880998080040010a99a98488099809003001080409843808b0a99a9848809980900300109843808b0803109a984080800911299a984880998090040010a99a98488099808003001080409844008b0a99a9848809980800300109844008b08030b1109842008b1191919191299a98460099815803241012179fa042660de6605660c266036a02ca0126054a004660de6605660c266036a02c6a6aa10e02010440046054a0066605660c266036a02c002605466052660526605200ca004a0066a6aaa0d6a0084440022660de6605660c266036a02ca0126054a00a6605660c266036a02c00260546605200ca00a26a6aaa0d2a00444400626a6aaa0d0a0024440042666aaa0d0660e80046a6aaa0ce00c444002660e80046a6aa1060200844002660e80040062660e60026a6aaa0cc00a44400426a6aaa0c400244400644660446660b200400e666a0cae2800c005200222330203330570020063335063714006002900111991180100099119900099000999aa8011919a81591199a8148018008011a81300099a8151111801980100090009119b8000148008005200030221200133233553022120012253353081013003002133507a00200110015079235355505e001222330653335063029006003333506305600148008d407c488ccd5415c88d4d541f400888ccd5416c88d4d5420404008894cd4c22004ccd5cd19b87001480002280422404400c4cc028ccd5541a001800800400c00c00400400c54cd4d41c8c8d4c0a0004888888888800d40104c19c588854cd4d41d00044008884c1ac584d4d541d140048800854cd4d41c0c06800c4c198588854cd4d41c80044c00c008884c1a8588d4c0acd4c0a400488800c88cd4c12c0089894cd4d418cc058010854cd4d4190c8d4c0a800488888888894cd4d41bcccd54c0ac4800540bc8d4d5420c04004894cd4c22804ccd5cd19b8f00200f08c0108b01135074003150730022135072353550830100122001150705006232323215335350683333333574800846666ae68cdc3a8012400846666aae7d4010941b08cccd55cf9aba25005253353506c306835742a00c426a0de60ec0022a0da4a0da0d40d246666ae68cdc3a801a400446666aae7d4014941b48cccd55cf9aba25006253353506d306935742a00e426a0e060f00022a0dc4a0dc0d60d446666ae68cdc3a8022400046666aae7d40188d41bc1d4941b81ac941b526499262506a2506a2506a2506a06721335507d301b00a00116135573aa00426aae7940044dd50008b0b09a98108009100111199aa980b090009119aa98060900091a9aa8388009119aa83a00119aa98078900091a9aa83a0009119aa83b801199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8388009119aa83a001199a9aa805800919aa98080900091a9aa83a8009119aa83c0011aa80900080091199aaa805010801000919aa98080900091a9aa83a8009119aa83c0011aa808000800999aaa80280e001000a8369a98100011111111111199aa981009000911a98180011111a981a8019119a982a8011299a984280999ab9a3371e02600210e0210c02266a0fc00a00e200e400ea0ee012222444666aa602c24002a0d866aa60142400246a6aa0de0024466aa0e40046aa018002666aa602c24002446a6aa0e000444a66a60ee666aa6036240026466a04444666a6a016006440040040026a6a0120024400266a01244a66a60f200420f620020f046a6aa0e6002446601400400a00c2006266a0e0008006a0da00266aa60142400246a6aa0de002446466aa0e6006600200a640026aa0f244a66a6a0e000226aa0180064426a6aa0ea00444a66a60f866018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0d8442244a66a6a0ca0022a0ce44266a0d0600800466aa600c240020080024466e0000800488d4c05800888888888894cd4d416cccd54c05c48005406c94cd4c1d0ccd5cd19b8f00c00107607513505e0011505d003210761074235301800122200223530170012220012353014001220012233702004002400244666ae68cdc4001000832032890008919a800a82ca82d11a9805000911a98070011111111111299a9a829a9999a981500590a82a90a82a90a82a90999aa980809000a80a11a980e00091299a9837a99a9837999ab9a3371e6a6066004440046a6066008440040e20e02666ae68cdc39a9819801110009a981980211000838838083809a82c8018a82c005909a980d800911a980f800911199aa980a09000911a98120011111a9814804111a98158029119299a983d99a9826002919a98268021299a983e999ab9a3371e0040020fe0fc2a00620fc40fc466a609a00840fc4a66a60fa666ae68cdc780100083f83f0a801883f099a83a00500488048a99a9a83100190a99a9a8318011099a9825001119a9825801119a9827801119a9828001119813801000904080919a98280011040809198138010009110408091119a9826802104080911299a984100999ab9a3370e00c00610802106022a66a610402666ae68cdc3802801042008418089982b002000884180884180883e0a99a9a8310009083e083e283580789931a982899ab9c491024c6600052498c8004d5418088448894cd4d41680044008884cc014008ccd54c01c48004014010004c8004d5417c88448894cd4d41640044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d40140048800448d4d40100048800888ccd5cd19b8f00200105d05c13350022253353504200221003100150411221233001003002120012212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040c00bc8ccc00800522100488100222323230010053200135503122335350280014800088d4d540b4008894cd4c0d0ccd5cd19b8f00200903603513007001130060033200135503022335350270014800088d4d540b0008894cd4c0ccccd5cd19b8f0020070350341001130060031122320013200135502e2253353502500110032213300600230040011222200412222003122220021222200120012222222221233333333300100a00900800700600500400300220011112221233300100400300211120011200112001225335301f002100110202323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba1500921350122233301e0030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae002003120012001121223002003112200112001122533353006002215333530070022153353019333573466e3cd4c030008888008d4c03000488800806c0684ccd5cd19b8735300c00222200135300c00122200101b01a101a213018161301716213017161533353006001213017162130171610192233223370600400266e08009201400126262122230030042122230020041222001200122212333001004003002200126262611220021221223300100400312001112212330010030021120012626261220021220012001112323001001223300330020020013322332233223333333330024891cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c800df05a0cc6b6f0d28aaa1812135bd9eebfbf5e8e80fd47da9989eb0048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011049fd87a9fd8799f581c02bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b413001ffffd87980ff0581840000d87b80821a001aee931a1e0876fef5a2190195a1005829d87a9fd8799f581c02bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b413001ffff190196a1676164647265737358390102bfce8ba41fbec162c147684f4e6802524d7af76ee7cee2e5ba3a5b3cf104c3f49a0337f2396862fd9374cbf1e2d3704c7c41dffb7c6f69") + +var tx_cbor_missing_utxo = []byte("84a8008282582098483df1666d5af7c4aca7ef28f112d225b81a34ef20b0ad4775bab0e41dbb3000825820ec6eb047f74e5412c116a819cdd43f1c27a29f2871241453019637b850461b4300018283581d710449932f9da0258d220c39f803ceb8e2c45fdc605e8dd42f35558b58821a05f5e100a1581c800df05a0cc6b6f0d28aaa1812135bd9eebfbf5e8e80fd47da9989eba14c537061636542756442696431015820419eab3b349f31a776e68b9483668b623ecf2d4895db2955705676393e7eb34d8258390198de7db6b2fb3d50d56a288b27bdcdf1e29c3247a94262dcb5172c65a3cd1a98844e76c802d75d3271991293a99e4295c00a161521afb3ca1a478ff2e2021a0008e1820758209bf796062b42a22108e1ad01c450d4f3527d936a319a7109d3fd100280a0395e0b582054bcb22a31a100080ffbf7edbac538b1517d99fa85ec77bfac089ab8249e27080d81825820ec6eb047f74e5412c116a819cdd43f1c27a29f2871241453019637b850461b4300108258390198de7db6b2fb3d50d56a288b27bdcdf1e29c3247a94262dcb5172c65a3cd1a98844e76c802d75d3271991293a99e4295c00a161521afb3ca1a4d6f1abd111a000d5243a30381591974591971010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199911191919199119999111191919191999111991191919191919911991199999111119191919199911199911199999999111111119911999991111199991111991199119911991199119911991199119911919191919191919191919191919191919191999911119911919111119191919191a982c0049119119119119111911192999a983c80b909a983f00091129999a983300d099838999a837a83c9840008021a9aa84480a80b11000998389991199ab9a3371200400212402122026604c60c600a605800c60c6a02a660e26601aa02a004a66a611c026604ea03000621200226605a60c66603aa03000660c600a2c2660e26604ea030006660e2666a0dea0f26a6aa11202a02c440020fc6601aa02a0042660e2666a0dea0f26a6aa11202a02c440020fc660e26601aa02a004660e26601e6603aa030006004660106a05c60c600aa028426a60fc002444a6666a60cc0342c2660e26601e6603aa030a028004660e26605a00200e6601000200626604ea0300062c2a6666a60c402c2a66a6114026644666ae68cdc480100084680847009806800a40042a66a6a10402605802a2610e022c442a66a6a1080200226112022c46442a66a6a10e0200226a6aa114026a6aa11402a0044400444a666a610002002426a610a02002444660f06602800c004660f0660686a06a60d400c01c660f0666a0ec0d200290011a9aa848009a9aa84800a80411000912999a98430080090b10b0999a83c0359981a180d00724004603400442c2660e8666a0e40ca6605c60280109001180a0011a9aa846009a9aa84600a80211000912999a984100800909a9843808009111983d1980b0030011983d1981b1a81b9836003008199a83c035800a400442c2c442611c022c266aa11202602c006602c0022a66a6a10402605802a26110022c4646442a66a6a10c020022a666a60fa6a6aa11202a00644002426a610402002444660ea66022a00c004660ea660626a06460cea00c016666a0e60cc002900110b0b1109847008b09a9aa84380a800910010980a0008b0b0b299a9a840809a815091199aa83111299a984680a99a9a83a981418148011084800884700899802181398148010008800800991a981c8009111111111005280a8983f0b110a99a9a841808008801110a99a9a842808008a999a983e00d109a984080800911299a984880998080040010a99a98488099809003001080409843808b0a99a9848809980900300109843808b0803109a984080800911299a984880998090040010a99a98488099808003001080409844008b0a99a9848809980800300109844008b08030b1109842008b1191919191299a98460099815803241012179fa042660de6605660c266036a02ca0126054a004660de6605660c266036a02c6a6aa10e02010440046054a0066605660c266036a02c002605466052660526605200ca004a0066a6aaa0d6a0084440022660de6605660c266036a02ca0126054a00a6605660c266036a02c00260546605200ca00a26a6aaa0d2a00444400626a6aaa0d0a0024440042666aaa0d0660e80046a6aaa0ce00c444002660e80046a6aa1060200844002660e80040062660e60026a6aaa0cc00a44400426a6aaa0c400244400644660446660b200400e666a0cae2800c005200222330203330570020063335063714006002900111991180100099119900099000999aa8011919a81591199a8148018008011a81300099a8151111801980100090009119b8000148008005200030221200133233553022120012253353081013003002133507a00200110015079235355505e001222330653335063029006003333506305600148008d407c488ccd5415c88d4d541f400888ccd5416c88d4d5420404008894cd4c22004ccd5cd19b87001480002280422404400c4cc028ccd5541a001800800400c00c00400400c54cd4d41c8c8d4c0a0004888888888800d40104c19c588854cd4d41d00044008884c1ac584d4d541d140048800854cd4d41c0c06800c4c198588854cd4d41c80044c00c008884c1a8588d4c0acd4c0a400488800c88cd4c12c0089894cd4d418cc058010854cd4d4190c8d4c0a800488888888894cd4d41bcccd54c0ac4800540bc8d4d5420c04004894cd4c22804ccd5cd19b8f00200f08c0108b01135074003150730022135072353550830100122001150705006232323215335350683333333574800846666ae68cdc3a8012400846666aae7d4010941b08cccd55cf9aba25005253353506c306835742a00c426a0de60ec0022a0da4a0da0d40d246666ae68cdc3a801a400446666aae7d4014941b48cccd55cf9aba25006253353506d306935742a00e426a0e060f00022a0dc4a0dc0d60d446666ae68cdc3a8022400046666aae7d40188d41bc1d4941b81ac941b526499262506a2506a2506a2506a06721335507d301b00a00116135573aa00426aae7940044dd50008b0b09a98108009100111199aa980b090009119aa98060900091a9aa8388009119aa83a00119aa98078900091a9aa83a0009119aa83b801199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8388009119aa83a001199a9aa805800919aa98080900091a9aa83a8009119aa83c0011aa80900080091199aaa805010801000919aa98080900091a9aa83a8009119aa83c0011aa808000800999aaa80280e001000a8369a98100011111111111199aa981009000911a98180011111a981a8019119a982a8011299a984280999ab9a3371e02600210e0210c02266a0fc00a00e200e400ea0ee012222444666aa602c24002a0d866aa60142400246a6aa0de0024466aa0e40046aa018002666aa602c24002446a6aa0e000444a66a60ee666aa6036240026466a04444666a6a016006440040040026a6a0120024400266a01244a66a60f200420f620020f046a6aa0e6002446601400400a00c2006266a0e0008006a0da00266aa60142400246a6aa0de002446466aa0e6006600200a640026aa0f244a66a6a0e000226aa0180064426a6aa0ea00444a66a60f866018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0d8442244a66a6a0ca0022a0ce44266a0d0600800466aa600c240020080024466e0000800488d4c05800888888888894cd4d416cccd54c05c48005406c94cd4c1d0ccd5cd19b8f00c00107607513505e0011505d003210761074235301800122200223530170012220012353014001220012233702004002400244666ae68cdc4001000832032890008919a800a82ca82d11a9805000911a98070011111111111299a9a829a9999a981500590a82a90a82a90a82a90999aa980809000a80a11a980e00091299a9837a99a9837999ab9a3371e6a6066004440046a6066008440040e20e02666ae68cdc39a9819801110009a981980211000838838083809a82c8018a82c005909a980d800911a980f800911199aa980a09000911a98120011111a9814804111a98158029119299a983d99a9826002919a98268021299a983e999ab9a3371e0040020fe0fc2a00620fc40fc466a609a00840fc4a66a60fa666ae68cdc780100083f83f0a801883f099a83a00500488048a99a9a83100190a99a9a8318011099a9825001119a9825801119a9827801119a9828001119813801000904080919a98280011040809198138010009110408091119a9826802104080911299a984100999ab9a3370e00c00610802106022a66a610402666ae68cdc3802801042008418089982b002000884180884180883e0a99a9a8310009083e083e283580789931a982899ab9c491024c6600052498c8004d5418088448894cd4d41680044008884cc014008ccd54c01c48004014010004c8004d5417c88448894cd4d41640044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d40140048800448d4d40100048800888ccd5cd19b8f00200105d05c13350022253353504200221003100150411221233001003002120012212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040c00bc8ccc00800522100488100222323230010053200135503122335350280014800088d4d540b4008894cd4c0d0ccd5cd19b8f00200903603513007001130060033200135503022335350270014800088d4d540b0008894cd4c0ccccd5cd19b8f0020070350341001130060031122320013200135502e2253353502500110032213300600230040011222200412222003122220021222200120012222222221233333333300100a00900800700600500400300220011112221233300100400300211120011200112001225335301f002100110202323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba1500921350122233301e0030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae002003120012001121223002003112200112001122533353006002215333530070022153353019333573466e3cd4c030008888008d4c03000488800806c0684ccd5cd19b8735300c00222200135300c00122200101b01a101a213018161301716213017161533353006001213017162130171610192233223370600400266e08009201400126262122230030042122230020041222001200122212333001004003002200126262611220021221223300100400312001112212330010030021120012626261220021220012001112323001001223300330020020013322332233223333333330024891cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c800df05a0cc6b6f0d28aaa1812135bd9eebfbf5e8e80fd47da9989eb0048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011049fd87a9fd8799f581c98de7db6b2fb3d50d56a288b27bdcdf1e29c3247a94262dcb5172c65413101ffffd87980ff0581840000d87b80821a001500c11a16ff8875f5a2190195a1005829d87a9fd8799f581c98de7db6b2fb3d50d56a288b27bdcdf1e29c3247a94262dcb5172c65413101ffff190196a1676164647265737358390198de7db6b2fb3d50d56a288b27bdcdf1e29c3247a94262dcb5172c65a3cd1a98844e76c802d75d3271991293a99e4295c00a161521afb3ca") + func TestTransactionContentUnmarshal(t *testing.T) { + invalidHereafter := "13885913" want := blockfrost.TransactionContent{ Hash: "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477", Block: "356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940", @@ -25,8 +30,8 @@ func TestTransactionContentUnmarshal(t *testing.T) { Fees: "182485", Deposit: "0", Size: 433, - InvalidBefore: "", - InvalidHereafter: "13885913", + InvalidBefore: nil, + InvalidHereafter: &invalidHereafter, UtxoCount: 4, } fp := filepath.Join(testdata, "json", "transactions", "transaction.json") @@ -92,8 +97,8 @@ func TestTransactionMetadataCborUnmarshal(t *testing.T) { fp := filepath.Join(testdata, "json", "transactions", "tx_cbor.json") want := []blockfrost.TransactionMetadataCbor{ { - Label: "1968", - CborMetadata: "\\xa100a16b436f6d62696e6174696f6e8601010101010c", + Label: "1968", + Metadata: "\\xa100a16b436f6d62696e6174696f6e8601010101010c", }, } got := []blockfrost.TransactionMetadataCbor{} @@ -251,3 +256,51 @@ func TestTransactionPoolRetirementsIntegration(t *testing.T) { want := []blockfrost.TransactionPoolCert{} testIntUtil(t, fp, &got, &want) } + +func TestTransactionEvaluateIntegration(t *testing.T) { + + api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) + got, err := api.TransactionEvaluate(context.TODO(), tx_cbor) + if err != nil { + t.Fatal(err) + } + + fp := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") + want := blockfrost.OgmiosResponse{} + + // reflection.id changes with each request which would failed the comparison against stored fixture + got.Reflection.Id = "dummy" + + testIntUtil(t, fp, &got, &want) +} +func TestTransactionEvaluateUTXOsIntegration(t *testing.T) { + + additionalUtxoSet := blockfrost.AdditionalUtxoSet{ + { + TxIn: blockfrost.AdditionalUtxoSetTxIn{ + TxID: "ec6eb047f74e5412c116a819cdd43f1c27a29f2871241453019637b850461b43", + Index: 0, + }, + TxOut: blockfrost.AdditionalUtxoSetTxOut{ + Address: "addr1qxvduldkktan65x4dg5gkfaaehc798pjg755yckuk5tjcedre5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qgcyhcc", + Value: blockfrost.Value{ + Coins: "1300000000", + }, + }, + }, + } + + api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) + got, err := api.TransactionEvaluateUTXOs(context.TODO(), tx_cbor_missing_utxo, additionalUtxoSet) + if err != nil { + t.Fatal(err) + } + + fp := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") + want := blockfrost.OgmiosResponse{} + + // reflection.id changes with each request which would failed the comparison against stored fixture + got.Reflection.Id = "dummy" + + testIntUtil(t, fp, &got, &want) +} diff --git a/api_transactions.go b/api_transactions.go index 2d58a11..aae8e95 100644 --- a/api_transactions.go +++ b/api_transactions.go @@ -10,17 +10,20 @@ import ( ) const ( - resourceTxs = "txs" - resourceTx = "tx" - resourceTxStakes = "stakes" - resourceTxUTXOs = "utxos" - resourceTxWithdrawals = "withdrawals" - resourceTxMetadata = "metadata" - resourceCbor = "cbor" - resourceTxDelegations = "delegations" - resourceTxPoolUpdates = "pool_updates" - resourceTxPoolRetires = "pool_retires" - resourceTxSubmit = "submit" + resourceTxs = "txs" + resourceTx = "tx" + resourceTxStakes = "stakes" + resourceTxUTXOs = "utxos" + resourceTxWithdrawals = "withdrawals" + resourceTxMetadata = "metadata" + resourceTxRedeemers = "redeemers" + resourceCbor = "cbor" + resourceTxDelegations = "delegations" + resourceTxPoolUpdates = "pool_updates" + resourceTxPoolRetires = "pool_retires" + resourceTxSubmit = "submit" + resourceTxEvaluate = "utils/txs/evaluate" + resourceTxEvaluateUtxos = "utils/txs/evaluate/utxos" ) type TransactionContent struct { @@ -52,10 +55,10 @@ type TransactionContent struct { Index int `json:"index"` // Left (included) endpoint of the timelock validity intervals - InvalidBefore string `json:"invalid_before"` + InvalidBefore *string `json:"invalid_before"` // Right (excluded) endpoint of the timelock validity intervals - InvalidHereafter string `json:"invalid_hereafter"` + InvalidHereafter *string `json:"invalid_hereafter"` // Count of the MIR certificates within the transaction MirCertCount int `json:"mir_cert_count"` @@ -103,30 +106,31 @@ type TxAmount struct { Unit string `json:"unit"` } +type TransactionInput struct { + Address string `json:"address"` + Amount []TxAmount `json:"amount"` + OutputIndex float32 `json:"output_index"` + TxHash string `json:"tx_hash"` + DataHash *string `json:"data_hash"` + Collateral bool `json:"collateral"` + InlineDatum *string `json:"inline_datum"` + ReferenceScriptHash *string `json:"reference_script_hash"` + Reference *bool `json:"reference"` +} + +type TransactionOutput struct { + Address string `json:"address"` + Amount []TxAmount `json:"amount"` + OutputIndex int `json:"output_index"` + DataHash *string `json:"data_hash"` + InlineDatum *string `json:"inline_datum"` + ReferenceScriptHash *string `json:"reference_script_hash"` +} type TransactionUTXOs struct { // Transaction hash - Hash string `json:"hash"` - Inputs []struct { - // Input address - Address string `json:"address"` - Amount []TxAmount `json:"amount"` - - // UTXO index in the transaction - OutputIndex float32 `json:"output_index"` - - // Hash of the UTXO transaction - TxHash string `json:"tx_hash"` - - DataHash string `json:"data_hash"` - Collateral bool `json:"collateral"` - } `json:"inputs"` - Outputs []struct { - // Output address - Address string `json:"address"` - Amount []TxAmount `json:"amount"` - OutputIndex int `json:"output_index"` - DataHash string `json:"data_hash"` - } `json:"outputs"` + Hash string `json:"hash"` + Inputs []TransactionInput `json:"inputs"` + Outputs []TransactionOutput `json:"outputs"` } type TransactionStakeAddressCert struct { @@ -191,7 +195,7 @@ type TransactionPoolCert struct { // Margin tax cost of the stake pool MarginCost float32 `json:"margin_cost"` - Metadata struct { + Metadata *struct { // Description of the stake pool Description string `json:"description"` @@ -261,19 +265,62 @@ type TransactionMetadata struct { } type TransactionMetadataCbor struct { + // Deprecated: Use Metadata instead. + CborMetadata *string `json:"cbor_metadata"` // Content of the CBOR metadata - CborMetadata string `json:"cbor_metadata"` + Metadata string `json:"metadata"` // Metadata label Label string `json:"label"` } type TransactionRedeemer struct { - TxIndex int `json:"tx_index"` - Purpose string `json:"purpose"` - UnitMem string `json:"unit_mem"` - UnitSteps string `json:"unit_steps"` - Fee string `json:"fee"` + TxIndex int `json:"tx_index"` + Purpose string `json:"purpose"` + ScriptHash string `json:"script_hash"` + RedeemerDataHash string `json:"redeemer_data_hash"` + UnitMem string `json:"unit_mem"` + UnitSteps string `json:"unit_steps"` + Fee string `json:"fee"` +} + +type Quantity string + +type Value struct { + Coins Quantity `json:"coins"` + Assets map[string]Quantity `json:"assets,omitempty"` +} + +type TxOutScript interface{} + +type AdditionalUtxoSetTxIn struct { + TxID string `json:"txId"` + Index int `json:"index"` +} + +type AdditionalUtxoSetTxOut struct { + Address string `json:"address"` + Value Value `json:"value"` + DatumHash *string `json:"datumHash"` + Datum interface{} `json:"datum"` // Could be various types + Script *TxOutScript `json:"script"` +} + +// AdditionalUtxoSet represents a slice of tuples (TxIn, TxOut) +type AdditionalUtxoSet []struct { + TxIn AdditionalUtxoSetTxIn `json:"txIn"` + TxOut AdditionalUtxoSetTxOut `json:"txOut"` +} + +type OgmiosResponse struct { + Type string `json:"type"` + Version string `json:"version"` + ServiceName string `json:"servicename"` + MethodName string `json:"methodname"` + Reflection struct { + Id string `json:"id"` + } `json:"reflection"` + Result json.RawMessage `json:"result"` } func (c *apiClient) Transaction(ctx context.Context, hash string) (tc TransactionContent, err error) { @@ -397,7 +444,7 @@ func (c *apiClient) TransactionMetadata(ctx context.Context, hash string) (tm [] } func (c *apiClient) TransactionMetadataInCBORs(ctx context.Context, hash string) (tmc []TransactionMetadataCbor, err error) { - requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", c.server, resourceTxs, hash, resourceTxMetadata)) + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s/%s", c.server, resourceTxs, hash, resourceTxMetadata, resourceCbor)) if err != nil { return } @@ -417,7 +464,7 @@ func (c *apiClient) TransactionMetadataInCBORs(ctx context.Context, hash string) } func (c *apiClient) TransactionRedeemers(ctx context.Context, hash string) (tm []TransactionRedeemer, err error) { - requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s/%s", c.server, resourceTxs, hash, resourceTxMetadata, resourceCbor)) + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", c.server, resourceTxs, hash, resourceTxRedeemers)) if err != nil { return } @@ -536,3 +583,69 @@ func (c *apiClient) TransactionSubmit(ctx context.Context, cbor []byte) (hash st } return hash, nil } + +func (c *apiClient) TransactionEvaluate(ctx context.Context, cbor []byte) (jsonResponse OgmiosResponse, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s", c.server, resourceTxEvaluate)) + + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, requestUrl.String(), bytes.NewReader(cbor)) + if err != nil { + return + } + req.Header.Add("Content-Type", "application/cbor") + res, err := c.handleRequest(req) + if err != nil { + return + } + + defer res.Body.Close() + if err = json.NewDecoder(res.Body).Decode(&jsonResponse); err != nil { + return + } + return jsonResponse, nil +} + +func (c *apiClient) TransactionEvaluateUTXOs(ctx context.Context, cbor []byte, additionalUtxoSet AdditionalUtxoSet) (jsonResponse OgmiosResponse, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s", c.server, resourceTxEvaluateUtxos)) + if err != nil { + return + } + + // Convert addition utxo set from custom go data type (array of struct with TxIn, TxOut properties) to + // format required by the API endpoint ([[TxIn, TxOut], ...]) + convertedAdditionalUtxoSet := make([][]interface{}, len(additionalUtxoSet)) + for i, utxo := range additionalUtxoSet { + convertedAdditionalUtxoSet[i] = []interface{}{utxo.TxIn, utxo.TxOut} + } + + payload := struct { + Cbor string `json:"cbor"` + AdditionalUtxoSet [][]interface{} `json:"additionalUtxoSet"` + }{ + Cbor: string(cbor), + AdditionalUtxoSet: convertedAdditionalUtxoSet, + } + + jsonData, err := json.Marshal(payload) + if err != nil { + return + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, requestUrl.String(), bytes.NewBuffer(jsonData)) + if err != nil { + return + } + req.Header.Add("Content-Type", "application/json") + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + if err = json.NewDecoder(res.Body).Decode(&jsonResponse); err != nil { + return + } + + return jsonResponse, nil +} diff --git a/client.go b/client.go index 03c314a..325eb40 100644 --- a/client.go +++ b/client.go @@ -34,7 +34,7 @@ type APIClientOptions struct { MaxRoutines int } -// NewAPICLient creates a client from APIClientOptions. If no options are provided, +// NewAPIClient creates a client from APIClientOptions. If no options are provided, // client with default configurations is returned. func NewAPIClient(options APIClientOptions) APIClient { if options.Server == "" { @@ -77,6 +77,8 @@ type APIClient interface { BlocksPrevious(ctx context.Context, hashOrNumber string) ([]Block, error) BlockBySlot(ctx context.Context, slotNumber int) (Block, error) BlocksBySlotAndEpoch(ctx context.Context, slotNumber int, epochNumber int) (Block, error) + BlocksAddresses(ctx context.Context, hashOrNumber string, query APIQueryParams) ([]BlockAffectedAddresses, error) + BlocksAddressesAll(ctx context.Context, hashOrNumber string) <-chan BlockAffectedAddressesResult EpochLatest(ctx context.Context) (Epoch, error) LatestEpochParameters(ctx context.Context) (EpochParameters, error) Epoch(ctx context.Context, epochNumber int) (Epoch, error) @@ -86,8 +88,8 @@ type APIClient interface { EpochPreviousAll(ctx context.Context, epochNumber int) <-chan EpochResult EpochStakeDistribution(ctx context.Context, epochNumber int, query APIQueryParams) ([]EpochStake, error) EpochStakeDistributionAll(ctx context.Context, epochNumber int) <-chan EpochStakeResult - EpochStakeDistributionByPool(ctx context.Context, epochNumber int, poolId string, query APIQueryParams) ([]EpochStake, error) - EpochStakeDistributionByPoolAll(ctx context.Context, epochNumber int, poolId string) <-chan EpochStakeResult + EpochStakeDistributionByPool(ctx context.Context, epochNumber int, poolId string, query APIQueryParams) ([]EpochStakeByPool, error) + EpochStakeDistributionByPoolAll(ctx context.Context, epochNumber int, poolId string) <-chan EpochStakeByPoolResult EpochBlockDistribution(ctx context.Context, epochNumber int, query APIQueryParams) ([]string, error) EpochBlockDistributionAll(ctx context.Context, epochNumber int) <-chan BlockDistributionResult EpochBlockDistributionByPool(ctx context.Context, epochNumber int, poolId string, query APIQueryParams) ([]string, error) @@ -99,6 +101,8 @@ type APIClient interface { AddressTransactionsAll(ctx context.Context, address string) <-chan AddressTxResult AddressUTXOs(ctx context.Context, address string, query APIQueryParams) ([]AddressUTXO, error) AddressUTXOsAll(ctx context.Context, address string) <-chan AddressUTXOResult + AddressUTXOsAsset(ctx context.Context, address, asset string, query APIQueryParams) ([]AddressUTXO, error) + AddressUTXOsAssetAll(ctx context.Context, address, asset string) <-chan AddressUTXOResult Account(ctx context.Context, stakeAddress string) (Account, error) AccountHistory(ctx context.Context, stakeAddress string, query APIQueryParams) ([]AccountHistory, error) AccountHistoryAll(ctx context.Context, address string) <-chan AccountHistoryResult @@ -117,14 +121,19 @@ type APIClient interface { AccountAssociatedAssets(ctx context.Context, stakeAddress string, query APIQueryParams) ([]AccountAssociatedAsset, error) AccountAssociatedAssetsAll(ctx context.Context, stakeAddress string) <-chan AccountAssociatedAssetsAll Asset(ctx context.Context, asset string) (Asset, error) - Assets(ctx context.Context, query APIQueryParams) ([]Asset, error) - AssetsAll(ctx context.Context) <-chan AssetResult + Assets(ctx context.Context, query APIQueryParams) ([]AssetByPolicy, error) + AssetsAll(ctx context.Context) <-chan AssetByPolicyResult AssetHistory(ctx context.Context, asset string) ([]AssetHistory, error) AssetTransactions(ctx context.Context, asset string) ([]AssetTransaction, error) AssetAddresses(ctx context.Context, asset string, query APIQueryParams) ([]AssetAddress, error) AssetAddressesAll(ctx context.Context, asset string) <-chan AssetAddressesAll - AssetsByPolicy(ctx context.Context, policyId string) ([]Asset, error) + AssetsByPolicy(ctx context.Context, policyId string) ([]AssetByPolicy, error) Genesis(ctx context.Context) (GenesisBlock, error) + Mempool(ctx context.Context, query APIQueryParams) ([]Mempool, error) + MempoolAll(ctx context.Context) <-chan MempoolResult + MempoolTx(ctx context.Context, hash string) (MempoolTransactionContent, error) + MempoolByAddress(ctx context.Context, address string, query APIQueryParams) ([]Mempool, error) + MempoolByAddressAll(ctx context.Context, address string) <-chan MempoolResult MetadataTxLabels(ctx context.Context, query APIQueryParams) ([]MetadataTxLabel, error) MetadataTxLabelsAll(ctx context.Context) <-chan MetadataTxLabelResult MetadataTxContentInJSON(ctx context.Context, label string, query APIQueryParams) ([]MetadataTxContentInJSON, error) @@ -174,4 +183,6 @@ type APIClient interface { TransactionPoolUpdateCerts(ctx context.Context, hash string) ([]TransactionPoolCert, error) TransactionPoolRetirementCerts(ctx context.Context, hash string) ([]TransactionPoolCert, error) TransactionSubmit(ctx context.Context, cbor []byte) (string, error) + TransactionEvaluate(ctx context.Context, cbor []byte) (OgmiosResponse, error) + TransactionEvaluateUTXOs(ctx context.Context, cbor []byte, additionalUtxoSet AdditionalUtxoSet) (OgmiosResponse, error) } diff --git a/go.mod b/go.mod index 1434d55..ba3df87 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,7 @@ module github.com/blockfrost/blockfrost-go -go 1.17 +go 1.21 -require github.com/stretchr/testify v1.7.0 +require github.com/hashicorp/go-retryablehttp v0.7.5 -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect -) +require github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index b358cb4..16f60f4 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,9 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -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/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ipfs.go b/ipfs.go index 76709e1..838ee8b 100644 --- a/ipfs.go +++ b/ipfs.go @@ -194,11 +194,11 @@ func (ip *ipfsClient) PinnedObjectsAll(ctx context.Context) <-chan PinnedObjectR } go func() { defer close(ch) - fetchScripts := true - for i := 1; fetchScripts; i++ { + fetchNextPage := true + for i := 1; fetchNextPage; i++ { select { case <-quit: - fetchScripts = false + fetchNextPage = false default: jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} } diff --git a/ipfs_test.go b/ipfs_test.go index d773e17..52809dc 100644 --- a/ipfs_test.go +++ b/ipfs_test.go @@ -3,7 +3,6 @@ package blockfrost_test import ( "context" "encoding/json" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -75,7 +74,7 @@ func TestIPFSResourceAddIntegration(t *testing.T) { fp_up := filepath.Join(testdata, "json", "ipfs", "ipfs_object.json") got, err := ipfs.Add(context.TODO(), fp_up) testErrorHelper(t, err) - *update = true + // *update = true // want := blockfrost.IPFSObject{} // fp_golden := filepath.Join(testdata, strings.ToLower(strings.TrimPrefix(t.Name(), "Test"))+".golden") @@ -188,7 +187,7 @@ func testIPFSResourceRemoveIntegration(t *testing.T, ipfs blockfrost.IPFSClient, func testIPFSResourceGatewayIntegration(t *testing.T, ipfs blockfrost.IPFSClient, ipfsPath string, fp_up string) { got, err := ipfs.Gateway(context.TODO(), ipfsPath) testErrorHelper(t, err) - want, err := ioutil.ReadFile(fp_up) + want, err := os.ReadFile(fp_up) testErrorHelper(t, err) if !reflect.DeepEqual(got, want) { t.Fatalf("expected %v got %v", want, got) diff --git a/testdata/addresstickerrecordsintegration.golden b/testdata/addresstickerrecordsintegration.golden index f7310ad..5813382 100644 --- a/testdata/addresstickerrecordsintegration.golden +++ b/testdata/addresstickerrecordsintegration.golden @@ -1,26 +1 @@ -[ - { - "tx_hash": "4750cad090a9e017ece7a66e68d0a65c06a2877a96abe6a5f0fa0a0bc7700aad", - "block_height": 5182730 - }, - { - "tx_hash": "b212f57ba9a2650849cd3bdbfe83dabfbcc16bb93b1197a183ef770481f61519", - "block_height": 5182851, - "tx_index": 6 - }, - { - "tx_hash": "5aa4bdab61b49e5c4f60abf25c2978cb4efaa5e7cbcfa1991e887976c9f7c88d", - "block_height": 5183033, - "tx_index": 6 - }, - { - "tx_hash": "15e7d1880769b0f173d62d7c7d8b0085afb7fb40b7c16e73ec405cce7fc372d8", - "block_height": 5183209, - "tx_index": 8 - }, - { - "tx_hash": "418d3d02a89a3f86a17c38d9cdeec510d13bc9300e04af8b1b3506c994e3aa31", - "block_height": 5183379, - "tx_index": 3 - } -] +[{"tx_hash":"4750cad090a9e017ece7a66e68d0a65c06a2877a96abe6a5f0fa0a0bc7700aad","block_height":5182730,"tx_index":0},{"tx_hash":"b212f57ba9a2650849cd3bdbfe83dabfbcc16bb93b1197a183ef770481f61519","block_height":5182851,"tx_index":6},{"tx_hash":"5aa4bdab61b49e5c4f60abf25c2978cb4efaa5e7cbcfa1991e887976c9f7c88d","block_height":5183033,"tx_index":6},{"tx_hash":"15e7d1880769b0f173d62d7c7d8b0085afb7fb40b7c16e73ec405cce7fc372d8","block_height":5183209,"tx_index":8},{"tx_hash":"418d3d02a89a3f86a17c38d9cdeec510d13bc9300e04af8b1b3506c994e3aa31","block_height":5183379,"tx_index":3}] \ No newline at end of file diff --git a/testdata/addressutxosasset.golden b/testdata/addressutxosasset.golden new file mode 100644 index 0000000..91d1bee --- /dev/null +++ b/testdata/addressutxosasset.golden @@ -0,0 +1 @@ +[{"address":"addr1q8zsjx7vxkl4esfejafhxthyew8c54c9ch95gkv3nz37sxrc9ty742qncmffaesxqarvqjmxmy36d9aht2duhmhvekgq3jd3w2","tx_hash":"619c90076e587b000856cc1a0830c45a51ec6ba9f8f8fb00dc9f4406b06ccf72","tx_index":1,"output_index":1,"amount":[{"unit":"lovelace","quantity":"1185250"},{"unit":"d436d9f6b754582f798fe33f4bed12133d47493f78b944b9cc55fd1853756d6d69744c6f64676534393539","quantity":"1"}],"block":"181f6b0368066e245e9510414d928f78a7ed73da33ffb2657103937275f8bb3d","data_hash":null,"inline_datum":null,"reference_script_hash":null}] \ No newline at end of file diff --git a/testdata/blocksaddressesintegration.golden b/testdata/blocksaddressesintegration.golden new file mode 100644 index 0000000..8a24982 --- /dev/null +++ b/testdata/blocksaddressesintegration.golden @@ -0,0 +1,26 @@ +[ + { + "address": "addr1qy65z4nksg07fuwqqan0dept5wpmuq03qk4yd04fzm7zefv6y7mwfu69rcakzew4hrvxn6yl3m528tdcjh8xugfnp7xs5etxyk", + "transactions": [ + { + "tx_hash": "f6780212de5df00d10d929d0ca33dc2ff60cc57f38bd2b3cb3b2dea36f0c20b6" + } + ] + }, + { + "address": "DdzFFzCqrhsi4bNidSbRncmdx7chZikAf28XDXuiC3JQix13DVFen5jmbNutzs6kKirxutiLK99Rdfx6PeXEN9wv8NCsPbemTHhPQPYd", + "transactions": [ + { + "tx_hash": "f6780212de5df00d10d929d0ca33dc2ff60cc57f38bd2b3cb3b2dea36f0c20b6" + } + ] + }, + { + "address": "DdzFFzCqrhsxUkrto5FLAqN23bF7eSWknrG5RmPaWCyKjeJqoDnPmxNfQs3iCcABBS9io9gXFArMXEHrbeEPCBexoPJGqXVTWTpiDRjG", + "transactions": [ + { + "tx_hash": "f6780212de5df00d10d929d0ca33dc2ff60cc57f38bd2b3cb3b2dea36f0c20b6" + } + ] + } +] diff --git a/testdata/epochparametersintegration.golden b/testdata/epochparametersintegration.golden index 4b1ef0c..571e998 100644 --- a/testdata/epochparametersintegration.golden +++ b/testdata/epochparametersintegration.golden @@ -1,22 +1,380 @@ { - "a0": 0.3, - "decentralisation_param": 0.54, - "e_max": 18, - "epoch": 225, - "extra_entropy": null, - "key_deposit": "2000000", - "max_block_header_size": 1100, - "max_block_size": 65536, - "max_tx_size": 16384, + "epoch": 453, "min_fee_a": 44, "min_fee_b": 155381, - "min_pool_cost": "340000000", - "min_utxo": "1000000", - "n_opt": 150, - "nonce": "edb2d417956becb8ae5c6e29028459ab1230c1c8620778b9a801cd99feae8fb9", + "max_block_size": 90112, + "max_tx_size": 16384, + "max_block_header_size": 1100, + "key_deposit": "2000000", "pool_deposit": "500000000", - "protocol_major_ver": 2, - "protocol_minor_ver": 0, + "e_max": 18, + "n_opt": 500, + "a0": 0.3, "rho": 0.003, - "tau": 0.2 -} + "tau": 0.2, + "decentralisation_param": 0, + "extra_entropy": null, + "protocol_major_ver": 8, + "protocol_minor_ver": 0, + "min_utxo": "4310", + "min_pool_cost": "170000000", + "nonce": "eef774539bbb4de884e9a26dbd8796b7ec6624b52a241f753afe50b9496fdfb4", + "cost_models": { + "PlutusV1": { + "addInteger-cpu-arguments-intercept": 205665, + "addInteger-cpu-arguments-slope": 812, + "addInteger-memory-arguments-intercept": 1, + "addInteger-memory-arguments-slope": 1, + "appendByteString-cpu-arguments-intercept": 1000, + "appendByteString-cpu-arguments-slope": 571, + "appendByteString-memory-arguments-intercept": 0, + "appendByteString-memory-arguments-slope": 1, + "appendString-cpu-arguments-intercept": 1000, + "appendString-cpu-arguments-slope": 24177, + "appendString-memory-arguments-intercept": 4, + "appendString-memory-arguments-slope": 1, + "bData-cpu-arguments": 1000, + "bData-memory-arguments": 32, + "blake2b_256-cpu-arguments-intercept": 117366, + "blake2b_256-cpu-arguments-slope": 10475, + "blake2b_256-memory-arguments": 4, + "cekApplyCost-exBudgetCPU": 23000, + "cekApplyCost-exBudgetMemory": 100, + "cekBuiltinCost-exBudgetCPU": 23000, + "cekBuiltinCost-exBudgetMemory": 100, + "cekConstCost-exBudgetCPU": 23000, + "cekConstCost-exBudgetMemory": 100, + "cekDelayCost-exBudgetCPU": 23000, + "cekDelayCost-exBudgetMemory": 100, + "cekForceCost-exBudgetCPU": 23000, + "cekForceCost-exBudgetMemory": 100, + "cekLamCost-exBudgetCPU": 23000, + "cekLamCost-exBudgetMemory": 100, + "cekStartupCost-exBudgetCPU": 100, + "cekStartupCost-exBudgetMemory": 100, + "cekVarCost-exBudgetCPU": 23000, + "cekVarCost-exBudgetMemory": 100, + "chooseData-cpu-arguments": 19537, + "chooseData-memory-arguments": 32, + "chooseList-cpu-arguments": 175354, + "chooseList-memory-arguments": 32, + "chooseUnit-cpu-arguments": 46417, + "chooseUnit-memory-arguments": 4, + "consByteString-cpu-arguments-intercept": 221973, + "consByteString-cpu-arguments-slope": 511, + "consByteString-memory-arguments-intercept": 0, + "consByteString-memory-arguments-slope": 1, + "constrData-cpu-arguments": 89141, + "constrData-memory-arguments": 32, + "decodeUtf8-cpu-arguments-intercept": 497525, + "decodeUtf8-cpu-arguments-slope": 14068, + "decodeUtf8-memory-arguments-intercept": 4, + "decodeUtf8-memory-arguments-slope": 2, + "divideInteger-cpu-arguments-constant": 196500, + "divideInteger-cpu-arguments-model-arguments-intercept": 453240, + "divideInteger-cpu-arguments-model-arguments-slope": 220, + "divideInteger-memory-arguments-intercept": 0, + "divideInteger-memory-arguments-minimum": 1, + "divideInteger-memory-arguments-slope": 1, + "encodeUtf8-cpu-arguments-intercept": 1000, + "encodeUtf8-cpu-arguments-slope": 28662, + "encodeUtf8-memory-arguments-intercept": 4, + "encodeUtf8-memory-arguments-slope": 2, + "equalsByteString-cpu-arguments-constant": 245000, + "equalsByteString-cpu-arguments-intercept": 216773, + "equalsByteString-cpu-arguments-slope": 62, + "equalsByteString-memory-arguments": 1, + "equalsData-cpu-arguments-intercept": 1060367, + "equalsData-cpu-arguments-slope": 12586, + "equalsData-memory-arguments": 1, + "equalsInteger-cpu-arguments-intercept": 208512, + "equalsInteger-cpu-arguments-slope": 421, + "equalsInteger-memory-arguments": 1, + "equalsString-cpu-arguments-constant": 187000, + "equalsString-cpu-arguments-intercept": 1000, + "equalsString-cpu-arguments-slope": 52998, + "equalsString-memory-arguments": 1, + "fstPair-cpu-arguments": 80436, + "fstPair-memory-arguments": 32, + "headList-cpu-arguments": 43249, + "headList-memory-arguments": 32, + "iData-cpu-arguments": 1000, + "iData-memory-arguments": 32, + "ifThenElse-cpu-arguments": 80556, + "ifThenElse-memory-arguments": 1, + "indexByteString-cpu-arguments": 57667, + "indexByteString-memory-arguments": 4, + "lengthOfByteString-cpu-arguments": 1000, + "lengthOfByteString-memory-arguments": 10, + "lessThanByteString-cpu-arguments-intercept": 197145, + "lessThanByteString-cpu-arguments-slope": 156, + "lessThanByteString-memory-arguments": 1, + "lessThanEqualsByteString-cpu-arguments-intercept": 197145, + "lessThanEqualsByteString-cpu-arguments-slope": 156, + "lessThanEqualsByteString-memory-arguments": 1, + "lessThanEqualsInteger-cpu-arguments-intercept": 204924, + "lessThanEqualsInteger-cpu-arguments-slope": 473, + "lessThanEqualsInteger-memory-arguments": 1, + "lessThanInteger-cpu-arguments-intercept": 208896, + "lessThanInteger-cpu-arguments-slope": 511, + "lessThanInteger-memory-arguments": 1, + "listData-cpu-arguments": 52467, + "listData-memory-arguments": 32, + "mapData-cpu-arguments": 64832, + "mapData-memory-arguments": 32, + "mkCons-cpu-arguments": 65493, + "mkCons-memory-arguments": 32, + "mkNilData-cpu-arguments": 22558, + "mkNilData-memory-arguments": 32, + "mkNilPairData-cpu-arguments": 16563, + "mkNilPairData-memory-arguments": 32, + "mkPairData-cpu-arguments": 76511, + "mkPairData-memory-arguments": 32, + "modInteger-cpu-arguments-constant": 196500, + "modInteger-cpu-arguments-model-arguments-intercept": 453240, + "modInteger-cpu-arguments-model-arguments-slope": 220, + "modInteger-memory-arguments-intercept": 0, + "modInteger-memory-arguments-minimum": 1, + "modInteger-memory-arguments-slope": 1, + "multiplyInteger-cpu-arguments-intercept": 69522, + "multiplyInteger-cpu-arguments-slope": 11687, + "multiplyInteger-memory-arguments-intercept": 0, + "multiplyInteger-memory-arguments-slope": 1, + "nullList-cpu-arguments": 60091, + "nullList-memory-arguments": 32, + "quotientInteger-cpu-arguments-constant": 196500, + "quotientInteger-cpu-arguments-model-arguments-intercept": 453240, + "quotientInteger-cpu-arguments-model-arguments-slope": 220, + "quotientInteger-memory-arguments-intercept": 0, + "quotientInteger-memory-arguments-minimum": 1, + "quotientInteger-memory-arguments-slope": 1, + "remainderInteger-cpu-arguments-constant": 196500, + "remainderInteger-cpu-arguments-model-arguments-intercept": 453240, + "remainderInteger-cpu-arguments-model-arguments-slope": 220, + "remainderInteger-memory-arguments-intercept": 0, + "remainderInteger-memory-arguments-minimum": 1, + "remainderInteger-memory-arguments-slope": 1, + "sha2_256-cpu-arguments-intercept": 806990, + "sha2_256-cpu-arguments-slope": 30482, + "sha2_256-memory-arguments": 4, + "sha3_256-cpu-arguments-intercept": 1927926, + "sha3_256-cpu-arguments-slope": 82523, + "sha3_256-memory-arguments": 4, + "sliceByteString-cpu-arguments-intercept": 265318, + "sliceByteString-cpu-arguments-slope": 0, + "sliceByteString-memory-arguments-intercept": 4, + "sliceByteString-memory-arguments-slope": 0, + "sndPair-cpu-arguments": 85931, + "sndPair-memory-arguments": 32, + "subtractInteger-cpu-arguments-intercept": 205665, + "subtractInteger-cpu-arguments-slope": 812, + "subtractInteger-memory-arguments-intercept": 1, + "subtractInteger-memory-arguments-slope": 1, + "tailList-cpu-arguments": 41182, + "tailList-memory-arguments": 32, + "trace-cpu-arguments": 212342, + "trace-memory-arguments": 32, + "unBData-cpu-arguments": 31220, + "unBData-memory-arguments": 32, + "unConstrData-cpu-arguments": 32696, + "unConstrData-memory-arguments": 32, + "unIData-cpu-arguments": 43357, + "unIData-memory-arguments": 32, + "unListData-cpu-arguments": 32247, + "unListData-memory-arguments": 32, + "unMapData-cpu-arguments": 38314, + "unMapData-memory-arguments": 32, + "verifyEd25519Signature-cpu-arguments-intercept": 57996947, + "verifyEd25519Signature-cpu-arguments-slope": 18975, + "verifyEd25519Signature-memory-arguments": 10 + }, + "PlutusV2": { + "addInteger-cpu-arguments-intercept": 205665, + "addInteger-cpu-arguments-slope": 812, + "addInteger-memory-arguments-intercept": 1, + "addInteger-memory-arguments-slope": 1, + "appendByteString-cpu-arguments-intercept": 1000, + "appendByteString-cpu-arguments-slope": 571, + "appendByteString-memory-arguments-intercept": 0, + "appendByteString-memory-arguments-slope": 1, + "appendString-cpu-arguments-intercept": 1000, + "appendString-cpu-arguments-slope": 24177, + "appendString-memory-arguments-intercept": 4, + "appendString-memory-arguments-slope": 1, + "bData-cpu-arguments": 1000, + "bData-memory-arguments": 32, + "blake2b_256-cpu-arguments-intercept": 117366, + "blake2b_256-cpu-arguments-slope": 10475, + "blake2b_256-memory-arguments": 4, + "cekApplyCost-exBudgetCPU": 23000, + "cekApplyCost-exBudgetMemory": 100, + "cekBuiltinCost-exBudgetCPU": 23000, + "cekBuiltinCost-exBudgetMemory": 100, + "cekConstCost-exBudgetCPU": 23000, + "cekConstCost-exBudgetMemory": 100, + "cekDelayCost-exBudgetCPU": 23000, + "cekDelayCost-exBudgetMemory": 100, + "cekForceCost-exBudgetCPU": 23000, + "cekForceCost-exBudgetMemory": 100, + "cekLamCost-exBudgetCPU": 23000, + "cekLamCost-exBudgetMemory": 100, + "cekStartupCost-exBudgetCPU": 100, + "cekStartupCost-exBudgetMemory": 100, + "cekVarCost-exBudgetCPU": 23000, + "cekVarCost-exBudgetMemory": 100, + "chooseData-cpu-arguments": 19537, + "chooseData-memory-arguments": 32, + "chooseList-cpu-arguments": 175354, + "chooseList-memory-arguments": 32, + "chooseUnit-cpu-arguments": 46417, + "chooseUnit-memory-arguments": 4, + "consByteString-cpu-arguments-intercept": 221973, + "consByteString-cpu-arguments-slope": 511, + "consByteString-memory-arguments-intercept": 0, + "consByteString-memory-arguments-slope": 1, + "constrData-cpu-arguments": 89141, + "constrData-memory-arguments": 32, + "decodeUtf8-cpu-arguments-intercept": 497525, + "decodeUtf8-cpu-arguments-slope": 14068, + "decodeUtf8-memory-arguments-intercept": 4, + "decodeUtf8-memory-arguments-slope": 2, + "divideInteger-cpu-arguments-constant": 196500, + "divideInteger-cpu-arguments-model-arguments-intercept": 453240, + "divideInteger-cpu-arguments-model-arguments-slope": 220, + "divideInteger-memory-arguments-intercept": 0, + "divideInteger-memory-arguments-minimum": 1, + "divideInteger-memory-arguments-slope": 1, + "encodeUtf8-cpu-arguments-intercept": 1000, + "encodeUtf8-cpu-arguments-slope": 28662, + "encodeUtf8-memory-arguments-intercept": 4, + "encodeUtf8-memory-arguments-slope": 2, + "equalsByteString-cpu-arguments-constant": 245000, + "equalsByteString-cpu-arguments-intercept": 216773, + "equalsByteString-cpu-arguments-slope": 62, + "equalsByteString-memory-arguments": 1, + "equalsData-cpu-arguments-intercept": 1060367, + "equalsData-cpu-arguments-slope": 12586, + "equalsData-memory-arguments": 1, + "equalsInteger-cpu-arguments-intercept": 208512, + "equalsInteger-cpu-arguments-slope": 421, + "equalsInteger-memory-arguments": 1, + "equalsString-cpu-arguments-constant": 187000, + "equalsString-cpu-arguments-intercept": 1000, + "equalsString-cpu-arguments-slope": 52998, + "equalsString-memory-arguments": 1, + "fstPair-cpu-arguments": 80436, + "fstPair-memory-arguments": 32, + "headList-cpu-arguments": 43249, + "headList-memory-arguments": 32, + "iData-cpu-arguments": 1000, + "iData-memory-arguments": 32, + "ifThenElse-cpu-arguments": 80556, + "ifThenElse-memory-arguments": 1, + "indexByteString-cpu-arguments": 57667, + "indexByteString-memory-arguments": 4, + "lengthOfByteString-cpu-arguments": 1000, + "lengthOfByteString-memory-arguments": 10, + "lessThanByteString-cpu-arguments-intercept": 197145, + "lessThanByteString-cpu-arguments-slope": 156, + "lessThanByteString-memory-arguments": 1, + "lessThanEqualsByteString-cpu-arguments-intercept": 197145, + "lessThanEqualsByteString-cpu-arguments-slope": 156, + "lessThanEqualsByteString-memory-arguments": 1, + "lessThanEqualsInteger-cpu-arguments-intercept": 204924, + "lessThanEqualsInteger-cpu-arguments-slope": 473, + "lessThanEqualsInteger-memory-arguments": 1, + "lessThanInteger-cpu-arguments-intercept": 208896, + "lessThanInteger-cpu-arguments-slope": 511, + "lessThanInteger-memory-arguments": 1, + "listData-cpu-arguments": 52467, + "listData-memory-arguments": 32, + "mapData-cpu-arguments": 64832, + "mapData-memory-arguments": 32, + "mkCons-cpu-arguments": 65493, + "mkCons-memory-arguments": 32, + "mkNilData-cpu-arguments": 22558, + "mkNilData-memory-arguments": 32, + "mkNilPairData-cpu-arguments": 16563, + "mkNilPairData-memory-arguments": 32, + "mkPairData-cpu-arguments": 76511, + "mkPairData-memory-arguments": 32, + "modInteger-cpu-arguments-constant": 196500, + "modInteger-cpu-arguments-model-arguments-intercept": 453240, + "modInteger-cpu-arguments-model-arguments-slope": 220, + "modInteger-memory-arguments-intercept": 0, + "modInteger-memory-arguments-minimum": 1, + "modInteger-memory-arguments-slope": 1, + "multiplyInteger-cpu-arguments-intercept": 69522, + "multiplyInteger-cpu-arguments-slope": 11687, + "multiplyInteger-memory-arguments-intercept": 0, + "multiplyInteger-memory-arguments-slope": 1, + "nullList-cpu-arguments": 60091, + "nullList-memory-arguments": 32, + "quotientInteger-cpu-arguments-constant": 196500, + "quotientInteger-cpu-arguments-model-arguments-intercept": 453240, + "quotientInteger-cpu-arguments-model-arguments-slope": 220, + "quotientInteger-memory-arguments-intercept": 0, + "quotientInteger-memory-arguments-minimum": 1, + "quotientInteger-memory-arguments-slope": 1, + "remainderInteger-cpu-arguments-constant": 196500, + "remainderInteger-cpu-arguments-model-arguments-intercept": 453240, + "remainderInteger-cpu-arguments-model-arguments-slope": 220, + "remainderInteger-memory-arguments-intercept": 0, + "remainderInteger-memory-arguments-minimum": 1, + "remainderInteger-memory-arguments-slope": 1, + "serialiseData-cpu-arguments-intercept": 1159724, + "serialiseData-cpu-arguments-slope": 392670, + "serialiseData-memory-arguments-intercept": 0, + "serialiseData-memory-arguments-slope": 2, + "sha2_256-cpu-arguments-intercept": 806990, + "sha2_256-cpu-arguments-slope": 30482, + "sha2_256-memory-arguments": 4, + "sha3_256-cpu-arguments-intercept": 1927926, + "sha3_256-cpu-arguments-slope": 82523, + "sha3_256-memory-arguments": 4, + "sliceByteString-cpu-arguments-intercept": 265318, + "sliceByteString-cpu-arguments-slope": 0, + "sliceByteString-memory-arguments-intercept": 4, + "sliceByteString-memory-arguments-slope": 0, + "sndPair-cpu-arguments": 85931, + "sndPair-memory-arguments": 32, + "subtractInteger-cpu-arguments-intercept": 205665, + "subtractInteger-cpu-arguments-slope": 812, + "subtractInteger-memory-arguments-intercept": 1, + "subtractInteger-memory-arguments-slope": 1, + "tailList-cpu-arguments": 41182, + "tailList-memory-arguments": 32, + "trace-cpu-arguments": 212342, + "trace-memory-arguments": 32, + "unBData-cpu-arguments": 31220, + "unBData-memory-arguments": 32, + "unConstrData-cpu-arguments": 32696, + "unConstrData-memory-arguments": 32, + "unIData-cpu-arguments": 43357, + "unIData-memory-arguments": 32, + "unListData-cpu-arguments": 32247, + "unListData-memory-arguments": 32, + "unMapData-cpu-arguments": 38314, + "unMapData-memory-arguments": 32, + "verifyEcdsaSecp256k1Signature-cpu-arguments": 35892428, + "verifyEcdsaSecp256k1Signature-memory-arguments": 10, + "verifyEd25519Signature-cpu-arguments-intercept": 57996947, + "verifyEd25519Signature-cpu-arguments-slope": 18975, + "verifyEd25519Signature-memory-arguments": 10, + "verifySchnorrSecp256k1Signature-cpu-arguments-intercept": 38887044, + "verifySchnorrSecp256k1Signature-cpu-arguments-slope": 32947, + "verifySchnorrSecp256k1Signature-memory-arguments": 10 + } + }, + "price_mem": 0.0577, + "price_step": 0.0000721, + "max_tx_ex_mem": "14000000", + "max_tx_ex_steps": "10000000000", + "max_block_ex_mem": "62000000", + "max_block_ex_steps": "20000000000", + "max_val_size": "5000", + "collateral_percent": 150, + "max_collateral_inputs": 3, + "coins_per_utxo_size": "4310", + "coins_per_utxo_word": "4310" +} \ No newline at end of file diff --git a/testdata/epochstakedistributionbypoolintegration.golden b/testdata/epochstakedistributionbypoolintegration.golden index 9f32f29..717a192 100644 --- a/testdata/epochstakedistributionbypoolintegration.golden +++ b/testdata/epochstakedistributionbypoolintegration.golden @@ -1,22 +1,22 @@ [ { "stake_address": "stake1uyq0yr438xvf533765w8s62eh5cmj33sed73cypt37l2m9ch78kup", - "pool_id": "", "amount": "2473962" }, { "stake_address": "stake1uyzrq7fqfyk3nz3z7wtcxpehf9euz6x7u739pyvc25zm8ec30p6h6", - "pool_id": "", "amount": "602852649955" }, { "stake_address": "stake1uyyvs6u6lqfc64xz6jztw8sqlv8p4fkpwzthhcjt8hfsvdc6z649m", - "pool_id": "", "amount": "10583216396" }, { "stake_address": "stake1uy9ynpevcnv32efnh3v73cm2ap4ys3d4dc82yxm7ccntlzcrdza5z", - "pool_id": "", "amount": "782838060" + }, + { + "stake_address": "stake1uy9xjxmldxflvxaz2znpkmgcq8z3svuq0v7tv6y3l4kja7clta3gj", + "amount": "100455621428" } ] diff --git a/testdata/integrationresourceredeemers.golden b/testdata/integrationresourceredeemers.golden new file mode 100644 index 0000000..acdee22 --- /dev/null +++ b/testdata/integrationresourceredeemers.golden @@ -0,0 +1,11 @@ +[ + { + "tx_hash": "a95d16e891e51f98a3b1d3fe862ed355ebc8abffb7a7269d86f775553d9e653f", + "tx_index": 0, + "purpose": "spend", + "datum_hash": "5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4", + "unit_mem": "520448", + "unit_steps": "211535239", + "fee": "45282" + } +] diff --git a/testdata/ipfsresourcepinnedintegration.golden b/testdata/ipfsresourcepinnedintegration.golden index a4309e9..a1b923b 100644 --- a/testdata/ipfsresourcepinnedintegration.golden +++ b/testdata/ipfsresourcepinnedintegration.golden @@ -1 +1 @@ -{"time_created":1649205656806,"time_pinned":1649205657450,"ipfs_hash":"QmUBzUmJn1dzMQ5ooN8yVvK6TRuTdFnxdkxZi8bQDQ3tuS","state":"queued","size":"125"} \ No newline at end of file +{"time_created":1701264369447,"time_pinned":1701273659655,"ipfs_hash":"QmUBzUmJn1dzMQ5ooN8yVvK6TRuTdFnxdkxZi8bQDQ3tuS","state":"queued","size":"125"} \ No newline at end of file diff --git a/testdata/ipfsresourcepinnedobjectsintegration.golden b/testdata/ipfsresourcepinnedobjectsintegration.golden index 6899239..7c9861f 100644 --- a/testdata/ipfsresourcepinnedobjectsintegration.golden +++ b/testdata/ipfsresourcepinnedobjectsintegration.golden @@ -1 +1 @@ -[{"time_created":1649205656806,"time_pinned":1649205657450,"ipfs_hash":"QmUBzUmJn1dzMQ5ooN8yVvK6TRuTdFnxdkxZi8bQDQ3tuS","state":"queued","size":"125"}] \ No newline at end of file +[{"time_created":1701264369447,"time_pinned":1701273659655,"ipfs_hash":"QmUBzUmJn1dzMQ5ooN8yVvK6TRuTdFnxdkxZi8bQDQ3tuS","state":"queued","size":"125"}] \ No newline at end of file diff --git a/testdata/json/mempool/mempool.json b/testdata/json/mempool/mempool.json new file mode 100644 index 0000000..15d87e8 --- /dev/null +++ b/testdata/json/mempool/mempool.json @@ -0,0 +1,8 @@ +[ + { + "tx_hash": "abc" + }, + { + "tx_hash": "def" + } +] diff --git a/testdata/json/mempool/transaction.json b/testdata/json/mempool/transaction.json new file mode 100644 index 0000000..4e6d9a9 --- /dev/null +++ b/testdata/json/mempool/transaction.json @@ -0,0 +1,55 @@ +{ + "tx": { + "hash": "1f96d3824eb2aeeb0b09b99748bb70ac681e0cae6e37e01c43958b79ca69c986", + "output_amount": [ + { + "unit": "lovelace", + "quantity": "2837715" + } + ], + "fees": "369133", + "deposit": "0", + "size": 4683, + "invalid_before": null, + "invalid_hereafter": "109798439", + "utxo_count": 2, + "withdrawal_count": 0, + "mir_cert_count": 0, + "delegation_count": 0, + "stake_cert_count": 0, + "pool_update_count": 0, + "pool_retire_count": 0, + "asset_mint_or_burn_count": 0, + "redeemer_count": 0, + "valid_contract": true + }, + "inputs": [ + { + "address": "addr1vx9wkegx062xmmdzfd69jz6dt48p5mse4v35mml5h6ceznq8ap8fz", + "tx_hash": "7aa461f4f924586864c74141d457e70cfb26b2b5b9cfea4c5d5580f037ef41da", + "output_index": 0, + "collateral": false, + "reference": false + } + ], + "outputs": [ + { + "address": "addr1vx9wkegx062xmmdzfd69jz6dt48p5mse4v35mml5h6ceznq8ap8fz", + "amount": [ + { + "unit": "lovelace", + "quantity": "2837715" + }, + { + "unit": "6787a47e9f73efe4002d763337140da27afa8eb9a39413d2c39d4286524144546f6b656e73", + "quantity": "15000" + } + ], + "output_index": 0, + "data_hash": null, + "inline_datum": null, + "collateral": false, + "reference_script_hash": null + } + ] +} diff --git a/testdata/json/transactions/tx_cbor.json b/testdata/json/transactions/tx_cbor.json index b683885..0eafa78 100644 --- a/testdata/json/transactions/tx_cbor.json +++ b/testdata/json/transactions/tx_cbor.json @@ -1,6 +1,6 @@ [ { - "label": "1968", - "cbor_metadata": "\\xa100a16b436f6d62696e6174696f6e8601010101010c" + "label": "1968", + "metadata": "\\xa100a16b436f6d62696e6174696f6e8601010101010c" } ] diff --git a/testdata/resourceaddresstransactions.golden b/testdata/resourceaddresstransactions.golden index af63e5a..843147d 100644 --- a/testdata/resourceaddresstransactions.golden +++ b/testdata/resourceaddresstransactions.golden @@ -1,10 +1,14 @@ [ { "tx_hash": "8180983d5de4de7b72b4bf26d9b99cc0d731dbc77bbb575d61521952bc2ad765", - "block_height": 4660161 + "tx_index": 0, + "block_height": 4660161, + "block_time": 1599473577 }, { "tx_hash": "8080ba96634105b099be0548f384dc70d2e485ea887b69fe7d1ceda9c9075f2f", - "block_height": 4975037 + "tx_index": 0, + "block_height": 4975037, + "block_time": 1605874409 } -] +] \ No newline at end of file diff --git a/testdata/resourceassetddressesintegration.golden b/testdata/resourceassetddressesintegration.golden index 7634137..47caf0b 100644 --- a/testdata/resourceassetddressesintegration.golden +++ b/testdata/resourceassetddressesintegration.golden @@ -1,3 +1 @@ -[ - {} -] +[{"address":"addr1qxexa3gg4vkzkl20t28pcvwtp73lqtenrqvznvd9xfsgnf3n949u7c4f48efr29tcm4hntedgeclgjvn3a9wdgypv95q3cacjy","quantity":"1"}] \ No newline at end of file diff --git a/testdata/resourceassetintegration.golden b/testdata/resourceassetintegration.golden index 885df35..17b7e1c 100644 --- a/testdata/resourceassetintegration.golden +++ b/testdata/resourceassetintegration.golden @@ -6,6 +6,6 @@ "quantity": "1", "initial_mint_tx_hash": "0b28244fade854bb15f0358de44872cfb2216744b95dc0855704dac34619f382", "mint_or_burn_count": 1, - "onchain_metadata": {}, - "metadata": {} + "onchain_metadata": null, + "metadata": null } diff --git a/testdata/resourceassetsbypolicyintegration.golden b/testdata/resourceassetsbypolicyintegration.golden index b636f9e..12b4e87 100644 --- a/testdata/resourceassetsbypolicyintegration.golden +++ b/testdata/resourceassetsbypolicyintegration.golden @@ -1,8 +1,6 @@ [ { "asset": "3a9241cd79895e3a8d65261b40077d4437ce71e9d7c8c6c00e3f658e4669727374636f696e", - "quantity": "1", - "onchain_metadata": {}, - "metadata": {} + "quantity": "1" } ] diff --git a/testdata/resourceassettransactionintegration.golden b/testdata/resourceassettransactionintegration.golden index 8f69261..745baa6 100644 --- a/testdata/resourceassettransactionintegration.golden +++ b/testdata/resourceassettransactionintegration.golden @@ -1 +1 @@ -[{"tx_hash":"0b28244fade854bb15f0358de44872cfb2216744b95dc0855704dac34619f382","tx_index":9,"block_height":5406748}] \ No newline at end of file +[{"tx_hash":"0b28244fade854bb15f0358de44872cfb2216744b95dc0855704dac34619f382","tx_index":9,"block_height":5406748,"block_time":1614635257},{"tx_hash":"a9736450137c9494bdfe520935fd7ccd00cc2dd840cde3ab0f6598814fc30954","tx_index":2,"block_height":8014735,"block_time":1668468279}] \ No newline at end of file diff --git a/testdata/resourceinfointegration.golden b/testdata/resourceinfointegration.golden index d3deb5d..bcf83a1 100644 --- a/testdata/resourceinfointegration.golden +++ b/testdata/resourceinfointegration.golden @@ -1,4 +1,4 @@ { "url": "https://blockfrost.io/", - "version": "0.19.4" + "version": "1.7.0" } diff --git a/testdata/transactionevaluateintegration.golden b/testdata/transactionevaluateintegration.golden new file mode 100644 index 0000000..a5928ef --- /dev/null +++ b/testdata/transactionevaluateintegration.golden @@ -0,0 +1 @@ +{"type":"jsonwsp/response","version":"1.0","servicename":"ogmios","methodname":"EvaluateTx","reflection":{"id":"dummy"},"result":{"EvaluationResult":{"spend:0":{"memory":1765011,"steps":503871230}}}} \ No newline at end of file diff --git a/testdata/transactionevaluateutxosintegration.golden b/testdata/transactionevaluateutxosintegration.golden new file mode 100644 index 0000000..531ce69 --- /dev/null +++ b/testdata/transactionevaluateutxosintegration.golden @@ -0,0 +1 @@ +{"type":"jsonwsp/response","version":"1.0","servicename":"ogmios","methodname":"EvaluateTx","result":{"EvaluationResult":{"spend:0":{"memory":1376449,"steps":385845365}}},"reflection":{"id":"dummy"}} \ No newline at end of file diff --git a/testdata/transactionintegration.golden b/testdata/transactionintegration.golden index da6c868..4aa4000 100644 --- a/testdata/transactionintegration.golden +++ b/testdata/transactionintegration.golden @@ -1 +1 @@ -{"asset_mint_or_burn_count":0,"block":"356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940","block_height":4953973,"delegation_count":0,"deposit":"0","fees":"182485","hash":"6e5f825c82c1c6d6b77f2a14092f3b78c8f1b66db6f4cf8caec1555b6f967b3b","index":1,"invalid_before":"","invalid_hereafter":"13885913","mir_cert_count":0,"output_amount":[{"quantity":"2472470544","unit":"lovelace"}],"pool_retire_count":0,"pool_update_count":0,"size":433,"slot":13878800,"stake_cert_count":0,"utxo_count":4,"withdrawal_count":0} \ No newline at end of file +{"asset_mint_or_burn_count":0,"block":"356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940","block_height":4953973,"block_time":1605445091,"delegation_count":0,"deposit":"0","fees":"182485","hash":"6e5f825c82c1c6d6b77f2a14092f3b78c8f1b66db6f4cf8caec1555b6f967b3b","index":1,"invalid_before":null,"invalid_hereafter":"13885913","mir_cert_count":0,"output_amount":[{"quantity":"2472470544","unit":"lovelace"}],"pool_retire_count":0,"pool_update_count":0,"redeemer_count":0,"size":433,"slot":13878800,"stake_cert_count":0,"utxo_count":4,"valid_contract":true,"withdrawal_count":0} \ No newline at end of file diff --git a/testdata/transactionutxos.golden b/testdata/transactionutxos.golden index dad8bcd..1f8532b 100644 --- a/testdata/transactionutxos.golden +++ b/testdata/transactionutxos.golden @@ -5,27 +5,37 @@ "address": "addr1q82aucg0xgdsvzdqtppz35ymw4x7dje2ug9wsvy3an6v47u6msd43ruwsyp6lvfz90thjgtaunfy2ynlk4etz8rn254qd8fxw5", "amount": [ { - "quantity": "1379280", - "unit": "lovelace" + "unit": "lovelace", + "quantity": "1379280" }, { - "quantity": "1", - "unit": "15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beee42616279416c69656e3035303637" + "unit": "15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beee42616279416c69656e3035303637", + "quantity": "1" } ], + "tx_hash": "536a0508eed03408c2d8a7d1820973fc6813a4273c3a1f404aa148421c0084ae", "output_index": 0, - "tx_hash": "536a0508eed03408c2d8a7d1820973fc6813a4273c3a1f404aa148421c0084ae" + "data_hash": null, + "inline_datum": null, + "reference_script_hash": null, + "collateral": false, + "reference": false }, { "address": "addr1q82aucg0xgdsvzdqtppz35ymw4x7dje2ug9wsvy3an6v47u6msd43ruwsyp6lvfz90thjgtaunfy2ynlk4etz8rn254qd8fxw5", "amount": [ { - "quantity": "99000000", - "unit": "lovelace" + "unit": "lovelace", + "quantity": "99000000" } ], + "tx_hash": "65e3f1d3950ca82aaaa97bf12184ca199d48c19ea7bcf7dd4a99ce1b6baccdb9", "output_index": 0, - "tx_hash": "65e3f1d3950ca82aaaa97bf12184ca199d48c19ea7bcf7dd4a99ce1b6baccdb9" + "data_hash": null, + "inline_datum": null, + "reference_script_hash": null, + "collateral": false, + "reference": false } ], "outputs": [ @@ -33,32 +43,47 @@ "address": "addr1q82kxsz4sxtz7c5wn5keer9y8648knhj8kcpkq99e24qye2q0ka7w7a9lvdxzkvkfzwk0uqmayyaqjllu6d003r5zd9qjrvatz", "amount": [ { - "quantity": "96330679", - "unit": "lovelace" + "unit": "lovelace", + "quantity": "96330679" } - ] + ], + "output_index": 0, + "data_hash": null, + "inline_datum": null, + "collateral": false, + "reference_script_hash": null }, { "address": "addr1q8c6g5u4grn35hdctcu0mr0yvx3gkdyry08dn7kcdnr7hp0j0a6c9hllaknrxj5hrj2dup0udx3x9ael8stcgw6pk96srye9p5", "amount": [ { - "quantity": "1379280", - "unit": "lovelace" + "unit": "lovelace", + "quantity": "1379280" }, { - "quantity": "1", - "unit": "15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beee42616279416c69656e3035303637" + "unit": "15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beee42616279416c69656e3035303637", + "quantity": "1" } - ] + ], + "output_index": 1, + "data_hash": null, + "inline_datum": null, + "collateral": false, + "reference_script_hash": null }, { "address": "addr1qxq47au29wss4g8acjk0zsmwwq0h34hhzump6stye9wuldm7nm0t6ad3jz9hy5v3smye0nvcumtzu43k7r36ag0w29qqdafvvk", "amount": [ { - "quantity": "2475000", - "unit": "lovelace" + "unit": "lovelace", + "quantity": "2475000" } - ] + ], + "output_index": 2, + "data_hash": null, + "inline_datum": null, + "collateral": false, + "reference_script_hash": null } ] -} +} \ No newline at end of file diff --git a/webhook.go b/webhook.go new file mode 100644 index 0000000..e358070 --- /dev/null +++ b/webhook.go @@ -0,0 +1,218 @@ +package blockfrost + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +type WebhookEventType string + +const ( + WebhookEventTypeBlock WebhookEventType = "block" + WebhookEventTypeDelegation WebhookEventType = "delegation" + WebhookEventTypeEpoch WebhookEventType = "epoch" + WebhookEventTypeTransaction WebhookEventType = "transaction" +) + +type TransactionPayload struct { + Tx Transaction `json:"tx"` + Inputs []TransactionInput `json:"inputs"` + Outputs []TransactionOutput `json:"outputs"` +} + +type StakeDelegationPayload struct { + Tx Transaction `json:"tx"` + Delegations []struct { + TransactionDelegation + Pool Pool `json:"pool"` + } +} + +type EpochPayload struct { + PreviousEpoch Epoch `json:"previous_epoch"` + CurrentEpoch struct { + Epoch int `json:"epoch"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + } `json:"current_epoch"` +} + +type WebhookEventCommon struct { + ID string `json:"id"` + WebhookID string `json:"webhook_id"` + Created int `json:"created"` + APIVersion int `json:"api_version,omitempty"` // omitempty because test fixtures do not include it +} + +type WebhookEventBlock struct { + WebhookEventCommon + Type string `json:"type"` // "block" + Payload Block `json:"payload"` +} + +type WebhookEventTransaction struct { + WebhookEventCommon + Type string `json:"type"` // "transaction" + Payload []TransactionPayload `json:"payload"` +} + +type WebhookEventEpoch struct { + WebhookEventCommon + Type string `json:"type"` // "epoch" + Payload EpochPayload `json:"payload"` +} + +type WebhookEventDelegation struct { + WebhookEventCommon + Type string `json:"type"` // "delegation" + Payload []StakeDelegationPayload `json:"payload"` +} + +type WebhookEvent interface{} + +const ( + // Signatures older than this will be rejected by ConstructEvent + DefaultTolerance time.Duration = 600 * time.Second + signingVersion string = "v1" +) + +var ( + ErrNotSigned error = errors.New("Missing blockfrost-signature header") + ErrInvalidHeader error = errors.New("Invalid blockfrost-signature header format") + ErrTooOld error = errors.New("Signature's timestamp is not within time tolerance") + ErrNoValidSignature error = errors.New("No valid signature") +) + +func computeSignature(t time.Time, payload []byte, secret string) []byte { + mac := hmac.New(sha256.New, []byte(secret)) + mac.Write([]byte(fmt.Sprintf("%d", t.Unix()))) + mac.Write([]byte(".")) + mac.Write(payload) + return mac.Sum(nil) +} + +type signedHeader struct { + timestamp time.Time + signatures [][]byte +} + +func parseSignatureHeader(header string) (*signedHeader, error) { + sh := &signedHeader{} + + if header == "" { + return sh, ErrNotSigned + } + + // Signed header looks like "t=1495999758,v1=ABC,v1=DEF,v0=GHI" + pairs := strings.Split(header, ",") + for _, pair := range pairs { + parts := strings.Split(pair, "=") + if len(parts) != 2 { + return sh, ErrInvalidHeader + } + + switch parts[0] { + case "t": + timestamp, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return sh, ErrInvalidHeader + } + sh.timestamp = time.Unix(timestamp, 0) + + case signingVersion: + sig, err := hex.DecodeString(parts[1]) + if err != nil { + continue // Ignore invalid signatures + } + + sh.signatures = append(sh.signatures, sig) + + default: + fmt.Printf("WARNING: Cannot parse part of the signature header, key \"%s\" is not supported by this version of Blockfrost SDK.\n", parts[0]) + continue // Ignore unknown parts of the header + } + } + + if len(sh.signatures) == 0 { + return sh, ErrNoValidSignature + } + + if sh.timestamp == (time.Time{}) { + return sh, ErrInvalidHeader + + } + + return sh, nil +} + +func VerifyWebhookSignature(payload []byte, header string, secret string) (WebhookEvent, error) { + return VerifyWebhookSignatureWithTolerance(payload, header, secret, DefaultTolerance) +} + +func VerifyWebhookSignatureWithTolerance(payload []byte, header string, secret string, tolerance time.Duration) (WebhookEvent, error) { + return verifyWebhookSignature(payload, header, secret, tolerance, true) +} + +func VerifyWebhookSignatureIgnoringTolerance(payload []byte, header string, secret string) (WebhookEvent, error) { + return verifyWebhookSignature(payload, header, secret, 0*time.Second, false) +} + +func verifyWebhookSignature(payload []byte, sigHeader string, secret string, tolerance time.Duration, enforceTolerance bool) (WebhookEvent, error) { + // First unmarshal into a generic map to inspect the type + var genericEvent map[string]interface{} + if err := json.Unmarshal(payload, &genericEvent); err != nil { + return nil, fmt.Errorf("failed to parse webhook body json: %s", err) + } + + // Determine the specific event type + eventType, ok := genericEvent["type"].(string) + if !ok { + return nil, errors.New("event type not found") + } + + var event WebhookEvent + + // Unmarshal into the specific event type based on the eventType + switch eventType { + case string(WebhookEventTypeBlock): + event = new(WebhookEventBlock) + case string(WebhookEventTypeTransaction): + event = new(WebhookEventTransaction) + case string(WebhookEventTypeEpoch): + event = new(WebhookEventEpoch) + case string(WebhookEventTypeDelegation): + event = new(WebhookEventDelegation) + default: + return nil, fmt.Errorf("unknown event type: %s", eventType) + } + + if err := json.Unmarshal(payload, &event); err != nil { + return nil, fmt.Errorf("failed to parse specific webhook event json: %s", err) + } + + header, err := parseSignatureHeader(sigHeader) + if err != nil { + return event, err + } + + expectedSignature := computeSignature(header.timestamp, payload, secret) + expiredTimestamp := time.Since(header.timestamp) > tolerance + if enforceTolerance && expiredTimestamp { + return event, ErrTooOld + } + + for _, sig := range header.signatures { + if hmac.Equal(expectedSignature, sig) { + return event, nil + } + } + + return event, ErrNoValidSignature +} diff --git a/webhook_test.go b/webhook_test.go new file mode 100644 index 0000000..1ce4144 --- /dev/null +++ b/webhook_test.go @@ -0,0 +1,75 @@ +package blockfrost_test + +import ( + "encoding/json" + "testing" + + "github.com/blockfrost/blockfrost-go" +) + +var validPayload string = `{"id":"47668401-c3a4-42d4-bac1-ad46515924a3","webhook_id":"cf68eb9c-635f-415e-a5a8-6233638f28d7","created":1650013856,"type":"block","payload":{"time":1650013853,"height":7126256,"hash":"f49521b67b440e5030adf124aee8f88881b7682ba07acf06c2781405b0f806a4","slot":58447562,"epoch":332,"epoch_slot":386762,"slot_leader":"pool1njjr0zn7uvydjy8067nprgwlyxqnznp9wgllfnag24nycgkda25","size":34617,"tx_count":13,"output":"13403118309871","fees":"4986390","block_vrf":"vrf_vk197w95j9alkwt8l4g7xkccknhn4pqwx65c5saxnn5ej3cpmps72msgpw69d","previous_block":"9e3f5bfc9f0be44cf6e14db9ed5f1efb6b637baff0ea1740bb6711786c724915","next_block":null,"confirmations":0}}` + +func TestVerifyWebhookSignature(t *testing.T) { + event, _ := blockfrost.VerifyWebhookSignatureIgnoringTolerance([]byte(validPayload), + // 2 signatures - first one is invalid, second one is valid + "t=1650013856,v1=abc,t=1650013856,v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e", + "59a1eb46-96f4-4f0b-8a03-b4d26e70593a") + + _, ok := event.(*blockfrost.WebhookEventBlock) + if !ok { + jsonData, _ := json.Marshal(event) + t.Fatalf("Invalid webhook type %s", jsonData) + } + + jsonData, err := json.Marshal(event) + if err != nil { + t.Fatalf("Error marshaling to JSON: %s", err) + } + + jsonString := string(jsonData) + + if jsonString != validPayload { + t.Fatalf("\nexpected %v \ngot %v", validPayload, jsonString) + + } +} +func TestVerifyWebhookSignatureOutOfTolerance(t *testing.T) { + _, err := blockfrost.VerifyWebhookSignature([]byte(validPayload), + "t=1650013856,v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e", + "59a1eb46-96f4-4f0b-8a03-b4d26e70593a") + + if err != blockfrost.ErrTooOld { + t.Fatalf("\nsuccess expected %v \ngot %v", blockfrost.ErrTooOld, err) + } + +} +func TestVerifyWebhookSignatureInvalidHeader(t *testing.T) { + _, err := blockfrost.VerifyWebhookSignature([]byte(validPayload), + "v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e", + "59a1eb46-96f4-4f0b-8a03-b4d26e70593a") + + if err != blockfrost.ErrInvalidHeader { + t.Fatalf("\nsuccess expected %v \ngot %v", blockfrost.ErrInvalidHeader, err) + } + +} +func TestVerifyWebhookSignatureNoSupportedSchema(t *testing.T) { + _, err := blockfrost.VerifyWebhookSignature([]byte(validPayload), + "v42=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e", + "59a1eb46-96f4-4f0b-8a03-b4d26e70593a") + + if err != blockfrost.ErrNoValidSignature { + t.Fatalf("\nsuccess expected %v \ngot %v", blockfrost.ErrNoValidSignature, err) + } + +} +func TestVerifyWebhookSignatureNoHeader(t *testing.T) { + _, err := blockfrost.VerifyWebhookSignature([]byte(validPayload), + "", + "59a1eb46-96f4-4f0b-8a03-b4d26e70593a") + + if err != blockfrost.ErrNotSigned { + t.Fatalf("\nsuccess expected %v \ngot %v", blockfrost.ErrNotSigned, err) + } + +}