diff --git a/rpc/client.go b/rpc/client.go index 11aff6c..2f10247 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -85,15 +85,24 @@ type ClientInformational interface { // If the param stateRootHash is nil, the client will make an additional RPC call to retrieve the latest stateRootHash. QueryGlobalStateByStateHash(ctx context.Context, stateRootHash *string, key string, path []string) (QueryGlobalStateResult, error) - // GetAccountInfoByBlochHash returns a JSON representation of an Account from the network. + // GetAccountInfoByBlockHash returns a JSON representation of an Account from the network. // The blockHash must refer to a Block after the Account's creation, or the method will return an empty response. - GetAccountInfoByBlochHash(ctx context.Context, blockHash string, pub keypair.PublicKey) (StateGetAccountInfo, error) - // GetAccountInfoByBlochHeight returns a JSON representation of an Account from the network. + GetAccountInfoByBlockHash(ctx context.Context, blockHash string, pub keypair.PublicKey) (StateGetAccountInfo, error) + // GetAccountInfoByBlockHeight returns a JSON representation of an Account from the network. // The blockHeight must refer to a Block after the Account's creation, or the method will return an empty response. - GetAccountInfoByBlochHeight(ctx context.Context, blockHeight uint64, pub keypair.PublicKey) (StateGetAccountInfo, error) + GetAccountInfoByBlockHeight(ctx context.Context, blockHeight uint64, pub keypair.PublicKey) (StateGetAccountInfo, error) // GetAccountInfo returns a JSON representation of an Account from the network. // This is the most generic interface. GetAccountInfo(ctx context.Context, blockIdentifier *ParamBlockIdentifier, accountIdentifier AccountIdentifier) (StateGetAccountInfo, error) + // GetPackageByBlockHeight returns a Package from the network by BlockHeight + // The blockHeight must refer to a Block after the Package's creation, or the method will return an empty response. + GetPackageByBlockHeight(ctx context.Context, packageHash string, blockHeight uint64) (StateGetPackage, error) + // GetPackageByBlockHash returns a Package from the network by BlockHash + // The blockHash must refer to a Block after the Package's creation, or the method will return an empty response. + GetPackageByBlockHash(ctx context.Context, packageHash string, blockHash string) (StateGetPackage, error) + // GetPackage returns a Package from the network + // This is the most generic interface. + GetPackage(ctx context.Context, packageIdentifier PackageIdentifier, blockIdentifier *ParamBlockIdentifier) (StateGetPackage, error) // GetLatestEntity returns latest AddressableEntity from the network. GetLatestEntity(ctx context.Context, entityIdentifier EntityIdentifier) (StateGetEntityResult, error) diff --git a/rpc/request.go b/rpc/request.go index d07226a..55d15b1 100644 --- a/rpc/request.go +++ b/rpc/request.go @@ -3,6 +3,7 @@ package rpc import ( "context" "strconv" + "strings" "github.com/make-software/casper-go-sdk/v2/types" "github.com/make-software/casper-go-sdk/v2/types/key" @@ -38,6 +39,7 @@ const ( MethodGetDictionaryItem Method = "state_get_dictionary_item" MethodGetStateBalance Method = "state_get_balance" MethodGetStateAccount Method = "state_get_account_info" + MethodGetStatePackage Method = "state_get_package" MethodGetStateEntity Method = "state_get_entity" MethodGetEraInfo Method = "chain_get_era_info_by_switch_block" MethodGetBlock Method = "chain_get_block" @@ -211,6 +213,27 @@ type SpeculativeExecParams struct { BlockIdentifier *BlockIdentifier `json:"block_identifier,omitempty"` } +type ParamStateGetPackage struct { + PackageIdentifier PackageIdentifier `json:"package_identifier"` + ParamBlockIdentifier +} + +type PackageIdentifier struct { + PackageAddr *string `json:"PackageAddr,omitempty"` + ContractPackageHash *string `json:"ContractPackageHash,omitempty"` +} + +func NewPackageIdentifierFromHash(hash string) PackageIdentifier { + if strings.HasPrefix(hash, "package-") { + return PackageIdentifier{ + PackageAddr: &hash, + } + } + return PackageIdentifier{ + ContractPackageHash: &hash, + } +} + type PurseIdentifier struct { MainPurseUnderPublicKey *keypair.PublicKey `json:"main_purse_under_public_key,omitempty"` MainPurseUnderAccountHash *key.AccountHash `json:"main_purse_under_account_hash,omitempty"` diff --git a/rpc/response.go b/rpc/response.go index 254e1e9..b7bec5d 100644 --- a/rpc/response.go +++ b/rpc/response.go @@ -42,6 +42,24 @@ func (b StateGetBalanceResult) GetRawJSON() json.RawMessage { return b.rawJSON } +type StateGetPackage struct { + ApiVersion string `json:"api_version"` + Package Package `json:"package"` + //MerkleProof is a construction created using a merkle trie that allows verification of the associated hashes. + MerkleProof json.RawMessage `json:"merkle_proof"` + + rawJSON json.RawMessage +} + +type Package struct { + ContractPackage *types.ContractPackage `json:"ContractPackage"` + Package *types.Package `json:"Package"` +} + +func (b StateGetPackage) GetRawJSON() json.RawMessage { + return b.rawJSON +} + type StateGetAccountInfo struct { ApiVersion string `json:"api_version"` Account types.Account `json:"account"` diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index 3bbe989..9a29bd8 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -253,7 +253,7 @@ func (c *client) GetEntityByBlockHeight(ctx context.Context, entityIdentifier En return result, nil } -func (c *client) GetAccountInfoByBlochHash(ctx context.Context, blockHash string, pub keypair.PublicKey) (StateGetAccountInfo, error) { +func (c *client) GetAccountInfoByBlockHash(ctx context.Context, blockHash string, pub keypair.PublicKey) (StateGetAccountInfo, error) { var result StateGetAccountInfo resp, err := c.processRequest(ctx, MethodGetStateAccount, ParamGetAccountInfoBalance{AccountIdentifier: pub.String(), ParamBlockIdentifier: NewParamBlockByHash(blockHash)}, &result) @@ -265,7 +265,7 @@ func (c *client) GetAccountInfoByBlochHash(ctx context.Context, blockHash string return result, nil } -func (c *client) GetAccountInfoByBlochHeight(ctx context.Context, blockHeight uint64, pub keypair.PublicKey) (StateGetAccountInfo, error) { +func (c *client) GetAccountInfoByBlockHeight(ctx context.Context, blockHeight uint64, pub keypair.PublicKey) (StateGetAccountInfo, error) { var result StateGetAccountInfo resp, err := c.processRequest(ctx, MethodGetStateAccount, ParamGetAccountInfoBalance{AccountIdentifier: pub.String(), ParamBlockIdentifier: NewParamBlockByHeight(blockHeight)}, &result) if err != nil { @@ -299,6 +299,45 @@ func (c *client) GetAccountInfo(ctx context.Context, blockIdentifier *ParamBlock return result, nil } +func (c *client) GetPackageByBlockHeight(ctx context.Context, packageHash string, blockHeight uint64) (StateGetPackage, error) { + var result StateGetPackage + + resp, err := c.processRequest(ctx, MethodGetStatePackage, ParamStateGetPackage{PackageIdentifier: NewPackageIdentifierFromHash(packageHash), ParamBlockIdentifier: NewParamBlockByHeight(blockHeight)}, &result) + if err != nil { + return StateGetPackage{}, err + } + + result.rawJSON = resp.Result + return result, nil +} + +func (c *client) GetPackageByBlockHash(ctx context.Context, packageHash string, blockHash string) (StateGetPackage, error) { + var result StateGetPackage + + resp, err := c.processRequest(ctx, MethodGetStatePackage, ParamStateGetPackage{PackageIdentifier: NewPackageIdentifierFromHash(packageHash), ParamBlockIdentifier: NewParamBlockByHash(blockHash)}, &result) + if err != nil { + return StateGetPackage{}, err + } + + result.rawJSON = resp.Result + return result, nil +} + +func (c *client) GetPackage(ctx context.Context, packageIdentifier PackageIdentifier, blockIdentifier *ParamBlockIdentifier) (StateGetPackage, error) { + if blockIdentifier == nil { + blockIdentifier = &ParamBlockIdentifier{} + } + + var result StateGetPackage + resp, err := c.processRequest(ctx, MethodGetStatePackage, ParamStateGetPackage{PackageIdentifier: packageIdentifier, ParamBlockIdentifier: *blockIdentifier}, &result) + if err != nil { + return StateGetPackage{}, err + } + + result.rawJSON = resp.Result + return result, nil +} + func (c *client) GetDictionaryItem(ctx context.Context, stateRootHash *string, uref, key string) (StateGetDictionaryResult, error) { return c.GetDictionaryItemByIdentifier(ctx, stateRootHash, ParamDictionaryIdentifier{ URef: &ParamDictionaryIdentifierURef{ diff --git a/tests/data/package/get-package-result-contract-package-v200.json b/tests/data/package/get-package-result-contract-package-v200.json new file mode 100644 index 0000000..d794340 --- /dev/null +++ b/tests/data/package/get-package-result-contract-package-v200.json @@ -0,0 +1,23 @@ +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "api_version": "2.0.0", + "package": { + "ContractPackage": { + "access_key": "uref-6fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e0-007", + "versions": [ + { + "protocol_version_major": 2, + "contract_version": 1, + "contract_hash": "contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1" + } + ], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + }, + "merkle_proof": "010000000116aa0d0ad73d3534ba543a574f2a521fd770e4cb436ca0741e8a02eae8fdbf08046fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e00701000000020000000100000025aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1000000000000000000020000000016080000002500309ea1908b14a258eb4246b0b4cf2181f9a079e97571bb5cf5edf2cc6b1dd26848001b5a6e381c3fd5e9983555281b928e75ee6e6b17eb4ca779581b2073313e9e7d5300162d2614d188e62d6b8d8148b9455feedb784c39230ef2df6610f4706da05a466300d25c1b5580816475df74470a0c397baf8c4c5ea1016f9742414fc145d048aca3a600f91a5d4a604d79b244b83c89716caf516e75e951f4e32995101916fdeea30721ae00a77ba4e92715b5285ff5bbda6cfc1a75fb6160d9f8c237abe9c7eef3aa0d448abb0032729b6868c15a80e2315f291020073922c81a445f3220e6214262823e7690a7e3000264f63a177c0d3bdf4501f85fc99ef7a25335f15cf01c7e5092e101eb13c8810001090000000001a3595f9802a8c4ecc4f74cbbcf6ea4cd484fb643ee6ed49f6d6a8ad4816f123002013ba7feb38dc3c94f0fc6dd8eefa88b87e0a6d0fb6372d8c166c0861812efa0970601fbbff6ca6374c82315ce70bd07ad0b6e503aab81fb9eedf4eee2806cdb44f5310a0062e02bef4f516c7bf650d0e7a7f92c3278515b193eb2067cea0b40f8208626d40b00b7aac0f3f6692862b38c63e0df2a381945f48c00f7dba20994dde801b102e3eb0d004cc57d085abe7a34c1c509178c52296cedf995770f0023a195cbf3b713494a9c0e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f01951506a8635a859363abd1c96f88d1b972d2b8be5b8620c9e01d9cf0933a477b1500cb808b7d2846894f16ea158ab0df1e92b8ce3bf9e19d1e06e66662f424906cb9" + } +} diff --git a/tests/data/package/get-package-result-package-v200.json b/tests/data/package/get-package-result-package-v200.json new file mode 100644 index 0000000..f5ac12e --- /dev/null +++ b/tests/data/package/get-package-result-package-v200.json @@ -0,0 +1,24 @@ +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "api_version": "2.0.0", + "package": { + "Package": { + "versions": [ + { + "entity_version_key": { + "protocol_version_major": 2, + "entity_version": 1 + }, + "addressable_entity_hash": "addressable-entity-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9" + } + ], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + }, + "merkle_proof": "010000000116aa0d0ad73d3534ba543a574f2a521fd770e4cb436ca0741e8a02eae8fdbf08046fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e00701000000020000000100000025aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1000000000000000000020000000016080000002500309ea1908b14a258eb4246b0b4cf2181f9a079e97571bb5cf5edf2cc6b1dd26848001b5a6e381c3fd5e9983555281b928e75ee6e6b17eb4ca779581b2073313e9e7d5300162d2614d188e62d6b8d8148b9455feedb784c39230ef2df6610f4706da05a466300d25c1b5580816475df74470a0c397baf8c4c5ea1016f9742414fc145d048aca3a600f91a5d4a604d79b244b83c89716caf516e75e951f4e32995101916fdeea30721ae00a77ba4e92715b5285ff5bbda6cfc1a75fb6160d9f8c237abe9c7eef3aa0d448abb0032729b6868c15a80e2315f291020073922c81a445f3220e6214262823e7690a7e3000264f63a177c0d3bdf4501f85fc99ef7a25335f15cf01c7e5092e101eb13c8810001090000000001a3595f9802a8c4ecc4f74cbbcf6ea4cd484fb643ee6ed49f6d6a8ad4816f123002013ba7feb38dc3c94f0fc6dd8eefa88b87e0a6d0fb6372d8c166c0861812efa0970601fbbff6ca6374c82315ce70bd07ad0b6e503aab81fb9eedf4eee2806cdb44f5310a0062e02bef4f516c7bf650d0e7a7f92c3278515b193eb2067cea0b40f8208626d40b00b7aac0f3f6692862b38c63e0df2a381945f48c00f7dba20994dde801b102e3eb0d004cc57d085abe7a34c1c509178c52296cedf995770f0023a195cbf3b713494a9c0e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f01951506a8635a859363abd1c96f88d1b972d2b8be5b8620c9e01d9cf0933a477b1500cb808b7d2846894f16ea158ab0df1e92b8ce3bf9e19d1e06e66662f424906cb9" + } +} diff --git a/tests/rpc/rpc_client_test.go b/tests/rpc/rpc_client_test.go index 41822c0..afcffc0 100644 --- a/tests/rpc/rpc_client_test.go +++ b/tests/rpc/rpc_client_test.go @@ -89,6 +89,37 @@ func Test_DefaultClient_GetDeploy_Example(t *testing.T) { } } +func Test_DefaultClient_GetPackage_Example(t *testing.T) { + tests := []struct { + filePath string + isPackage bool + }{ + { + filePath: "../data/package/get-package-result-contract-package-v200.json", + }, + { + filePath: "../data/package/get-package-result-package-v200.json", + isPackage: true, + }, + } + for _, tt := range tests { + t.Run("GetTransaction", func(t *testing.T) { + server := SetupServer(t, tt.filePath) + defer server.Close() + client := casper.NewRPCClient(casper.NewRPCHandler(server.URL, http.DefaultClient)) + result, err := client.GetPackageByBlockHeight(context.Background(), "package-0009ea4441f4700325d9c38b0b6df415537596e1204abe4f6a94b6996aebf2f1", 0) + require.NoError(t, err) + require.NotEmpty(t, result) + + if tt.isPackage { + require.NotEmpty(t, result.Package.Package) + } else { + require.NotEmpty(t, result.Package.ContractPackage) + } + }) + } +} + func Test_DefaultClient_GetTransaction_Example(t *testing.T) { tests := []struct { filePath string @@ -467,7 +498,7 @@ func Test_DefaultClient_GetAccountInfoByBlochHash(t *testing.T) { pubKey, err := casper.NewPublicKey("01018525deae6091abccab6704a0fa44e12c495eec9e8fe6929862e1b75580e715") require.NoError(t, err) blockHash := "bf06bdb1616050cea5862333d1f4787718f1011c95574ba92378419eefeeee59" - res, err := client.GetAccountInfoByBlochHash(context.Background(), blockHash, pubKey) + res, err := client.GetAccountInfoByBlockHash(context.Background(), blockHash, pubKey) require.NoError(t, err) assert.Equal(t, "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c", res.Account.AccountHash.ToPrefixedString()) } @@ -478,7 +509,7 @@ func Test_DefaultClient_GetAccountInfoByBlochHeight(t *testing.T) { client := casper.NewRPCClient(casper.NewRPCHandler(server.URL, http.DefaultClient)) pubKey, err := casper.NewPublicKey("01018525deae6091abccab6704a0fa44e12c495eec9e8fe6929862e1b75580e715") require.NoError(t, err) - res, err := client.GetAccountInfoByBlochHeight(context.Background(), 185, pubKey) + res, err := client.GetAccountInfoByBlockHeight(context.Background(), 185, pubKey) require.NoError(t, err) assert.Equal(t, "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c", res.Account.AccountHash.ToPrefixedString()) }