diff --git a/book/src/jsonrpc-api.md b/book/src/jsonrpc-api.md index 19d93aeba0..9ee575e596 100644 --- a/book/src/jsonrpc-api.md +++ b/book/src/jsonrpc-api.md @@ -25,6 +25,7 @@ Methods * [getAccountInfo](#getaccountinfo) * [getBalance](#getbalance) * [getClusterNodes](#getclusternodes) +* [getProgramAccounts](#getprogramaccounts) * [getRecentBlockhash](#getrecentblockhash) * [getSignatureStatus](#getsignaturestatus) * [getSlotLeader](#getslotleader) @@ -96,6 +97,32 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, " {"jsonrpc":"2.0","result":true,"id":1} ``` +--- + +### getAccountInfo +Returns all information associated with the account of provided Pubkey + +##### Parameters: +* `string` - Pubkey of account to query, as base-58 encoded string + +##### Results: +The result field will be a JSON object with the following sub fields: + +* `lamports`, number of lamports assigned to this account, as a signed 64-bit integer +* `owner`, array of 32 bytes representing the program this account has been assigned to +* `data`, array of bytes representing any data associated with the account +* `executable`, boolean indicating if the account contains a program (and is strictly read-only) + +##### Example: +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getAccountInfo", "params":["2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"]}' http://localhost:8899 + +// Result +{"jsonrpc":"2.0","result":{"executable":false,"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"id":1} +``` + + --- ### getBalance @@ -142,28 +169,29 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, " --- -### getAccountInfo -Returns all information associated with the account of provided Pubkey +### getProgramAccounts +Returns all accounts owned by the provided program Pubkey ##### Parameters: -* `string` - Pubkey of account to query, as base-58 encoded string +* `string` - Pubkey of program, as base-58 encoded string ##### Results: -The result field will be a JSON object with the following sub fields: +The result field will be an array of arrays. Each sub array will contain: +* `string` - a the account Pubkey as base-58 encoded string +and a JSON object, with the following sub fields: * `lamports`, number of lamports assigned to this account, as a signed 64-bit integer * `owner`, array of 32 bytes representing the program this account has been assigned to * `data`, array of bytes representing any data associated with the account * `executable`, boolean indicating if the account contains a program (and is strictly read-only) -* `loader`, array of 32 bytes representing the loader for this program (if `executable`), otherwise all ##### Example: ```bash // Request -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getAccountInfo", "params":["2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"]}' http://localhost:8899 +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getProgramAccounts", "params":["8nQwAgzN2yyUzrukXsCa3JELBYqDQrqJ3UyHiWazWxHR"]}' http://localhost:8899 // Result -{"jsonrpc":"2.0","result":{"executable":false,"loader":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"id":1} +{"jsonrpc":"2.0","result":[["BqGKYtAKu69ZdWEBtZHh4xgJY1BYa2YBiBReQE3pe383", {"executable":false,"owner":[50,28,250,90,221,24,94,136,147,165,253,136,1,62,196,215,225,34,222,212,99,84,202,223,245,13,149,99,149,231,91,96],"lamports":1,"data":[]], ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"executable":false,"owner":[50,28,250,90,221,24,94,136,147,165,253,136,1,62,196,215,225,34,222,212,99,84,202,223,245,13,149,99,149,231,91,96],"lamports":10,"data":[]]]},"id":1} ``` --- @@ -402,7 +430,7 @@ for a given account public key changes ##### Notification Format: ```bash -{"jsonrpc": "2.0","method": "accountNotification", "params": {"result": {"executable":false,"loader":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"subscription":0}} +{"jsonrpc": "2.0","method": "accountNotification", "params": {"result": {"executable":false,"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"subscription":0}} ``` --- diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a5504be051..b31dcfb6fc 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -70,6 +70,15 @@ impl JsonRpcRequestProcessor { .ok_or_else(Error::invalid_request) } + pub fn get_program_accounts(&self, program_id: &Pubkey) -> Result> { + Ok(self + .bank() + .get_program_accounts_modified_since_parent(&program_id) + .into_iter() + .map(|(pubkey, account)| (pubkey.to_string(), account)) + .collect()) + } + pub fn get_balance(&self, pubkey: &Pubkey) -> u64 { self.bank().get_balance(&pubkey) } @@ -210,6 +219,9 @@ pub trait RpcSol { #[rpc(meta, name = "getAccountInfo")] fn get_account_info(&self, _: Self::Metadata, _: String) -> Result; + #[rpc(meta, name = "getProgramAccounts")] + fn get_program_accounts(&self, _: Self::Metadata, _: String) -> Result>; + #[rpc(meta, name = "getBalance")] fn get_balance(&self, _: Self::Metadata, _: String) -> Result; @@ -297,6 +309,19 @@ impl RpcSol for RpcSolImpl { .get_account_info(&pubkey) } + fn get_program_accounts( + &self, + meta: Self::Metadata, + id: String, + ) -> Result> { + debug!("get_program_accounts rpc request received: {:?}", id); + let program_id = verify_pubkey(id)?; + meta.request_processor + .read() + .unwrap() + .get_program_accounts(&program_id) + } + fn get_balance(&self, meta: Self::Metadata, id: String) -> Result { debug!("get_balance rpc request received: {:?}", id); let pubkey = verify_pubkey(id)?; @@ -535,7 +560,7 @@ mod tests { fn start_rpc_handler_with_tx( pubkey: &Pubkey, - ) -> (MetaIoHandler, Meta, Hash, Keypair, Pubkey) { + ) -> (MetaIoHandler, Meta, Arc, Hash, Keypair, Pubkey) { let (bank_forks, alice) = new_bank_forks(); let bank = bank_forks.read().unwrap().working_bank(); let exit = Arc::new(AtomicBool::new(false)); @@ -567,7 +592,7 @@ mod tests { request_processor, cluster_info, }; - (io, meta, blockhash, alice, leader.id) + (io, meta, bank, blockhash, alice, leader.id) } #[test] @@ -595,7 +620,8 @@ mod tests { #[test] fn test_rpc_get_balance() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#, @@ -613,7 +639,8 @@ mod tests { #[test] fn test_rpc_get_cluster_nodes() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getClusterNodes"}}"#); let res = io.handle_request_sync(&req, meta); @@ -633,7 +660,8 @@ mod tests { #[test] fn test_rpc_get_slot_leader() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getSlotLeader"}}"#); let res = io.handle_request_sync(&req, meta); @@ -649,7 +677,8 @@ mod tests { #[test] fn test_rpc_get_tx_count() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getTransactionCount"}}"#); let res = io.handle_request_sync(&req, meta); @@ -664,7 +693,8 @@ mod tests { #[test] fn test_rpc_get_total_supply() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getTotalSupply"}}"#); let rep = io.handle_request_sync(&req, meta); @@ -689,7 +719,8 @@ mod tests { #[test] fn test_rpc_get_account_info() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}"]}}"#, @@ -713,10 +744,46 @@ mod tests { assert_eq!(expected, result); } + #[test] + fn test_rpc_get_program_accounts() { + let bob = Keypair::new(); + let (io, meta, bank, blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob.pubkey()); + + let new_program_id = Pubkey::new_rand(); + let tx = system_transaction::assign(&bob, blockhash, &new_program_id); + bank.process_transaction(&tx).unwrap(); + let req = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"getProgramAccounts","params":["{}"]}}"#, + new_program_id + ); + let res = io.handle_request_sync(&req, meta); + let expected = format!( + r#"{{ + "jsonrpc":"2.0", + "result":[["{}", {{ + "owner": {:?}, + "lamports": 20, + "data": [], + "executable": false + }}]], + "id":1}} + "#, + bob.pubkey(), + new_program_id.as_ref() + ); + let expected: Response = + serde_json::from_str(&expected).expect("expected response deserialization"); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(expected, result); + } + #[test] fn test_rpc_confirm_tx() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, blockhash, alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, blockhash, alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash); let req = format!( @@ -735,7 +802,8 @@ mod tests { #[test] fn test_rpc_get_signature_status() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, blockhash, alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, blockhash, alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash); let req = format!( @@ -799,7 +867,8 @@ mod tests { #[test] fn test_rpc_get_recent_blockhash() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getRecentBlockhash"}}"#); let res = io.handle_request_sync(&req, meta); @@ -825,7 +894,8 @@ mod tests { #[test] fn test_rpc_fail_request_airdrop() { let bob_pubkey = Pubkey::new_rand(); - let (io, meta, _blockhash, _alice, _leader_pubkey) = start_rpc_handler_with_tx(&bob_pubkey); + let (io, meta, _bank, _blockhash, _alice, _leader_pubkey) = + start_rpc_handler_with_tx(&bob_pubkey); // Expect internal error because no drone is available let req = format!(