Skip to content

Commit

Permalink
Merge #4351
Browse files Browse the repository at this point in the history
4351: Transactions on JSON-RPC server r=Fraser999 a=Fraser999

This PR upgrades the JSON-RPC server to handle `Transaction`s while leaving the legacy `Deploy` interface unchanged.

The new RPCs are `account_put_transaction` and `info_get_transaction` with similar behaviour and semantics as `account_put_deploy` and `info_get_deploy` respectively.

The speculative execution server was also updated, with a new endpoint `speculative_exec_txn` taking a `Transaction` while leaving the original `speculative_exec` unchanged.

This PR also includes an attempt to unify the approaches to handling versioned data in storage.  I extended the notion of the `VersionedDatabases` to try and make the usage of these less accident-prone, making the actual DBs private and exposing methods which ensure old, pre-existing data in the `legacy` DB is exclusively using legacy types and bincode encoding, while new data is held in the `current` DB and uses bytesrepr-encoding of the keys and values.  In doing this, most of the methods in `storage/write_block.rs` were deleted, so I moved the couple of remaining methods back into `storage.rs` and deleted that file.  I also moved the `Config` and `Event` types unchanged to their own files to try and declutter storage.rs a little.

Closes [roadmap#185](casper-network/roadmap#185).

Co-authored-by: Fraser Hutchison <fraser@casperlabs.io>
  • Loading branch information
casperlabs-bors-ng[bot] and Fraser999 authored Oct 27, 2023
2 parents 00f7a48 + 88979d9 commit 10f1118
Show file tree
Hide file tree
Showing 33 changed files with 3,039 additions and 1,471 deletions.
32 changes: 20 additions & 12 deletions node/src/components/consensus/era_supervisor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
use tracing::{debug, error, info, trace, warn};

use casper_types::{
AsymmetricType, BlockHash, BlockHeader, Chainspec, ConsensusProtocolName, Deploy, DeployHash,
Digest, DisplayIter, EraId, PublicKey, RewardedSignatures, TimeDiff, Timestamp,
AsymmetricType, BlockHash, BlockHeader, Chainspec, ConsensusProtocolName, DeployHash, Digest,
DisplayIter, EraId, PublicKey, RewardedSignatures, TimeDiff, Timestamp, Transaction,
TransactionHash,
};

use crate::{
Expand Down Expand Up @@ -1359,18 +1360,18 @@ impl SerializedMessage {
}
}

async fn get_deploys<REv>(
async fn get_transactions<REv>(
effect_builder: EffectBuilder<REv>,
hashes: Vec<DeployHash>,
) -> Option<Vec<Deploy>>
hashes: Vec<TransactionHash>,
) -> Option<Vec<Transaction>>
where
REv: From<StorageRequest>,
{
effect_builder
.get_deploys_from_storage(hashes)
.get_transactions_from_storage(hashes)
.await
.into_iter()
.map(|maybe_deploy| maybe_deploy.map(|deploy| deploy.into_naive()))
.map(|maybe_transaction| maybe_transaction.map(|transaction| transaction.into_naive()))
.collect()
}

Expand All @@ -1386,28 +1387,35 @@ async fn execute_finalized_block<REv>(
.store_finalized_approvals(deploy_hash.into(), finalized_approvals.into())
.await;
}
// Get all deploys in order they appear in the finalized block.
let deploys = match get_deploys(
// Get all transactions in order they appear in the finalized block.
let transactions = match get_transactions(
effect_builder,
finalized_block
.deploy_and_transfer_hashes()
.cloned()
.map(|deploy_hash| TransactionHash::from(*deploy_hash))
.collect_vec(),
)
.await
{
Some(deploys) => deploys,
Some(transactions) => transactions,
None => {
fatal!(
effect_builder,
"Could not fetch deploys and transfers for finalized block: {:?}",
"Could not fetch transactions for finalized block: {:?}",
finalized_block
)
.await;
return;
}
};

let deploys = transactions
.into_iter()
.filter_map(|txn| match txn {
Transaction::Deploy(deploy) => Some(deploy),
Transaction::V1(_) => None,
})
.collect();
let executable_block =
ExecutableBlock::from_finalized_block_and_deploys(finalized_block, deploys);
effect_builder
Expand Down
22 changes: 14 additions & 8 deletions node/src/components/contract_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use casper_storage::{
use casper_types::{
bytesrepr::Bytes, package::PackageKindTag, BlockHash, BlockHeaderV2, Chainspec,
ChainspecRawBytes, ChainspecRegistry, Digest, EraId, Key, ProtocolVersion, Timestamp,
UpgradeConfig, U512,
Transaction, UpgradeConfig, U512,
};

use crate::{
Expand Down Expand Up @@ -674,19 +674,25 @@ impl ContractRuntime {
}
.ignore()
}
ContractRuntimeRequest::SpeculativeDeployExecution {
ContractRuntimeRequest::SpeculativelyExecute {
execution_prestate,
deploy,
transaction,
responder,
} => {
let deploy_item = match *transaction {
Transaction::Deploy(deploy) => DeployItem::from(deploy),
Transaction::V1(_) => {
return responder
.respond(Err(engine_state::Error::InvalidDeployItemVariant(
"temp error until EE handles transactions".to_string(),
)))
.ignore();
}
};
let engine_state = Arc::clone(&self.engine_state);
async move {
let result = run_intensive_task(move || {
execute_only(
engine_state.as_ref(),
execution_prestate,
DeployItem::from((*deploy).clone()),
)
execute_only(engine_state.as_ref(), execution_prestate, deploy_item)
})
.await;
responder.respond(result).await
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ impl FetchItem for BlockHeader {
}

fn validate(&self, _metadata: &EmptyValidationMetadata) -> Result<(), Self::ValidationError> {
// No need for further validation. The received header has necessarily had its hash
// computed to be the same value we used for the fetch ID if we got here.
Ok(())
}
}
Expand Down
24 changes: 8 additions & 16 deletions node/src/components/gossiper/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use tokio::time;
use tracing::debug;

use casper_types::{
testing::TestRng, BlockV2, Chainspec, ChainspecRawBytes, Deploy, EraId, FinalitySignature,
ProtocolVersion, TimeDiff, Transaction, TransactionV1,
testing::TestRng, BlockV2, Chainspec, ChainspecRawBytes, EraId, FinalitySignature,
ProtocolVersion, TimeDiff, Transaction,
};

use super::*;
Expand Down Expand Up @@ -332,14 +332,6 @@ impl NetworkedReactor for Reactor {
}
}

fn random_txn(rng: &mut TestRng) -> Transaction {
if rng.gen() {
Transaction::from(Deploy::random_valid_native_transfer(rng))
} else {
Transaction::from(TransactionV1::random(rng))
}
}

fn announce_transaction_received(
transaction: &Transaction,
) -> impl FnOnce(EffectBuilder<Event>) -> Effects<Event> {
Expand All @@ -359,7 +351,7 @@ async fn run_gossip(rng: &mut TestRng, network_size: usize, txn_count: usize) {

// Create `txn_count` random transactions.
let (all_txn_hashes, mut txns): (BTreeSet<_>, Vec<_>) = iter::repeat_with(|| {
let txn = random_txn(rng);
let txn = Transaction::random(rng);
(txn.hash(), txn)
})
.take(txn_count)
Expand Down Expand Up @@ -420,7 +412,7 @@ async fn should_get_from_alternate_source() {
let node_ids = network.add_nodes(rng, NETWORK_SIZE).await;

// Create random transaction.
let txn = random_txn(rng);
let txn = Transaction::random(rng);
let txn_hash = txn.hash();

// Give the transaction to nodes 0 and 1 to be gossiped.
Expand Down Expand Up @@ -498,7 +490,7 @@ async fn should_timeout_gossip_response() {
let mut node_ids = network.add_nodes(rng, infection_target as usize + 1).await;

// Create random transaction.
let txn = random_txn(rng);
let txn = Transaction::random(rng);
let txn_hash = txn.hash();

// Give the transaction to node 0 to be gossiped.
Expand Down Expand Up @@ -575,7 +567,7 @@ async fn should_timeout_new_item_from_peer() {
// received, no component triggers the `ItemReceived` event.
reactor_0.fake_transaction_acceptor.set_active(false);

let txn = random_txn(rng);
let txn = Transaction::random(rng);

// Give the transaction to node 1 to gossip to node 0.
network
Expand Down Expand Up @@ -631,7 +623,7 @@ async fn should_not_gossip_old_stored_item_again() {
let node_ids = network.add_nodes(rng, NETWORK_SIZE).await;
let node_0 = node_ids[0];

let txn = random_txn(rng);
let txn = Transaction::random(rng);

// Store the transaction on node 0.
let store_txn = |effect_builder: EffectBuilder<Event>| {
Expand Down Expand Up @@ -701,7 +693,7 @@ async fn should_ignore_unexpected_message(message_type: Unexpected) {
let node_ids = network.add_nodes(rng, NETWORK_SIZE).await;
let node_0 = node_ids[0];

let txn = Box::new(random_txn(rng));
let txn = Box::new(Transaction::random(rng));

let message = match message_type {
Unexpected::Response => Message::GossipResponse {
Expand Down
3 changes: 2 additions & 1 deletion node/src/components/rpc_server/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use casper_types::ProtocolVersion;

use super::{
rpcs::{
account::PutDeploy,
account::{PutDeploy, PutTransaction},
chain::{
GetBlock, GetBlockTransfers, GetEraInfoBySwitchBlock, GetEraSummary, GetStateRootHash,
},
Expand Down Expand Up @@ -37,6 +37,7 @@ pub(super) async fn run<REv: ReactorEventT>(
) {
let mut handlers = RequestHandlersBuilder::new();
PutDeploy::register_as_handler(effect_builder, api_version, &mut handlers);
PutTransaction::register_as_handler(effect_builder, api_version, &mut handlers);
GetBlock::register_as_handler(effect_builder, api_version, &mut handlers);
GetBlockTransfers::register_as_handler(effect_builder, api_version, &mut handlers);
GetStateRootHash::register_as_handler(effect_builder, api_version, &mut handlers);
Expand Down
82 changes: 81 additions & 1 deletion node/src/components/rpc_server/rpcs/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::debug;

use casper_types::{Deploy, DeployHash, ProtocolVersion, Transaction};
use casper_types::{Deploy, DeployHash, ProtocolVersion, Transaction, TransactionHash};

use super::{
docs::{DocExample, DOCS_EXAMPLE_PROTOCOL_VERSION},
Expand All @@ -24,6 +24,14 @@ static PUT_DEPLOY_RESULT: Lazy<PutDeployResult> = Lazy::new(|| PutDeployResult {
deploy_hash: *Deploy::doc_example().hash(),
});

static PUT_TRANSACTION_PARAMS: Lazy<PutTransactionParams> = Lazy::new(|| PutTransactionParams {
transaction: Transaction::doc_example().clone(),
});
static PUT_TRANSACTION_RESULT: Lazy<PutTransactionResult> = Lazy::new(|| PutTransactionResult {
api_version: DOCS_EXAMPLE_PROTOCOL_VERSION,
transaction_hash: Transaction::doc_example().hash(),
});

/// Params for "account_put_deploy" RPC request.
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -95,3 +103,75 @@ impl RpcWithParams for PutDeploy {
}
}
}

/// Params for "account_put_transaction" RPC request.
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PutTransactionParams {
/// The `Transaction`.
pub transaction: Transaction,
}

impl DocExample for PutTransactionParams {
fn doc_example() -> &'static Self {
&PUT_TRANSACTION_PARAMS
}
}

/// Result for "account_put_transaction" RPC response.
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PutTransactionResult {
/// The RPC API version.
#[schemars(with = "String")]
pub api_version: ProtocolVersion,
/// The transaction hash.
pub transaction_hash: TransactionHash,
}

impl DocExample for PutTransactionResult {
fn doc_example() -> &'static Self {
&PUT_TRANSACTION_RESULT
}
}

/// "account_put_transaction" RPC
pub struct PutTransaction {}

#[async_trait]
impl RpcWithParams for PutTransaction {
const METHOD: &'static str = "account_put_transaction";
type RequestParams = PutTransactionParams;
type ResponseResult = PutTransactionResult;

async fn do_handle_request<REv: ReactorEventT>(
effect_builder: EffectBuilder<REv>,
api_version: ProtocolVersion,
params: Self::RequestParams,
) -> Result<Self::ResponseResult, Error> {
let transaction_hash = params.transaction.hash();

let accept_transaction_result = effect_builder
.try_accept_transaction(params.transaction, None)
.await;

match accept_transaction_result {
Ok(_) => {
debug!(%transaction_hash, "transaction was stored");
let result = Self::ResponseResult {
api_version,
transaction_hash,
};
Ok(result)
}
Err(error) => {
debug!(
%transaction_hash,
%error,
"the transaction submitted by the client was invalid",
);
Err(Error::new(ErrorCode::InvalidTransaction, error.to_string()))
}
}
}
}
24 changes: 19 additions & 5 deletions node/src/components/rpc_server/rpcs/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use serde_json::{json, Value};
use casper_types::ProtocolVersion;

use super::{
account::PutDeploy,
account::{PutDeploy, PutTransaction},
chain::{
GetBlock, GetBlockTransfers, GetEraInfoBySwitchBlock, GetEraSummary, GetStateRootHash,
},
info::{GetChainspec, GetDeploy, GetPeers, GetStatus, GetValidatorChanges},
info::{GetChainspec, GetDeploy, GetPeers, GetStatus, GetTransaction, GetValidatorChanges},
state::{
GetAccountInfo, GetAuctionInfo, GetBalance, GetDictionaryItem, GetItem, QueryBalance,
QueryGlobalState,
Expand Down Expand Up @@ -65,8 +65,16 @@ pub(crate) static OPEN_RPC_SCHEMA: Lazy<OpenRpcSchema> = Lazy::new(|| {
},
};

schema.push_with_params::<PutDeploy>("receives a Deploy to be executed by the network");
schema.push_with_params::<GetDeploy>("returns a Deploy from the network");
schema.push_with_params::<PutDeploy>(
"receives a Deploy to be executed by the network (DEPRECATED: use \
`account_put_transaction` instead)",
);
schema
.push_with_params::<PutTransaction>("receives a Transaction to be executed by the network");
schema.push_with_params::<GetDeploy>(
"returns a Deploy from the network (DEPRECATED: use `info_get_transaction` instead)",
);
schema.push_with_params::<GetTransaction>("returns a Transaction from the network");
schema.push_with_params::<GetAccountInfo>("returns an Account from the network");
schema.push_with_params::<GetDictionaryItem>("returns an item from a Dictionary");
schema.push_with_params::<QueryGlobalState>(
Expand Down Expand Up @@ -457,7 +465,7 @@ impl RpcWithoutParams for ListRpcs {

mod doc_example_impls {
use casper_types::{
account::Account, Deploy, EraEndV1, EraEndV2, EraReport, PublicKey, Timestamp,
account::Account, Deploy, EraEndV1, EraEndV2, EraReport, PublicKey, Timestamp, Transaction,
};

use super::DocExample;
Expand All @@ -468,6 +476,12 @@ mod doc_example_impls {
}
}

impl DocExample for Transaction {
fn doc_example() -> &'static Self {
Transaction::example()
}
}

impl DocExample for Account {
fn doc_example() -> &'static Self {
Account::example()
Expand Down
Loading

0 comments on commit 10f1118

Please sign in to comment.